Merge m-c to fx-team. a=merge
Merge m-c to fx-team. a=merge
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1037,8 +1037,11 @@ pref("services.mobileid.server.uri", "ht
// Enable mapped array buffer.
#ifndef XP_WIN
pref("dom.mapped_arraybuffer.enabled", true);
#endif
// UDPSocket API
pref("dom.udpsocket.enabled", true);
+
+// Enable TV Manager API
+pref("dom.tv.enabled", true);
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
<!--original fetch url was git://codeaurora.org/-->
<remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
<!--original fetch url was https://git.mozilla.org/releases-->
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<!-- B2G specific things. -->
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
- <project name="gaia" path="gaia" remote="mozillaorg" revision="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
+ <project name="gaia" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<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="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
--- 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="70eb0cb0977d6295e7da8896f9efb9f3ca1c13ea">
<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="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
+ <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
<!--original fetch url was https://git.mozilla.org/releases-->
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<!-- B2G specific things. -->
<project name="platform_build" path="build" remote="b2g" revision="8986df0f82e15ac2798df0b6c2ee3435400677ac">
<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="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
+ <project name="gaia" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
<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="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
- <project name="gaia" path="gaia" remote="mozillaorg" revision="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
+ <project name="gaia" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<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="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
--- 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="70eb0cb0977d6295e7da8896f9efb9f3ca1c13ea">
<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="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
+ <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/releng-flame-kk.tt
+++ b/b2g/config/flame-kk/releng-flame-kk.tt
@@ -1,9 +1,8 @@
[
{
-"size": 120750384,
-"digest": "0e0a0b0dcca020e3283ce8deb33d0eed48fab16ef2fd919120bd7b5abba00713210be17f466d11bf77cca3c9e3b663805be61774476cc669f0a75736d901edfd",
+"size": 91247216,
+"digest": "2b4be549f98695488ea7288d9e7f8ac0fa45112bedefa485a6e016c4af73fa21bb6b3992beda516f268417207c5deb57afad3959d3b1fbd07d5269b3a6be6a27",
"algorithm": "sha512",
-"filename": "backup-flame.tar.xz",
-"comment": "v188-1"
+"filename": "backup-flame.tar.xz"
}
]
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
<!--original fetch url was git://codeaurora.org/-->
<remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
<!--original fetch url was https://git.mozilla.org/releases-->
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<!-- B2G specific things. -->
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
- <project name="gaia" path="gaia" remote="mozillaorg" revision="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
+ <project name="gaia" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<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="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
<!--original fetch url was https://git.mozilla.org/releases-->
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<!-- B2G specific things. -->
<project name="platform_build" path="build" remote="b2g" revision="8986df0f82e15ac2798df0b6c2ee3435400677ac">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
- <project name="gaia" path="gaia" remote="mozillaorg" revision="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
+ <project name="gaia" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
<project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
{
"git": {
"git_revision": "",
"remote": "",
"branch": ""
},
- "revision": "ab5fb977047100ac985402e022d24da1dea68f24",
+ "revision": "4830af61b34d751c57da15abfc2efcda571b12c0",
"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="70eb0cb0977d6295e7da8896f9efb9f3ca1c13ea">
<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="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
+ <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
<!-- 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="70eb0cb0977d6295e7da8896f9efb9f3ca1c13ea">
<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="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
+ <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
<project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
<!--original fetch url was https://git.mozilla.org/releases-->
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<!-- B2G specific things. -->
<project name="platform_build" path="build" remote="b2g" revision="8986df0f82e15ac2798df0b6c2ee3435400677ac">
<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="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
+ <project name="gaia" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
<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="70eb0cb0977d6295e7da8896f9efb9f3ca1c13ea">
<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="a9a847920b51b79c4ad4ad339f0a005777a6228c"/>
+ <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0dfc1996eb583c8b507a82bf6b8319624bba23ea"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
<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/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -192,16 +192,17 @@
@BINPATH@/components/dom_sidebar.xpt
@BINPATH@/components/dom_cellbroadcast.xpt
@BINPATH@/components/dom_mobilemessage.xpt
@BINPATH@/components/dom_storage.xpt
@BINPATH@/components/dom_stylesheets.xpt
@BINPATH@/components/dom_telephony.xpt
@BINPATH@/components/dom_threads.xpt
@BINPATH@/components/dom_traversal.xpt
+@BINPATH@/components/dom_tv.xpt
@BINPATH@/components/dom_views.xpt
@BINPATH@/components/dom_voicemail.xpt
#ifdef MOZ_WEBSPEECH
@BINPATH@/components/dom_webspeechrecognition.xpt
#endif
@BINPATH@/components/dom_xbl.xpt
@BINPATH@/components/dom_xpath.xpt
@BINPATH@/components/dom_xul.xpt
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -1,26 +1,26 @@
[DEFAULT]
-skip-if = e10s # Bug ?????? - devtools tests disabled with e10s
subsuite = devtools
support-files =
addon1.xpi
addon2.xpi
addon3.xpi
addon4.xpi
addon5.xpi
code_binary_search.coffee
code_binary_search.js
code_binary_search.map
code_blackboxing_blackboxme.js
code_blackboxing_one.js
code_blackboxing_three.js
code_blackboxing_two.js
code_breakpoints-break-on-last-line-of-script-on-reload.js
code_breakpoints-other-tabs.js
+ code_frame-script.js
code_function-search-01.js
code_function-search-02.js
code_function-search-03.js
code_location-changes.js
code_math.js
code_math.map
code_math.min.js
code_math_bogus_map.js
@@ -93,238 +93,457 @@ support-files =
doc_watch-expressions.html
doc_watch-expression-button.html
doc_with-frame.html
head.js
sjs_random-javascript.sjs
testactors.js
[browser_dbg_aaa_run_first_leaktest.js]
+skip-if = e10s
[browser_dbg_addonactor.js]
+skip-if = e10s
[browser_dbg_addon-sources.js]
+skip-if = e10s
[browser_dbg_addon-modules.js]
+skip-if = e10s
[browser_dbg_addon-modules-unpacked.js]
+skip-if = e10s
[browser_dbg_addon-panels.js]
+skip-if = e10s
[browser_dbg_addon-console.js]
-skip-if = os == 'win' # bug 1005274
+skip-if = e10s || os == 'win' # bug 1005274
[browser_dbg_auto-pretty-print-01.js]
+skip-if = e10s
[browser_dbg_auto-pretty-print-02.js]
+skip-if = e10s
[browser_dbg_bfcache.js]
+skip-if = e10s
[browser_dbg_blackboxing-01.js]
+skip-if = e10s && debug
[browser_dbg_blackboxing-02.js]
+skip-if = e10s && debug
[browser_dbg_blackboxing-03.js]
+skip-if = e10s && debug
[browser_dbg_blackboxing-04.js]
+skip-if = e10s && debug
[browser_dbg_blackboxing-05.js]
+skip-if = e10s && debug
[browser_dbg_blackboxing-06.js]
+skip-if = e10s && debug
[browser_dbg_breadcrumbs-access.js]
+skip-if = e10s
[browser_dbg_break-on-dom-01.js]
+skip-if = e10s
[browser_dbg_break-on-dom-02.js]
+skip-if = e10s
[browser_dbg_break-on-dom-03.js]
+skip-if = e10s
[browser_dbg_break-on-dom-04.js]
+skip-if = e10s
[browser_dbg_break-on-dom-05.js]
+skip-if = e10s
[browser_dbg_break-on-dom-06.js]
+skip-if = e10s
[browser_dbg_break-on-dom-07.js]
+skip-if = e10s
[browser_dbg_break-on-dom-08.js]
+skip-if = e10s
[browser_dbg_break-on-dom-event-01.js]
-skip-if = os == "mac" || e10s # Bug 895426
+skip-if = e10s || os == "mac" || e10s # Bug 895426
[browser_dbg_break-on-dom-event-02.js]
+skip-if = e10s
[browser_dbg_breakpoints-actual-location.js]
+skip-if = e10s
[browser_dbg_breakpoints-actual-location2.js]
+skip-if = e10s
[browser_dbg_breakpoints-break-on-last-line-of-script-on-reload.js]
+skip-if = e10s
[browser_dbg_breakpoints-button-01.js]
+skip-if = e10s
[browser_dbg_breakpoints-button-02.js]
+skip-if = e10s
[browser_dbg_breakpoints-contextmenu-add.js]
+skip-if = e10s
[browser_dbg_breakpoints-contextmenu.js]
+skip-if = e10s
[browser_dbg_breakpoints-disabled-reload.js]
+skip-if = e10s
[browser_dbg_breakpoints-editor.js]
+skip-if = e10s
[browser_dbg_breakpoints-highlight.js]
+skip-if = e10s
[browser_dbg_breakpoints-new-script.js]
+skip-if = e10s
[browser_dbg_breakpoints-other-tabs.js]
+skip-if = e10s
[browser_dbg_breakpoints-pane.js]
+skip-if = e10s
[browser_dbg_breakpoints-reload.js]
+skip-if = e10s
[browser_dbg_chrome-create.js]
+skip-if = e10s
[browser_dbg_chrome-debugging.js]
+skip-if = e10s
[browser_dbg_clean-exit-window.js]
skip-if = true # Bug 933950 (leaky test)
[browser_dbg_clean-exit.js]
+skip-if = e10s
[browser_dbg_closure-inspection.js]
+skip-if = e10s
[browser_dbg_cmd-blackbox.js]
+skip-if = e10s
[browser_dbg_cmd-break.js]
+skip-if = e10s
[browser_dbg_cmd-dbg.js]
+skip-if = e10s
[browser_dbg_conditional-breakpoints-01.js]
+skip-if = e10s
[browser_dbg_conditional-breakpoints-02.js]
+skip-if = e10s
[browser_dbg_conditional-breakpoints-03.js]
+skip-if = e10s
[browser_dbg_conditional-breakpoints-04.js]
+skip-if = e10s
[browser_dbg_server-conditional-bp-01.js]
+skip-if = e10s
[browser_dbg_server-conditional-bp-02.js]
+skip-if = e10s
[browser_dbg_server-conditional-bp-03.js]
+skip-if = e10s
[browser_dbg_server-conditional-bp-04.js]
+skip-if = e10s
[browser_dbg_controller-evaluate-01.js]
+skip-if = e10s
[browser_dbg_controller-evaluate-02.js]
+skip-if = e10s
[browser_dbg_debugger-statement.js]
+skip-if = e10s
[browser_dbg_editor-contextmenu.js]
+skip-if = e10s
[browser_dbg_editor-mode.js]
+skip-if = e10s
[browser_dbg_event-listeners-01.js]
+skip-if = e10s
[browser_dbg_event-listeners-02.js]
+skip-if = e10s
[browser_dbg_event-listeners-03.js]
+skip-if = e10s
[browser_dbg_file-reload.js]
+skip-if = e10s
[browser_dbg_function-display-name.js]
+skip-if = e10s
[browser_dbg_global-method-override.js]
+skip-if = e10s
[browser_dbg_globalactor.js]
+skip-if = e10s
[browser_dbg_hit-counts-01.js]
+skip-if = e10s
[browser_dbg_hit-counts-02.js]
+skip-if = e10s
[browser_dbg_host-layout.js]
+skip-if = e10s
[browser_dbg_iframes.js]
+skip-if = e10s
[browser_dbg_instruments-pane-collapse.js]
+skip-if = e10s
[browser_dbg_interrupts.js]
+skip-if = e10s
[browser_dbg_listaddons.js]
+skip-if = e10s
[browser_dbg_listtabs-01.js]
+skip-if = e10s
[browser_dbg_listtabs-02.js]
+skip-if = e10s
[browser_dbg_listtabs-03.js]
+skip-if = e10s
[browser_dbg_location-changes-01-simple.js]
+skip-if = e10s
[browser_dbg_location-changes-02-blank.js]
+skip-if = e10s
[browser_dbg_location-changes-03-new.js]
+skip-if = e10s
[browser_dbg_location-changes-04-breakpoint.js]
+skip-if = e10s
[browser_dbg_multiple-windows.js]
+skip-if = e10s
[browser_dbg_navigation.js]
+skip-if = e10s
[browser_dbg_no-page-sources.js]
+skip-if = e10s
[browser_dbg_on-pause-highlight.js]
+skip-if = e10s
[browser_dbg_on-pause-raise.js]
-skip-if = os == "linux" || e10s # Bug 888811 & bug 891176
+skip-if = e10s || os == "linux" || e10s # Bug 888811 & bug 891176
[browser_dbg_optimized-out-vars.js]
+skip-if = e10s
[browser_dbg_panel-size.js]
+skip-if = e10s
[browser_dbg_parser-01.js]
+skip-if = e10s
[browser_dbg_parser-02.js]
+skip-if = e10s
[browser_dbg_parser-03.js]
+skip-if = e10s
[browser_dbg_parser-04.js]
+skip-if = e10s
[browser_dbg_parser-05.js]
+skip-if = e10s
[browser_dbg_parser-06.js]
+skip-if = e10s
[browser_dbg_parser-07.js]
+skip-if = e10s
[browser_dbg_parser-08.js]
+skip-if = e10s
[browser_dbg_parser-09.js]
+skip-if = e10s
[browser_dbg_parser-10.js]
+skip-if = e10s
[browser_dbg_pause-exceptions-01.js]
+skip-if = e10s
[browser_dbg_pause-exceptions-02.js]
+skip-if = e10s
[browser_dbg_pause-resume.js]
+skip-if = e10s
[browser_dbg_pause-warning.js]
+skip-if = e10s
[browser_dbg_paused-keybindings.js]
+skip-if = e10s
[browser_dbg_pretty-print-01.js]
+skip-if = e10s
[browser_dbg_pretty-print-02.js]
+skip-if = e10s
[browser_dbg_pretty-print-03.js]
+skip-if = e10s
[browser_dbg_pretty-print-04.js]
+skip-if = e10s
[browser_dbg_pretty-print-05.js]
+skip-if = e10s
[browser_dbg_pretty-print-06.js]
+skip-if = e10s
[browser_dbg_pretty-print-07.js]
+skip-if = e10s
[browser_dbg_pretty-print-08.js]
+skip-if = e10s
[browser_dbg_pretty-print-09.js]
+skip-if = e10s
[browser_dbg_pretty-print-10.js]
+skip-if = e10s
[browser_dbg_pretty-print-11.js]
+skip-if = e10s
[browser_dbg_pretty-print-12.js]
+skip-if = e10s
[browser_dbg_pretty-print-13.js]
+skip-if = e10s
[browser_dbg_pretty-print-on-paused.js]
+skip-if = e10s
[browser_dbg_progress-listener-bug.js]
+skip-if = e10s
[browser_dbg_reload-preferred-script-01.js]
+skip-if = e10s
[browser_dbg_reload-preferred-script-02.js]
+skip-if = e10s
[browser_dbg_reload-preferred-script-03.js]
+skip-if = e10s
[browser_dbg_reload-same-script.js]
+skip-if = e10s
[browser_dbg_scripts-switching-01.js]
+skip-if = e10s
[browser_dbg_scripts-switching-02.js]
+skip-if = e10s
[browser_dbg_scripts-switching-03.js]
+skip-if = e10s
[browser_dbg_search-autofill-identifier.js]
+skip-if = e10s
[browser_dbg_search-basic-01.js]
+skip-if = e10s
[browser_dbg_search-basic-02.js]
+skip-if = e10s
[browser_dbg_search-basic-03.js]
+skip-if = e10s
[browser_dbg_search-basic-04.js]
+skip-if = e10s
[browser_dbg_search-global-01.js]
+skip-if = e10s
[browser_dbg_search-global-02.js]
+skip-if = e10s
[browser_dbg_search-global-03.js]
+skip-if = e10s
[browser_dbg_search-global-04.js]
+skip-if = e10s
[browser_dbg_search-global-05.js]
+skip-if = e10s
[browser_dbg_search-global-06.js]
+skip-if = e10s
[browser_dbg_search-popup-jank.js]
+skip-if = e10s
[browser_dbg_search-sources-01.js]
+skip-if = e10s
[browser_dbg_search-sources-02.js]
+skip-if = e10s
[browser_dbg_search-sources-03.js]
+skip-if = e10s
[browser_dbg_search-symbols.js]
+skip-if = e10s
[browser_dbg_searchbox-help-popup-01.js]
+skip-if = e10s
[browser_dbg_searchbox-help-popup-02.js]
+skip-if = e10s
[browser_dbg_searchbox-parse.js]
+skip-if = e10s
[browser_dbg_source-maps-01.js]
+skip-if = e10s
[browser_dbg_source-maps-02.js]
+skip-if = e10s
[browser_dbg_source-maps-03.js]
+skip-if = e10s
[browser_dbg_source-maps-04.js]
+skip-if = e10s
[browser_dbg_sources-cache.js]
+skip-if = e10s
[browser_dbg_sources-labels.js]
+skip-if = e10s
[browser_dbg_sources-sorting.js]
+skip-if = e10s
[browser_dbg_split-console-paused-reload.js]
+skip-if = e10s
[browser_dbg_stack-01.js]
+skip-if = e10s
[browser_dbg_stack-02.js]
+skip-if = e10s
[browser_dbg_stack-03.js]
+skip-if = e10s
[browser_dbg_stack-04.js]
+skip-if = e10s
[browser_dbg_stack-05.js]
+skip-if = e10s
[browser_dbg_stack-06.js]
+skip-if = e10s
[browser_dbg_stack-07.js]
+skip-if = e10s
[browser_dbg_step-out.js]
+skip-if = e10s
[browser_dbg_tabactor-01.js]
+skip-if = e10s
[browser_dbg_tabactor-02.js]
+skip-if = e10s
[browser_dbg_terminate-on-tab-close.js]
+skip-if = e10s
[browser_dbg_tracing-01.js]
+skip-if = e10s
[browser_dbg_tracing-02.js]
+skip-if = e10s
[browser_dbg_tracing-03.js]
+skip-if = e10s
[browser_dbg_tracing-04.js]
+skip-if = e10s
[browser_dbg_tracing-05.js]
+skip-if = e10s
[browser_dbg_tracing-06.js]
+skip-if = e10s
[browser_dbg_tracing-07.js]
+skip-if = e10s
[browser_dbg_tracing-08.js]
+skip-if = e10s
[browser_dbg_variables-view-01.js]
+skip-if = e10s
[browser_dbg_variables-view-02.js]
+skip-if = e10s
[browser_dbg_variables-view-03.js]
+skip-if = e10s
[browser_dbg_variables-view-04.js]
+skip-if = e10s
[browser_dbg_variables-view-05.js]
+skip-if = e10s
[browser_dbg_variables-view-06.js]
+skip-if = e10s
[browser_dbg_variables-view-accessibility.js]
+skip-if = e10s
[browser_dbg_variables-view-data.js]
+skip-if = e10s
[browser_dbg_variables-view-edit-cancel.js]
+skip-if = e10s
[browser_dbg_variables-view-edit-click.js]
-skip-if = (os == 'mac' || os == 'win') && (debug == false) # Bug 986166
+skip-if = e10s || (os == 'mac' || os == 'win') && (debug == false) # Bug 986166
[browser_dbg_variables-view-edit-getset-01.js]
+skip-if = e10s
[browser_dbg_variables-view-edit-getset-02.js]
+skip-if = e10s
[browser_dbg_variables-view-edit-value.js]
+skip-if = e10s
[browser_dbg_variables-view-edit-watch.js]
+skip-if = e10s
[browser_dbg_variables-view-filter-01.js]
+skip-if = e10s
[browser_dbg_variables-view-filter-02.js]
+skip-if = e10s
[browser_dbg_variables-view-filter-03.js]
+skip-if = e10s
[browser_dbg_variables-view-filter-04.js]
+skip-if = e10s
[browser_dbg_variables-view-filter-05.js]
+skip-if = e10s
[browser_dbg_variables-view-filter-pref.js]
+skip-if = e10s
[browser_dbg_variables-view-filter-searchbox.js]
+skip-if = e10s
[browser_dbg_variables-view-frame-parameters-01.js]
+skip-if = e10s
[browser_dbg_variables-view-frame-parameters-02.js]
+skip-if = e10s
[browser_dbg_variables-view-frame-parameters-03.js]
+skip-if = e10s
[browser_dbg_variables-view-frame-with.js]
+skip-if = e10s
[browser_dbg_variables-view-frozen-sealed-nonext.js]
-skip-if = buildapp == 'mulet'
+skip-if = e10s || buildapp == 'mulet'
[browser_dbg_variables-view-hide-non-enums.js]
+skip-if = e10s
[browser_dbg_variables-view-large-array-buffer.js]
+skip-if = e10s
[browser_dbg_variables-view-override-01.js]
+skip-if = e10s
[browser_dbg_variables-view-override-02.js]
+skip-if = e10s
[browser_dbg_variables-view-popup-01.js]
+skip-if = e10s
[browser_dbg_variables-view-popup-02.js]
+skip-if = e10s
[browser_dbg_variables-view-popup-03.js]
+skip-if = e10s
[browser_dbg_variables-view-popup-04.js]
+skip-if = e10s
[browser_dbg_variables-view-popup-05.js]
+skip-if = e10s
[browser_dbg_variables-view-popup-06.js]
+skip-if = e10s
[browser_dbg_variables-view-popup-07.js]
+skip-if = e10s
[browser_dbg_variables-view-popup-08.js]
+skip-if = e10s
[browser_dbg_variables-view-popup-09.js]
+skip-if = e10s
[browser_dbg_variables-view-popup-10.js]
+skip-if = e10s
[browser_dbg_variables-view-popup-11.js]
+skip-if = e10s
[browser_dbg_variables-view-popup-12.js]
+skip-if = e10s
[browser_dbg_variables-view-popup-13.js]
+skip-if = e10s
[browser_dbg_variables-view-popup-14.js]
+skip-if = e10s
[browser_dbg_variables-view-popup-15.js]
+skip-if = e10s
[browser_dbg_variables-view-popup-16.js]
+skip-if = e10s
[browser_dbg_variables-view-reexpand-01.js]
+skip-if = e10s
[browser_dbg_variables-view-reexpand-02.js]
+skip-if = e10s
[browser_dbg_variables-view-reexpand-03.js]
+skip-if = e10s
[browser_dbg_variables-view-webidl.js]
+skip-if = e10s
[browser_dbg_watch-expressions-01.js]
+skip-if = e10s
[browser_dbg_watch-expressions-02.js]
+skip-if = e10s
--- a/browser/devtools/debugger/test/browser_dbg_blackboxing-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_blackboxing-01.js
@@ -2,22 +2,21 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test that if we black box a source and then refresh, it is still black boxed.
*/
const TAB_URL = EXAMPLE_URL + "doc_binary_search.html";
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
function test() {
- initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+ initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
- gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
waitForSourceShown(gPanel, ".coffee")
.then(testBlackBoxSource)
.then(testBlackBoxReload)
.then(() => closeDebuggerAndFinish(gPanel))
.then(null, aError => {
@@ -43,12 +42,11 @@ function testBlackBoxReload() {
ok(bbButton.checked, "Should still be black boxed.");
ok(selectedSource.classList.contains("black-boxed"),
"'black-boxed' class should still be applied");
});
}
registerCleanupFunction(function() {
gTab = null;
- gDebuggee = null;
gPanel = null;
gDebugger = null;
});
--- a/browser/devtools/debugger/test/browser_dbg_blackboxing-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_blackboxing-02.js
@@ -4,23 +4,22 @@
/**
* Test that black boxed frames are compressed into a single frame on the stack
* view.
*/
const TAB_URL = EXAMPLE_URL + "doc_blackboxing.html";
const BLACKBOXME_URL = EXAMPLE_URL + "code_blackboxing_blackboxme.js"
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
let gFrames;
function test() {
- initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+ initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
- gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gFrames = gDebugger.DebuggerView.StackFrames;
waitForSourceShown(gPanel, BLACKBOXME_URL)
.then(testBlackBoxSource)
.then(testBlackBoxStack)
.then(() => resumeDebuggerThenCloseAndFinish(gPanel))
@@ -39,21 +38,18 @@ function testBlackBoxSource() {
function testBlackBoxStack() {
let finished = waitForSourceAndCaretAndScopes(gPanel, ".html", 21).then(() => {
is(gFrames.itemCount, 3,
"Should only get 3 frames.");
is(gDebugger.document.querySelectorAll(".dbg-stackframe-black-boxed").length, 1,
"And one of them should be the combined black boxed frames.");
});
- // Spin the event loop before causing the debuggee to pause, to allow
- // this function to return first.
- executeSoon(() => gDebuggee.runTest());
+ callInTab(gTab, "runTest");
return finished;
}
registerCleanupFunction(function() {
gTab = null;
- gDebuggee = null;
gPanel = null;
gDebugger = null;
gFrames = null;
});
--- a/browser/devtools/debugger/test/browser_dbg_blackboxing-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_blackboxing-03.js
@@ -4,36 +4,35 @@
/**
* Test that black boxed frames are compressed into a single frame on the stack
* view when we are already paused.
*/
const TAB_URL = EXAMPLE_URL + "doc_blackboxing.html";
const BLACKBOXME_URL = EXAMPLE_URL + "code_blackboxing_blackboxme.js"
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
let gFrames;
function test() {
- initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+ initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
- gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gFrames = gDebugger.DebuggerView.StackFrames;
waitForSourceAndCaretAndScopes(gPanel, ".html", 21)
.then(testBlackBoxStack)
.then(testBlackBoxSource)
.then(() => resumeDebuggerThenCloseAndFinish(gPanel))
.then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
- gDebuggee.runTest();
+ callInTab(gTab, "runTest");
});
}
function testBlackBoxStack() {
is(gFrames.itemCount, 6,
"Should get 6 frames.");
is(gDebugger.document.querySelectorAll(".dbg-stackframe-black-boxed").length, 0,
"And none of them are black boxed.");
@@ -47,13 +46,12 @@ function testBlackBoxSource() {
"Should only get 3 frames.");
is(gDebugger.document.querySelectorAll(".dbg-stackframe-black-boxed").length, 1,
"And one of them should be the combined black boxed frames.");
});
}
registerCleanupFunction(function() {
gTab = null;
- gDebuggee = null;
gPanel = null;
gDebugger = null;
gFrames = null;
});
--- a/browser/devtools/debugger/test/browser_dbg_blackboxing-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_blackboxing-04.js
@@ -4,23 +4,22 @@
/**
* Test that we get a stack frame for each black boxed source, not a single one
* for all of them.
*/
const TAB_URL = EXAMPLE_URL + "doc_blackboxing.html";
const BLACKBOXME_URL = EXAMPLE_URL + "code_blackboxing_blackboxme.js"
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
let gFrames;
function test() {
- initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+ initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
- gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gFrames = gDebugger.DebuggerView.StackFrames;
waitForSourceShown(gPanel, BLACKBOXME_URL)
.then(blackBoxSources)
.then(testBlackBoxStack)
.then(() => resumeDebuggerThenCloseAndFinish(gPanel))
@@ -41,21 +40,18 @@ function blackBoxSources() {
function testBlackBoxStack() {
let finished = waitForSourceAndCaretAndScopes(gPanel, ".html", 21).then(() => {
is(gFrames.itemCount, 4,
"Should get 4 frames (one -> two -> three -> doDebuggerStatement).");
is(gDebugger.document.querySelectorAll(".dbg-stackframe-black-boxed").length, 3,
"And 'one', 'two', and 'three' should each have their own black boxed frame.");
});
- // Spin the event loop before causing the debuggee to pause, to allow
- // this function to return first.
- executeSoon(() => gDebuggee.one());
+ callInTab(gTab, "one");
return finished;
}
registerCleanupFunction(function() {
gTab = null;
- gDebuggee = null;
gPanel = null;
gDebugger = null;
gFrames = null;
});
--- a/browser/devtools/debugger/test/browser_dbg_blackboxing-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_blackboxing-05.js
@@ -3,23 +3,22 @@
/**
* Test that a "this source is blackboxed" message is shown when necessary
* and can be properly dismissed.
*/
const TAB_URL = EXAMPLE_URL + "doc_binary_search.html";
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
let gDeck;
function test() {
- initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+ initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
- gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gDeck = gDebugger.document.getElementById("editor-deck");
waitForSourceShown(gPanel, ".coffee")
.then(testSourceEditorShown)
.then(toggleBlackBoxing.bind(null, gPanel))
.then(testBlackBoxMessageShown)
@@ -59,13 +58,12 @@ function testSourceEditorShownAgain() {
}
function getEditorBlackboxMessageButton() {
return gDebugger.document.getElementById("black-boxed-message-button");
}
registerCleanupFunction(function() {
gTab = null;
- gDebuggee = null;
gPanel = null;
gDebugger = null;
gDeck = null;
});
--- a/browser/devtools/debugger/test/browser_dbg_blackboxing-06.js
+++ b/browser/devtools/debugger/test/browser_dbg_blackboxing-06.js
@@ -3,35 +3,34 @@
/**
* Test that clicking the black box checkbox when paused doesn't re-select the
* currently paused frame's source.
*/
const TAB_URL = EXAMPLE_URL + "doc_blackboxing.html";
-let gTab, gDebuggee, gPanel, gDebugger;
+let gTab, gPanel, gDebugger;
let gSources;
function test() {
- initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+ initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
gTab = aTab;
- gDebuggee = aDebuggee;
gPanel = aPanel;
gDebugger = gPanel.panelWin;
gSources = gDebugger.DebuggerView.Sources;
waitForSourceAndCaretAndScopes(gPanel, ".html", 21)
.then(testBlackBox)
.then(() => resumeDebuggerThenCloseAndFinish(gPanel))
.then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
- gDebuggee.runTest();
+ callInTab(gTab, "runTest");
});
}
function testBlackBox() {
const selectedUrl = gSources.selectedValue;
let finished = waitForSourceShown(gPanel, "blackboxme.js").then(() => {
const newSelectedUrl = gSources.selectedValue;
@@ -45,13 +44,12 @@ function testBlackBox() {
});
gSources.selectedIndex = 0;
return finished;
}
registerCleanupFunction(function() {
gTab = null;
- gDebuggee = null;
gPanel = null;
gDebugger = null;
gSources = null;
});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/code_frame-script.js
@@ -0,0 +1,9 @@
+"use strict";
+
+dump("Frame script loaded.\n");
+
+addMessageListener("test:call", function (message) {
+ dump("Calling function with name " + message.data + ".\n");
+
+ XPCNativeWrapper.unwrap(content)[message.data]();
+});
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -23,16 +23,17 @@ let { DebuggerServer } = Cu.import("reso
let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
let { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
let EventEmitter = require("devtools/toolkit/event-emitter");
const { promiseInvoke } = require("devtools/async-utils");
let TargetFactory = devtools.TargetFactory;
let Toolbox = devtools.Toolbox;
const EXAMPLE_URL = "http://example.com/browser/browser/devtools/debugger/test/";
+const FRAME_SCRIPT_URL = getRootDirectory(gTestPath) + "code_frame-script.js";
gDevTools.testing = true;
SimpleTest.registerCleanupFunction(() => {
gDevTools.testing = false;
});
// All tests are asynchronous.
waitForExplicitFinish();
@@ -81,16 +82,19 @@ function addTab(aUrl, aWindow) {
let deferred = promise.defer();
let targetWindow = aWindow || window;
let targetBrowser = targetWindow.gBrowser;
targetWindow.focus();
let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
let linkedBrowser = tab.linkedBrowser;
+ info("Loading frame script with url " + FRAME_SCRIPT_URL + ".");
+ linkedBrowser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
+
linkedBrowser.addEventListener("load", function onLoad() {
linkedBrowser.removeEventListener("load", onLoad, true);
info("Tab added and finished loading: " + aUrl);
deferred.resolve(tab);
}, true);
return deferred.promise;
}
@@ -931,9 +935,33 @@ function pushPrefs(...aPrefs) {
SpecialPowers.pushPrefEnv({"set": aPrefs}, deferred.resolve);
return deferred.promise;
}
function popPrefs() {
let deferred = promise.defer();
SpecialPowers.popPrefEnv(deferred.resolve);
return deferred.promise;
-}
\ No newline at end of file
+}
+
+function sendMessageToTab(tab, name, data, objects) {
+ info("Sending message with name " + name + " to tab.");
+
+ tab.linkedBrowser.messageManager.sendAsyncMessage(name, data, objects);
+}
+
+function waitForMessageFromTab(tab, name) {
+ info("Waiting for message with name " + name + " from tab.");
+
+ return new Promise(function (resolve) {
+ let messageManager = tab.linkedBrowser.messageManager;
+ messageManager.addMessageListener(name, function listener(message) {
+ messageManager.removeMessageListener(name, listener);
+ resolve(message);
+ });
+ });
+}
+
+function callInTab(tab, name) {
+ info("Calling function with name " + name + " in tab.");
+
+ sendMessageToTab(tab, "test:call", name);
+}
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -226,16 +226,17 @@
@BINPATH@/components/dom_permissionsettings.xpt
@BINPATH@/components/dom_sidebar.xpt
@BINPATH@/components/dom_cellbroadcast.xpt
@BINPATH@/components/dom_mobilemessage.xpt
@BINPATH@/components/dom_storage.xpt
@BINPATH@/components/dom_stylesheets.xpt
@BINPATH@/components/dom_telephony.xpt
@BINPATH@/components/dom_traversal.xpt
+@BINPATH@/components/dom_tv.xpt
@BINPATH@/components/dom_voicemail.xpt
#ifdef MOZ_WEBSPEECH
@BINPATH@/components/dom_webspeechrecognition.xpt
#endif
@BINPATH@/components/dom_workers.xpt
@BINPATH@/components/dom_xbl.xpt
@BINPATH@/components/dom_xpath.xpt
@BINPATH@/components/dom_xul.xpt
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -120,16 +120,24 @@ PluginContent.prototype = {
if (tagMimetype == "") {
tagMimetype = pluginElement.type;
}
if (this.isKnownPlugin(pluginElement)) {
pluginTag = pluginHost.getPluginTagForType(pluginElement.actualType);
pluginName = BrowserUtils.makeNicePluginName(pluginTag.name);
+ // Convert this from nsIPluginTag so it can be serialized.
+ let properties = ["name", "description", "filename", "version", "enabledState"];
+ let pluginTagCopy = {};
+ for (let prop of properties) {
+ pluginTagCopy[prop] = pluginTag[prop];
+ }
+ pluginTag = pluginTagCopy;
+
permissionString = pluginHost.getPermissionStringForType(pluginElement.actualType);
fallbackType = pluginElement.defaultFallbackType;
blocklistState = pluginHost.getBlocklistStateForType(pluginElement.actualType);
// Make state-softblocked == state-notblocked for our purposes,
// they have the same UI. STATE_OUTDATED should not exist for plugin
// items, but let's alias it anyway, just in case.
if (blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED ||
blocklistState == Ci.nsIBlocklistService.STATE_OUTDATED) {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -3122,31 +3122,49 @@ nsDocShell::NotifyAsyncPanZoomStarted(co
nsWeakPtr ref = iter.GetNext();
nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
if (obs) {
obs->AsyncPanZoomStarted(aScrollPos);
} else {
mScrollObservers.RemoveElement(ref);
}
}
+
+ // Also notify child docshell
+ for (uint32_t i = 0; i < mChildList.Length(); ++i) {
+ nsCOMPtr<nsIDocShell> kid = do_QueryInterface(ChildAt(i));
+ if (kid) {
+ nsDocShell* docShell = static_cast<nsDocShell*>(kid.get());
+ docShell->NotifyAsyncPanZoomStarted(aScrollPos);
+ }
+ }
}
void
nsDocShell::NotifyAsyncPanZoomStopped(const mozilla::CSSIntPoint aScrollPos)
{
nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
while (iter.HasMore()) {
nsWeakPtr ref = iter.GetNext();
nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
if (obs) {
obs->AsyncPanZoomStopped(aScrollPos);
} else {
mScrollObservers.RemoveElement(ref);
}
}
+
+ // Also notify child docshell
+ for (uint32_t i = 0; i < mChildList.Length(); ++i) {
+ nsCOMPtr<nsIDocShell> kid = do_QueryInterface(ChildAt(i));
+ if (kid) {
+ nsDocShell* docShell = static_cast<nsDocShell*>(kid.get());
+ docShell->NotifyAsyncPanZoomStopped(aScrollPos);
+ }
+ }
}
NS_IMETHODIMP
nsDocShell::NotifyScrollObservers()
{
nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
while (iter.HasMore()) {
nsWeakPtr ref = iter.GetNext();
@@ -10386,18 +10404,18 @@ nsDocShell::DoURILoad(nsIURI * aURI,
}
}
//hack
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(do_QueryInterface(channel));
if (httpChannelInternal) {
if (aForceAllowCookies) {
- httpChannelInternal->SetForceAllowThirdPartyCookie(true);
- }
+ httpChannelInternal->SetThirdPartyFlags(nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
+ }
if (aFirstParty) {
httpChannelInternal->SetDocumentURI(aURI);
} else {
httpChannelInternal->SetDocumentURI(aReferrerURI);
}
}
nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(channel));
--- a/dom/apps/PermissionsTable.jsm
+++ b/dom/apps/PermissionsTable.jsm
@@ -488,16 +488,21 @@ this.PermissionsTable = { geolocation:
access: ["read", "write"],
additional: ["settings-api"]
},
"engineering-mode": {
app: DENY_ACTION,
trusted: DENY_ACTION,
privileged: DENY_ACTION,
certified: ALLOW_ACTION
+ },
+ "tv": {
+ app: DENY_ACTION,
+ privileged: DENY_ACTION,
+ certified: ALLOW_ACTION
}
};
/**
* Append access modes to the permission name as suffixes.
* e.g. permission name 'contacts' with ['read', 'write'] =
* ['contacts-read', contacts-write']
* @param string aPermName
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -34,16 +34,17 @@
#include "mozilla/dom/PowerManager.h"
#include "mozilla/dom/WakeLock.h"
#include "mozilla/dom/power/PowerManagerService.h"
#include "mozilla/dom/CellBroadcast.h"
#include "mozilla/dom/MobileMessageManager.h"
#include "mozilla/dom/ServiceWorkerContainer.h"
#include "mozilla/dom/Telephony.h"
#include "mozilla/dom/Voicemail.h"
+#include "mozilla/dom/TVManager.h"
#include "mozilla/Hal.h"
#include "nsISiteSpecificUserAgent.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/StaticPtr.h"
#include "Connection.h"
#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
#include "nsGlobalWindow.h"
#ifdef MOZ_B2G
@@ -168,16 +169,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGeolocation)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotification)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBatteryManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPowerManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCellBroadcast)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMobileMessageManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTelephony)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVoicemail)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTVManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection)
#ifdef MOZ_B2G_RIL
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMobileConnections)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIccManager)
#endif
#ifdef MOZ_B2G_BT
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBluetooth)
#endif
@@ -251,16 +253,20 @@ Navigator::Invalidate()
mTelephony = nullptr;
}
if (mVoicemail) {
mVoicemail->Shutdown();
mVoicemail = nullptr;
}
+ if (mTVManager) {
+ mTVManager = nullptr;
+ }
+
if (mConnection) {
mConnection->Shutdown();
mConnection = nullptr;
}
#ifdef MOZ_B2G_RIL
if (mMobileConnections) {
mMobileConnections = nullptr;
@@ -1088,17 +1094,20 @@ Navigator::SendBeacon(const nsAString& a
nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(do_QueryInterface(channel));
nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
if (!httpChannelInternal) {
aRv.Throw(NS_ERROR_DOM_BAD_URI);
return false;
}
bool isForeign = true;
thirdPartyUtil->IsThirdPartyWindow(mWindow, uri, &isForeign);
- httpChannelInternal->SetForceAllowThirdPartyCookie(!isForeign);
+ uint32_t thirdPartyFlags = isForeign ?
+ 0 :
+ nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW;
+ httpChannelInternal->SetThirdPartyFlags(thirdPartyFlags);
nsCString mimeType;
if (!aData.IsNull()) {
nsCOMPtr<nsIInputStream> in;
if (aData.Value().IsString()) {
nsCString stringData = NS_ConvertUTF16toUTF8(aData.Value().GetAsString());
nsCOMPtr<nsIStringInputStream> strStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
@@ -1574,16 +1583,29 @@ Navigator::GetMozTelephony(ErrorResult&
return nullptr;
}
mTelephony = Telephony::Create(mWindow, aRv);
}
return mTelephony;
}
+TVManager*
+Navigator::GetTv()
+{
+ if (!mTVManager) {
+ if (!mWindow) {
+ return nullptr;
+ }
+ mTVManager = TVManager::Create(mWindow);
+ }
+
+ return mTVManager;
+}
+
#ifdef MOZ_B2G
already_AddRefed<Promise>
Navigator::GetMobileIdAssertion(const MobileIdOptions& aOptions,
ErrorResult& aRv)
{
if (!mWindow || !mWindow->GetDocShell()) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
@@ -2319,16 +2341,47 @@ Navigator::HasMobileIdSupport(JSContext*
uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
permMgr->TestPermissionFromPrincipal(principal, "mobileid", &permission);
return permission == nsIPermissionManager::PROMPT_ACTION ||
permission == nsIPermissionManager::ALLOW_ACTION;
}
#endif
/* static */
+bool
+Navigator::HasTVSupport(JSContext* aCx, JSObject* aGlobal)
+{
+ JS::Rooted<JSObject*> global(aCx, aGlobal);
+
+ nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(global);
+ if (!win) {
+ return false;
+ }
+
+ // Just for testing, we can enable TV for any kind of app.
+ if (Preferences::GetBool("dom.testing.tv_enabled_for_hosted_apps", false)) {
+ return true;
+ }
+
+ nsIDocument* doc = win->GetExtantDoc();
+ if (!doc || !doc->NodePrincipal()) {
+ return false;
+ }
+
+ nsIPrincipal* principal = doc->NodePrincipal();
+ uint16_t status;
+ if (NS_FAILED(principal->GetAppStatus(&status))) {
+ return false;
+ }
+
+ // Only support TV Manager API for certified apps for now.
+ return status == nsIPrincipal::APP_STATUS_CERTIFIED;
+}
+
+/* static */
already_AddRefed<nsPIDOMWindow>
Navigator::GetWindowFromGlobal(JSObject* aGlobal)
{
nsCOMPtr<nsPIDOMWindow> win =
do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(aGlobal));
MOZ_ASSERT(!win || win->IsInnerWindow());
return win.forget();
}
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -88,16 +88,17 @@ class BluetoothManager;
class IccManager;
class MobileConnectionArray;
#endif
class PowerManager;
class CellBroadcast;
class Telephony;
class Voicemail;
+class TVManager;
namespace time {
class TimeManager;
} // namespace time
namespace system {
#ifdef MOZ_AUDIO_CHANNEL_MANAGER
class AudioChannelManager;
@@ -218,16 +219,17 @@ public:
void GetDeviceStorages(const nsAString& aType,
nsTArray<nsRefPtr<nsDOMDeviceStorage> >& aStores,
ErrorResult& aRv);
DesktopNotificationCenter* GetMozNotification(ErrorResult& aRv);
CellBroadcast* GetMozCellBroadcast(ErrorResult& aRv);
MobileMessageManager* GetMozMobileMessage();
Telephony* GetMozTelephony(ErrorResult& aRv);
Voicemail* GetMozVoicemail(ErrorResult& aRv);
+ TVManager* GetTv();
network::Connection* GetConnection(ErrorResult& aRv);
nsDOMCameraManager* GetMozCameras(ErrorResult& aRv);
MediaDevices* GetMediaDevices(ErrorResult& aRv);
void MozSetMessageHandler(const nsAString& aType,
systemMessageCallback* aCallback,
ErrorResult& aRv);
bool MozHasPendingMessage(const nsAString& aType, ErrorResult& aRv);
#ifdef MOZ_B2G
@@ -300,16 +302,18 @@ public:
static bool HasDataStoreSupport(nsIPrincipal* aPrincipal);
static bool HasDataStoreSupport(JSContext* cx, JSObject* aGlobal);
#ifdef MOZ_B2G
static bool HasMobileIdSupport(JSContext* aCx, JSObject* aGlobal);
#endif
+ static bool HasTVSupport(JSContext* aCx, JSObject* aGlobal);
+
nsPIDOMWindow* GetParentObject() const
{
return GetWindow();
}
virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;
private:
@@ -329,16 +333,17 @@ private:
#ifdef MOZ_B2G_FM
nsRefPtr<FMRadio> mFMRadio;
#endif
nsRefPtr<PowerManager> mPowerManager;
nsRefPtr<CellBroadcast> mCellBroadcast;
nsRefPtr<MobileMessageManager> mMobileMessageManager;
nsRefPtr<Telephony> mTelephony;
nsRefPtr<Voicemail> mVoicemail;
+ nsRefPtr<TVManager> mTVManager;
nsRefPtr<network::Connection> mConnection;
#ifdef MOZ_B2G_RIL
nsRefPtr<MobileConnectionArray> mMobileConnections;
nsRefPtr<IccManager> mIccManager;
#endif
#ifdef MOZ_B2G_BT
nsRefPtr<bluetooth::BluetoothManager> mBluetooth;
#endif
--- a/dom/base/ThirdPartyUtil.cpp
+++ b/dom/base/ThirdPartyUtil.cpp
@@ -1,8 +1,10 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 sts=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ThirdPartyUtil.h"
#include "nsNetUtil.h"
#include "nsIServiceManager.h"
#include "nsIHttpChannelInternal.h"
@@ -161,30 +163,57 @@ ThirdPartyUtil::IsThirdPartyChannel(nsIC
nsIURI* aURI,
bool* aResult)
{
NS_ENSURE_ARG(aChannel);
NS_ASSERTION(aResult, "null outparam pointer");
nsresult rv;
bool doForce = false;
+ bool checkWindowChain = true;
+ bool parentIsThird = false;
nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
do_QueryInterface(aChannel);
if (httpChannelInternal) {
- rv = httpChannelInternal->GetForceAllowThirdPartyCookie(&doForce);
+ uint32_t flags;
+ rv = httpChannelInternal->GetThirdPartyFlags(&flags);
NS_ENSURE_SUCCESS(rv, rv);
+ doForce = (flags & nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
+
// If aURI was not supplied, and we're forcing, then we're by definition
// not foreign. If aURI was supplied, we still want to check whether it's
// foreign with respect to the channel URI. (The forcing only applies to
// whatever window hierarchy exists above the channel.)
if (doForce && !aURI) {
*aResult = false;
return NS_OK;
}
+
+ if (flags & nsIHttpChannelInternal::THIRD_PARTY_PARENT_IS_THIRD_PARTY) {
+ // Check that the two PARENT_IS_{THIRD,SAME}_PARTY are mutually exclusive.
+ MOZ_ASSERT(!(flags & nsIHttpChannelInternal::THIRD_PARTY_PARENT_IS_SAME_PARTY));
+
+ // If we're not forcing and we know that the window chain of the channel
+ // is third party, then we know now that we're third party.
+ if (!doForce) {
+ *aResult = true;
+ return NS_OK;
+ }
+
+ checkWindowChain = false;
+ parentIsThird = true;
+ } else {
+ // In e10s, we can't check the parent chain in the parent, so we do so
+ // in the child and send the result to the parent.
+ // Note that we only check the window chain if neither
+ // THIRD_PARTY_PARENT_IS_* flag is set.
+ checkWindowChain = !(flags & nsIHttpChannelInternal::THIRD_PARTY_PARENT_IS_SAME_PARTY);
+ parentIsThird = false;
+ }
}
// Obtain the URI from the channel, and its base domain.
nsCOMPtr<nsIURI> channelURI;
aChannel->GetURI(getter_AddRefs(channelURI));
NS_ENSURE_TRUE(channelURI, NS_ERROR_INVALID_ARG);
nsCString channelDomain;
@@ -201,16 +230,22 @@ ThirdPartyUtil::IsThirdPartyChannel(nsIC
// If it's foreign, or we're forcing, we're done.
if (result || doForce) {
*aResult = result;
return NS_OK;
}
}
+ // If we've already computed this in the child process, we're done.
+ if (!checkWindowChain) {
+ *aResult = parentIsThird;
+ return NS_OK;
+ }
+
// Find the associated window and its parent window.
nsCOMPtr<nsILoadContext> ctx;
NS_QueryNotificationCallbacks(aChannel, ctx);
if (!ctx) return NS_ERROR_INVALID_ARG;
// If there is no window, the consumer kicking off the load didn't provide one
// to the channel. This is limited to loads of certain types of resources. If
// those loads require cookies, the forceAllowThirdPartyCookie property should
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -4486,23 +4486,29 @@ nsContentUtils::AppendNodeTextContent(ns
}
}
}
return true;
}
bool
-nsContentUtils::HasNonEmptyTextContent(nsINode* aNode)
+nsContentUtils::HasNonEmptyTextContent(nsINode* aNode,
+ TextContentDiscoverMode aDiscoverMode)
{
for (nsIContent* child = aNode->GetFirstChild();
child;
child = child->GetNextSibling()) {
if (child->IsNodeOfType(nsINode::eTEXT) &&
child->TextLength() > 0) {
+ return true;
+ }
+
+ if (aDiscoverMode == eRecurseIntoChildren &&
+ HasNonEmptyTextContent(child, aDiscoverMode)) {
return true;
}
}
return false;
}
/* static */
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1235,22 +1235,29 @@ public:
/**
* Same as GetNodeTextContents but appends the result rather than sets it.
*/
static bool AppendNodeTextContent(nsINode* aNode, bool aDeep,
nsAString& aResult, const mozilla::fallible_t&);
/**
- * Utility method that checks if a given node has any non-empty
- * children.
- * NOTE! This method does not descend recursivly into elements.
- * Though it would be easy to make it so if needed
+ * Utility method that checks if a given node has any non-empty children. This
+ * method does not descend recursively into children by default.
+ *
+ * @param aDiscoverMode Set to eRecurseIntoChildren to descend recursively
+ * into children.
*/
- static bool HasNonEmptyTextContent(nsINode* aNode);
+ enum TextContentDiscoverMode MOZ_ENUM_TYPE(uint8_t) {
+ eRecurseIntoChildren, eDontRecurseIntoChildren
+ };
+
+ static bool HasNonEmptyTextContent(
+ nsINode* aNode,
+ TextContentDiscoverMode aDiscoverMode = eDontRecurseIntoChildren);
/**
* Delete strings allocated for nsContentList matches
*/
static void DestroyMatchString(void* aData);
/**
* Unbinds the content from the tree and nulls it out if it's not null.
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -702,16 +702,18 @@ GK_ATOM(oncompositionend, "oncomposition
GK_ATOM(oncompositionstart, "oncompositionstart")
GK_ATOM(oncompositionupdate, "oncompositionupdate")
GK_ATOM(onconfigurationchange, "onconfigurationchange")
GK_ATOM(onconnect, "onconnect")
GK_ATOM(onconnected, "onconnected")
GK_ATOM(onconnecting, "onconnecting")
GK_ATOM(oncontextmenu, "oncontextmenu")
GK_ATOM(oncopy, "oncopy")
+GK_ATOM(oncurrentchannelchanged, "oncurrentchannelchanged")
+GK_ATOM(oncurrentsourcechanged, "oncurrentsourcechanged")
GK_ATOM(oncut, "oncut")
GK_ATOM(ondatachange, "ondatachange")
GK_ATOM(ondataerror, "ondataerror")
GK_ATOM(ondblclick, "ondblclick")
GK_ATOM(ondeleted, "ondeleted")
GK_ATOM(ondeliverysuccess, "ondeliverysuccess")
GK_ATOM(ondeliveryerror, "ondeliveryerror")
GK_ATOM(ondevicefound, "ondevicefound")
@@ -741,16 +743,17 @@ GK_ATOM(ondragdrop, "ondragdrop")
GK_ATOM(ondragend, "ondragend")
GK_ATOM(ondragenter, "ondragenter")
GK_ATOM(ondragexit, "ondragexit")
GK_ATOM(ondraggesture, "ondraggesture")
GK_ATOM(ondragleave, "ondragleave")
GK_ATOM(ondragover, "ondragover")
GK_ATOM(ondragstart, "ondragstart")
GK_ATOM(ondrop, "ondrop")
+GK_ATOM(oneitbroadcasted, "oneitbroadcasted")
GK_ATOM(onenabled, "onenabled")
GK_ATOM(onenterpincodereq, "onenterpincodereq")
GK_ATOM(onemergencycbmodechange, "onemergencycbmodechange")
GK_ATOM(onerror, "onerror")
GK_ATOM(onevicted, "onevicted")
GK_ATOM(onfacesdetected, "onfacesdetected")
GK_ATOM(onfailed, "onfailed")
GK_ATOM(onfetch, "onfetch")
@@ -843,16 +846,17 @@ GK_ATOM(onremoteresumed, "onremoteresume
GK_ATOM(onresourcetimingbufferfull, "onresourcetimingbufferfull")
GK_ATOM(onretrieving, "onretrieving")
GK_ATOM(onRequest, "onRequest")
GK_ATOM(onrequestmediaplaystatus, "onrequestmediaplaystatus")
GK_ATOM(onreset, "onreset")
GK_ATOM(onresuming, "onresuming")
GK_ATOM(onresize, "onresize")
GK_ATOM(onrtchange, "onrtchange")
+GK_ATOM(onscanningstatechanged, "onscanningstatechanged")
GK_ATOM(onscostatuschanged, "onscostatuschanged")
GK_ATOM(onscroll, "onscroll")
GK_ATOM(onselect, "onselect")
GK_ATOM(onsending, "onsending")
GK_ATOM(onsent, "onsent")
GK_ATOM(onset, "onset")
GK_ATOM(onshow, "onshow")
GK_ATOM(onshutter, "onshutter")
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -197,16 +197,17 @@
#include "mozilla/Telemetry.h"
#include "nsLocation.h"
#include "nsHTMLDocument.h"
#include "nsWrapperCacheInlines.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "prrng.h"
#include "nsSandboxFlags.h"
#include "TimeChangeObserver.h"
+#include "TouchCaret.h"
#include "mozilla/dom/AudioContext.h"
#include "mozilla/dom/BlobBinding.h"
#include "mozilla/dom/BrowserElementDictionariesBinding.h"
#include "mozilla/dom/Console.h"
#include "mozilla/dom/Fetch.h"
#include "mozilla/dom/FunctionBinding.h"
#include "mozilla/dom/HashChangeEvent.h"
#include "mozilla/dom/MozSelfSupportBinding.h"
@@ -9351,16 +9352,34 @@ nsGlobalWindow::UpdateCommands(const nsA
anAction));
}
}
if (gSelectionCaretPrefEnabled && mDoc && anAction.EqualsLiteral("selectionchange")) {
SelectionChangeEventInit init;
init.mBubbles = true;
if (aSel) {
+ bool isTouchCaretVisible = false;
+ bool isCollapsed = aSel->Collapsed();
+
+ nsIPresShell *shell = mDoc->GetShell();
+ if (shell) {
+ nsRefPtr<TouchCaret> touchCaret = shell->GetTouchCaret();
+ if (touchCaret) {
+ isTouchCaretVisible = touchCaret->GetVisibility();
+ }
+ }
+
+ // Dispatch selection change events when touch caret is visible even if selection
+ // is collapsed because it could be the shortcut mode, otherwise ignore this
+ // UpdateCommands
+ if (isCollapsed && !isTouchCaretVisible) {
+ return NS_OK;
+ }
+
Selection* selection = static_cast<Selection*>(aSel);
int32_t rangeCount = selection->GetRangeCount();
nsLayoutUtils::RectAccumulator accumulator;
for (int32_t idx = 0; idx < rangeCount; ++idx) {
nsRange* range = selection->GetRangeAt(idx);
nsRange::CollectClientRects(&accumulator, range,
range->GetStartParent(), range->StartOffset(),
range->GetEndParent(), range->EndOffset(),
--- a/dom/events/test/test_all_synthetic_events.html
+++ b/dom/events/test/test_all_synthetic_events.html
@@ -292,16 +292,20 @@ const kEventConstructors = {
MozMmsEvent: { create: function (aName, aProps) {
return new MozMmsEvent(aName, aProps);
},
},
MozNFCPeerEvent: { create: function (aName, aProps) {
return new MozNFCPeerEvent(aName, aProps);
},
},
+ MozNFCTagEvent: { create: function (aName, aProps) {
+ return new MozNFCTagEvent(aName, aProps);
+ },
+ },
MozOtaStatusEvent: { create: function (aName, aProps) {
return new MozOtaStatusEvent(aName, aProps);
},
},
MozSettingsEvent: { create: function (aName, aProps) {
return new MozSettingsEvent(aName, aProps);
},
},
@@ -463,16 +467,48 @@ const kEventConstructors = {
TrackEvent: { create: function (aName, aProps) {
return new TrackEvent(aName, aProps);
},
},
TransitionEvent: { create: function (aName, aProps) {
return new TransitionEvent(aName, aProps);
},
},
+ TVCurrentChannelChangedEvent: { create: function (aName, aProps) {
+ return new TVCurrentChannelChangedEvent(aName, aProps);
+ },
+ },
+ TVCurrentProgramChangedEvent: { create: function (aName, aProps) {
+ return new TVCurrentProgramChangedEvent(aName, aProps);
+ },
+ },
+ TVCurrentSourceChangedEvent: { create: function (aName, aProps) {
+ return new TVCurrentSourceChangedEvent(aName, aProps);
+ },
+ },
+ TVEITBroadcastedEvent: { create: function (aName, aProps) {
+ return new TVEITBroadcastedEvent(aName, aProps);
+ },
+ },
+ TVParentalControlChangedEvent: { create: function (aName, aProps) {
+ return new TVParentalControlChangedEvent(aName, aProps);
+ },
+ },
+ TVProtectionStateChangedEvent: { create: function (aName, aProps) {
+ return new TVProtectionStateChangedEvent(aName, aProps);
+ },
+ },
+ TVScanningStateChangedEvent: { create: function (aName, aProps) {
+ return new TVScanningStateChangedEvent(aName, aProps);
+ },
+ },
+ TVTunerChangedEvent: { create: function (aName, aProps) {
+ return new TVTunerChangedEvent(aName, aProps);
+ },
+ },
UIEvent: { create: function (aName, aProps) {
return new UIEvent(aName, aProps);
},
},
UserProximityEvent: { create: function (aName, aProps) {
return new UserProximityEvent(aName, aProps);
},
},
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -601,21 +601,16 @@ HTMLMediaElement::OnChannelRedirect(nsIC
return NS_OK;
}
void HTMLMediaElement::ShutdownDecoder()
{
RemoveMediaElementFromURITable();
NS_ASSERTION(mDecoder, "Must have decoder to shut down");
- // TODO: This should be handled by ChangeNetworkState() so we have only one
- // place to call StopProgress().
- if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
- mDecoder->StopProgress();
- }
mDecoder->Shutdown();
mDecoder = nullptr;
}
void HTMLMediaElement::AbortExistingLoads()
{
// Abort any already-running instance of the resource selection algorithm.
mLoadWaitStatus = NOT_WAITING;
@@ -1787,21 +1782,17 @@ NS_IMETHODIMP HTMLMediaElement::SetMuted
already_AddRefed<DOMMediaStream>
HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded)
{
nsIDOMWindow* window = OwnerDoc()->GetInnerWindow();
if (!window) {
return nullptr;
}
-#ifdef MOZ_EME
- if (ContainsRestrictedContent()) {
- return nullptr;
- }
-#endif
+
OutputMediaStream* out = mOutputStreams.AppendElement();
#ifdef DEBUG
// Estimate hints based on the type of the media element
// under the preference media.capturestream_hints for the
// debug builds only. This allows WebRTC Peer Connection
// to behave appropriately when media streams generated
// via mozCaptureStream*() are added to the Peer Connection.
// This functionality is planned to be used as part of Audio
@@ -3996,31 +3987,20 @@ NS_IMETHODIMP HTMLMediaElement::CanPlayC
#ifdef MOZ_EME
MediaKeys*
HTMLMediaElement::GetMediaKeys() const
{
return mMediaKeys;
}
-bool
-HTMLMediaElement::ContainsRestrictedContent()
-{
- return GetMediaKeys() != nullptr;
-}
-
already_AddRefed<Promise>
HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
ErrorResult& aRv)
{
- if (MozAudioCaptured()) {
- aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
- return nullptr;
- }
-
nsCOMPtr<nsIGlobalObject> global =
do_QueryInterface(OwnerDoc()->GetInnerWindow());
if (!global) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
@@ -4034,29 +4014,27 @@ HTMLMediaElement::SetMediaKeys(mozilla::
promise->MaybeReject(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
return promise.forget();
}
if (mMediaKeys) {
// Existing MediaKeys object. Shut it down.
mMediaKeys->Shutdown();
mMediaKeys = nullptr;
}
-
+
mMediaKeys = aMediaKeys;
if (mMediaKeys) {
if (NS_FAILED(mMediaKeys->Bind(this))) {
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
mMediaKeys = nullptr;
return promise.forget();
}
if (mDecoder) {
mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
}
- // Update the same-origin status.
- NotifyDecoderPrincipalChanged();
}
promise->MaybeResolve(JS::UndefinedHandleValue);
return promise.forget();
}
MediaWaitingFor
HTMLMediaElement::WaitingFor() const
{
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -545,17 +545,16 @@ public:
bool IsEventAttributeName(nsIAtom* aName) MOZ_OVERRIDE;
// Returns the principal of the "top level" document; the origin displayed
// in the URL bar of the browser window.
already_AddRefed<nsIPrincipal> GetTopLevelPrincipal();
- bool ContainsRestrictedContent();
#endif // MOZ_EME
bool MozAutoplayEnabled() const
{
return mAutoplayEnabled;
}
already_AddRefed<DOMMediaStream> MozCaptureStream(ErrorResult& aRv);
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -32,16 +32,17 @@
#include "nsITextControlFrame.h"
#include "nsLayoutUtils.h"
#include "nsLinebreakConverter.h"
#include "nsMappedAttributes.h"
#include "nsPIDOMWindow.h"
#include "nsPresContext.h"
#include "nsPresState.h"
#include "nsReadableUtils.h"
+#include "nsRuleData.h"
#include "nsStyleConsts.h"
#include "nsTextEditorState.h"
#include "nsIController.h"
static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID);
#define NS_NO_CONTENT_DISPATCH (1 << 0)
@@ -402,16 +403,28 @@ HTMLTextAreaElement::ParseAttribute(int3
return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
aResult);
}
void
HTMLTextAreaElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
nsRuleData* aData)
{
+ if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Text)) {
+ // wrap=off
+ nsCSSValue* whiteSpace = aData->ValueForWhiteSpace();
+ if (whiteSpace->GetUnit() == eCSSUnit_Null) {
+ const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::wrap);
+ if (value && value->Type() == nsAttrValue::eString &&
+ value->Equals(nsGkAtoms::OFF, eIgnoreCase)) {
+ whiteSpace->SetIntValue(NS_STYLE_WHITESPACE_PRE, eCSSUnit_Enumerated);
+ }
+ }
+ }
+
nsGenericHTMLFormElementWithState::MapDivAlignAttributeInto(aAttributes, aData);
nsGenericHTMLFormElementWithState::MapCommonAttributesInto(aAttributes, aData);
}
nsChangeHint
HTMLTextAreaElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
int32_t aModType) const
{
@@ -426,17 +439,23 @@ HTMLTextAreaElement::GetAttributeChangeH
NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE);
}
return retval;
}
NS_IMETHODIMP_(bool)
HTMLTextAreaElement::IsAttributeMapped(const nsIAtom* aAttribute) const
{
+ static const MappedAttributeEntry attributes[] {
+ { &nsGkAtoms::wrap },
+ { nullptr }
+ };
+
static const MappedAttributeEntry* const map[] = {
+ attributes,
sDivAlignAttributeMap,
sCommonAttributeMap,
};
return FindAttributeDependence(aAttribute, map);
}
nsMapRuleToAttributesFunc
new file mode 100644
--- /dev/null
+++ b/dom/html/reftests/82711-1-ref.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+<body>
+<textarea rows="10" cols="25" wrap="off">
+ 0 1 2 3
+ 4 5
+
+ 6 7 8
+ 9
+
+
+ This is a long line that could wrap.
+</textarea>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/html/reftests/82711-1.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+<body>
+<textarea rows="10" cols="25" style="white-space: pre">
+ 0 1 2 3
+ 4 5
+
+ 6 7 8
+ 9
+
+
+ This is a long line that could wrap.
+</textarea>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/html/reftests/82711-2-ref.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+<body>
+<textarea rows="10" cols="25" wrap="off" style="white-space: normal">
+ 0 1 2 3
+ 4 5
+
+ 6 7 8
+ 9
+
+
+ This is a long line that could wrap.
+</textarea>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/html/reftests/82711-2.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<html>
+<body>
+<textarea rows="10" cols="25" style="white-space: normal">
+0 1 2 3 4 5 6 7 8 9 This is a long line that could wrap.
+</textarea>
+</body>
+</html>
--- a/dom/html/reftests/reftest.list
+++ b/dom/html/reftests/reftest.list
@@ -1,15 +1,18 @@
# autofocus attribute (we can't test with mochitests)
include autofocus/reftest.list
include toblob-todataurl/reftest.list
skip-if(B2G) == 41464-1a.html 41464-1-ref.html
skip-if(B2G) == 41464-1b.html 41464-1-ref.html
== 52019-1.html 52019-1-ref.html
+== 82711-1.html 82711-1-ref.html
+== 82711-2.html 82711-2-ref.html
+!= 82711-1-ref.html 82711-2-ref.html
!= 468263-1a.html about:blank
!= 468263-1b.html about:blank
!= 468263-1c.html about:blank
!= 468263-1d.html about:blank
== 468263-2.html 468263-2-ref.html
== 468263-2.html 468263-2-alternate-ref.html
== 484200-1.html 484200-1-ref.html
== 485377.html 485377-ref.html
--- a/dom/inputmethod/Keyboard.jsm
+++ b/dom/inputmethod/Keyboard.jsm
@@ -244,24 +244,44 @@ this.Keyboard = {
case 'Keyboard:Unregister':
this._keyboardMM = null;
this._keyboardID = -1;
break;
}
},
forwardEvent: function keyboardForwardEvent(newEventName, msg) {
- this.formMM = msg.target.QueryInterface(Ci.nsIFrameLoaderOwner)
- .frameLoader.messageManager;
+ let mm = msg.target.QueryInterface(Ci.nsIFrameLoaderOwner)
+ .frameLoader.messageManager;
+ if (newEventName === 'Keyboard:FocusChange' &&
+ msg.data.type === 'blur') {
+ // A blur message can't be sent to the keyboard if the focus has
+ // already taken away at first place.
+ // This check is here to prevent problem caused by out-of-order
+ // ipc messages from two processes.
+ if (mm !== this.formMM) {
+ return false;
+ }
+
+ this.sendToKeyboard(newEventName, msg.data);
+ return true;
+ }
+
+ this.formMM = mm;
this.sendToKeyboard(newEventName, msg.data);
+ return true;
},
handleFocusChange: function keyboardHandleFocusChange(msg) {
- this.forwardEvent('Keyboard:FocusChange', msg);
+ let isSent = this.forwardEvent('Keyboard:FocusChange', msg);
+
+ if (!isSent) {
+ return;
+ }
// Chrome event, used also to render value selectors; that's why we need
// the info about choices / min / max here as well...
SystemAppProxy.dispatchEvent({
type: 'inputmethod-contextchange',
inputType: msg.data.type,
value: msg.data.value,
choices: JSON.stringify(msg.data.choices),
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -38,16 +38,17 @@
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/ipc/TestShellChild.h"
#include "mozilla/layers/CompositorChild.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/layers/PCompositorChild.h"
#include "mozilla/layers/SharedBufferManagerChild.h"
#include "mozilla/net/NeckoChild.h"
+#include "mozilla/plugins/PluginModuleParent.h"
#if defined(MOZ_CONTENT_SANDBOX)
#if defined(XP_WIN)
#define TARGET_SANDBOX_EXPORTS
#include "mozilla/sandboxTarget.h"
#include "nsDirectoryServiceDefs.h"
#elif defined(XP_LINUX)
#include "mozilla/Sandbox.h"
@@ -927,16 +928,23 @@ ContentChild::DeallocPCycleCollectWithLo
{
// ...so when we get here, there's nothing for us to do.
//
// Also, we're already in ~CycleCollectWithLogsChild (q.v.) at
// this point, so we shouldn't touch the actor in any case.
return true;
}
+mozilla::plugins::PPluginModuleParent*
+ContentChild::AllocPPluginModuleParent(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess)
+{
+ return plugins::PluginModuleContentParent::Create(aTransport, aOtherProcess);
+}
+
PContentBridgeChild*
ContentChild::AllocPContentBridgeChild(mozilla::ipc::Transport* aTransport,
base::ProcessId aOtherProcess)
{
return ContentBridgeChild::Create(aTransport, aOtherProcess);
}
PContentBridgeParent*
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -96,16 +96,20 @@ public:
ContentBridgeParent* GetLastBridge() {
MOZ_ASSERT(mLastBridge);
ContentBridgeParent* parent = mLastBridge;
mLastBridge = nullptr;
return parent;
}
nsRefPtr<ContentBridgeParent> mLastBridge;
+ PPluginModuleParent *
+ AllocPPluginModuleParent(mozilla::ipc::Transport* transport,
+ base::ProcessId otherProcess) MOZ_OVERRIDE;
+
PContentBridgeParent*
AllocPContentBridgeParent(mozilla::ipc::Transport* transport,
base::ProcessId otherProcess) MOZ_OVERRIDE;
PContentBridgeChild*
AllocPContentBridgeChild(mozilla::ipc::Transport* transport,
base::ProcessId otherProcess) MOZ_OVERRIDE;
PCompositorChild*
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -65,16 +65,17 @@
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/ipc/PFileDescriptorSetParent.h"
#include "mozilla/ipc/TestShellParent.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/layers/CompositorParent.h"
#include "mozilla/layers/ImageBridgeParent.h"
#include "mozilla/layers/SharedBufferManagerParent.h"
#include "mozilla/net/NeckoParent.h"
+#include "mozilla/plugins/PluginBridge.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/unused.h"
#include "nsAnonymousTemporaryFile.h"
#include "nsAppRunner.h"
#include "nsAutoPtr.h"
#include "nsCDefaultURIFixup.h"
@@ -917,16 +918,30 @@ ContentParent::AnswerBridgeToChildProces
return PContentBridge::Bridge(this, cp);
} else {
// You can't bridge to a process you didn't open!
KillHard();
return false;
}
}
+bool
+ContentParent::AnswerLoadPlugin(const uint32_t& aPluginId)
+{
+ return mozilla::plugins::SetupBridge(aPluginId, this);
+}
+
+bool
+ContentParent::RecvFindPlugins(const uint32_t& aPluginEpoch,
+ nsTArray<PluginTag>* aPlugins,
+ uint32_t* aNewPluginEpoch)
+{
+ return mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
+}
+
/*static*/ TabParent*
ContentParent::CreateBrowserOrApp(const TabContext& aContext,
Element* aFrameElement,
ContentParent* aOpenerContentParent)
{
if (!sCanLaunchSubprocesses) {
return nullptr;
}
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -143,16 +143,21 @@ public:
virtual bool RecvCreateChildProcess(const IPCTabContext& aContext,
const hal::ProcessPriority& aPriority,
uint64_t* aId,
bool* aIsForApp,
bool* aIsForBrowser) MOZ_OVERRIDE;
virtual bool AnswerBridgeToChildProcess(const uint64_t& id) MOZ_OVERRIDE;
+ virtual bool AnswerLoadPlugin(const uint32_t& aPluginId) MOZ_OVERRIDE;
+ virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch,
+ nsTArray<PluginTag>* aPlugins,
+ uint32_t* aNewPluginEpoch) MOZ_OVERRIDE;
+
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver)
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_NSIDOMGEOPOSITIONCALLBACK
/**
* MessageManagerCallback methods that we override.
--- a/dom/ipc/CrashReporterChild.cpp
+++ b/dom/ipc/CrashReporterChild.cpp
@@ -20,17 +20,17 @@ CrashReporterChild::GetCrashReporter()
const InfallibleTArray<PCrashReporterChild*>* reporters = nullptr;
switch (XRE_GetProcessType()) {
case GeckoProcessType_Content: {
ContentChild* child = ContentChild::GetSingleton();
reporters = &child->ManagedPCrashReporterChild();
break;
}
case GeckoProcessType_Plugin: {
- PluginModuleChild* child = PluginModuleChild::current();
+ PluginModuleChild* child = PluginModuleChild::GetChrome();
reporters = &child->ManagedPCrashReporterChild();
break;
}
default:
break;
}
if (reporters && reporters->Length() > 0) {
return reporters->ElementAt(0);
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -20,32 +20,34 @@ include protocol PDeviceStorageRequest;
include protocol PFileDescriptorSet;
include protocol PFMRadio;
include protocol PFileSystemRequest;
include protocol PHal;
include protocol PImageBridge;
include protocol PMemoryReportRequest;
include protocol PMobileConnection;
include protocol PNecko;
+include protocol PPluginModule;
include protocol PPrinting;
include protocol PScreenManager;
include protocol PSharedBufferManager;
include protocol PSms;
include protocol PSpeechSynthesis;
include protocol PStorage;
include protocol PTelephony;
include protocol PTestShell;
include protocol PVoicemail;
include protocol PJavaScript;
include protocol PRemoteSpellcheckEngine;
include DOMTypes;
include JavaScriptTypes;
include InputStreamParams;
include PTabContext;
include URIParams;
+include PluginTypes;
include ProtocolTypes;
// Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
// are put into different UnifiedProtocolsXX.cpp files.
// XXX Remove this once bug 1069073 is fixed
include "mozilla/dom/PContentBridgeParent.h";
include "mozilla/dom/indexedDB/SerializationHelpers.h";
@@ -323,16 +325,18 @@ struct ClipboardCapabilities {
union MaybeFileDesc {
FileDescriptor;
void_t;
};
prio(normal upto high) intr protocol PContent
{
+ parent spawns PPluginModule;
+
parent opens PCompositor;
parent opens PSharedBufferManager;
parent opens PImageBridge;
child opens PBackground;
manages PAsmJSCacheEntry;
manages PBlob;
manages PBluetooth;
@@ -527,16 +531,39 @@ parent:
returns (bool isOffline, nsString[] dictionaries,
ClipboardCapabilities clipboardCaps);
sync CreateChildProcess(IPCTabContext context,
ProcessPriority priority)
returns (uint64_t id, bool isForApp, bool isForBrowser);
intr BridgeToChildProcess(uint64_t id);
+ /**
+ * This call connects the content process to a plugin process. While this
+ * call runs, a new PluginModuleParent will be created in the ContentChild
+ * via bridging. The corresponding PluginModuleChild will live in the plugin
+ * process. We use intr semantics here to ensure that the PluginModuleParent
+ * allocation message is dispatched before LoadPlugin returns.
+ */
+ intr LoadPlugin(uint32_t pluginId);
+
+ /**
+ * This call returns the set of plugins loaded in the chrome
+ * process. However, in many cases this set will not have changed since the
+ * last FindPlugins message. Consequently, the chrome process increments an
+ * epoch number every time the set of plugins changes. The content process
+ * sends up the last epoch it observed. If the epochs are the same, the
+ * chrome process returns no plugins. Otherwise it returns a complete list.
+ *
+ * |pluginEpoch| is the epoch last observed by the content
+ * process. |newPluginEpoch| is the current epoch in the chrome process. If
+ * |pluginEpoch == newPluginEpoch|, then |plugins| will be left empty.
+ */
+ sync FindPlugins(uint32_t pluginEpoch) returns (PluginTag[] plugins, uint32_t newPluginEpoch);
+
async PJavaScript();
sync PRemoteSpellcheckEngine();
PDeviceStorageRequest(DeviceStorageParams params);
PFileSystemRequest(FileSystemParams params);
sync PCrashReporter(NativeThreadId tid, uint32_t processType);
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -494,18 +494,19 @@ void MediaDecoder::Shutdown()
// Force any outstanding seek and byterange requests to complete
// to prevent shutdown from deadlocking.
if (mResource) {
mResource->Close();
}
ChangeState(PLAY_STATE_SHUTDOWN);
- // If we hit this assertion, there might be a bug in network state transition.
- NS_ASSERTION(!mProgressTimer, "Progress timer should've been stopped.");
+ if (mProgressTimer) {
+ StopProgress();
+ }
mOwner = nullptr;
MediaShutdownManager::Instance().Unregister(this);
}
MediaDecoder::~MediaDecoder()
{
MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/gmp-plugin/gmp-test-decryptor.cpp
+++ b/dom/media/gmp-plugin/gmp-test-decryptor.cpp
@@ -7,18 +7,18 @@
#include "gmp-test-storage.h"
#include <string>
#include <vector>
#include <iostream>
#include <istream>
#include <iterator>
#include <sstream>
-#include <assert.h>
+#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/NullPtr.h"
using namespace std;
FakeDecryptor* FakeDecryptor::sInstance = nullptr;
static bool sFinishedTruncateTest = false;
@@ -31,30 +31,30 @@ MaybeFinish()
if (sFinishedTruncateTest && sFinishedReplaceTest && sMultiClientTest) {
FakeDecryptor::Message("test-storage complete");
}
}
FakeDecryptor::FakeDecryptor()
: mCallback(nullptr)
{
- assert(!sInstance);
+ MOZ_ASSERT(!sInstance);
sInstance = this;
}
void FakeDecryptor::DecryptingComplete()
{
sInstance = nullptr;
delete this;
}
void
FakeDecryptor::Message(const std::string& aMessage)
{
- assert(sInstance);
+ MOZ_ASSERT(sInstance);
const static std::string sid("fake-session-id");
sInstance->mCallback->SessionMessage(sid.c_str(), sid.size(),
(const uint8_t*)aMessage.c_str(), aMessage.size(),
nullptr, 0);
}
std::vector<std::string>
Tokenize(const std::string& aString)
--- a/dom/media/gmp-plugin/gmp-test-storage.cpp
+++ b/dom/media/gmp-plugin/gmp-test-storage.cpp
@@ -1,16 +1,17 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gmp-test-storage.h"
#include <vector>
-#include <assert.h>
+
+#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
class WriteRecordClient : public GMPRecordClient {
public:
GMPErr Init(GMPRecord* aRecord,
GMPTask* aContinuation,
const uint8_t* aData,
uint32_t aDataSize) {
@@ -114,17 +115,17 @@ private:
GMPRecord* mRecord;
ReadContinuation* mContinuation;
};
GMPErr
ReadRecord(const std::string& aRecordName,
ReadContinuation* aContinuation)
{
- assert(aContinuation);
+ MOZ_ASSERT(aContinuation);
GMPRecord* record;
ReadRecordClient* client = new ReadRecordClient();
auto err = GMPOpenRecord(aRecordName.c_str(),
aRecordName.size(),
&record,
client);
if (GMP_FAILED(err)) {
return err;
@@ -135,24 +136,24 @@ ReadRecord(const std::string& aRecordNam
extern GMPPlatformAPI* g_platform_api; // Defined in gmp-fake.cpp
GMPErr
GMPOpenRecord(const char* aName,
uint32_t aNameLength,
GMPRecord** aOutRecord,
GMPRecordClient* aClient)
{
- assert(g_platform_api);
+ MOZ_ASSERT(g_platform_api);
return g_platform_api->createrecord(aName, aNameLength, aOutRecord, aClient);
}
GMPErr
GMPRunOnMainThread(GMPTask* aTask)
{
- assert(g_platform_api);
+ MOZ_ASSERT(g_platform_api);
return g_platform_api->runonmainthread(aTask);
}
class OpenRecordClient : public GMPRecordClient {
public:
GMPErr Init(GMPRecord* aRecord,
OpenContinuation* aContinuation) {
mRecord = aRecord;
@@ -175,17 +176,17 @@ private:
GMPRecord* mRecord;
OpenContinuation* mContinuation;
};
GMPErr
GMPOpenRecord(const std::string& aRecordName,
OpenContinuation* aContinuation)
{
- assert(aContinuation);
+ MOZ_ASSERT(aContinuation);
GMPRecord* record;
OpenRecordClient* client = new OpenRecordClient();
auto err = GMPOpenRecord(aRecordName.c_str(),
aRecordName.size(),
&record,
client);
if (GMP_FAILED(err)) {
return err;
--- a/dom/media/mediasource/SourceBufferDecoder.h
+++ b/dom/media/mediasource/SourceBufferDecoder.h
@@ -79,28 +79,16 @@ public:
}
void SetTaskQueue(MediaTaskQueue* aTaskQueue)
{
MOZ_ASSERT((!mTaskQueue && aTaskQueue) || (mTaskQueue && !aTaskQueue));
mTaskQueue = aTaskQueue;
}
- void BreakCycles()
- {
- if (mReader) {
- mReader->BreakCycles();
- mReader = nullptr;
- }
- mTaskQueue = nullptr;
-#ifdef MOZ_EME
- mCDMProxy = nullptr;
-#endif
- }
-
#ifdef MOZ_EME
virtual nsresult SetCDMProxy(CDMProxy* aProxy)
{
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mCDMProxy = aProxy;
return NS_OK;
}
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -386,17 +386,17 @@ TrackBuffer::ContainsTime(int64_t aTime)
}
void
TrackBuffer::BreakCycles()
{
MOZ_ASSERT(NS_IsMainThread());
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
- mDecoders[i]->BreakCycles();
+ mDecoders[i]->GetReader()->BreakCycles();
}
mDecoders.Clear();
// These are cleared in Shutdown()
MOZ_ASSERT(mInitializedDecoders.IsEmpty());
MOZ_ASSERT(!mParentDecoder);
}
deleted file mode 100644
--- a/dom/media/test/eme.js
+++ /dev/null
@@ -1,178 +0,0 @@
-const KEYSYSTEM_TYPE = "org.w3.clearkey";
-
-function bail(message)
-{
- return function(err) {
- ok(false, message);
- if (err) {
- info(err);
- }
- SimpleTest.finish();
- }
-}
-
-function ArrayBufferToString(arr)
-{
- var str = '';
- var view = new Uint8Array(arr);
- for (var i = 0; i < view.length; i++) {
- str += String.fromCharCode(view[i]);
- }
- return str;
-}
-
-function StringToArrayBuffer(str)
-{
- var arr = new ArrayBuffer(str.length);
- var view = new Uint8Array(arr);
- for (var i = 0; i < str.length; i++) {
- view[i] = str.charCodeAt(i);
- }
- return arr;
-}
-
-function Base64ToHex(str)
-{
- var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
- var res = "";
- for (var i = 0; i < bin.length; i++) {
- res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
- }
- return res;
-}
-
-function HexToBase64(hex)
-{
- var bin = "";
- for (var i = 0; i < hex.length; i += 2) {
- bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
- }
- return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
-}
-
-function UpdateSessionFunc(test) {
- return function(ev) {
- var msgStr = ArrayBufferToString(ev.message);
- var msg = JSON.parse(msgStr);
-
- info("got message from CDM: " + msgStr);
- is(msg.type, test.sessionType, "Key session type should match");
- ok(msg.kids, "message event should contain key ID array");
-
- var outKeys = [];
-
- for (var i = 0; i < msg.kids.length; i++) {
- var id64 = msg.kids[i];
- var idHex = Base64ToHex(msg.kids[i]).toLowerCase();
- var key = test.keys[idHex];
-
- if (key) {
- info("found key " + key + " for key id " + idHex);
- outKeys.push({
- "kty":"oct",
- "alg":"A128KW",
- "kid":id64,
- "k":HexToBase64(key)
- });
- } else {
- bail("Couldn't find key for key id " + idHex);
- }
- }
-
- var update = JSON.stringify({
- "keys" : outKeys,
- "type" : msg.type
- });
- info("sending update message to CDM: " + update);
-
- ev.target.update(StringToArrayBuffer(update)).then(function() {
- info("MediaKeySession update ok!");
- }, bail("MediaKeySession update failed"));
- }
-}
-
-function PlayFragmented(test, elem)
-{
- return new Promise(function(resolve, reject) {
- var ms = new MediaSource();
- elem.src = URL.createObjectURL(ms);
-
- var sb;
- var curFragment = 0;
-
- function addNextFragment() {
- if (curFragment >= test.fragments.length) {
- ms.endOfStream();
- resolve();
- return;
- }
-
- var fragmentFile = test.fragments[curFragment++];
-
- var req = new XMLHttpRequest();
- req.open("GET", fragmentFile);
- req.responseType = "arraybuffer";
-
- req.addEventListener("load", function() {
- sb.appendBuffer(new Uint8Array(req.response));
- });
-
- info("fetching resource " + fragmentFile);
- req.send(null);
- }
-
- ms.addEventListener("sourceopen", function () {
- sb = ms.addSourceBuffer(test.type);
- sb.addEventListener("updateend", addNextFragment);
-
- addNextFragment();
- })
- });
-}
-
-// Returns a promise that is resovled when the media element is ready to have
-// its play() function called; when it's loaded MSE fragments, or once the load
-// has started for non-MSE video.
-function LoadTest(test, elem)
-{
- if (test.fragments) {
- return PlayFragmented(test, elem);
- }
-
- // This file isn't fragmented; set the media source normally.
- return new Promise(function(resolve, reject) {
- elem.src = test.name;
- resolve();
- });
-}
-
-function SetupEME(test, token, params)
-{
- var v = document.createElement("video");
-
- var onSetKeysFail = (params && params.onSetKeysFail)
- ? params.onSetKeysFail
- : bail(token + " Failed to set MediaKeys on <video> element");
-
- v.addEventListener("encrypted", function(ev) {
- info(token + " got encrypted event");
- MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
- info(token + " created MediaKeys object ok");
- mediaKeys.sessions = [];
- return v.setMediaKeys(mediaKeys);
- }, bail("failed to create MediaKeys object")).then(function() {
- info(token + " set MediaKeys on <video> element ok");
-
- var session = v.mediaKeys.createSession(test.sessionType);
- if (params && params.onsessioncreated) {
- params.onsessioncreated(session);
- }
- session.addEventListener("message", UpdateSessionFunc(test));
- session.generateRequest(ev.initDataType, ev.initData).then(function() {
- }, bail(token + " Failed to initialise MediaKeySession"));
-
- }, onSetKeysFail);
- });
-
- return v;
-}
--- a/dom/media/test/manifest.js
+++ b/dom/media/test/manifest.js
@@ -119,17 +119,17 @@ var gPlayTests = [
// oggz-chop stream
{ name:"bug482461.ogv", type:"video/ogg", duration:4.34 },
// Theora only oggz-chop stream
{ name:"bug482461-theora.ogv", type:"video/ogg", duration:4.138 },
// With first frame a "duplicate" (empty) frame.
{ name:"bug500311.ogv", type:"video/ogg", duration:1.96 },
// Small audio file
- { name:"small-shot.ogg", type:"video/ogg", duration:0.276 },
+ { name:"small-shot.ogg", type:"audio/ogg", duration:0.276 },
// More audio in file than video.
{ name:"short-video.ogv", type:"video/ogg", duration:1.081 },
// First Theora data packet is zero bytes.
{ name:"bug504613.ogv", type:"video/ogg", duration:Number.NaN },
// Multiple audio streams.
{ name:"bug516323.ogv", type:"video/ogg", duration:4.208 },
// oggz-chop with non-keyframe as first frame
{ name:"bug556821.ogv", type:"video/ogg", duration:2.551 },
@@ -674,23 +674,27 @@ function checkMetadata(msg, e, test) {
ok(Math.abs(e.duration - test.duration) < 0.1,
msg + " duration (" + e.duration + ") should be around " + test.duration);
}
}
// Returns the first test from candidates array which we can play with the
// installed video backends.
function getPlayableVideo(candidates) {
- var v = document.createElement("video");
- var resources = candidates.filter(function(x){return /^video/.test(x.type) && v.canPlayType(x.type);});
+ var resources = getPlayableVideos(candidates);
if (resources.length > 0)
return resources[0];
return null;
}
+function getPlayableVideos(candidates) {
+ var v = document.createElement("video");
+ return candidates.filter(function(x){return /^video/.test(x.type) && v.canPlayType(x.type);});
+}
+
function getPlayableAudio(candidates) {
var v = document.createElement("audio");
var resources = candidates.filter(function(x){return /^audio/.test(x.type) && v.canPlayType(x.type);});
if (resources.length > 0)
return resources[0];
return null;
}
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -135,17 +135,16 @@ support-files =
detodos.opus
detodos.opus^headers^
detodos.webm
detodos.webm^headers^
dirac.ogg
dirac.ogg^headers^
dynamic_redirect.sjs
dynamic_resource.sjs
- eme.js
gizmo-frag-cenc1.m4s
gizmo-frag-cenc2.m4s
gizmo-frag-cencinit.mp4
file_access_controls.html
fragment_noplay.js
fragment_play.js
gizmo.mp4
gizmo.mp4^headers^
@@ -356,21 +355,17 @@ skip-if = (toolkit == 'android' && proce
[test_contentDuration7.html]
[test_controls.html]
[test_currentTime.html]
[test_decode_error.html]
[test_decoder_disable.html]
[test_defaultMuted.html]
[test_delay_load.html]
skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
-[test_eme_canvas_blocked.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
-[test_eme_playback.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
-[test_eme_stream_capture_blocked.html]
+[test_encryptedMediaExtensions.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
[test_error_in_video_document.html]
skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !debug) # bug 608634
[test_error_on_404.html]
[test_fastSeek.html]
skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
[test_fastSeek-forwards.html]
[test_imagecapture.html]
@@ -397,17 +392,16 @@ skip-if = (toolkit == 'android' && proce
[test_mediarecorder_getencodeddata.html]
[test_mediarecorder_record_4ch_audiocontext.html]
[test_mediarecorder_record_audiocontext.html]
[test_mediarecorder_record_audiocontext_mlk.html]
[test_mediarecorder_record_audionode.html]
[test_mediarecorder_record_gum_video_timeslice.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' # mimetype check, bug 969289
[test_mediarecorder_record_immediate_stop.html]
-skip-if = toolkit == 'gonk' && debug
[test_mediarecorder_record_no_timeslice.html]
skip-if = toolkit == 'gonk' && debug
[test_mediarecorder_record_nosrc.html]
[test_mediarecorder_record_session.html]
[test_mediarecorder_record_startstopstart.html]
skip-if = toolkit == 'gonk' && debug
[test_mediarecorder_record_stopms.html]
[test_mediarecorder_record_timeslice.html]
deleted file mode 100644
--- a/dom/media/test/test_eme_canvas_blocked.html
+++ /dev/null
@@ -1,67 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
- <title>Test Encrypted Media Extensions</title>
- <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
- <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
- <script type="text/javascript" src="manifest.js"></script>
- <script type="text/javascript" src="eme.js"></script>
-</head>
-<body>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-var manager = new MediaTestManager;
-
-function startTest(test, token)
-{
- manager.started(token);
-
- var sessions = [];
-
- var v = SetupEME(test, token);
- v.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
-
- v.addEventListener("canplay", function(ev) {
- var video = ev.target;
- var canvas = document.createElement("canvas");
- canvas.width = video.videoWidth;
- canvas.height = video.videoHeight;
- document.body.appendChild(canvas);
- var ctx = canvas.getContext("2d");
- var threwError = false;
- try {
- ctx.drawImage(video, 0, 0);
- } catch (ex) {
- threwError = true;
- }
- ok(threwError, token + " - Should throw an error when trying to draw EME video to canvas.");
- manager.finished(token);
- });
-
- v.addEventListener("error", bail(token + " got error event"));
-
- LoadTest(test, v);
-}
-
-function beginTest() {
- manager.runTests(gEMETests, startTest);
-}
-
-var prefs = [
- [ "media.mediasource.enabled", true ],
- [ "media.mediasource.ignore_codecs", true ],
-];
-
-if (/Linux/.test(navigator.userAgent) ||
- !document.createElement('video').canPlayType("video/mp4")) {
- // XXX remove once we have mp4 PlatformDecoderModules on all platforms.
- prefs.push([ "media.fragmented-mp4.exposed", true ]);
- prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
-}
-
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/dom/media/test/test_eme_stream_capture_blocked.html
+++ /dev/null
@@ -1,102 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
- <title>Test Encrypted Media Extensions</title>
- <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
- <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
- <script type="text/javascript" src="manifest.js"></script>
- <script type="text/javascript" src="eme.js"></script>
-</head>
-<body>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-var manager = new MediaTestManager;
-
-function startTest(test, token)
-{
- // Three cases:
- // 1. setting MediaKeys on an element captured by MediaElementSource should fail, and
- // 2. creating a MediaElementSource on a media element with a MediaKeys should fail, and
- // 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail.
-
- // Case 1. setting MediaKeys on an element captured by MediaElementSource should fail.
- var case1token = token + "_case1";
- var setKeysFailed = function() {
- ok(true, case1token + " setMediaKeys failed as expected.");
- manager.finished(case1token);
- };
- var v1 = SetupEME(test, case1token, { onSetKeysFail: setKeysFailed });
- var context = new AudioContext();
- var node = context.createMediaElementSource(v1);
- v1.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
- v1.addEventListener("error", bail(case1token + " got error event"));
- v1.addEventListener("canplay", function(ev) {
- ok(false, case1token + " should never reach canplay, as setMediaKeys should fail");
- });
- manager.started(case1token);
- LoadTest(test, v1);
-
-
- // Case 2. creating a MediaElementSource on a media element with a MediaKeys should fail.
- var case2token = token + "_case2";
- var v2 = SetupEME(test, case2token);
- v2.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
- v2.addEventListener("error", bail(case2token + " got error event"));
- v2.addEventListener("canplay", function(ev) {
- ok(true, case2token + " should reach canplay");
- var threw = false;
- try {
- var context = new AudioContext();
- var node = context.createMediaElementSource(v2);
- } catch (e) {
- threw = true;
- }
- ok(threw, "Should throw an error creating a MediaElementSource on an EME video.");
- manager.finished(case2token);
- });
- manager.started(case2token);
- LoadTest(test, v2);
-
-
- // Case 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail.
- var case3token = token + "_case3";
- var v3 = SetupEME(test, case3token);
- v3.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
- v3.addEventListener("error", bail(case3token + " got error event"));
- v3.addEventListener("canplay", function(ev) {
- ok(true, case3token + " should reach canplay");
- var threw = false;
- try {
- var stream = v3.mozCaptureStreamUntilEnded();
- } catch (e) {
- threw = true;
- }
- ok(threw, "Should throw an error calling mozCaptureStreamUntilEnded an EME video.");
- manager.finished(case3token);
- });
- manager.started(case3token);
- LoadTest(test, v3);
-}
-
-function beginTest() {
- manager.runTests(gEMETests, startTest);
-}
-
-var prefs = [
- [ "media.mediasource.enabled", true ],
- [ "media.mediasource.ignore_codecs", true ],
-];
-
-if (/Linux/.test(navigator.userAgent) ||
- !document.createElement('video').canPlayType("video/mp4")) {
- // XXX remove once we have mp4 PlatformDecoderModules on all platforms.
- prefs.push([ "media.fragmented-mp4.exposed", true ]);
- prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
-}
-
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
-</script>
-</pre>
-</body>
-</html>
rename from dom/media/test/test_eme_playback.html
rename to dom/media/test/test_encryptedMediaExtensions.html
--- a/dom/media/test/test_eme_playback.html
+++ b/dom/media/test/test_encryptedMediaExtensions.html
@@ -1,96 +1,246 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test Encrypted Media Extensions</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="manifest.js"></script>
- <script type="text/javascript" src="eme.js"></script>
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
var manager = new MediaTestManager;
+const KEYSYSTEM_TYPE = "org.w3.clearkey";
-function KeysChangeFunc(session, keys, token) {
+function bail(message)
+{
+ return function(err) {
+ ok(false, message);
+ if (err) {
+ info(err);
+ }
+ SimpleTest.finish();
+ }
+}
+
+function ArrayBufferToString(arr)
+{
+ var str = '';
+ var view = new Uint8Array(arr);
+ for (var i = 0; i < view.length; i++) {
+ str += String.fromCharCode(view[i]);
+ }
+ return str;
+}
+
+function StringToArrayBuffer(str)
+{
+ var arr = new ArrayBuffer(str.length);
+ var view = new Uint8Array(arr);
+ for (var i = 0; i < str.length; i++) {
+ view[i] = str.charCodeAt(i);
+ }
+ return arr;
+}
+
+function Base64ToHex(str)
+{
+ var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
+ var res = "";
+ for (var i = 0; i < bin.length; i++) {
+ res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
+ }
+ return res;
+}
+
+function HexToBase64(hex)
+{
+ var bin = "";
+ for (var i = 0; i < hex.length; i += 2) {
+ bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
+ }
+ return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
+}
+
+function UpdateSessionFunc(test) {
+ return function(ev) {
+ var msgStr = ArrayBufferToString(ev.message);
+ var msg = JSON.parse(msgStr);
+
+ info("got message from CDM: " + msgStr);
+ is(msg.type, test.sessionType, "Key session type should match");
+ ok(msg.kids, "message event should contain key ID array");
+
+ var outKeys = [];
+
+ for (var i = 0; i < msg.kids.length; i++) {
+ var id64 = msg.kids[i];
+ var idHex = Base64ToHex(msg.kids[i]).toLowerCase();
+ var key = test.keys[idHex];
+
+ if (key) {
+ info("found key " + key + " for key id " + idHex);
+ outKeys.push({
+ "kty":"oct",
+ "alg":"A128KW",
+ "kid":id64,
+ "k":HexToBase64(key)
+ });
+ } else {
+ bail("Couldn't find key for key id " + idHex);
+ }
+ }
+
+ var update = JSON.stringify({
+ "keys" : outKeys,
+ "type" : msg.type
+ });
+ info("sending update message to CDM: " + update);
+
+ ev.target.update(StringToArrayBuffer(update)).then(function() {
+ info("MediaKeySession update ok!");
+ }, bail("MediaKeySession update failed"));
+ }
+}
+
+function PlayFragmented(test, elem)
+{
+ var ms = new MediaSource();
+ elem.src = URL.createObjectURL(ms);
+
+ var sb;
+ var curFragment = 0;
+
+ function addNextFragment() {
+ if (curFragment >= test.fragments.length) {
+ ms.endOfStream();
+ elem.play();
+ return;
+ }
+
+ var fragmentFile = test.fragments[curFragment++];
+
+ var req = new XMLHttpRequest();
+ req.open("GET", fragmentFile);
+ req.responseType = "arraybuffer";
+
+ req.addEventListener("load", function() {
+ sb.appendBuffer(new Uint8Array(req.response));
+ });
+
+ info("fetching resource " + fragmentFile);
+ req.send(null);
+ }
+
+ ms.addEventListener("sourceopen", function () {
+ sb = ms.addSourceBuffer(test.type);
+ sb.addEventListener("updateend", addNextFragment);
+
+ addNextFragment();
+ });
+}
+
+function PlayTest(test, elem)
+{
+ if (test.fragments) {
+ PlayFragmented(test, elem);
+ return;
+ }
+
+ // This file isn't fragmented; set the media source normally.
+ elem.src = test.name;
+ elem.play();
+}
+
+function KeysChangeFunc(session, keys) {
session.keyIdsReceived = [];
for (var keyid in keys) {
info("Set " + keyid + " to false in session.keyIdsReceived");
session.keyIdsReceived[keyid] = false;
}
return function(ev) {
var session = ev.target;
session.gotKeysChanged = true;
session.getUsableKeyIds().then(function(keyIds) {
for (var k = 0; k < keyIds.length; k++) {
var kid = Base64ToHex(window.btoa(ArrayBufferToString(keyIds[k])));
- ok(kid in session.keyIdsReceived, token + " session.keyIdsReceived contained " + kid + " as expected.");
+ ok(kid in session.keyIdsReceived, "session.keyIdsReceived contained " + kid + " as expected.");
session.keyIdsReceived[kid] = true;
}
}, bail("Failed to get keyIds"));
}
}
function startTest(test, token)
{
- manager.started(token);
-
- var sessions = [];
+ manager.started(test._token);
- var v = SetupEME(test, token,
- {
- onsessioncreated: function(session) {
- sessions.push(session);
- session.addEventListener("keyschange", KeysChangeFunc(session, test.keys, token), false);
- }
- }
- );
-
+ var v = document.createElement("video");
var gotEncrypted = false;
var gotPlaying = false;
v.addEventListener("encrypted", function(ev) {
+ gotEncrypted = true;
+
+ info(token + " got encrypted event");
ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
token + " MediaKeys should support this keysystem");
- gotEncrypted = true;
+
+ MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
+ info(token + " created MediaKeys object ok");
+ mediaKeys.sessions = [];
+ return v.setMediaKeys(mediaKeys);
+ }, bail("failed to create MediaKeys object")).then(function() {
+ info(token + " set MediaKeys on <video> element ok");
+
+ ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
+ "MediaKeys should still support keysystem after CDM created...");
+
+ var session = v.mediaKeys.createSession(test.sessionType);
+ v.mediaKeys.sessions.push(session);
+ session.addEventListener("keyschange", KeysChangeFunc(session, test.keys), false);
+ session.addEventListener("message", UpdateSessionFunc(test));
+ session.generateRequest(ev.initDataType, ev.initData).then(function() {
+ }, bail(token + " Failed to initialise MediaKeySession"));
+
+ }, bail(token + " Failed to set MediaKeys on <video> element"));
});
v.addEventListener("playing", function () { gotPlaying = true; });
v.addEventListener("ended", function(ev) {
ok(true, token + " got ended event");
- manager.finished(token);
+ manager.finished(test._token);
ok(gotEncrypted, token + " encrypted event should have fired");
ok(gotPlaying, token + " playing event should have fired");
ok(Math.abs(test.duration - v.duration) < 0.1,
token + " Duration of video should be corrrect");
ok(Math.abs(test.duration - v.currentTime) < 0.1,
token + " Current time should be same as duration");
-
// Verify all sessions had all keys went sent the to the CDM usable, and thus
// that we received keyschange event(s).
+ var sessions = v.mediaKeys.sessions;
is(sessions.length, 1, "should have 1 session");
for (var i = 0; i < sessions.length; i++) {
var session = sessions[i];
- ok(session.gotKeysChanged, token + " should have received at least one keychange event");
+ ok(session.gotKeysChanged, "Should have received at least one keychange event");
for (var kid in session.keyIdsReceived) {
- ok(session.keyIdsReceived[kid], token + " key with id " + kid + " was usable as expected");
+ ok(session.keyIdsReceived[kid], "key with id " + kid + " was usable as expected");
}
}
-
- });
+ });
v.addEventListener("error", bail(token + " got error event"));
- LoadTest(test, v).then(function(){v.play();}, bail(token + " failed to load"));
+ PlayTest(test, v);
}
function testIsTypeSupported()
{
var t = MediaKeys.isTypeSupported;
const clearkey = "org.w3.clearkey";
ok(!t("bogus", "bogon", "video/bogus"), "Invalid type.");
ok(t(clearkey), "ClearKey supported.");
--- a/dom/media/test/test_reset_src.html
+++ b/dom/media/test/test_reset_src.html
@@ -9,78 +9,87 @@ https://bugzilla.mozilla.org/show_bug.cg
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="manifest.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=804875">Mozilla Bug 804875</a>
-<video style="border: 4px solid red" controls></video>
<canvas></canvas>
<pre id="test">
<script class="testbody" type="text/javascript">
+var manager = new MediaTestManager;
+
+function finish(v) {
+ removeNodeAndSource(v);
+ manager.finished(v.token);
+}
+
function onLoadedMetadata_Audio(e) {
var t = e.target;
- is(t.videoHeight, 0, "videoHeight should be zero when there is no video.");
- is(t.videoWidth, 0, "videoWidth should be zero when there is no video.");
- is(t.mozPaintedFrames, 0, "mozPaintedFrames should be zero when there is no video.");
- is(t.mozFrameDelay, 0, "mozFrameDelay should be zero when there is no video.");
+ is(t.videoHeight, 0, t.name + ": videoHeight should be zero when there is no video.");
+ is(t.videoWidth, 0, t.name + ": videoWidth should be zero when there is no video.");
+ is(t.mozPaintedFrames, 0, t.name + ": mozPaintedFrames should be zero when there is no video.");
+ is(t.mozFrameDelay, 0, t.name + ": mozFrameDelay should be zero when there is no video.");
var c = document.getElementsByTagName("canvas")[0].getContext("2d");
try {
c.drawImage(t, 0, 0, t.videoHeight, t.videoWidth);
} catch (e) {
- ok(true, "Trying to draw to a canvas should throw, since we don't have a frame anymore");
- SimpleTest.finish();
+ ok(true, t.name + ": Trying to draw to a canvas should throw, since we don't have a frame anymore");
+ finish(t);
return;
}
- ok(false, "We should not succeed to draw a video frame on the canvas.");
+ ok(false, t.name + ": We should not succeed to draw a video frame on the canvas.");
}
function onTimeUpdate_Video(e) {
var t = e.target;
if (t.currentTime < t.duration / 4) {
return;
}
t.removeEventListener("timeupdate", onTimeUpdate_Video);
- ok(t.mozPaintedFrames > 0, "mozPaintedFrames should be positive, is " + t.mozPaintedFrames + ".");
- ok(t.mozFrameDelay >= 0, "mozFrameDelay should be positive or zero, is " + t.mozFrameDelay + ".");
+ ok(t.mozPaintedFrames > 0, t.name + ": mozPaintedFrames should be positive, is " + t.mozPaintedFrames + ".");
+ ok(t.mozFrameDelay >= 0, t.name + ": mozFrameDelay should be positive or zero, is " + t.mozFrameDelay + ".");
- if (v._firstTime) {
+ if (t._firstTime) {
t.src = t.src;
- v._firstTime = false;
+ t._firstTime = false;
} else {
var source = getPlayableAudio(gPlayTests);
if (!source) {
todo("No audio file available.")
- SimpleTest.finish();
+ finish(t);
} else {
t.removeEventListener("loadedmetadata", onLoadedMetadata_Video);
t.addEventListener("loadedmetadata", onLoadedMetadata_Audio);
t.src = source.name;
}
}
}
function onLoadedMetadata_Video(e) {
var t = e.target;
- ok(t.videoHeight != 0, "We should have a videoHeight.");
- ok(t.videoWidth != 0, "We should have a videoWidth.");
+ isnot(t.videoHeight, 0, t.name + ": We should have a videoHeight.");
+ isnot(t.videoWidth, 0, t.name + ": We should have a videoWidth.");
t.addEventListener("timeupdate", onTimeUpdate_Video);
t.play();
}
-var v = document.getElementsByTagName("video")[0];
-v._firstTime = true;
-var source = getPlayableVideo(gPlayTests);
-if (!source) {
- todo("No video file available.");
-} else {
+function startTest(test, token) {
+ var v = document.createElement('video');
+ document.body.appendChild(v);
+ v.preload = "metadata";
+ v._firstTime = true;
v.addEventListener("loadedmetadata", onLoadedMetadata_Video);
- v.src = source.name;
- SimpleTest.waitForExplicitFinish();
+ v.src = test.name;
+ v.token = token;
+ v.name = test.name;
+ manager.started(token);
}
+
+manager.runTests(getPlayableVideos(gSmallTests.concat(gSeekTests)), startTest);
</script>
</pre>
</body>
</html>
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -280,22 +280,16 @@ AudioContext::CreateAnalyser()
already_AddRefed<MediaElementAudioSourceNode>
AudioContext::CreateMediaElementSource(HTMLMediaElement& aMediaElement,
ErrorResult& aRv)
{
if (mIsOffline) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return nullptr;
}
-#ifdef MOZ_EME
- if (aMediaElement.ContainsRestrictedContent()) {
- aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
- return nullptr;
- }
-#endif
nsRefPtr<DOMMediaStream> stream = aMediaElement.MozCaptureStream(aRv);
if (aRv.Failed()) {
return nullptr;
}
nsRefPtr<MediaElementAudioSourceNode> mediaElementAudioSourceNode =
new MediaElementAudioSourceNode(this, stream);
return mediaElementAudioSourceNode.forget();
}
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -88,16 +88,17 @@ DIRS += [
'ipc',
'identity',
'workers',
'camera',
'audiochannel',
'promise',
'smil',
'telephony',
+ 'tv',
'voicemail',
'inputmethod',
'webidl',
'xbl',
'xml',
'xslt',
'xul',
'resourcestats',
--- a/dom/network/NetUtils.cpp
+++ b/dom/network/NetUtils.cpp
@@ -23,16 +23,18 @@ InitNetUtilsLib()
static void*
GetNetUtilsLibHandle()
{
PR_CallOnce(&sInitNetUtilsLib, InitNetUtilsLib);
return sNetUtilsLib;
}
+mozilla::Mutex NetUtils::sIfcMutex("NetUtils::sIfcMutex");
+
// static
void*
NetUtils::GetSharedLibrary()
{
void* netLib = GetNetUtilsLibHandle();
if (!netLib) {
NS_WARNING("No /system/lib/libnetutils.so");
}
@@ -57,94 +59,93 @@ DEFINE_DLFUNC(ifc_reset_connections, int
DEFINE_DLFUNC(ifc_set_default_route, int32_t, const char*, in_addr_t)
DEFINE_DLFUNC(ifc_add_route, int32_t, const char*, const char*, uint32_t, const char*)
DEFINE_DLFUNC(ifc_remove_route, int32_t, const char*, const char*, uint32_t, const char*)
DEFINE_DLFUNC(ifc_remove_host_routes, int32_t, const char*)
DEFINE_DLFUNC(ifc_remove_default_route, int32_t, const char*)
DEFINE_DLFUNC(dhcp_stop, int32_t, const char*)
NetUtils::NetUtils()
- : mIfcMutex("NetUtils::mIfcMutex")
{
}
int32_t NetUtils::do_ifc_enable(const char *ifname)
{
USE_DLFUNC(ifc_enable)
- mozilla::MutexAutoLock lock(mIfcMutex);
+ mozilla::MutexAutoLock lock(sIfcMutex);
return ifc_enable(ifname);
}
int32_t NetUtils::do_ifc_disable(const char *ifname)
{
USE_DLFUNC(ifc_disable)
- mozilla::MutexAutoLock lock(mIfcMutex);
+ mozilla::MutexAutoLock lock(sIfcMutex);
return ifc_disable(ifname);
}
int32_t NetUtils::do_ifc_configure(const char *ifname,
in_addr_t address,
uint32_t prefixLength,
in_addr_t gateway,
in_addr_t dns1,
in_addr_t dns2)
{
USE_DLFUNC(ifc_configure)
- mozilla::MutexAutoLock lock(mIfcMutex);
+ mozilla::MutexAutoLock lock(sIfcMutex);
int32_t ret = ifc_configure(ifname, address, prefixLength, gateway, dns1, dns2);
return ret;
}
int32_t NetUtils::do_ifc_reset_connections(const char *ifname,
const int32_t resetMask)
{
USE_DLFUNC(ifc_reset_connections)
- mozilla::MutexAutoLock lock(mIfcMutex);
+ mozilla::MutexAutoLock lock(sIfcMutex);
return ifc_reset_connections(ifname, resetMask);
}
int32_t NetUtils::do_ifc_set_default_route(const char *ifname,
in_addr_t gateway)
{
USE_DLFUNC(ifc_set_default_route)
- mozilla::MutexAutoLock lock(mIfcMutex);
+ mozilla::MutexAutoLock lock(sIfcMutex);
return ifc_set_default_route(ifname, gateway);
}
int32_t NetUtils::do_ifc_add_route(const char *ifname,
const char *dst,
uint32_t prefixLength,
const char *gateway)
{
USE_DLFUNC(ifc_add_route)
- mozilla::MutexAutoLock lock(mIfcMutex);
+ mozilla::MutexAutoLock lock(sIfcMutex);
return ifc_add_route(ifname, dst, prefixLength, gateway);
}
int32_t NetUtils::do_ifc_remove_route(const char *ifname,
const char *dst,
uint32_t prefixLength,
const char *gateway)
{
USE_DLFUNC(ifc_remove_route)
- mozilla::MutexAutoLock lock(mIfcMutex);
+ mozilla::MutexAutoLock lock(sIfcMutex);
return ifc_remove_route(ifname, dst, prefixLength, gateway);
}
int32_t NetUtils::do_ifc_remove_host_routes(const char *ifname)
{
USE_DLFUNC(ifc_remove_host_routes)
- mozilla::MutexAutoLock lock(mIfcMutex);
+ mozilla::MutexAutoLock lock(sIfcMutex);
return ifc_remove_host_routes(ifname);
}
int32_t NetUtils::do_ifc_remove_default_route(const char *ifname)
{
USE_DLFUNC(ifc_remove_default_route)
- mozilla::MutexAutoLock lock(mIfcMutex);
+ mozilla::MutexAutoLock lock(sIfcMutex);
return ifc_remove_default_route(ifname);
}
int32_t NetUtils::do_dhcp_stop(const char *ifname)
{
USE_DLFUNC(dhcp_stop)
return dhcp_stop(ifname);
}
--- a/dom/network/NetUtils.h
+++ b/dom/network/NetUtils.h
@@ -56,17 +56,17 @@ public:
char *dns2,
char *server,
uint32_t *lease,
char* vendorinfo);
static int32_t SdkVersion();
private:
- mozilla::Mutex mIfcMutex;
+ static mozilla::Mutex sIfcMutex;
};
// Defines a function type with the right arguments and return type.
#define DEFINE_DLFUNC(name, ret, args...) typedef ret (*FUNC##name)(args);
// Set up a dlsymed function ready to use.
#define USE_DLFUNC(name) \
FUNC##name name = (FUNC##name) dlsym(GetSharedLibrary(), #name); \
--- a/dom/nfc/NfcContentHelper.js
+++ b/dom/nfc/NfcContentHelper.js
@@ -403,22 +403,29 @@ NfcContentHelper.prototype = {
if (result.errorMsg) {
this.fireRequestError(atob(result.requestId), result.errorMsg);
} else {
this.fireRequestSuccess(atob(result.requestId), result);
}
break;
case "NFC:DOMEvent":
switch (result.event) {
- case NFC.NFC_PEER_EVENT_READY:
+ case NFC.PEER_EVENT_READY:
this.eventTarget.notifyPeerReady(result.sessionToken);
break;
- case NFC.NFC_PEER_EVENT_LOST:
+ case NFC.PEER_EVENT_LOST:
this.eventTarget.notifyPeerLost(result.sessionToken);
break;
+ case NFC.TAG_EVENT_FOUND:
+ let event = new NfcTagEvent(result.techList);
+ this.eventTarget.notifyTagFound(result.sessionToken, event, result.records);
+ break;
+ case NFC.TAG_EVENT_LOST:
+ this.eventTarget.notifyTagLost(result.sessionToken);
+ break;
}
break;
}
},
handle: function handle(name, result) {
switch (name) {
case NFC.SETTING_NFC_DEBUG:
@@ -457,11 +464,20 @@ NfcContentHelper.prototype = {
handleCheckP2PRegistrationResponse: function handleCheckP2PRegistrationResponse(result) {
// Privilaged status API. Always fire success to avoid using exposed props.
// The receiver must check the boolean mapped status code to handle.
let requestId = atob(result.requestId);
this.fireRequestSuccess(requestId, !result.errorMsg);
},
};
+function NfcTagEvent(techList) {
+ this.techList = techList;
+}
+NfcTagEvent.prototype = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsINfcTagEvent]),
+
+ techList: null
+};
+
if (NFC_ENABLED) {
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NfcContentHelper]);
}
--- a/dom/nfc/gonk/Nfc.js
+++ b/dom/nfc/gonk/Nfc.js
@@ -242,29 +242,44 @@ XPCOMUtils.defineLazyGetter(this, "gMess
let isValid = (sessionToken != null) && (target != null);
if (!isValid) {
debug("Peer already lost or " + appId + " is not a registered PeerReadytarget");
return;
}
// Remember the target that receives onpeerready.
this.currentPeer = target;
- this.notifyDOMEvent(target, {event: NFC.NFC_PEER_EVENT_READY,
+ this.notifyDOMEvent(target, {event: NFC.PEER_EVENT_READY,
sessionToken: sessionToken});
},
+ onTagFound: function onTagFound(message) {
+ message.event = NFC.TAG_EVENT_FOUND;
+ for (let target of this.eventTargets) {
+ this.notifyDOMEvent(target, message);
+ }
+ delete message.event;
+ },
+
+ onTagLost: function onTagLost(sessionToken) {
+ for (let target of this.eventTargets) {
+ this.notifyDOMEvent(target, {event: NFC.TAG_EVENT_LOST,
+ sessionToken: sessionToken});
+ }
+ },
+
onPeerLost: function onPeerLost(sessionToken) {
if (!this.currentPeer) {
// The target is already killed.
return;
}
// For peerlost, the message is delievered to the target which
// onpeerready has been called before.
- this.notifyDOMEvent(this.currentPeer, {event: NFC.NFC_PEER_EVENT_LOST,
+ this.notifyDOMEvent(this.currentPeer, {event: NFC.PEER_EVENT_LOST,
sessionToken: sessionToken});
this.currentPeer = null;
},
/**
* nsIMessageListener interface methods.
*/
@@ -521,27 +536,34 @@ Nfc.prototype = {
break;
case "TechDiscoveredNotification":
message.type = "techDiscovered";
// Update the upper layers with a session token (alias)
message.sessionToken =
SessionHelper.registerSession(message.sessionId, message.techList);
// Do not expose the actual session to the content
+ let sessionId = message.sessionId;
delete message.sessionId;
+ if (!SessionHelper.isP2PSession(sessionId)) {
+ gMessageManager.onTagFound(message);
+ }
+
gSystemMessenger.broadcastMessage("nfc-manager-tech-discovered", message);
break;
case "TechLostNotification":
message.type = "techLost";
// Update the upper layers with a session token (alias)
message.sessionToken = SessionHelper.getToken(message.sessionId);
if (SessionHelper.isP2PSession(message.sessionId)) {
gMessageManager.onPeerLost(message.sessionToken);
+ } else {
+ gMessageManager.onTagLost(message.sessionToken);
}
SessionHelper.unregisterSession(message.sessionId);
// Do not expose the actual session to the content
delete message.sessionId;
gSystemMessenger.broadcastMessage("nfc-manager-tech-lost", message);
break;
--- a/dom/nfc/gonk/nfc_consts.js
+++ b/dom/nfc/gonk/nfc_consts.js
@@ -81,13 +81,15 @@ this.NFC_POWER_LEVEL_DISABLED = 0;
this.NFC_POWER_LEVEL_LOW = 1;
this.NFC_POWER_LEVEL_ENABLED = 2;
this.TOPIC_MOZSETTINGS_CHANGED = "mozsettings-changed";
this.TOPIC_XPCOM_SHUTDOWN = "xpcom-shutdown";
this.SETTING_NFC_DEBUG = "nfc.debugging.enabled";
-this.NFC_PEER_EVENT_READY = 0x01;
-this.NFC_PEER_EVENT_LOST = 0x02;
+this.PEER_EVENT_READY = 0x01;
+this.PEER_EVENT_LOST = 0x02;
+this.TAG_EVENT_FOUND = 0x03;
+this.TAG_EVENT_LOST = 0x04;
// Allow this file to be imported via Components.utils.import().
this.EXPORTED_SYMBOLS = Object.keys(this);
--- a/dom/nfc/nsINfcContentHelper.idl
+++ b/dom/nfc/nsINfcContentHelper.idl
@@ -2,34 +2,62 @@
* 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"
#include "nsIDOMDOMRequest.idl"
interface nsIVariant;
-[scriptable, uuid(e81cc1ac-6f0b-4581-a9fb-7ee47ed0158e)]
+[scriptable, uuid(44eb02f4-6eba-4c12-8ed1-c142513776c9)]
+interface nsINfcTagEvent : nsISupports
+{
+ readonly attribute nsIVariant techList;
+};
+
+[scriptable, uuid(8d77d653-f76a-4a21-ae3f-c4cc6074d8ec)]
interface nsINfcDOMEventTarget : nsISupports
{
/**
+ * Callback function used to notify tagfound.
+ *
+ * @param sessionToken
+ * SessionToken received from parent process
+ * @param event
+ * nsINfcTagFoundEvent received from parent process.
+ * @param ndefRecords
+ * NDEF records pre-read during tag-discovered.
+ */
+ void notifyTagFound(in DOMString sessionToken,
+ in nsINfcTagEvent event,
+ in nsIVariant ndefRecords);
+
+ /**
+ * Callback function used to notify taglost.
+ *
+ * @param sessionToken
+ * SessionToken received from parent process
+ */
+ void notifyTagLost(in DOMString sessionToken);
+
+ /**
* Callback function used to notify peerready.
*
* @param sessionToken
- * SessionToken received from Chrome process
+ * SessionToken received from parent process
*/
- void notifyPeerReady(in DOMString sessionToken);
+ void notifyPeerReady(in DOMString sessionToken);
/**
* Callback function used to notify peerlost.
*
* @param sessionToken
- * SessionToken received from Chrome process
+ * SessionToken received from parent process
*/
- void notifyPeerLost(in DOMString sessionToken);
+ void notifyPeerLost(in DOMString sessionToken);
};
[scriptable, uuid(cb9c934d-a7fa-422b-bcc1-4ac39741e6ec)]
interface nsINfcContentHelper : nsISupports
{
const long NFC_EVENT_PEER_READY = 0x01;
const long NFC_EVENT_PEER_LOST = 0x02;
@@ -39,104 +67,104 @@ interface nsINfcContentHelper : nsISuppo
nsIDOMDOMRequest readNDEF(in nsIDOMWindow window, in DOMString sessionToken);
nsIDOMDOMRequest writeNDEF(in nsIDOMWindow window, in nsIVariant records, in DOMString sessionToken);
nsIDOMDOMRequest makeReadOnlyNDEF(in nsIDOMWindow window, in DOMString sessionToken);
nsIDOMDOMRequest connect(in nsIDOMWindow window, in unsigned long techType, in DOMString sessionToken);
nsIDOMDOMRequest close(in nsIDOMWindow window, in DOMString sessionToken);
- /**
- * Initiate Send file operation
- *
- * @param window
- * Current window
- *
- * @param blob
- * Raw data of the file to be sent. This object represents a file-like
- * (nsIDOMFile) object of immutable, raw data. The blob data needs
- * to be 'object wrapped' before calling this interface.
- *
- * @param sessionToken
- * Current token
- *
- * Returns DOMRequest, if initiation of send file operation is successful
- * then 'onsuccess' is called else 'onerror'
- */
+ /**
+ * Initiate send file operation.
+ *
+ * @param window
+ * Current window
+ *
+ * @param blob
+ * Raw data of the file to be sent. This object represents a file-like
+ * (nsIDOMFile) object of immutable, raw data. The blob data needs
+ * to be 'object wrapped' before calling this interface.
+ *
+ * @param sessionToken
+ * Current token
+ *
+ * Returns DOMRequest, if initiation of send file operation is successful
+ * then 'onsuccess' is called else 'onerror'
+ */
nsIDOMDOMRequest sendFile(in nsIDOMWindow window,
in jsval blob,
in DOMString sessionToken);
/**
* Register the event target.
*
* @param target An instance of the nsINfcDOMEventTarget.
*/
void registerEventTarget(in nsINfcDOMEventTarget target);
- /**
- * Register the given application id with Chrome process
- *
- * @param window
- * Current window
- *
- * @param appId
- * Application ID to be registered
- */
+ /**
+ * Register the given application id with parent process
+ *
+ * @param window
+ * Current window
+ *
+ * @param appId
+ * Application ID to be registered
+ */
void registerTargetForPeerReady(in nsIDOMWindow window,
in unsigned long appId);
- /**
- * Unregister the given application id with Chrome process
- *
- * @param window
- * Current window
- *
- * @param appId
- * Application ID to be registered
- */
+ /**
+ * Unregister the given application id with parent process
+ *
+ * @param window
+ * Current window
+ *
+ * @param appId
+ * Application ID to be registered
+ */
void unregisterTargetForPeerReady(in nsIDOMWindow window,
in unsigned long appId);
- /**
- * Checks if the given application's id is a registered peer target (with the Chrome process)
- *
- * @param window
- * Current window
- *
- * @param appId
- * Application ID to be updated with Chrome process
- *
- * Returns DOMRequest, if appId is registered then 'onsuccess' is called else 'onerror'
- */
+ /**
+ * Checks if the given application's id is a registered peer target (with the parent process)
+ *
+ * @param window
+ * Current window
+ *
+ * @param appId
+ * Application ID to be updated with parent process
+ *
+ * Returns DOMRequest, if appId is registered then 'onsuccess' is called else 'onerror'
+ */
nsIDOMDOMRequest checkP2PRegistration(in nsIDOMWindow window, in unsigned long appId);
- /**
- * Notify the Chrome process that user has accepted to share nfc message on P2P UI
- *
- * @param window
- * Current window
- *
- * @param appId
- * Application ID that is capable of handling NFC_EVENT_PEER_READY event
- */
+ /**
+ * Notify the parent process that user has accepted to share nfc message on P2P UI
+ *
+ * @param window
+ * Current window
+ *
+ * @param appId
+ * Application ID that is capable of handling NFC_EVENT_PEER_READY event
+ */
void notifyUserAcceptedP2P(in nsIDOMWindow window, in unsigned long appId);
- /**
- * Notify the status of sendFile operation to Chrome process
- *
- * @param window
- * Current window
- *
- * @param status
- * Status of sendFile operation
- *
- * @param requestId
- * Request ID of SendFile DOM Request
- */
+ /**
+ * Notify the status of sendFile operation to parent process
+ *
+ * @param window
+ * Current window
+ *
+ * @param status
+ * Status of sendFile operation
+ *
+ * @param requestId
+ * Request ID of SendFile DOM Request
+ */
void notifySendFileStatus(in nsIDOMWindow window,
in octet status,
in DOMString requestId);
/**
* Power on the NFC hardware and start polling for NFC tags or devices.
*/
nsIDOMDOMRequest startPoll(in nsIDOMWindow window);
--- a/dom/nfc/nsNfc.js
+++ b/dom/nfc/nsNfc.js
@@ -120,16 +120,21 @@ function mozNfc() {
mozNfc.prototype = {
_nfcContentHelper: null,
_window: null,
nfcObject: null,
init: function init(aWindow) {
debug("mozNfc init called");
this._window = aWindow;
+ this.defineEventHandlerGetterSetter("ontagfound");
+ this.defineEventHandlerGetterSetter("ontaglost");
+ this.defineEventHandlerGetterSetter("onpeerready");
+ this.defineEventHandlerGetterSetter("onpeerlost");
+
if (this._nfcContentHelper) {
this._nfcContentHelper.init(aWindow);
}
},
// Only apps which have nfc-manager permission can call the following interfaces
// 'checkP2PRegistration' , 'notifyUserAcceptedP2P' , 'notifySendFileStatus',
// 'startPoll', 'stopPoll', and 'powerOff'.
@@ -157,54 +162,39 @@ mozNfc.prototype = {
stopPoll: function stopPoll() {
return this._nfcContentHelper.stopPoll(this._window);
},
powerOff: function powerOff() {
return this._nfcContentHelper.powerOff(this._window);
},
- getNFCTag: function getNFCTag(sessionToken) {
- let obj = new MozNFCTag(this._window, sessionToken);
- if (this._nfcContentHelper.checkSessionToken(sessionToken)) {
- return this._window.MozNFCTag._create(this._window, obj);
- }
- return null;
- },
-
getNFCPeer: function getNFCPeer(sessionToken) {
if (!sessionToken || !this._nfcContentHelper.checkSessionToken(sessionToken)) {
return null;
}
if (!this.nfcObject || this.nfcObject.session != sessionToken) {
let obj = new MozNFCPeer(this._window, sessionToken);
this.nfcObject = obj;
this.nfcObject.contentObject = this._window.MozNFCPeer._create(this._window, obj);
}
return this.nfcObject.contentObject;
},
- // get/set onpeerready
- get onpeerready() {
- return this.__DOM_IMPL__.getEventHandler("onpeerready");
- },
-
- set onpeerready(handler) {
- this.__DOM_IMPL__.setEventHandler("onpeerready", handler);
- },
-
- // get/set onpeerlost
- get onpeerlost() {
- return this.__DOM_IMPL__.getEventHandler("onpeerlost");
- },
-
- set onpeerlost(handler) {
- this.__DOM_IMPL__.setEventHandler("onpeerlost", handler);
+ defineEventHandlerGetterSetter: function defineEventHandlerGetterSetter(name) {
+ Object.defineProperty(this, name, {
+ get: function get() {
+ return this.__DOM_IMPL__.getEventHandler(name);
+ },
+ set: function set(handler) {
+ this.__DOM_IMPL__.setEventHandler(name, handler);
+ }
+ });
},
eventListenerWasAdded: function(eventType) {
if (eventType !== "peerready") {
return;
}
let appId = this._window.document.nodePrincipal.appId;
@@ -215,16 +205,64 @@ mozNfc.prototype = {
if (eventType !== "peerready") {
return;
}
let appId = this._window.document.nodePrincipal.appId;
this._nfcContentHelper.unregisterTargetForPeerReady(this._window, appId);
},
+ notifyTagFound: function notifyTagFound(sessionToken, event, records) {
+ if (this.hasDeadWrapper()) {
+ dump("this._window or this.__DOM_IMPL__ is a dead wrapper.");
+ return;
+ }
+
+ if (!this.checkPermissions(["nfc-read", "nfc-write"])) {
+ return;
+ }
+
+ let tag = new MozNFCTag(this._window, sessionToken);
+ let tagContentObj = this._window.MozNFCTag._create(this._window, tag);
+
+ let length = records ? records.length : 0;
+ let ndefRecords = records ? [] : null;
+ for (let i = 0; i < length; i++) {
+ let record = records[i];
+ ndefRecords.push(new this._window.MozNDEFRecord({tnf: record.tnf,
+ type: record.type,
+ id: record.id,
+ payload: record.payload}));
+ }
+
+ let eventData = {
+ "tag": tagContentObj,
+ "ndefRecords": ndefRecords
+ };
+
+ debug("fire ontagfound " + sessionToken);
+ let tagEvent = new this._window.MozNFCTagEvent("tagfound", eventData);
+ this.__DOM_IMPL__.dispatchEvent(tagEvent);
+ },
+
+ notifyTagLost: function notifyTagLost(sessionToken) {
+ if (this.hasDeadWrapper()) {
+ dump("this._window or this.__DOM_IMPL__ is a dead wrapper.");
+ return;
+ }
+
+ if (!this.checkPermissions(["nfc-read", "nfc-write"])) {
+ return;
+ }
+
+ debug("fire ontaglost " + sessionToken);
+ let event = new this._window.Event("taglost");
+ this.__DOM_IMPL__.dispatchEvent(event);
+ },
+
notifyPeerReady: function notifyPeerReady(sessionToken) {
if (this.hasDeadWrapper()) {
dump("this._window or this.__DOM_IMPL__ is a dead wrapper.");
return;
}
this.session = sessionToken;
@@ -254,16 +292,31 @@ mozNfc.prototype = {
this.session = null;
debug("fire onpeerlost");
let event = new this._window.Event("peerlost");
this.__DOM_IMPL__.dispatchEvent(event);
},
+ checkPermissions: function checkPermissions(perms) {
+ let principal = this._window.document.nodePrincipal;
+ for (let perm of perms) {
+ let permValue =
+ Services.perms.testExactPermissionFromPrincipal(principal, perm);
+ if (permValue == Ci.nsIPermissionManager.ALLOW_ACTION) {
+ return true;
+ } else {
+ debug("doesn't have " + perm + " permission.");
+ }
+ }
+
+ return false;
+ },
+
hasDeadWrapper: function hasDeadWrapper() {
return Cu.isDeadWrapper(this._window) || Cu.isDeadWrapper(this.__DOM_IMPL__);
},
classID: Components.ID("{6ff2b290-2573-11e3-8224-0800200c9a66}"),
contractID: "@mozilla.org/navigatorNfc;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
Ci.nsIDOMGlobalPropertyInitializer,
--- a/dom/nfc/tests/marionette/test_nfc_peer.js
+++ b/dom/nfc/tests/marionette/test_nfc_peer.js
@@ -186,34 +186,21 @@ function testPeerShouldThrow() {
function testPeerInvalidToken() {
log("testPeerInvalidToken");
let peer = nfc.getNFCPeer("fakeSessionToken");
is(peer, null, "NFCPeer should be null on wrong session token");
runNextTest();
}
-/**
- * Added for completeness in Bug 1042651,
- * TODO: remove once Bug 963531 lands
- */
-function testTagInvalidToken() {
- log("testTagInvalidToken");
- let tag = nfc.getNFCTag("fakeSessionToken");
- is(tag, null, "NFCTag should be null on wrong session token");
-
- runNextTest();
-}
-
let tests = [
testPeerReady,
testGetNFCPeer,
testCheckP2PRegFailure,
testPeerLostShouldBeCalled,
testPeerLostShouldNotBeCalled,
testPeerShouldThrow,
- testPeerInvalidToken,
- testTagInvalidToken
+ testPeerInvalidToken
];
SpecialPowers.pushPermissions(
[{"type": "nfc-manager", "allow": true, context: document},
{"type": "nfc-write", "allow": true, context: document}], runTests);
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -75,17 +75,18 @@
#include "mozilla/Mutex.h"
#include "mozilla/PluginLibrary.h"
using mozilla::PluginLibrary;
#include "mozilla/PluginPRLibrary.h"
using mozilla::PluginPRLibrary;
#include "mozilla/plugins/PluginModuleParent.h"
-using mozilla::plugins::PluginModuleParent;
+using mozilla::plugins::PluginModuleChromeParent;
+using mozilla::plugins::PluginModuleContentParent;
#ifdef MOZ_X11
#include "mozilla/X11Util.h"
#endif
#ifdef XP_WIN
#include <windows.h>
#include "mozilla/WindowsVersion.h"
@@ -242,16 +243,20 @@ nsNPAPIPlugin::PluginCrashed(const nsASt
{
nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
host->PluginCrashed(this, pluginDumpID, browserDumpID);
}
bool
nsNPAPIPlugin::RunPluginOOP(const nsPluginTag *aPluginTag)
{
+ if (XRE_GetProcessType() == GeckoProcessType_Content) {
+ return true;
+ }
+
#if (MOZ_WIDGET_GTK == 3)
// We force OOP on Linux/GTK3 because some plugins use GTK2 and both GTK
// libraries can't be loaded in the same process.
return true;
#else
if (PR_GetEnv("MOZ_DISABLE_OOP_PLUGINS")) {
return false;
}
@@ -383,18 +388,22 @@ nsNPAPIPlugin::RunPluginOOP(const nsPlug
inline PluginLibrary*
GetNewPluginLibrary(nsPluginTag *aPluginTag)
{
if (!aPluginTag) {
return nullptr;
}
+ if (XRE_GetProcessType() == GeckoProcessType_Content) {
+ return PluginModuleContentParent::LoadModule(aPluginTag->mId);
+ }
+
if (nsNPAPIPlugin::RunPluginOOP(aPluginTag)) {
- return PluginModuleParent::LoadModule(aPluginTag->mFullPath.get());
+ return PluginModuleChromeParent::LoadModule(aPluginTag->mFullPath.get(), aPluginTag->mId);
}
return new PluginPRLibrary(aPluginTag->mFullPath.get(), aPluginTag->mLibrary);
}
// Creates an nsNPAPIPlugin object. One nsNPAPIPlugin object exists per plugin (not instance).
nsresult
nsNPAPIPlugin::CreatePlugin(nsPluginTag *aPluginTag, nsNPAPIPlugin** aResult)
{
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -43,17 +43,20 @@
#include "nsPluginLogging.h"
#include "nsIScriptChannel.h"
#include "nsIBlocklistService.h"
#include "nsVersionComparator.h"
#include "nsIObjectLoadingContent.h"
#include "nsIWritablePropertyBag2.h"
#include "nsICategoryManager.h"
#include "nsPluginStreamListenerPeer.h"
+#include "mozilla/dom/ContentChild.h"
#include "mozilla/LoadInfo.h"
+#include "mozilla/plugins/PluginBridge.h"
+#include "mozilla/plugins/PluginTypes.h"
#include "mozilla/Preferences.h"
#include "nsEnumeratorUtils.h"
#include "nsXPCOM.h"
#include "nsXPCOMCID.h"
#include "nsISupportsPrimitives.h"
#include "nsXULAppAPI.h"
@@ -101,16 +104,17 @@
#endif
#if MOZ_CRASHREPORTER
#include "nsExceptionHandler.h"
#endif
using namespace mozilla;
using mozilla::TimeStamp;
+using mozilla::plugins::PluginTag;
// Null out a strong ref to a linked list iteratively to avoid
// exhausting the stack (bug 486349).
#define NS_ITERATIVE_UNREF_LIST(type_, list_, mNext_) \
{ \
while (list_) { \
type_ temp = list_->mNext_; \
list_->mNext_ = nullptr; \
@@ -304,16 +308,20 @@ nsPluginHost::GetInst()
}
bool nsPluginHost::IsRunningPlugin(nsPluginTag * aPluginTag)
{
if (!aPluginTag || !aPluginTag->mPlugin) {
return false;
}
+ if (aPluginTag->mContentProcessRunningCount) {
+ return true;
+ }
+
for (uint32_t i = 0; i < mInstances.Length(); i++) {
nsNPAPIPluginInstance *instance = mInstances[i].get();
if (instance &&
instance->GetPlugin() == aPluginTag->mPlugin &&
instance->IsRunning()) {
return true;
}
}
@@ -1267,16 +1275,86 @@ nsresult nsPluginHost::EnsurePluginLoade
if (NS_FAILED(rv)) {
return rv;
}
aPluginTag->mPlugin = plugin;
}
return NS_OK;
}
+nsresult
+nsPluginHost::GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin)
+{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
+ // If plugins haven't been scanned yet, do so now
+ LoadPlugins();
+
+ nsPluginTag* pluginTag = PluginWithId(aPluginId);
+ if (pluginTag) {
+ nsresult rv = EnsurePluginLoaded(pluginTag);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // We only get here if a content process doesn't have a PluginModuleParent
+ // for the given plugin already. Therefore, this counter is counting the
+ // number of outstanding PluginModuleParents for the plugin, excluding the
+ // one from the chrome process.
+ pluginTag->mContentProcessRunningCount++;
+ NS_ADDREF(*aPlugin = pluginTag->mPlugin);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+class nsPluginUnloadRunnable : public nsRunnable
+{
+public:
+ explicit nsPluginUnloadRunnable(uint32_t aPluginId) : mPluginId(aPluginId) {}
+
+ NS_IMETHOD Run()
+ {
+ nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ if (!host) {
+ return NS_OK;
+ }
+ nsPluginTag* pluginTag = host->PluginWithId(mPluginId);
+ if (!pluginTag) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(pluginTag->mContentProcessRunningCount > 0);
+ pluginTag->mContentProcessRunningCount--;
+
+ if (!pluginTag->mContentProcessRunningCount) {
+ if (!host->IsRunningPlugin(pluginTag)) {
+ pluginTag->TryUnloadPlugin(false);
+ }
+ }
+ return NS_OK;
+ }
+
+protected:
+ uint32_t mPluginId;
+};
+
+void
+nsPluginHost::NotifyContentModuleDestroyed(uint32_t aPluginId)
+{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
+ // This is called in response to a message from the plugin. Don't unload the
+ // plugin until the message handler is off the stack.
+ nsRefPtr<nsPluginUnloadRunnable> runnable =
+ new nsPluginUnloadRunnable(aPluginId);
+ NS_DispatchToMainThread(runnable);
+}
+
nsresult nsPluginHost::GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin)
{
nsresult rv = NS_ERROR_FAILURE;
*aPlugin = nullptr;
if (!aMimeType)
return NS_ERROR_ILLEGAL_VALUE;
@@ -1609,16 +1687,27 @@ nsPluginHost::FirstPluginWithPath(const
for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
if (tag->mFullPath.Equals(path)) {
return tag;
}
}
return nullptr;
}
+nsPluginTag*
+nsPluginHost::PluginWithId(uint32_t aId)
+{
+ for (nsPluginTag* tag = mPlugins; tag; tag = tag->mNext) {
+ if (tag->mId == aId) {
+ return tag;
+ }
+ }
+ return nullptr;
+}
+
namespace {
int64_t GetPluginLastModifiedTime(const nsCOMPtr<nsIFile>& localfile)
{
PRTime fileModTime = 0;
#if defined(XP_MACOSX)
// On OS X the date of a bundle's "contents" (i.e. of its Info.plist file)
@@ -1695,22 +1784,42 @@ struct CompareFilesByTime
Equals(const nsCOMPtr<nsIFile>& a, const nsCOMPtr<nsIFile>& b) const
{
return GetPluginLastModifiedTime(a) == GetPluginLastModifiedTime(b);
}
};
} // anonymous namespace
+void
+nsPluginHost::AddPluginTag(nsPluginTag* aPluginTag)
+{
+ aPluginTag->mNext = mPlugins;
+ mPlugins = aPluginTag;
+
+ if (aPluginTag->IsActive()) {
+ nsAdoptingCString disableFullPage =
+ Preferences::GetCString(kPrefDisableFullPage);
+ for (uint32_t i = 0; i < aPluginTag->mMimeTypes.Length(); i++) {
+ if (!IsTypeInList(aPluginTag->mMimeTypes[i], disableFullPage)) {
+ RegisterWithCategoryManager(aPluginTag->mMimeTypes[i],
+ ePluginRegister);
+ }
+ }
+ }
+}
+
typedef NS_NPAPIPLUGIN_CALLBACK(char *, NP_GETMIMEDESCRIPTION)(void);
nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
bool aCreatePluginList,
bool *aPluginsChanged)
{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
NS_ENSURE_ARG_POINTER(aPluginsChanged);
nsresult rv;
*aPluginsChanged = false;
#ifdef PLUGIN_LOGGING
nsAutoCString dirPath;
pluginsDir->GetNativePath(dirPath);
@@ -1890,65 +1999,32 @@ nsresult nsPluginHost::ScanPluginsDirect
}
// If we're not creating a plugin list, simply looking for changes,
// then we're done.
if (!aCreatePluginList) {
return NS_OK;
}
- // Add plugin tags such that the list is ordered by modification date,
- // newest to oldest. This is ugly, it'd be easier with just about anything
- // other than a single-directional linked list.
- if (mPlugins) {
- nsPluginTag *prev = nullptr;
- nsPluginTag *next = mPlugins;
- while (next) {
- if (pluginTag->mLastModifiedTime >= next->mLastModifiedTime) {
- pluginTag->mNext = next;
- if (prev) {
- prev->mNext = pluginTag;
- } else {
- mPlugins = pluginTag;
- }
- break;
- }
- prev = next;
- next = prev->mNext;
- if (!next) {
- prev->mNext = pluginTag;
- }
- }
- } else {
- mPlugins = pluginTag;
- }
-
- if (pluginTag->IsActive()) {
- nsAdoptingCString disableFullPage =
- Preferences::GetCString(kPrefDisableFullPage);
- for (uint32_t i = 0; i < pluginTag->mMimeTypes.Length(); i++) {
- if (!IsTypeInList(pluginTag->mMimeTypes[i], disableFullPage)) {
- RegisterWithCategoryManager(pluginTag->mMimeTypes[i],
- ePluginRegister);
- }
- }
- }
+ AddPluginTag(pluginTag);
}
if (warnOutdated) {
Preferences::SetBool("plugins.update.notifyUser", true);
}
return NS_OK;
}
nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
bool aCreatePluginList,
bool *aPluginsChanged)
{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
bool hasMore;
while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) {
nsCOMPtr<nsISupports> supports;
nsresult rv = dirEnum->GetNext(getter_AddRefs(supports));
if (NS_FAILED(rv))
continue;
nsCOMPtr<nsIFile> nextDir(do_QueryInterface(supports, &rv));
if (NS_FAILED(rv))
@@ -1963,16 +2039,44 @@ nsresult nsPluginHost::ScanPluginsDirect
// if changes are detected and we are not creating the list, do not proceed
if (!aCreatePluginList && *aPluginsChanged)
break;
}
return NS_OK;
}
+void
+nsPluginHost::IncrementChromeEpoch()
+{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+ mPluginEpoch++;
+}
+
+uint32_t
+nsPluginHost::ChromeEpoch()
+{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+ return mPluginEpoch;
+}
+
+uint32_t
+nsPluginHost::ChromeEpochForContent()
+{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
+ return mPluginEpoch;
+}
+
+void
+nsPluginHost::SetChromeEpochForContent(uint32_t aEpoch)
+{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
+ mPluginEpoch = aEpoch;
+}
+
nsresult nsPluginHost::LoadPlugins()
{
#ifdef ANDROID
if (XRE_GetProcessType() == GeckoProcessType_Content) {
return NS_OK;
}
#endif
// do not do anything if it is already done
@@ -1985,35 +2089,92 @@ nsresult nsPluginHost::LoadPlugins()
bool pluginschanged;
nsresult rv = FindPlugins(true, &pluginschanged);
if (NS_FAILED(rv))
return rv;
// only if plugins have changed will we notify plugin-change observers
if (pluginschanged) {
+ if (XRE_GetProcessType() == GeckoProcessType_Default) {
+ IncrementChromeEpoch();
+ }
+
nsCOMPtr<nsIObserverService> obsService =
mozilla::services::GetObserverService();
if (obsService)
obsService->NotifyObservers(nullptr, "plugins-list-updated", nullptr);
}
return NS_OK;
}
+nsresult
+nsPluginHost::FindPluginsInContent(bool aCreatePluginList, bool* aPluginsChanged)
+{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
+
+ dom::ContentChild* cp = dom::ContentChild::GetSingleton();
+ nsTArray<PluginTag> plugins;
+ uint32_t parentEpoch;
+ if (!cp->SendFindPlugins(ChromeEpochForContent(), &plugins, &parentEpoch)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ if (parentEpoch != ChromeEpochForContent()) {
+ SetChromeEpochForContent(parentEpoch);
+ *aPluginsChanged = true;
+ if (!aCreatePluginList) {
+ return NS_OK;
+ }
+
+ for (size_t i = 0; i < plugins.Length(); i++) {
+ PluginTag& tag = plugins[i];
+
+ // Don't add the same plugin again.
+ if (PluginWithId(tag.id())) {
+ continue;
+ }
+
+ nsPluginTag *pluginTag = new nsPluginTag(tag.id(),
+ tag.name().get(),
+ tag.description().get(),
+ tag.filename().get(),
+ "", // aFullPath
+ tag.version().get(),
+ nsTArray<nsCString>(tag.mimeTypes()),
+ nsTArray<nsCString>(tag.mimeDescriptions()),
+ nsTArray<nsCString>(tag.extensions()),
+ tag.isJavaPlugin(),
+ tag.isFlashPlugin(),
+ tag.lastModifiedTime(),
+ tag.isFromExtension());
+ AddPluginTag(pluginTag);
+ }
+ }
+
+ mPluginsLoaded = true;
+ return NS_OK;
+}
+
// if aCreatePluginList is false we will just scan for plugins
// and see if any changes have been made to the plugins.
// This is needed in ReloadPlugins to prevent possible recursive reloads
nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChanged)
{
Telemetry::AutoTimer<Telemetry::FIND_PLUGINS> telemetry;
NS_ENSURE_ARG_POINTER(aPluginsChanged);
*aPluginsChanged = false;
+
+ if (XRE_GetProcessType() == GeckoProcessType_Content) {
+ return FindPluginsInContent(aCreatePluginList, aPluginsChanged);
+ }
+
nsresult rv;
// Read cached plugins info. If the profile isn't yet available then don't
// scan for plugins
if (ReadPluginInfo() == NS_ERROR_NOT_AVAILABLE)
return NS_OK;
#ifdef XP_WIN
@@ -2162,19 +2323,68 @@ nsresult nsPluginHost::FindPlugins(bool
// No more need for cached plugins. Clear it up.
NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
return NS_OK;
}
+bool
+mozilla::plugins::FindPluginsForContent(uint32_t aPluginEpoch,
+ nsTArray<PluginTag>* aPlugins,
+ uint32_t* aNewPluginEpoch)
+{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
+ nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ host->FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
+ return true;
+}
+
+void
+nsPluginHost::FindPluginsForContent(uint32_t aPluginEpoch,
+ nsTArray<PluginTag>* aPlugins,
+ uint32_t* aNewPluginEpoch)
+{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
+ // Load plugins so that the epoch is correct.
+ LoadPlugins();
+
+ *aNewPluginEpoch = ChromeEpoch();
+ if (aPluginEpoch == ChromeEpoch()) {
+ return;
+ }
+
+ nsTArray<nsRefPtr<nsPluginTag>> plugins;
+ GetPlugins(plugins);
+
+ for (size_t i = 0; i < plugins.Length(); i++) {
+ nsRefPtr<nsPluginTag> tag = plugins[i];
+ aPlugins->AppendElement(PluginTag(tag->mId,
+ tag->mName,
+ tag->mDescription,
+ tag->mMimeTypes,
+ tag->mMimeDescriptions,
+ tag->mExtensions,
+ tag->mIsJavaPlugin,
+ tag->mIsFlashPlugin,
+ tag->mFileName,
+ tag->mVersion,
+ tag->mLastModifiedTime,
+ tag->IsFromExtension()));
+ }
+}
+
nsresult
nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
ReadPluginInfo();
WritePluginInfo();
NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
if (!aPluginTag) {
return NS_OK;
}
@@ -2256,16 +2466,17 @@ nsPluginHost::RegisterWithCategoryManage
true);
}
}
}
nsresult
nsPluginHost::WritePluginInfo()
{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
nsresult rv = NS_OK;
nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
if (NS_FAILED(rv))
return rv;
directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(mPluginRegFile));
@@ -2400,16 +2611,18 @@ nsPluginHost::WritePluginInfo()
NS_ENSURE_SUCCESS(rv, rv);
rv = pluginReg->MoveToNative(parent, kPluginRegistryFilename);
return rv;
}
nsresult
nsPluginHost::ReadPluginInfo()
{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+
const long PLUGIN_REG_MIMETYPES_ARRAY_SIZE = 12;
const long PLUGIN_REG_MAX_MIMETYPES = 1000;
// we need to import the legacy flags from the plugin registry once
const bool pluginStateImported =
Preferences::GetDefaultBool("plugin.importedState", false);
nsresult rv;
@@ -3537,16 +3750,17 @@ nsPluginHost::PluginCrashed(nsNPAPIPlugi
}
}
// Only after all instances have been invalidated is it safe to null
// out nsPluginTag.mPlugin. The next time we try to create an
// instance of this plugin we reload it (launch a new plugin process).
crashedPluginTag->mPlugin = nullptr;
+ crashedPluginTag->mContentProcessRunningCount = 0;
#ifdef XP_WIN
CheckForDisabledWindows();
#endif
}
nsNPAPIPluginInstance*
nsPluginHost::FindInstance(const char *mimetype)
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -23,24 +23,26 @@
#include "nsTArray.h"
#include "nsTObserverArray.h"
#include "nsITimer.h"
#include "nsPluginTags.h"
#include "nsPluginPlayPreviewInfo.h"
#include "nsIEffectiveTLDService.h"
#include "nsIIDNService.h"
#include "nsCRT.h"
+#include "mozilla/plugins/PluginTypes.h"
class nsNPAPIPlugin;
class nsIComponentManager;
class nsIFile;
class nsIChannel;
class nsPluginNativeWindow;
class nsObjectLoadingContent;
class nsPluginInstanceOwner;
+class nsPluginUnloadRunnable;
class nsNPAPIPluginInstance;
class nsNPAPIPluginStreamListener;
class nsIPluginInstanceOwner;
class nsIInputStream;
class nsIStreamListener;
class nsInvalidPluginTag : public nsISupports
{
@@ -83,16 +85,19 @@ public:
nsresult SetUpPluginInstance(const char *aMimeType,
nsIURI *aURL,
nsPluginInstanceOwner *aOwner);
bool PluginExistsForType(const char* aMimeType);
nsresult IsPluginEnabledForExtension(const char* aExtension, const char* &aMimeType);
void GetPlugins(nsTArray<nsRefPtr<nsPluginTag> >& aPluginArray);
+ void FindPluginsForContent(uint32_t aPluginEpoch,
+ nsTArray<mozilla::plugins::PluginTag>* aPlugins,
+ uint32_t* aNewPluginEpoch);
nsresult GetURL(nsISupports* pluginInst,
const char* url,
const char* target,
nsNPAPIPluginStreamListener* streamListener,
const char* altHost,
const char* referrer,
bool forceJSEnabled);
@@ -186,22 +191,26 @@ public:
nsresult InstantiatePluginInstance(const char *aMimeType, nsIURI* aURL,
nsObjectLoadingContent *aContent,
nsPluginInstanceOwner** aOwner);
// Does not accept nullptr and should never fail.
nsPluginTag* TagForPlugin(nsNPAPIPlugin* aPlugin);
nsresult GetPlugin(const char *aMimeType, nsNPAPIPlugin** aPlugin);
+ nsresult GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin);
+ void NotifyContentModuleDestroyed(uint32_t aPluginId);
nsresult NewPluginStreamListener(nsIURI* aURL,
nsNPAPIPluginInstance* aInstance,
nsIStreamListener **aStreamListener);
private:
+ friend class nsPluginUnloadRunnable;
+
nsresult
TrySetUpPluginInstance(const char *aMimeType, nsIURI *aURL, nsPluginInstanceOwner *aOwner);
nsPluginTag*
FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches);
// Return an nsPluginTag for this type, if any. If aCheckEnabled is
// true, only enabled plugins will be returned.
@@ -209,24 +218,28 @@ private:
FindPluginForType(const char* aMimeType, bool aCheckEnabled);
nsPluginTag*
FindPluginEnabledForExtension(const char* aExtension, const char* &aMimeType);
nsresult
FindStoppedPluginForURL(nsIURI* aURL, nsIPluginInstanceOwner *aOwner);
+ nsresult FindPluginsInContent(bool aCreatePluginList, bool * aPluginsChanged);
+
nsresult
FindPlugins(bool aCreatePluginList, bool * aPluginsChanged);
// Registers or unregisters the given mime type with the category manager
// (performs no checks - see UpdateCategoryManager)
enum nsRegisterType { ePluginRegister, ePluginUnregister };
void RegisterWithCategoryManager(nsCString &aMimeType, nsRegisterType aType);
+ void AddPluginTag(nsPluginTag* aPluginTag);
+
nsresult
ScanPluginsDirectory(nsIFile *pluginsDir,
bool aCreatePluginList,
bool *aPluginsChanged);
nsresult
ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
bool aCreatePluginList,
@@ -250,21 +263,33 @@ private:
// Checks to see if a tag object is in our list of live tags.
bool IsLiveTag(nsIPluginTag* tag);
// Checks our list of live tags for an equivalent tag.
nsPluginTag* HaveSamePlugin(const nsPluginTag * aPluginTag);
// Returns the first plugin at |path|
nsPluginTag* FirstPluginWithPath(const nsCString& path);
+ nsPluginTag* PluginWithId(uint32_t aId);
nsresult EnsurePrivateDirServiceProvider();
void OnPluginInstanceDestroyed(nsPluginTag* aPluginTag);
+ // To be used by the chrome process whenever the set of plugins changes.
+ void IncrementChromeEpoch();
+
+ // To be used by the chrome process; returns the current epoch.
+ uint32_t ChromeEpoch();
+
+ // To be used by the content process to get/set the last observed epoch value
+ // from the chrome process.
+ uint32_t ChromeEpochForContent();
+ void SetChromeEpochForContent(uint32_t aEpoch);
+
nsRefPtr<nsPluginTag> mPlugins;
nsRefPtr<nsPluginTag> mCachedPlugins;
nsRefPtr<nsInvalidPluginTag> mInvalidPlugins;
nsTArray< nsRefPtr<nsPluginPlayPreviewInfo> > mPlayPreviewMimeTypes;
bool mPluginsLoaded;
// set by pref plugin.override_internal_types
bool mOverrideInternalTypes;
@@ -290,16 +315,22 @@ private:
nsresult NormalizeHostname(nsCString& host);
nsresult EnumerateSiteData(const nsACString& domain,
const InfallibleTArray<nsCString>& sites,
InfallibleTArray<nsCString>& result,
bool firstMatchOnly);
nsWeakPtr mCurrentDocument; // weak reference, we use it to id document only
+ // This epoch increases each time we load the list of plugins from disk.
+ // In the chrome process, this stores the actual epoch.
+ // In the content process, this stores the last epoch value observed
+ // when reading plugins from chrome.
+ uint32_t mPluginEpoch;
+
static nsIFile *sPluginTempDir;
// We need to hold a global ptr to ourselves because we register for
// two different CIDs for some reason...
static nsPluginHost* sInst;
};
class MOZ_STACK_CLASS PluginDestructionGuard : protected PRCList
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -61,20 +61,24 @@ MakePrefNameForPlugin(const char* const
static nsCString
GetStatePrefNameForPlugin(nsPluginTag* aTag)
{
return MakePrefNameForPlugin("state", aTag);
}
/* nsPluginTag */
+uint32_t nsPluginTag::sNextId;
+
nsPluginTag::nsPluginTag(nsPluginInfo* aPluginInfo,
int64_t aLastModifiedTime,
bool fromExtension)
- : mName(aPluginInfo->fName),
+ : mId(sNextId++),
+ mContentProcessRunningCount(0),
+ mName(aPluginInfo->fName),
mDescription(aPluginInfo->fDescription),
mLibrary(nullptr),
mIsJavaPlugin(false),
mIsFlashPlugin(false),
mFileName(aPluginInfo->fFileName),
mFullPath(aPluginInfo->fFullPath),
mVersion(aPluginInfo->fVersion),
mLastModifiedTime(aLastModifiedTime),
@@ -98,17 +102,19 @@ nsPluginTag::nsPluginTag(const char* aNa
const char* aVersion,
const char* const* aMimeTypes,
const char* const* aMimeDescriptions,
const char* const* aExtensions,
int32_t aVariants,
int64_t aLastModifiedTime,
bool fromExtension,
bool aArgsAreUTF8)
- : mName(aName),
+ : mId(sNextId++),
+ mContentProcessRunningCount(0),
+ mName(aName),
mDescription(aDescription),
mLibrary(nullptr),
mIsJavaPlugin(false),
mIsFlashPlugin(false),
mFileName(aFileName),
mFullPath(aFullPath),
mVersion(aVersion),
mLastModifiedTime(aLastModifiedTime),
@@ -119,16 +125,49 @@ nsPluginTag::nsPluginTag(const char* aNa
{
InitMime(aMimeTypes, aMimeDescriptions, aExtensions,
static_cast<uint32_t>(aVariants));
if (!aArgsAreUTF8)
EnsureMembersAreUTF8();
FixupVersion();
}
+nsPluginTag::nsPluginTag(uint32_t aId,
+ const char* aName,
+ const char* aDescription,
+ const char* aFileName,
+ const char* aFullPath,
+ const char* aVersion,
+ nsTArray<nsCString> aMimeTypes,
+ nsTArray<nsCString> aMimeDescriptions,
+ nsTArray<nsCString> aExtensions,
+ bool aIsJavaPlugin,
+ bool aIsFlashPlugin,
+ int64_t aLastModifiedTime,
+ bool aFromExtension)
+ : mId(aId),
+ mContentProcessRunningCount(0),
+ mName(aName),
+ mDescription(aDescription),
+ mMimeTypes(aMimeTypes),
+ mMimeDescriptions(aMimeDescriptions),
+ mExtensions(aExtensions),
+ mLibrary(nullptr),
+ mIsJavaPlugin(aIsJavaPlugin),
+ mIsFlashPlugin(aIsFlashPlugin),
+ mFileName(aFileName),
+ mVersion(aVersion),
+ mLastModifiedTime(aLastModifiedTime),
+ mNiceFileName(),
+ mCachedBlocklistState(nsIBlocklistService::STATE_NOT_BLOCKED),
+ mCachedBlocklistStateValid(false),
+ mIsFromExtension(aFromExtension)
+{
+}
+
nsPluginTag::~nsPluginTag()
{
NS_ASSERTION(!mNext, "Risk of exhausting the stack space, bug 486349");
}
NS_IMPL_ISUPPORTS(nsPluginTag, nsIPluginTag)
void nsPluginTag::InitMime(const char* const* aMimeTypes,
--- a/dom/plugins/base/nsPluginTags.h
+++ b/dom/plugins/base/nsPluginTags.h
@@ -46,16 +46,29 @@ public:
const char* aVersion,
const char* const* aMimeTypes,
const char* const* aMimeDescriptions,
const char* const* aExtensions,
int32_t aVariants,
int64_t aLastModifiedTime,
bool fromExtension,
bool aArgsAreUTF8 = false);
+ nsPluginTag(uint32_t aId,
+ const char* aName,
+ const char* aDescription,
+ const char* aFileName,
+ const char* aFullPath,
+ const char* aVersion,
+ nsTArray<nsCString> aMimeTypes,
+ nsTArray<nsCString> aMimeDescriptions,
+ nsTArray<nsCString> aExtensions,
+ bool aIsJavaPlugin,
+ bool aIsFlashPlugin,
+ int64_t aLastModifiedTime,
+ bool aFromExtension);
void TryUnloadPlugin(bool inShutdown);
// plugin is enabled and not blocklisted
bool IsActive();
bool IsEnabled();
void SetEnabled(bool enabled);
@@ -69,16 +82,20 @@ public:
void ImportFlagsToPrefs(uint32_t flag);
bool HasSameNameAndMimes(const nsPluginTag *aPluginTag) const;
nsCString GetNiceFileName();
bool IsFromExtension() const;
nsRefPtr<nsPluginTag> mNext;
+ uint32_t mId;
+
+ // Number of PluginModuleParents living in all content processes.
+ size_t mContentProcessRunningCount;
nsCString mName; // UTF-8
nsCString mDescription; // UTF-8
nsTArray<nsCString> mMimeTypes; // UTF-8
nsTArray<nsCString> mMimeDescriptions; // UTF-8
nsTArray<nsCString> mExtensions; // UTF-8
PRLibrary *mLibrary;
nsRefPtr<nsNPAPIPlugin> mPlugin;
bool mIsJavaPlugin;
@@ -101,11 +118,13 @@ private:
bool mIsFromExtension;
void InitMime(const char* const* aMimeTypes,
const char* const* aMimeDescriptions,
const char* const* aExtensions,
uint32_t aVariantCount);
nsresult EnsureMembersAreUTF8();
void FixupVersion();
+
+ static uint32_t sNextId;
};
#endif // nsPluginTags_h_
--- a/dom/plugins/ipc/PPluginModule.ipdl
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -1,28 +1,31 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PPluginInstance;
include protocol PPluginScriptableObject;
include protocol PCrashReporter;
+include protocol PContent;
using NPError from "npapi.h";
using NPNVariable from "npapi.h";
using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
using class mac_plugin_interposing::NSCursorInfo from "mozilla/plugins/PluginMessageUtils.h";
using struct nsID from "nsID.h";
namespace mozilla {
namespace plugins {
intr protocol PPluginModule
{
+ bridges PContent, PPluginModule;
+
manages PPluginInstance;
manages PCrashReporter;
both:
// Window-specific message which instructs the interrupt mechanism to enter
// a nested event loop for the current interrupt call.
async ProcessNativeEventsInInterruptCall();
@@ -102,16 +105,20 @@ parent:
// OS X Specific calls to allow the plugin to manage the cursor.
async SetCursor(NSCursorInfo cursorInfo);
async ShowCursor(bool show);
async PushCursor(NSCursorInfo cursorInfo);
async PopCursor();
sync GetNativeCursorsSupported() returns (bool supported);
- sync NPN_SetException(nullable PPluginScriptableObject actor,
- nsCString message);
+ sync NPN_SetException(nsCString message);
async NPN_ReloadPlugins(bool aReloadPages);
+
+ // Notifies the chrome process that a PluginModuleChild linked to a content
+ // process was destroyed. The chrome process may choose to asynchronously shut
+ // down the plugin process in response.
+ async NotifyContentModuleDestroyed();
};
} // namespace plugins
} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/plugins/ipc/PluginBridge.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et :
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_plugins_PluginBridge_h
+#define mozilla_plugins_PluginBridge_h
+
+namespace mozilla {
+
+namespace dom {
+class ContentParent;
+}
+
+namespace plugins {
+
+bool
+SetupBridge(uint32_t aPluginId, dom::ContentParent* aContentParent);
+
+bool
+FindPluginsForContent(uint32_t aPluginEpoch,
+ nsTArray<PluginTag>* aPlugins,
+ uint32_t* aNewPluginEpoch);
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif // mozilla_plugins_PluginBridge_h
--- a/dom/plugins/ipc/PluginHangUIParent.cpp
+++ b/dom/plugins/ipc/PluginHangUIParent.cpp
@@ -64,17 +64,17 @@ private:
uint32_t mResponseTimeMs;
uint32_t mTimeoutMs;
};
} // anonymous namespace
namespace mozilla {
namespace plugins {
-PluginHangUIParent::PluginHangUIParent(PluginModuleParent* aModule,
+PluginHangUIParent::PluginHangUIParent(PluginModuleChromeParent* aModule,
const int32_t aHangUITimeoutPref,
const int32_t aChildTimeoutPref)
: mMutex("mozilla::plugins::PluginHangUIParent::mMutex"),
mModule(aModule),
mTimeoutPrefMs(static_cast<uint32_t>(aHangUITimeoutPref) * 1000U),
mIPCTimeoutMs(static_cast<uint32_t>(aChildTimeoutPref) * 1000U),
mMainThreadMessageLoop(MessageLoop::current()),
mIsShowing(false),
--- a/dom/plugins/ipc/PluginHangUIParent.h
+++ b/dom/plugins/ipc/PluginHangUIParent.h
@@ -15,32 +15,32 @@
#include "mozilla/Mutex.h"
#include "mozilla/plugins/PluginMessageUtils.h"
#include "MiniShmParent.h"
namespace mozilla {
namespace plugins {
-class PluginModuleParent;
+class PluginModuleChromeParent;
/**
* This class is responsible for launching and communicating with the
* plugin-hang-ui process.
*
* NOTE: PluginHangUIParent is *not* an IPDL actor! In this case, "Parent"
* is describing the fact that firefox is the parent process to the
* plugin-hang-ui process, which is the PluginHangUIChild.
* PluginHangUIParent and PluginHangUIChild are a matched pair.
* @see PluginHangUIChild
*/
class PluginHangUIParent : public MiniShmObserver
{
public:
- PluginHangUIParent(PluginModuleParent* aModule,
+ PluginHangUIParent(PluginModuleChromeParent* aModule,
const int32_t aHangUITimeoutPref,
const int32_t aChildTimeoutPref);
virtual ~PluginHangUIParent();
/**
* Spawn the plugin-hang-ui.exe child process and terminate the given
* plugin container process if the user elects to stop the hung plugin.
*
@@ -130,17 +130,17 @@ private:
bool
UnwatchHangUIChildProcess(bool aWait);
static
VOID CALLBACK SOnHangUIProcessExit(PVOID aContext, BOOLEAN aIsTimer);
private:
Mutex mMutex;
- PluginModuleParent* mModule;
+ PluginModuleChromeParent* mModule;
const uint32_t mTimeoutPrefMs;
const uint32_t mIPCTimeoutMs;
MessageLoop* mMainThreadMessageLoop;
bool mIsShowing;
unsigned int mLastUserResponse;
base::ProcessHandle mHangUIProcessHandle;
NativeWindowHandle mMainWindowHandle;
HANDLE mRegWait;
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -160,16 +160,17 @@ PluginInstanceChild::PluginInstanceChild
, mIsTransparent(false)
, mSurfaceType(gfxSurfaceType::Max)
, mCurrentInvalidateTask(nullptr)
, mCurrentAsyncSetWindowTask(nullptr)
, mPendingPluginCall(false)
, mDoAlphaExtraction(false)
, mHasPainted(false)
, mSurfaceDifferenceRect(0,0,0,0)
+ , mDestroyed(false)
{
memset(&mWindow, 0, sizeof(mWindow));
mWindow.type = NPWindowTypeWindow;
mData.ndata = (void*) this;
mData.pdata = nullptr;
#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
mWindow.ws_info = &mWsInfo;
memset(&mWsInfo, 0, sizeof(mWsInfo));
@@ -207,17 +208,17 @@ PluginInstanceChild::~PluginInstanceChil
UnscheduleTimer(mCARefreshTimer);
}
#endif
}
int
PluginInstanceChild::GetQuirks()
{
- return PluginModuleChild::current()->GetQuirks();
+ return PluginModuleChild::GetChrome()->GetQuirks();
}
NPError
PluginInstanceChild::InternalGetNPObjectForValue(NPNVariable aValue,
NPObject** aObject)
{
PluginScriptableObjectChild* actor = nullptr;
NPError result = NPERR_NO_ERROR;
@@ -2388,17 +2389,17 @@ PluginInstanceChild::GetActorForNPObject
if (aObject->_class == PluginScriptableObjectChild::GetClass()) {
// One of ours! It's a browser-provided object.
ChildNPObject* object = static_cast<ChildNPObject*>(aObject);
NS_ASSERTION(object->parent, "Null actor!");
return object->parent;
}
PluginScriptableObjectChild* actor =
- PluginModuleChild::current()->GetActorForNPObject(aObject);
+ PluginScriptableObjectChild::GetActorForNPObject(aObject);
if (actor) {
// Plugin-provided object that we've previously wrapped.
return actor;
}
actor = new PluginScriptableObjectChild(LocalObject);
if (!SendPPluginScriptableObjectConstructor(actor)) {
NS_ERROR("Failed to send constructor message!");
@@ -3271,17 +3272,17 @@ PluginInstanceChild::ShowPluginFrame()
XSync(mWsInfo.display, False);
} else
#endif
#ifdef XP_WIN
if (SharedDIBSurface::IsSharedDIBSurface(mCurrentSurface)) {
SharedDIBSurface* s = static_cast<SharedDIBSurface*>(mCurrentSurface.get());
if (!mCurrentSurfaceActor) {
base::SharedMemoryHandle handle = nullptr;
- s->ShareToProcess(PluginModuleChild::current()->OtherProcess(), &handle);
+ s->ShareToProcess(OtherProcess(), &handle);
mCurrentSurfaceActor =
SendPPluginSurfaceConstructor(handle,
mCurrentSurface->GetSize(),
haveTransparentPixels);
}
currSurf = mCurrentSurfaceActor;
s->Flush();
@@ -3673,22 +3674,23 @@ PluginInstanceChild::ClearAllSurfaces()
mCGLayer = nullptr;
}
mDoubleBufferCARenderer.ClearFrontSurface();
mDoubleBufferCARenderer.ClearBackSurface();
#endif
}
-bool
-PluginInstanceChild::AnswerNPP_Destroy(NPError* aResult)
+void
+PluginInstanceChild::Destroy()
{
- PLUGIN_LOG_DEBUG_METHOD;
- AssertPluginThread();
- *aResult = NPERR_NO_ERROR;
+ if (mDestroyed) {
+ return;
+ }
+ mDestroyed = true;
#if defined(OS_WIN)
SetProp(mPluginWindowHWND, kPluginIgnoreSubclassProperty, (HANDLE)1);
#endif
InfallibleTArray<PBrowserStreamChild*> streams;
ManagedPBrowserStreamChild(streams);
@@ -3702,17 +3704,17 @@ PluginInstanceChild::AnswerNPP_Destroy(N
for (uint32_t i = 0; i < streams.Length(); ++i)
static_cast<BrowserStreamChild*>(streams[i])->FinishDelivery();
mTimers.Clear();
// NPP_Destroy() should be a synchronization point for plugin threads
// calling NPN_AsyncCall: after this function returns, they are no longer
// allowed to make async calls on this instance.
- PluginModuleChild::current()->NPP_Destroy(this);
+ static_cast<PluginModuleChild *>(Manager())->NPP_Destroy(this);
mData.ndata = 0;
if (mCurrentInvalidateTask) {
mCurrentInvalidateTask->Cancel();
mCurrentInvalidateTask = nullptr;
}
if (mCurrentAsyncSetWindowTask) {
mCurrentAsyncSetWindowTask->Cancel();
@@ -3724,17 +3726,17 @@ PluginInstanceChild::AnswerNPP_Destroy(N
mAsyncInvalidateTask->Cancel();
mAsyncInvalidateTask = nullptr;
}
}
ClearAllSurfaces();
mDeletingHash = new nsTHashtable<DeletingObjectEntry>;
- PluginModuleChild::current()->FindNPObjectsForInstance(this);
+ PluginScriptableObjectChild::NotifyOfInstanceShutdown(this);
mDeletingHash->EnumerateEntries(InvalidateObject, nullptr);
mDeletingHash->EnumerateEntries(DeleteObject, nullptr);
// Null out our cached actors as they should have been killed in the
// PluginInstanceDestroyed call above.
mCachedWindowActor = nullptr;
mCachedElementActor = nullptr;
@@ -3756,11 +3758,27 @@ PluginInstanceChild::AnswerNPP_Destroy(N
#ifdef MOZ_WIDGET_GTK
if (mWindow.type == NPWindowTypeWindow && !mXEmbed) {
xt_client_xloop_destroy();
}
#endif
#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
DeleteWindow();
#endif
+}
+
+bool
+PluginInstanceChild::AnswerNPP_Destroy(NPError* aResult)
+{
+ PLUGIN_LOG_DEBUG_METHOD;
+ AssertPluginThread();
+ *aResult = NPERR_NO_ERROR;
+
+ Destroy();
return true;
}
+
+void
+PluginInstanceChild::ActorDestroy(ActorDestroyReason why)
+{
+ Destroy();
+}
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -50,17 +50,18 @@ namespace plugins {
class PBrowserStreamChild;
class BrowserStreamChild;
class StreamNotifyChild;
class PluginInstanceChild : public PPluginInstanceChild
{
friend class BrowserStreamChild;
friend class PluginStreamChild;
- friend class StreamNotifyChild;
+ friend class StreamNotifyChild;
+ friend class PluginScriptableObjectChild;
#ifdef OS_WIN
friend LRESULT CALLBACK PluginWindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
static LRESULT CALLBACK PluginWindowProcInternal(HWND hWnd,
UINT message,
@@ -519,16 +520,20 @@ private:
void ClearCurrentSurface();
// Swap mCurrentSurface/mBackSurface and their associated actors
void SwapSurfaces();
// Clear all surfaces in response to NPP_Destroy
void ClearAllSurfaces();
+ void Destroy();
+
+ void ActorDestroy(ActorDestroyReason why);
+
// Set as true when SetupLayer called
// and go with different path in InvalidateRect function
bool mLayersRendering;
// Current surface available for rendering
nsRefPtr<gfxASurface> mCurrentSurface;
// Back surface, just keeping reference to
@@ -592,14 +597,17 @@ private:
// that we ask a plugin to paint at least once even if it's invisible;
// some plugin (instances) rely on this in order to work properly.
bool mHasPainted;
// Cached rectangle rendered to previous surface(mBackSurface)
// Used for reading back to current surface and syncing data,
// in plugin coordinates.
nsIntRect mSurfaceDifferenceRect;
+
+ // Has this instance been destroyed, either by ActorDestroy or NPP_Destroy?
+ bool mDestroyed;
};
} // namespace plugins
} // namespace mozilla
#endif // ifndef dom_plugins_PluginInstanceChild_h
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -1773,17 +1773,17 @@ PluginInstanceParent::SharedSurfaceSetWi
if (NS_FAILED(mSharedSurfaceDib.Create(reinterpret_cast<HDC>(aWindow->window),
newPort.width, newPort.height, false)))
return false;
// save the new shared surface size we just allocated
mSharedSize = newPort;
base::SharedMemoryHandle handle;
- if (NS_FAILED(mSharedSurfaceDib.ShareToProcess(mParent->ChildProcessHandle(), &handle)))
+ if (NS_FAILED(mSharedSurfaceDib.ShareToProcess(OtherProcess(), &handle)))
return false;
aRemoteWindow.surfaceHandle = handle;
return true;
}
void
--- a/dom/plugins/ipc/PluginInterposeOSX.mm
+++ b/dom/plugins/ipc/PluginInterposeOSX.mm
@@ -540,17 +540,17 @@ void NSCursorInfo::SetCustomImageData(ui
}
// This should never be called from the browser process -- only from the
// plugin process.
bool NSCursorInfo::GetNativeCursorsSupported()
{
if (mNativeCursorsSupported == -1) {
AssertPluginThread();
- PluginModuleChild *pmc = PluginModuleChild::current();
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
if (pmc) {
bool result = pmc->GetNativeCursorsSupported();
if (result) {
mNativeCursorsSupported = 1;
} else {
mNativeCursorsSupported = 0;
}
}
@@ -686,60 +686,60 @@ void FocusPluginProcess() {
SetFrontProcess(&this_process);
}
}
void NotifyBrowserOfPluginShowWindow(uint32_t window_id, CGRect bounds,
bool modal) {
AssertPluginThread();
- PluginModuleChild *pmc = PluginModuleChild::current();
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
if (pmc)
pmc->PluginShowWindow(window_id, modal, bounds);
}
void NotifyBrowserOfPluginHideWindow(uint32_t window_id, CGRect bounds) {
AssertPluginThread();
- PluginModuleChild *pmc = PluginModuleChild::current();
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
if (pmc)
pmc->PluginHideWindow(window_id);
}
void NotifyBrowserOfSetCursor(NSCursorInfo& aCursorInfo)
{
AssertPluginThread();
- PluginModuleChild *pmc = PluginModuleChild::current();
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
if (pmc) {
pmc->SetCursor(aCursorInfo);
}
}
void NotifyBrowserOfShowCursor(bool show)
{
AssertPluginThread();
- PluginModuleChild *pmc = PluginModuleChild::current();
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
if (pmc) {
pmc->ShowCursor(show);
}
}
void NotifyBrowserOfPushCursor(NSCursorInfo& aCursorInfo)
{
AssertPluginThread();
- PluginModuleChild *pmc = PluginModuleChild::current();
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
if (pmc) {
pmc->PushCursor(aCursorInfo);
}
}
void NotifyBrowserOfPopCursor()
{
AssertPluginThread();
- PluginModuleChild *pmc = PluginModuleChild::current();
+ PluginModuleChild *pmc = PluginModuleChild::GetChrome();
if (pmc) {
pmc->PopCursor();
}
}
struct WindowInfo {
uint32_t window_id;
CGRect bounds;
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -60,17 +60,18 @@ using mozilla::dom::CrashReporterChild;
using mozilla::dom::PCrashReporterChild;
#if defined(XP_WIN)
const wchar_t * kFlashFullscreenClass = L"ShockwaveFlashFullScreen";
const wchar_t * kMozillaWindowClass = L"MozillaWindowClass";
#endif
namespace {
-PluginModuleChild* gInstance = nullptr;
+PluginModuleChild* gChromeInstance = nullptr;
+nsTArray<PluginModuleChild*>* gAllInstances;
}
#ifdef MOZ_WIDGET_QT
typedef void (*_gtk_init_fn)(int argc, char **argv);
static _gtk_init_fn s_gtk_init = nullptr;
static PRLibrary *sGtkLib = nullptr;
#endif
@@ -79,80 +80,156 @@ static PRLibrary *sGtkLib = nullptr;
static bool gDelayFlashFocusReplyUntilEval = false;
// Used to fix GetWindowInfo problems with internal flash settings dialogs
static WindowsDllInterceptor sUser32Intercept;
typedef BOOL (WINAPI *GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi);
static GetWindowInfoPtr sGetWindowInfoPtrStub = nullptr;
static HWND sBrowserHwnd = nullptr;
#endif
-PluginModuleChild::PluginModuleChild()
+/* static */
+PluginModuleChild*
+PluginModuleChild::CreateForContentProcess(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess)
+{
+ PluginModuleChild* child = new PluginModuleChild(false);
+ ProcessHandle handle;
+ if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
+ // XXX need to kill |aOtherProcess|, it's boned
+ return nullptr;
+ }
+
+ if (!child->InitForContent(handle, XRE_GetIOMessageLoop(), aTransport)) {
+ return nullptr;
+ }
+
+ return child;
+}
+
+PluginModuleChild::PluginModuleChild(bool aIsChrome)
: mLibrary(0)
, mPluginFilename("")
, mQuirks(QUIRKS_NOT_INITIALIZED)
+ , mIsChrome(aIsChrome)
+ , mTransport(nullptr)
, mShutdownFunc(0)
, mInitializeFunc(0)
#if defined(OS_WIN) || defined(OS_MACOSX)
, mGetEntryPointsFunc(0)
#elif defined(MOZ_WIDGET_GTK)
, mNestedLoopTimerId(0)
#elif defined(MOZ_WIDGET_QT)
, mNestedLoopTimerObject(0)
#endif
#ifdef OS_WIN
, mNestedEventHook(nullptr)
, mGlobalCallWndProcHook(nullptr)
#endif
{
- NS_ASSERTION(!gInstance, "Something terribly wrong here!");
+ if (!gAllInstances) {
+ gAllInstances = new nsTArray<PluginModuleChild*>(1);
+ }
+ gAllInstances->AppendElement(this);
+
memset(&mFunctions, 0, sizeof(mFunctions));
- memset(&mSavedData, 0, sizeof(mSavedData));
- gInstance = this;
+ if (mIsChrome) {
+ MOZ_ASSERT(!gChromeInstance);
+ gChromeInstance = this;
+ }
mUserAgent.SetIsVoid(true);
#ifdef XP_MACOSX
mac_plugin_interposing::child::SetUpCocoaInterposing();
#endif
}
PluginModuleChild::~PluginModuleChild()
{
- NS_ASSERTION(gInstance == this, "Something terribly wrong here!");
-
- // We don't unload the plugin library in case it uses atexit handlers or
- // other similar hooks.
+ if (mTransport) {
+ // For some reason IPDL doesn't autmatically delete the channel for a
+ // bridged protocol (bug 1090570). So we have to do it ourselves. This
+ // code is only invoked for PluginModuleChild instances created via
+ // bridging; otherwise mTransport is null.
+ XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new DeleteTask<Transport>(mTransport));
+ }
- DeinitGraphics();
- PluginScriptableObjectChild::ClearIdentifiers();
+ gAllInstances->RemoveElement(this);
+ MOZ_ASSERT_IF(mIsChrome, gAllInstances->Length() == 0);
+ if (gAllInstances->IsEmpty()) {
+ delete gAllInstances;
+ gAllInstances = nullptr;
+ }
- gInstance = nullptr;
+ if (mIsChrome) {
+ MOZ_ASSERT(gChromeInstance == this);
+
+ // We don't unload the plugin library in case it uses atexit handlers or
+ // other similar hooks.
+
+ DeinitGraphics();
+ PluginScriptableObjectChild::ClearIdentifiers();
+
+ gChromeInstance = nullptr;
+ }
}
// static
PluginModuleChild*
-PluginModuleChild::current()
+PluginModuleChild::GetChrome()
{
- NS_ASSERTION(gInstance, "Null instance!");
- return gInstance;
+ MOZ_ASSERT(gChromeInstance);
+ return gChromeInstance;
}
bool
-PluginModuleChild::Init(const std::string& aPluginFilename,
- base::ProcessHandle aParentProcessHandle,
- MessageLoop* aIOLoop,
- IPC::Channel* aChannel)
+PluginModuleChild::CommonInit(base::ProcessHandle aParentProcessHandle,
+ MessageLoop* aIOLoop,
+ IPC::Channel* aChannel)
{
PLUGIN_LOG_DEBUG_METHOD;
- GetIPCChannel()->SetAbortOnError(true);
-
// Request Windows message deferral behavior on our channel. This
// applies to the top level and all sub plugin protocols since they
// all share the same channel.
+ // Bug 1090573 - Don't do this for connections to content processes.
GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
+ if (!Open(aChannel, aParentProcessHandle, aIOLoop))
+ return false;
+
+ memset((void*) &mFunctions, 0, sizeof(mFunctions));
+ mFunctions.size = sizeof(mFunctions);
+ mFunctions.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+
+ return true;
+}
+
+bool
+PluginModuleChild::InitForContent(base::ProcessHandle aParentProcessHandle,
+ MessageLoop* aIOLoop,
+ IPC::Channel* aChannel)
+{
+ if (!CommonInit(aParentProcessHandle, aIOLoop, aChannel)) {
+ return false;
+ }
+
+ mTransport = aChannel;
+
+ mLibrary = GetChrome()->mLibrary;
+ mQuirks = GetChrome()->mQuirks;
+ mFunctions = GetChrome()->mFunctions;
+
+ return true;
+}
+
+bool
+PluginModuleChild::InitForChrome(const std::string& aPluginFilename,
+ base::ProcessHandle aParentProcessHandle,
+ MessageLoop* aIOLoop,
+ IPC::Channel* aChannel)
+{
#ifdef XP_WIN
COMMessageFilter::Initialize(this);
#endif
NS_ASSERTION(aChannel, "need a channel");
if (!InitGraphics())
return false;
@@ -196,22 +273,21 @@ PluginModuleChild::Init(const std::strin
#endif
{
nsresult rv = pluginFile.LoadPlugin(&mLibrary);
if (NS_FAILED(rv))
return false;
}
NS_ASSERTION(mLibrary, "couldn't open shared object");
- if (!Open(aChannel, aParentProcessHandle, aIOLoop))
+ if (!CommonInit(aParentProcessHandle, aIOLoop, aChannel)) {
return false;
+ }
- memset((void*) &mFunctions, 0, sizeof(mFunctions));
- mFunctions.size = sizeof(mFunctions);
- mFunctions.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+ GetIPCChannel()->SetAbortOnError(true);
// TODO: use PluginPRLibrary here
#if defined(OS_LINUX) || defined(OS_BSD)
mShutdownFunc =
(NP_PLUGINSHUTDOWN) PR_FindFunctionSymbol(mLibrary, "NP_Shutdown");
// create the new plugin handler
@@ -586,16 +662,17 @@ PluginModuleChild::DeinitGraphics()
XCloseDisplay(DefaultXDisplay());
#endif
}
bool
PluginModuleChild::AnswerNP_Shutdown(NPError *rv)
{
AssertPluginThread();
+ MOZ_ASSERT(mIsChrome);
#if defined XP_WIN
mozilla::widget::StopAudioSession();
#endif
// the PluginModuleParent shuts down this process after this interrupt
// call pops off its stack
@@ -673,16 +750,23 @@ PluginModuleChild::RecvSetAudioSessionDa
void
PluginModuleChild::QuickExit()
{
NS_WARNING("plugin process _exit()ing");
_exit(0);
}
+PPluginModuleChild*
+PluginModuleChild::AllocPPluginModuleChild(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess)
+{
+ return PluginModuleChild::CreateForContentProcess(aTransport, aOtherProcess);
+}
+
PCrashReporterChild*
PluginModuleChild::AllocPCrashReporterChild(mozilla::dom::NativeThreadId* id,
uint32_t* processType)
{
return new CrashReporterChild();
}
bool
@@ -703,16 +787,27 @@ PluginModuleChild::AnswerPCrashReporterC
*processType = XRE_GetProcessType();
#endif
return true;
}
void
PluginModuleChild::ActorDestroy(ActorDestroyReason why)
{
+ if (!mIsChrome) {
+ PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
+ if (chromeInstance) {
+ chromeInstance->SendNotifyContentModuleDestroyed();
+ }
+
+ // Destroy ourselves once we finish other teardown activities.
+ MessageLoop::current()->PostTask(FROM_HERE, new DeleteTask<PluginModuleChild>(this));
+ return;
+ }
+
if (AbnormalShutdown == why) {
NS_WARNING("shutting down early because of crash!");
QuickExit();
}
// doesn't matter why we're being destroyed; it's up to us to
// initiate (clean) shutdown
XRE_ShutdownChildProcess();
@@ -727,69 +822,16 @@ const char*
PluginModuleChild::GetUserAgent()
{
if (mUserAgent.IsVoid() && !CallNPN_UserAgent(&mUserAgent))
return nullptr;
return NullableStringGet(mUserAgent);
}
-bool
-PluginModuleChild::RegisterActorForNPObject(NPObject* aObject,
- PluginScriptableObjectChild* aActor)
-{
- AssertPluginThread();
- NS_ASSERTION(aObject && aActor, "Null pointer!");
-
- NPObjectData* d = mObjectMap.GetEntry(aObject);
- if (!d) {
- NS_ERROR("NPObject not in object table");
- return false;
- }
-
- d->actor = aActor;
- return true;
-}
-
-void
-PluginModuleChild::UnregisterActorForNPObject(NPObject* aObject)
-{
- AssertPluginThread();
- NS_ASSERTION(aObject, "Null pointer!");
-
- NPObjectData* d = mObjectMap.GetEntry(aObject);
- NS_ASSERTION(d, "NPObject not in object table");
- if (d) {
- d->actor = nullptr;
- }
-}
-
-PluginScriptableObjectChild*
-PluginModuleChild::GetActorForNPObject(NPObject* aObject)
-{
- AssertPluginThread();
- NS_ASSERTION(aObject, "Null pointer!");
-
- NPObjectData* d = mObjectMap.GetEntry(aObject);
- if (!d) {
- NS_ERROR("Plugin using object not created with NPN_CreateObject?");
- return nullptr;
- }
-
- return d->actor;
-}
-
-#ifdef DEBUG
-bool
-PluginModuleChild::NPObjectIsRegistered(NPObject* aObject)
-{
- return !!mObjectMap.GetEntry(aObject);
-}
-#endif
-
//-----------------------------------------------------------------------------
// FIXME/cjones: just getting this out of the way for the moment ...
namespace mozilla {
namespace plugins {
namespace child {
static NPError
@@ -1080,17 +1122,17 @@ NPError
case NPNVjavascriptEnabledBool: // Intentional fall-through
case NPNVasdEnabledBool: // Intentional fall-through
case NPNVisOfflineBool: // Intentional fall-through
case NPNVSupportsXEmbedBool: // Intentional fall-through
case NPNVSupportsWindowless: { // Intentional fall-through
NPError result;
bool value;
- PluginModuleChild::current()->
+ PluginModuleChild::GetChrome()->
CallNPN_GetValue_WithBoolReturn(aVariable, &result, &value);
*(NPBool*)aValue = value ? true : false;
return result;
}
#if defined(MOZ_WIDGET_GTK)
case NPNVxDisplay: {
if (aNPP) {
return InstCast(aNPP)->NPN_GetValue(aVariable, aValue);
@@ -1271,17 +1313,20 @@ uint32_t
}
void
_reloadplugins(NPBool aReloadPages)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD_VOID();
- PluginModuleChild::current()->SendNPN_ReloadPlugins(!!aReloadPages);
+ // Send the reload message to all modules. Chrome will need to reload from
+ // disk and content will need to request a new list of plugin tags from
+ // chrome.
+ PluginModuleChild::GetChrome()->SendNPN_ReloadPlugins(!!aReloadPages);
}
void
_invalidaterect(NPP aNPP,
NPRect* aInvalidRect)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD_VOID();
@@ -1310,17 +1355,17 @@ void
// never be necessary.
}
const char*
_useragent(NPP aNPP)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD(nullptr);
- return PluginModuleChild::current()->GetUserAgent();
+ return PluginModuleChild::GetChrome()->GetUserAgent();
}
void*
_memalloc(uint32_t aSize)
{
PLUGIN_LOG_DEBUG_FUNCTION;
// Only assert plugin thread here for consistency with in-process plugins.
AssertPluginThread();
@@ -1541,28 +1586,17 @@ void
void
_setexception(NPObject* aNPObj,
const NPUTF8* aMessage)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD_VOID();
- PluginModuleChild* self = PluginModuleChild::current();
- PluginScriptableObjectChild* actor = nullptr;
- if (aNPObj) {
- actor = self->GetActorForNPObject(aNPObj);
- if (!actor) {
- NS_ERROR("Failed to get actor!");
- return;
- }
- }
-
- self->SendNPN_SetException(static_cast<PPluginScriptableObjectChild*>(actor),
- NullableString(aMessage));
+ // Do nothing. We no longer support this API.
}
void
_pushpopupsenabledstate(NPP aNPP,
NPBool aEnabled)
{
PLUGIN_LOG_DEBUG_FUNCTION;
ENSURE_PLUGIN_THREAD_VOID();
@@ -1742,17 +1776,17 @@ NPError
NPBool success = _convertpoint(instance,
pluginX, pluginY, NPCoordinateSpacePlugin,
&screenX, &screenY, NPCoordinateSpaceScreen);
if (success) {
return mozilla::plugins::PluginUtilsOSX::ShowCocoaContextMenu(menu,
screenX, screenY,
- PluginModuleChild::current(),
+ PluginModuleChild::GetChrome(),
ProcessBrowserEvents);
} else {
NS_WARNING("Convertpoint failed, could not created contextmenu.");
return NPERR_GENERIC_ERROR;
}
#else
NS_WARNING("Not supported on this platform!");
@@ -1800,32 +1834,34 @@ void
//-----------------------------------------------------------------------------
bool
PluginModuleChild::AnswerNP_GetEntryPoints(NPError* _retval)
{
PLUGIN_LOG_DEBUG_METHOD;
AssertPluginThread();
+ MOZ_ASSERT(mIsChrome);
#if defined(OS_LINUX) || defined(OS_BSD)
return true;
#elif defined(OS_WIN) || defined(OS_MACOSX)
*_retval = mGetEntryPointsFunc(&mFunctions);
return true;
#else
# error Please implement me for your platform
#endif
}
bool
PluginModuleChild::AnswerNP_Initialize(NPError* _retval)
{
PLUGIN_LOG_DEBUG_METHOD;
AssertPluginThread();
+ MOZ_ASSERT(mIsChrome);
#ifdef OS_WIN
SetEventHooks();
#endif
#ifdef MOZ_X11
// Send the parent our X socket to act as a proxy reference for our X
// resources.
@@ -2045,20 +2081,17 @@ PluginModuleChild::NPN_CreateObject(NPP
}
if (newObject) {
newObject->_class = aClass;
newObject->referenceCount = 1;
NS_LOG_ADDREF(newObject, 1, "NPObject", sizeof(NPObject));
}
- NPObjectData* d = static_cast<PluginModuleChild*>(i->Manager())
- ->mObjectMap.PutEntry(newObject);
- NS_ASSERTION(!d->instance, "New NPObject already mapped?");
- d->instance = i;
+ PluginScriptableObjectChild::RegisterObject(newObject, i);
return newObject;
}
NPObject*
PluginModuleChild::NPN_RetainObject(NPObject* aNPObj)
{
AssertPluginThread();
@@ -2072,25 +2105,25 @@ PluginModuleChild::NPN_RetainObject(NPOb
return aNPObj;
}
void
PluginModuleChild::NPN_ReleaseObject(NPObject* aNPObj)
{
AssertPluginThread();
- NPObjectData* d = current()->mObjectMap.GetEntry(aNPObj);
- if (!d) {
+ PluginInstanceChild* instance = PluginScriptableObjectChild::GetInstanceForNPObject(aNPObj);
+ if (!instance) {
NS_ERROR("Releasing object not in mObjectMap?");
return;
}
DeletingObjectEntry* doe = nullptr;
- if (d->instance->mDeletingHash) {
- doe = d->instance->mDeletingHash->GetEntry(aNPObj);
+ if (instance->mDeletingHash) {
+ doe = instance->mDeletingHash->GetEntry(aNPObj);
if (!doe) {
NS_ERROR("An object for a destroyed instance isn't in the instance deletion hash");
return;
}
if (doe->mDeleted)
return;
}
@@ -2109,39 +2142,21 @@ void
PluginModuleChild::DeallocNPObject(NPObject* aNPObj)
{
if (aNPObj->_class && aNPObj->_class->deallocate) {
aNPObj->_class->deallocate(aNPObj);
} else {
child::_memfree(aNPObj);
}
- NPObjectData* d = current()->mObjectMap.GetEntry(aNPObj);
- if (d->actor)
- d->actor->NPObjectDestroyed();
-
- current()->mObjectMap.RemoveEntry(aNPObj);
-}
+ PluginScriptableObjectChild* actor = PluginScriptableObjectChild::GetActorForNPObject(aNPObj);
+ if (actor)
+ actor->NPObjectDestroyed();
-void
-PluginModuleChild::FindNPObjectsForInstance(PluginInstanceChild* instance)
-{
- NS_ASSERTION(instance->mDeletingHash, "filling null mDeletingHash?");
- mObjectMap.EnumerateEntries(CollectForInstance, instance);
-}
-
-PLDHashOperator
-PluginModuleChild::CollectForInstance(NPObjectData* d, void* userArg)
-{
- PluginInstanceChild* instance = static_cast<PluginInstanceChild*>(userArg);
- if (d->instance == instance) {
- NPObject* o = d->GetKey();
- instance->mDeletingHash->PutEntry(o);
- }
- return PL_DHASH_NEXT;
+ PluginScriptableObjectChild::UnregisterObject(aNPObj);
}
NPIdentifier
PluginModuleChild::NPN_GetStringIdentifier(const NPUTF8* aName)
{
PLUGIN_LOG_DEBUG_FUNCTION;
AssertPluginThread();
@@ -2267,17 +2282,17 @@ PluginModuleChild::CallWindowProcHook(in
}
return CallNextHookEx(nullptr, nCode, wParam, lParam);
}
LRESULT CALLBACK
PluginModuleChild::NestedInputEventHook(int nCode, WPARAM wParam, LPARAM lParam)
{
- PluginModuleChild* self = current();
+ PluginModuleChild* self = GetChrome();
uint32_t len = self->mIncallPumpingStack.Length();
if (nCode >= 0 && len && !self->mIncallPumpingStack[len - 1]._spinning) {
MessageLoop* loop = MessageLoop::current();
self->SendProcessNativeEventsInInterruptCall();
IncallFrame& f = self->mIncallPumpingStack[len - 1];
f._spinning = true;
f._savedNestableTasksAllowed = loop->NestableTasksAllowed();
loop->SetNestableTasksAllowed(true);
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -71,16 +71,20 @@ protected:
}
virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
// Implement the PPluginModuleChild interface
virtual bool AnswerNP_GetEntryPoints(NPError* rv) MOZ_OVERRIDE;
virtual bool AnswerNP_Initialize(NPError* rv) MOZ_OVERRIDE;
+ virtual PPluginModuleChild*
+ AllocPPluginModuleChild(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess) MOZ_OVERRIDE;
+
virtual PPluginInstanceChild*
AllocPPluginInstanceChild(const nsCString& aMimeType,
const uint16_t& aMode,
const InfallibleTArray<nsCString>& aNames,
const InfallibleTArray<nsCString>& aValues,
NPError* rv) MOZ_OVERRIDE;
virtual bool
@@ -135,43 +139,44 @@ protected:
virtual bool
RecvProcessNativeEventsInInterruptCall() MOZ_OVERRIDE;
virtual bool
AnswerGeckoGetProfile(nsCString* aProfile) MOZ_OVERRIDE;
public:
- PluginModuleChild();
+ PluginModuleChild(bool aIsChrome);
virtual ~PluginModuleChild();
+ bool CommonInit(base::ProcessHandle aParentProcessHandle,
+ MessageLoop* aIOLoop,
+ IPC::Channel* aChannel);
+
// aPluginFilename is UTF8, not native-charset!
- bool Init(const std::string& aPluginFilename,
- base::ProcessHandle aParentProcessHandle,
- MessageLoop* aIOLoop,
- IPC::Channel* aChannel);
+ bool InitForChrome(const std::string& aPluginFilename,
+ base::ProcessHandle aParentProcessHandle,
+ MessageLoop* aIOLoop,
+ IPC::Channel* aChannel);
+
+ bool InitForContent(base::ProcessHandle aParentProcessHandle,
+ MessageLoop* aIOLoop,
+ IPC::Channel* aChannel);
+
+ static PluginModuleChild*
+ CreateForContentProcess(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess);
void CleanUp();
const char* GetUserAgent();
static const NPNetscapeFuncs sBrowserFuncs;
- static PluginModuleChild* current();
-
- bool RegisterActorForNPObject(NPObject* aObject,
- PluginScriptableObjectChild* aActor);
-
- void UnregisterActorForNPObject(NPObject* aObject);
-
- PluginScriptableObjectChild* GetActorForNPObject(NPObject* aObject);
-
-#ifdef DEBUG
- bool NPObjectIsRegistered(NPObject* aObject);
-#endif
+ static PluginModuleChild* GetChrome();
/**
* The child implementation of NPN_CreateObject.
*/
static NPObject* NPN_CreateObject(NPP aNPP, NPClass* aClass);
/**
* The child implementation of NPN_RetainObject.
*/
@@ -294,27 +299,29 @@ private:
virtual void ExitedCxxStack() MOZ_OVERRIDE;
#endif
PRLibrary* mLibrary;
nsCString mPluginFilename; // UTF8
nsCString mUserAgent;
int mQuirks;
+ bool mIsChrome;
+ Transport* mTransport;
+
// we get this from the plugin
NP_PLUGINSHUTDOWN mShutdownFunc;
#if defined(OS_LINUX) || defined(OS_BSD)
NP_PLUGINUNIXINIT mInitializeFunc;
#elif defined(OS_WIN) || defined(OS_MACOSX)
NP_PLUGININIT mInitializeFunc;
NP_GETENTRYPOINTS mGetEntryPointsFunc;
#endif
NPPluginFuncs mFunctions;
- NPSavedData mSavedData;
#if defined(MOZ_WIDGET_GTK)
// If a plugin spins a nested glib event loop in response to a
// synchronous IPC message from the browser, the loop might break
// only after the browser responds to a request sent by the
// plugin. This can happen if a plugin uses gtk's synchronous
// copy/paste, for example. But because the browser is blocked on
// a condvar, it can't respond to the request. This situation
@@ -348,56 +355,28 @@ private:
// g_main_context_iteration, or 0 when dispatched directly from
// MessagePumpForUI.
int mTopLoopDepth;
# endif
#elif defined (MOZ_WIDGET_QT)
NestedLoopTimer *mNestedLoopTimerObject;
#endif
- struct NPObjectData : public nsPtrHashKey<NPObject>
- {
- explicit NPObjectData(const NPObject* key)
- : nsPtrHashKey<NPObject>(key)
- , instance(nullptr)
- , actor(nullptr)
- { }
-
- // never nullptr
- PluginInstanceChild* instance;
-
- // sometimes nullptr (no actor associated with an NPObject)
- PluginScriptableObjectChild* actor;
- };
- /**
- * mObjectMap contains all the currently active NPObjects (from NPN_CreateObject until the
- * final release/dealloc, whether or not an actor is currently associated with the object.
- */
- nsTHashtable<NPObjectData> mObjectMap;
-
public: // called by PluginInstanceChild
/**
* Dealloc an NPObject after last-release or when the associated instance
* is destroyed. This function will remove the object from mObjectMap.
*/
static void DeallocNPObject(NPObject* o);
NPError NPP_Destroy(PluginInstanceChild* instance) {
return mFunctions.destroy(instance->GetNPP(), 0);
}
- /**
- * Fill PluginInstanceChild.mDeletingHash with all the remaining NPObjects
- * associated with that instance.
- */
- void FindNPObjectsForInstance(PluginInstanceChild* instance);
-
private:
- static PLDHashOperator CollectForInstance(NPObjectData* d, void* userArg);
-
#if defined(OS_WIN)
virtual void EnteredCall() MOZ_OVERRIDE;
virtual void ExitedCall() MOZ_OVERRIDE;
// Entered/ExitedCall notifications keep track of whether the plugin has
// entered a nested event loop within this interrupt call.
struct IncallFrame
{
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -10,19 +10,22 @@
#include <QtCore/QEventLoop>
#include "NestedLoopTimer.h"
#endif
#include "mozilla/plugins/PluginModuleParent.h"
#include "base/process_util.h"
#include "mozilla/Attributes.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/PCrashReporterParent.h"
#include "mozilla/ipc/MessageChannel.h"
#include "mozilla/plugins/BrowserStreamParent.h"
+#include "mozilla/plugins/PluginBridge.h"
#include "mozilla/plugins/PluginInstanceParent.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/unused.h"
#include "nsAutoPtr.h"
#include "nsCRT.h"
#include "nsIFile.h"
#include "nsIObserverService.h"
@@ -78,26 +81,92 @@ static const char kHangUIMinDisplayPref[
template<>
struct RunnableMethodTraits<mozilla::plugins::PluginModuleParent>
{
typedef mozilla::plugins::PluginModuleParent Class;
static void RetainCallee(Class* obj) { }
static void ReleaseCallee(Class* obj) { }
};
+bool
+mozilla::plugins::SetupBridge(uint32_t aPluginId, dom::ContentParent* aContentParent)
+{
+ nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ nsRefPtr<nsNPAPIPlugin> plugin;
+ nsresult rv = host->GetPluginForContentProcess(aPluginId, getter_AddRefs(plugin));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ PluginModuleParent* chromeParent = static_cast<PluginModuleParent*>(plugin->GetLibrary());
+ return PPluginModule::Bridge(aContentParent, chromeParent);
+}
+
+PluginModuleContentParent* PluginModuleContentParent::sSavedModuleParent;
+
+/* static */ PluginLibrary*
+PluginModuleContentParent::LoadModule(uint32_t aPluginId)
+{
+ MOZ_ASSERT(!sSavedModuleParent);
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
+
+ /*
+ * We send a LoadPlugin message to the chrome process using an intr
+ * message. Before it sends its response, it sends a message to create
+ * PluginModuleParent instance. That message is handled by
+ * PluginModuleContentParent::Create, which saves the instance in
+ * sSavedModuleParent. We fetch it from there after LoadPlugin finishes.
+ */
+ dom::ContentChild* cp = dom::ContentChild::GetSingleton();
+ if (!cp->CallLoadPlugin(aPluginId)) {
+ return nullptr;
+ }
+
+ PluginModuleContentParent* parent = sSavedModuleParent;
+ MOZ_ASSERT(parent);
+ sSavedModuleParent = nullptr;
+
+ return parent;
+}
+
+/* static */ PluginModuleContentParent*
+PluginModuleContentParent::Create(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess)
+{
+ nsAutoPtr<PluginModuleContentParent> parent(new PluginModuleContentParent());
+ ProcessHandle handle;
+ if (!base::OpenProcessHandle(aOtherProcess, &handle)) {
+ // Bug 1090578 - need to kill |aOtherProcess|, it's boned.
+ return nullptr;
+ }
+
+ MOZ_ASSERT(!sSavedModuleParent);
+ sSavedModuleParent = parent;
+
+ DebugOnly<bool> ok = parent->Open(aTransport, handle, XRE_GetIOMessageLoop(),
+ mozilla::ipc::ParentSide);
+ MOZ_ASSERT(ok);
+
+ // Request Windows message deferral behavior on our channel. This
+ // applies to the top level and all sub plugin protocols since they
+ // all share the same channel.
+ parent->GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
+
+ return parent.forget();
+}
+
// static
PluginLibrary*
-PluginModuleParent::LoadModule(const char* aFilePath)
+PluginModuleChromeParent::LoadModule(const char* aFilePath, uint32_t aPluginId)
{
PLUGIN_LOG_DEBUG_FUNCTION;
int32_t prefSecs = Preferences::GetInt(kLaunchTimeoutPref, 0);
// Block on the child process being launched and initialized.
- nsAutoPtr<PluginModuleParent> parent(new PluginModuleParent(aFilePath));
+ nsAutoPtr<PluginModuleChromeParent> parent(new PluginModuleChromeParent(aFilePath, aPluginId));
bool launched = parent->mSubprocess->Launch(prefSecs * 1000);
if (!launched) {
// We never reached open
parent->mShutdown = true;
return nullptr;
}
parent->Open(parent->mSubprocess->GetChannel(),
parent->mSubprocess->GetChildProcessHandle());
@@ -119,25 +188,50 @@ PluginModuleParent::LoadModule(const cha
mozilla::MutexAutoLock lock(parent->mCrashReporterMutex);
parent->mCrashReporter = parent->CrashReporter();
#endif
#endif
return parent.forget();
}
-
-PluginModuleParent::PluginModuleParent(const char* aFilePath)
- : mSubprocess(new PluginProcessParent(aFilePath))
+PluginModuleParent::PluginModuleParent(bool aIsChrome)
+ : mIsChrome(aIsChrome)
, mShutdown(false)
, mClearSiteDataSupported(false)
, mGetSitesWithDataSupported(false)
, mNPNIface(nullptr)
, mPlugin(nullptr)
, mTaskFactory(MOZ_THIS_IN_INITIALIZER_LIST())
+{
+}
+
+PluginModuleParent::~PluginModuleParent()
+{
+ if (!OkToCleanup()) {
+ NS_RUNTIMEABORT("unsafe destruction");
+ }
+
+ if (!mShutdown) {
+ NS_WARNING("Plugin host deleted the module without shutting down.");
+ NPError err;
+ NP_Shutdown(&err);
+ }
+}
+
+PluginModuleContentParent::PluginModuleContentParent()
+ : PluginModuleParent(false)
+{
+}
+
+PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath, uint32_t aPluginId)
+ : PluginModuleParent(true)
+ , mSubprocess(new PluginProcessParent(aFilePath))
+ , mPluginId(aPluginId)
+ , mChromeTaskFactory(MOZ_THIS_IN_INITIALIZER_LIST())
, mHangAnnotationFlags(0)
#ifdef XP_WIN
, mPluginCpuUsageOnHang()
, mHangUIParent(nullptr)
, mHangUIEnabled(true)
, mIsTimerReset(true)
#ifdef MOZ_CRASHREPORTER
, mCrashReporterMutex("PluginModuleParent::mCrashReporterMutex")
@@ -160,17 +254,17 @@ PluginModuleParent::PluginModuleParent(c
#ifdef MOZ_ENABLE_PROFILER_SPS
InitPluginProfiling();
#endif
mozilla::HangMonitor::RegisterAnnotator(*this);
}
-PluginModuleParent::~PluginModuleParent()
+PluginModuleChromeParent::~PluginModuleChromeParent()
{
if (!OkToCleanup()) {
NS_RUNTIMEABORT("unsafe destruction");
}
#ifdef MOZ_ENABLE_PROFILER_SPS
ShutdownPluginProfiling();
#endif
@@ -207,17 +301,17 @@ PluginModuleParent::~PluginModuleParent(
}
#endif
mozilla::HangMonitor::UnregisterAnnotator(*this);
}
#ifdef MOZ_CRASHREPORTER
void
-PluginModuleParent::WriteExtraDataForMinidump(AnnotationTable& notes)
+PluginModuleChromeParent::WriteExtraDataForMinidump(AnnotationTable& notes)
{
#ifdef XP_WIN
// mCrashReporterMutex is already held by the caller
mCrashReporterMutex.AssertCurrentThreadOwns();
#endif
typedef nsDependentCString CS;
// Get the plugin filename, try to get just the file leafname
@@ -252,58 +346,58 @@ PluginModuleParent::WriteExtraDataForMin
#endif
}
#endif
}
}
#endif // MOZ_CRASHREPORTER
void
-PluginModuleParent::SetChildTimeout(const int32_t aChildTimeout)
+PluginModuleChromeParent::SetChildTimeout(const int32_t aChildTimeout)
{
int32_t timeoutMs = (aChildTimeout > 0) ? (1000 * aChildTimeout) :
MessageChannel::kNoTimeout;
SetReplyTimeoutMs(timeoutMs);
}
void
-PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule)
+PluginModuleChromeParent::TimeoutChanged(const char* aPref, void* aModule)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
#ifndef XP_WIN
if (!strcmp(aPref, kChildTimeoutPref)) {
// The timeout value used by the parent for children
int32_t timeoutSecs = Preferences::GetInt(kChildTimeoutPref, 0);
- static_cast<PluginModuleParent*>(aModule)->SetChildTimeout(timeoutSecs);
+ static_cast<PluginModuleChromeParent*>(aModule)->SetChildTimeout(timeoutSecs);
#else
if (!strcmp(aPref, kChildTimeoutPref) ||
!strcmp(aPref, kHangUIMinDisplayPref) ||
!strcmp(aPref, kHangUITimeoutPref)) {
- static_cast<PluginModuleParent*>(aModule)->EvaluateHangUIState(true);
+ static_cast<PluginModuleChromeParent*>(aModule)->EvaluateHangUIState(true);
#endif // XP_WIN
} else if (!strcmp(aPref, kParentTimeoutPref)) {
// The timeout value used by the child for its parent
int32_t timeoutSecs = Preferences::GetInt(kParentTimeoutPref, 0);
- unused << static_cast<PluginModuleParent*>(aModule)->SendSetParentHangTimeout(timeoutSecs);
+ unused << static_cast<PluginModuleChromeParent*>(aModule)->SendSetParentHangTimeout(timeoutSecs);
}
}
void
-PluginModuleParent::CleanupFromTimeout(const bool aFromHangUI)
+PluginModuleChromeParent::CleanupFromTimeout(const bool aFromHangUI)
{
if (mShutdown) {
return;
}
if (!OkToCleanup()) {
// there's still plugin code on the C++ stack, try again
MessageLoop::current()->PostDelayedTask(
FROM_HERE,
- mTaskFactory.NewRunnableMethod(
- &PluginModuleParent::CleanupFromTimeout, aFromHangUI), 10);
+ mChromeTaskFactory.NewRunnableMethod(
+ &PluginModuleChromeParent::CleanupFromTimeout, aFromHangUI), 10);
return;
}
/* If the plugin container was terminated by the Plugin Hang UI,
then either the I/O thread detects a channel error, or the
main thread must set the error (whomever gets there first).
OTOH, if we terminate and return false from
ShouldContinueFromReplyTimeout, then the channel state has
@@ -380,35 +474,35 @@ GetProcessCpuUsage(const InfallibleTArra
return true;
}
} // anonymous namespace
#endif // #ifdef XP_WIN
void
-PluginModuleParent::EnteredCxxStack()
+PluginModuleChromeParent::EnteredCxxStack()
{
mHangAnnotationFlags |= kInPluginCall;
}
void
-PluginModuleParent::ExitedCxxStack()
+PluginModuleChromeParent::ExitedCxxStack()
{
mHangAnnotationFlags = 0;
#ifdef XP_WIN
FinishHangUI();
#endif
}
/**
* This function is always called by the HangMonitor thread.
*/
void
-PluginModuleParent::AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations)
+PluginModuleChromeParent::AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations)
{
uint32_t flags = mHangAnnotationFlags;
if (flags) {
/* We don't actually annotate anything specifically for kInPluginCall;
we use it to determine whether to annotate other things. It will
be pretty obvious from the ChromeHang stack that we're in a plugin
call when the hang occurred. */
if (flags & kHangUIShown) {
@@ -446,32 +540,32 @@ CreateFlashMinidump(DWORD processId, Thr
bool res = CreateAdditionalChildMinidump(handle, 0, parentMinidump, name);
base::CloseProcessHandle(handle);
return res;
}
#endif
bool
-PluginModuleParent::ShouldContinueFromReplyTimeout()
+PluginModuleChromeParent::ShouldContinueFromReplyTimeout()
{
#ifdef XP_WIN
if (LaunchHangUI()) {
return true;
}
// If LaunchHangUI returned false then we should proceed with the
// original plugin hang behaviour and kill the plugin container.
FinishHangUI();
#endif // XP_WIN
TerminateChildProcess(MessageLoop::current());
return false;
}
void
-PluginModuleParent::TerminateChildProcess(MessageLoop* aMsgLoop)
+PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop)
{
#ifdef MOZ_CRASHREPORTER
#ifdef XP_WIN
mozilla::MutexAutoLock lock(mCrashReporterMutex);
CrashReporterParent* crashReporter = mCrashReporter;
if (!crashReporter) {
// If mCrashReporter is null then the hang has ended, the plugin module
// is shutting down. There's nothing to do here.
@@ -551,18 +645,18 @@ PluginModuleParent::TerminateChildProces
}
#endif
// this must run before the error notification from the channel,
// or not at all
bool isFromHangUI = aMsgLoop != MessageLoop::current();
aMsgLoop->PostTask(
FROM_HERE,
- mTaskFactory.NewRunnableMethod(
- &PluginModuleParent::CleanupFromTimeout, isFromHangUI));
+ mChromeTaskFactory.NewRunnableMethod(
+ &PluginModuleChromeParent::CleanupFromTimeout, isFromHangUI));
if (!KillProcess(OtherProcess(), 1, false))
NS_WARNING("failed to kill subprocess!");
}
bool
PluginModuleParent::GetPluginDetails(nsACString& aPluginName,
nsACString& aPluginVersion)
@@ -577,17 +671,17 @@ PluginModuleParent::GetPluginDetails(nsA
}
aPluginName = pluginTag->mName;
aPluginVersion = pluginTag->mVersion;
return true;
}
#ifdef XP_WIN
void
-PluginModuleParent::EvaluateHangUIState(const bool aReset)
+PluginModuleChromeParent::EvaluateHangUIState(const bool aReset)
{
int32_t minDispSecs = Preferences::GetInt(kHangUIMinDisplayPref, 10);
int32_t autoStopSecs = Preferences::GetInt(kChildTimeoutPref, 0);
int32_t timeoutSecs = 0;
if (autoStopSecs > 0 && autoStopSecs < minDispSecs) {
/* If we're going to automatically terminate the plugin within a
time frame shorter than minDispSecs, there's no point in
showing the hang UI; it would just flash briefly on the screen. */
@@ -611,17 +705,17 @@ PluginModuleParent::EvaluateHangUIState(
autoStopSecs *= 2;
}
}
mIsTimerReset = false;
SetChildTimeout(autoStopSecs);
}
bool
-PluginModuleParent::LaunchHangUI()
+PluginModuleChromeParent::LaunchHangUI()
{
if (!mHangUIEnabled) {
return false;
}
if (mHangUIParent) {
if (mHangUIParent->IsShowing()) {
// We've already shown the UI but the timeout has expired again.
return false;
@@ -648,17 +742,17 @@ PluginModuleParent::LaunchHangUI()
after kChildTimeoutPref seconds if the user doesn't respond to
the hang UI. */
EvaluateHangUIState(false);
}
return retval;
}
void
-PluginModuleParent::FinishHangUI()
+PluginModuleChromeParent::FinishHangUI()
{
if (mHangUIEnabled && mHangUIParent) {
bool needsCancel = mHangUIParent->IsShowing();
// If we're still showing, send a Cancel notification
if (needsCancel) {
mHangUIParent->Cancel();
}
/* If we cancelled the UI or if the user issued a response,
@@ -669,25 +763,25 @@ PluginModuleParent::FinishHangUI()
UI was displayed. Now that we're finishing the UI, we need to
switch it back to kHangUITimeoutPref. */
EvaluateHangUIState(true);
}
}
}
void
-PluginModuleParent::OnHangUIContinue()
+PluginModuleChromeParent::OnHangUIContinue()
{
mHangAnnotationFlags |= kHangUIContinued;
}
#endif // XP_WIN
#ifdef MOZ_CRASHREPORTER
CrashReporterParent*
-PluginModuleParent::CrashReporter()
+PluginModuleChromeParent::CrashReporter()
{
return static_cast<CrashReporterParent*>(ManagedPCrashReporterParent()[0]);
}
#ifdef MOZ_CRASHREPORTER_INJECTOR
static void
RemoveMinidump(nsIFile* minidump)
{
@@ -699,17 +793,17 @@ RemoveMinidump(nsIFile* minidump)
if (GetExtraFileForMinidump(minidump,
getter_AddRefs(extraFile))) {
extraFile->Remove(true);
}
}
#endif // MOZ_CRASHREPORTER_INJECTOR
void
-PluginModuleParent::ProcessFirstMinidump()
+PluginModuleChromeParent::ProcessFirstMinidump()
{
#ifdef XP_WIN
mozilla::MutexAutoLock lock(mCrashReporterMutex);
#endif
CrashReporterParent* crashReporter = CrashReporter();
if (!crashReporter)
return;
@@ -776,20 +870,16 @@ PluginModuleParent::ProcessFirstMinidump
}
#endif
void
PluginModuleParent::ActorDestroy(ActorDestroyReason why)
{
switch (why) {
case AbnormalShutdown: {
-#ifdef MOZ_CRASHREPORTER
- ProcessFirstMinidump();
-#endif
-
mShutdown = true;
// Defer the PluginCrashed method so that we don't re-enter
// and potentially modify the actor child list while enumerating it.
if (mPlugin)
MessageLoop::current()->PostTask(
FROM_HERE,
mTaskFactory.NewRunnableMethod(
&PluginModuleParent::NotifyPluginCrashed));
@@ -800,16 +890,28 @@ PluginModuleParent::ActorDestroy(ActorDe
break;
default:
NS_RUNTIMEABORT("Unexpected shutdown reason for toplevel actor.");
}
}
void
+PluginModuleChromeParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (why == AbnormalShutdown) {
+#ifdef MOZ_CRASHREPORTER
+ ProcessFirstMinidump();
+#endif
+ }
+
+ PluginModuleParent::ActorDestroy(why);
+}
+
+void
PluginModuleParent::NotifyPluginCrashed()
{
if (!OkToCleanup()) {
// there's still plugin code on the C++ stack. try again
MessageLoop::current()->PostDelayedTask(
FROM_HERE,
mTaskFactory.NewRunnableMethod(
&PluginModuleParent::NotifyPluginCrashed), 10);
@@ -1168,23 +1270,26 @@ PluginModuleParent::NP_Initialize(NPNets
mNPNIface = bFuncs;
if (mShutdown) {
*error = NPERR_GENERIC_ERROR;
return NS_ERROR_FAILURE;
}
- if (!CallNP_Initialize(error)) {
- Close();
- return NS_ERROR_FAILURE;
- }
- else if (*error != NPERR_NO_ERROR) {
- Close();
- return NS_OK;
+ *error = NPERR_NO_ERROR;
+ if (IsChrome()) {
+ if (!CallNP_Initialize(error)) {
+ Close();
+ return NS_ERROR_FAILURE;
+ }
+ else if (*error != NPERR_NO_ERROR) {
+ Close();
+ return NS_OK;
+ }
}
SetPluginFuncs(pFuncs);
return NS_OK;
}
#else
nsresult
@@ -1194,16 +1299,27 @@ PluginModuleParent::NP_Initialize(NPNets
mNPNIface = bFuncs;
if (mShutdown) {
*error = NPERR_GENERIC_ERROR;
return NS_ERROR_FAILURE;
}
+ *error = NPERR_NO_ERROR;
+ return NS_OK;
+}
+
+nsresult
+PluginModuleChromeParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
+{
+ nsresult rv = PluginModuleParent::NP_Initialize(bFuncs, error);
+ if (NS_FAILED(rv))
+ return rv;
+
if (!CallNP_Initialize(error)) {
Close();
return NS_ERROR_FAILURE;
}
if (*error != NPERR_NO_ERROR) {
Close();
return NS_OK;
}
@@ -1233,17 +1349,20 @@ PluginModuleParent::NP_Shutdown(NPError*
{
PLUGIN_LOG_DEBUG_METHOD;
if (mShutdown) {
*error = NPERR_GENERIC_ERROR;
return NS_ERROR_FAILURE;
}
- bool ok = CallNP_Shutdown(error);
+ bool ok = true;
+ if (IsChrome()) {
+ ok = CallNP_Shutdown(error);
+ }
// if NP_Shutdown() is nested within another interrupt call, this will
// break things. but lord help us if we're doing that anyway; the
// plugin dso will have been unloaded on the other side by the
// CallNP_Shutdown() message
Close();
return ok ? NS_OK : NS_ERROR_FAILURE;
@@ -1271,26 +1390,31 @@ PluginModuleParent::NP_GetValue(void *fu
}
#if defined(XP_WIN) || defined(XP_MACOSX)
nsresult
PluginModuleParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
{
NS_ASSERTION(pFuncs, "Null pointer!");
- // We need to have the child process update its function table
- // here by actually calling NP_GetEntryPoints since the parent's
- // function table can reflect nullptr entries in the child's table.
- if (!CallNP_GetEntryPoints(error)) {
- return NS_ERROR_FAILURE;
- }
- else if (*error != NPERR_NO_ERROR) {
- return NS_OK;
+ // We need to have the plugin process update its function table here by
+ // actually calling NP_GetEntryPoints. The parent's function table will
+ // reflect nullptr entries in the child's table once SetPluginFuncs is
+ // called.
+
+ if (IsChrome()) {
+ if (!CallNP_GetEntryPoints(error)) {
+ return NS_ERROR_FAILURE;
+ }
+ else if (*error != NPERR_NO_ERROR) {
+ return NS_OK;
+ }
}
+ *error = NPERR_NO_ERROR;
SetPluginFuncs(pFuncs);
return NS_OK;
}
#endif
nsresult
PluginModuleParent::NPP_New(NPMIMEType pluginType, NPP instance,
@@ -1342,19 +1466,25 @@ PluginModuleParent::NPP_New(NPMIMEType p
return NS_ERROR_FAILURE;
}
if (*error != NPERR_NO_ERROR) {
NPP_Destroy(instance, 0);
return NS_ERROR_FAILURE;
}
+ UpdatePluginTimeout();
+
+ return NS_OK;
+}
+
+void
+PluginModuleChromeParent::UpdatePluginTimeout()
+{
TimeoutChanged(kParentTimeoutPref, this);
-
- return NS_OK;
}
nsresult
PluginModuleParent::NPP_ClearSiteData(const char* site, uint64_t flags,
uint64_t maxAge)
{
if (!mClearSiteDataSupported)
return NS_ERROR_NOT_AVAILABLE;
@@ -1523,25 +1653,38 @@ PluginModuleParent::RecvPluginHideWindow
return false;
#endif
}
PCrashReporterParent*
PluginModuleParent::AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id,
uint32_t* processType)
{
+ MOZ_CRASH("unreachable");
+}
+
+bool
+PluginModuleParent::DeallocPCrashReporterParent(PCrashReporterParent* actor)
+{
+ MOZ_CRASH("unreachable");
+}
+
+PCrashReporterParent*
+PluginModuleChromeParent::AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id,
+ uint32_t* processType)
+{
#ifdef MOZ_CRASHREPORTER
return new CrashReporterParent();
#else
return nullptr;
#endif
}
bool
-PluginModuleParent::DeallocPCrashReporterParent(PCrashReporterParent* actor)
+PluginModuleChromeParent::DeallocPCrashReporterParent(PCrashReporterParent* actor)
{
#ifdef MOZ_CRASHREPORTER
#ifdef XP_WIN
mozilla::MutexAutoLock lock(mCrashReporterMutex);
if (actor == static_cast<PCrashReporterParent*>(mCrashReporter)) {
mCrashReporter = nullptr;
}
#endif
@@ -1617,42 +1760,44 @@ PluginModuleParent::RecvGetNativeCursors
#else
NS_NOTREACHED(
"PluginInstanceParent::RecvGetNativeCursorSupportLevel not implemented!");
return false;
#endif
}
bool
-PluginModuleParent::RecvNPN_SetException(PPluginScriptableObjectParent* aActor,
- const nsCString& aMessage)
+PluginModuleParent::RecvNPN_SetException(const nsCString& aMessage)
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
- NPObject* aNPObj = nullptr;
- if (aActor) {
- aNPObj = static_cast<PluginScriptableObjectParent*>(aActor)->GetObject(true);
- if (!aNPObj) {
- NS_ERROR("Failed to get object!");
- return false;
- }
- }
- mozilla::plugins::parent::_setexception(aNPObj, NullableStringGet(aMessage));
+ // This function ignores its first argument.
+ mozilla::plugins::parent::_setexception(nullptr, NullableStringGet(aMessage));
return true;
}
bool
PluginModuleParent::RecvNPN_ReloadPlugins(const bool& aReloadPages)
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
mozilla::plugins::parent::_reloadplugins(aReloadPages);
return true;
}
+bool
+PluginModuleChromeParent::RecvNotifyContentModuleDestroyed()
+{
+ nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ if (host) {
+ host->NotifyContentModuleDestroyed(mPluginId);
+ }
+ return true;
+}
+
#ifdef MOZ_CRASHREPORTER_INJECTOR
// We only add the crash reporter to subprocess which have the filename
// FlashPlayerPlugin*
#define FLASH_PROCESS_PREFIX "FLASHPLAYERPLUGIN"
static DWORD
GetFlashChildOfPID(DWORD pid, HANDLE snapshot)
@@ -1673,17 +1818,17 @@ GetFlashChildOfPID(DWORD pid, HANDLE sna
}
return 0;
}
// We only look for child processes of the Flash plugin, NPSWF*
#define FLASH_PLUGIN_PREFIX "NPSWF"
void
-PluginModuleParent::InitializeInjector()
+PluginModuleChromeParent::InitializeInjector()
{
if (!Preferences::GetBool("dom.ipc.plugins.flash.subprocess.crashreporter.enabled", false))
return;
nsCString path(Process()->GetPluginFilePath().c_str());
ToUpperCase(path);
int32_t lastSlash = path.RFindCharInSet("\\/");
if (kNotFound == lastSlash)
@@ -1705,17 +1850,17 @@ PluginModuleParent::InitializeInjector()
mFlashProcess2 = GetFlashChildOfPID(mFlashProcess1, snapshot);
if (mFlashProcess2) {
InjectCrashReporterIntoProcess(mFlashProcess2, this);
}
}
}
void
-PluginModuleParent::OnCrash(DWORD processID)
+PluginModuleChromeParent::OnCrash(DWORD processID)
{
if (!mShutdown) {
GetIPCChannel()->CloseWithError();
KillProcess(OtherProcess(), 1, false);
}
}
#endif // MOZ_CRASHREPORTER_INJECTOR
@@ -1751,26 +1896,26 @@ PluginProfilerObserver::Observe(nsISuppo
if (success && !result.IsEmpty()) {
pse->AddSubProfile(result.get());
}
}
return NS_OK;
}
void
-PluginModuleParent::InitPluginProfiling()
+PluginModuleChromeParent::InitPluginProfiling()
{
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
if (observerService) {
mProfilerObserver = new PluginProfilerObserver(this);
observerService->AddObserver(mProfilerObserver, "profiler-subprocess", false);
}
}
void
-PluginModuleParent::ShutdownPluginProfiling()
+PluginModuleChromeParent::ShutdownPluginProfiling()
{
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
if (observerService) {
observerService->RemoveObserver(mProfilerObserver, "profiler-subprocess");
}
}
#endif
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -48,104 +48,75 @@ class PluginHangUIParent;
*
* This class implements the NPP API from the perspective of the rest
* of Gecko, forwarding NPP calls along to the child process that is
* actually running the plugin.
*
* This class /also/ implements a version of the NPN API, because the
* child process needs to make these calls back into Gecko proper.
* This class is responsible for "actually" making those function calls.
+ *
+ * If a plugin is running, there will always be one PluginModuleParent for it in
+ * the chrome process. In addition, any content process using the plugin will
+ * have its own PluginModuleParent. The subclasses PluginModuleChromeParent and
+ * PluginModuleContentParent implement functionality that is specific to one
+ * case or the other.
*/
class PluginModuleParent
: public PPluginModuleParent
, public PluginLibrary
#ifdef MOZ_CRASHREPORTER_INJECTOR
, public CrashReporter::InjectorCrashCallback
#endif
- , public mozilla::HangMonitor::Annotator
{
-private:
+protected:
typedef mozilla::PluginLibrary PluginLibrary;
typedef mozilla::dom::PCrashReporterParent PCrashReporterParent;
typedef mozilla::dom::CrashReporterParent CrashReporterParent;
-protected:
-
PPluginInstanceParent*
AllocPPluginInstanceParent(const nsCString& aMimeType,
const uint16_t& aMode,
const InfallibleTArray<nsCString>& aNames,
const InfallibleTArray<nsCString>& aValues,
NPError* rv) MOZ_OVERRIDE;
virtual bool
DeallocPPluginInstanceParent(PPluginInstanceParent* aActor) MOZ_OVERRIDE;
public:
- // aFilePath is UTF8, not native!
- explicit PluginModuleParent(const char* aFilePath);
+ explicit PluginModuleParent(bool aIsChrome);
virtual ~PluginModuleParent();
+ bool IsChrome() const { return mIsChrome; }
+
virtual void SetPlugin(nsNPAPIPlugin* plugin) MOZ_OVERRIDE
{
mPlugin = plugin;
}
virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
- /**
- * LoadModule
- *
- * This may or may not launch a plugin child process,
- * and may or may not be very expensive.
- */
- static PluginLibrary* LoadModule(const char* aFilePath);
-
const NPNetscapeFuncs* GetNetscapeFuncs() {
return mNPNIface;
}
- PluginProcessParent* Process() const { return mSubprocess; }
- base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); }
-
bool OkToCleanup() const {
return !IsOnCxxStack();
}
void ProcessRemoteNativeEventsInInterruptCall();
- void TerminateChildProcess(MessageLoop* aMsgLoop);
-
- virtual void
- EnteredCxxStack() MOZ_OVERRIDE;
-
- virtual void
- ExitedCxxStack() MOZ_OVERRIDE;
-
- virtual void
- AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) MOZ_OVERRIDE;
-
-#ifdef XP_WIN
- /**
- * Called by Plugin Hang UI to notify that the user has clicked continue.
- * Used for chrome hang annotations.
- */
- void
- OnHangUIContinue();
-#endif // XP_WIN
-
protected:
virtual mozilla::ipc::RacyInterruptPolicy
MediateInterruptRace(const Message& parent, const Message& child) MOZ_OVERRIDE
{
return MediateRace(parent, child);
}
- virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
-
virtual bool
RecvBackUpXResources(const FileDescriptor& aXSocketFd) MOZ_OVERRIDE;
virtual bool
AnswerNPN_UserAgent(nsCString* userAgent) MOZ_OVERRIDE;
virtual bool
AnswerNPN_GetValue_WithBoolReturn(const NPNVariable& aVariable,
@@ -182,37 +153,30 @@ protected:
virtual bool
RecvPopCursor() MOZ_OVERRIDE;
virtual bool
RecvGetNativeCursorsSupported(bool* supported) MOZ_OVERRIDE;
virtual bool
- RecvNPN_SetException(PPluginScriptableObjectParent* aActor,
- const nsCString& aMessage) MOZ_OVERRIDE;
+ RecvNPN_SetException(const nsCString& aMessage) MOZ_OVERRIDE;
virtual bool
RecvNPN_ReloadPlugins(const bool& aReloadPages) MOZ_OVERRIDE;
static PluginInstanceParent* InstCast(NPP instance);
static BrowserStreamParent* StreamCast(NPP instance, NPStream* s);
-private:
- void SetPluginFuncs(NPPluginFuncs* aFuncs);
+protected:
+ virtual void UpdatePluginTimeout() {}
- // Implement the module-level functions from NPAPI; these are
- // normally resolved directly from the DSO.
-#ifdef OS_LINUX
- NPError NP_Initialize(const NPNetscapeFuncs* npnIface,
- NPPluginFuncs* nppIface);
-#else
- NPError NP_Initialize(const NPNetscapeFuncs* npnIface);
- NPError NP_GetEntryPoints(NPPluginFuncs* nppIface);
-#endif
+ virtual bool RecvNotifyContentModuleDestroyed() MOZ_OVERRIDE { return true; }
+
+ void SetPluginFuncs(NPPluginFuncs* aFuncs);
// NPP-like API that Gecko calls are trampolined into. These
// messages then get forwarded along to the plugin instance,
// and then eventually the child process.
static NPError NPP_Destroy(NPP instance, NPSavedData** save);
static NPError NPP_SetWindow(NPP instance, NPWindow* window);
@@ -250,16 +214,17 @@ private:
const nsIntRect& aRect) MOZ_OVERRIDE;
#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error);
#else
virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error);
#endif
virtual nsresult NP_Shutdown(NPError* error);
+
virtual nsresult NP_GetMIMEDescription(const char** mimeDesc);
virtual nsresult NP_GetValue(void *future, NPPVariable aVariable,
void *aValue, NPError* error);
#if defined(XP_WIN) || defined(XP_MACOSX)
virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error);
#endif
virtual nsresult NPP_New(NPMIMEType pluginType, NPP instance,
uint16_t mode, int16_t argc, char* argn[],
@@ -269,54 +234,152 @@ private:
uint64_t maxAge);
virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& result);
#if defined(XP_MACOSX)
virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing);
virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor);
#endif
-private:
- CrashReporterParent* CrashReporter();
-
-#ifdef MOZ_CRASHREPORTER
- void ProcessFirstMinidump();
- void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes);
-#endif
- void CleanupFromTimeout(const bool aByHangUI);
- void SetChildTimeout(const int32_t aChildTimeout);
- static void TimeoutChanged(const char* aPref, void* aModule);
+protected:
void NotifyPluginCrashed();
-#ifdef MOZ_ENABLE_PROFILER_SPS
- void InitPluginProfiling();
- void ShutdownPluginProfiling();
-#endif
-
- PluginProcessParent* mSubprocess;
+ bool mIsChrome;
bool mShutdown;
bool mClearSiteDataSupported;
bool mGetSitesWithDataSupported;
const NPNetscapeFuncs* mNPNIface;
nsNPAPIPlugin* mPlugin;
ScopedMethodFactory<PluginModuleParent> mTaskFactory;
nsString mPluginDumpID;
nsString mBrowserDumpID;
nsString mHangID;
nsRefPtr<nsIObserver> mProfilerObserver;
+ nsCString mPluginName;
+ nsCString mPluginVersion;
+
+#ifdef MOZ_X11
+ // Dup of plugin's X socket, used to scope its resources to this
+ // object instead of the plugin process's lifetime
+ ScopedClose mPluginXSocketFdDup;
+#endif
+
+ bool
+ GetPluginDetails(nsACString& aPluginName, nsACString& aPluginVersion);
+
+ friend class mozilla::dom::CrashReporterParent;
+};
+
+class PluginModuleContentParent : public PluginModuleParent
+{
+ public:
+ static PluginLibrary* LoadModule(uint32_t aPluginId);
+
+ static PluginModuleContentParent* Create(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess);
+
+ private:
+ explicit PluginModuleContentParent();
+
+#ifdef MOZ_CRASHREPORTER_INJECTOR
+ void OnCrash(DWORD processID) MOZ_OVERRIDE {}
+#endif
+
+ static PluginModuleContentParent* sSavedModuleParent;
+};
+
+class PluginModuleChromeParent
+ : public PluginModuleParent
+ , public mozilla::HangMonitor::Annotator
+{
+ public:
+ /**
+ * LoadModule
+ *
+ * This may or may not launch a plugin child process,
+ * and may or may not be very expensive.
+ */
+ static PluginLibrary* LoadModule(const char* aFilePath, uint32_t aPluginId);
+
+ virtual ~PluginModuleChromeParent();
+
+ void TerminateChildProcess(MessageLoop* aMsgLoop);
+
+#ifdef XP_WIN
+ /**
+ * Called by Plugin Hang UI to notify that the user has clicked continue.
+ * Used for chrome hang annotations.
+ */
+ void
+ OnHangUIContinue();
+#endif // XP_WIN
+
+private:
+ virtual void
+ EnteredCxxStack() MOZ_OVERRIDE;
+
+ void
+ ExitedCxxStack() MOZ_OVERRIDE;
+
+ virtual void
+ AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) MOZ_OVERRIDE;
+
+ virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
+
+#ifdef MOZ_CRASHREPORTER
+ void ProcessFirstMinidump();
+ void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes);
+#endif
+
+ virtual PCrashReporterParent*
+ AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id,
+ uint32_t* processType) MOZ_OVERRIDE;
+ virtual bool
+ DeallocPCrashReporterParent(PCrashReporterParent* actor) MOZ_OVERRIDE;
+
+ PluginProcessParent* Process() const { return mSubprocess; }
+ base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); }
+
+#if !defined(XP_UNIX) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GONK)
+ virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error);
+#endif
+
+ virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
+
+ // aFilePath is UTF8, not native!
+ explicit PluginModuleChromeParent(const char* aFilePath, uint32_t aPluginId);
+
+ CrashReporterParent* CrashReporter();
+
+ void CleanupFromTimeout(const bool aByHangUI);
+ void SetChildTimeout(const int32_t aChildTimeout);
+ static void TimeoutChanged(const char* aPref, void* aModule);
+
+ virtual void UpdatePluginTimeout() MOZ_OVERRIDE;
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ void InitPluginProfiling();
+ void ShutdownPluginProfiling();
+#endif
+
+ virtual bool RecvNotifyContentModuleDestroyed() MOZ_OVERRIDE;
+
+ PluginProcessParent* mSubprocess;
+ uint32_t mPluginId;
+
+ ScopedMethodFactory<PluginModuleChromeParent> mChromeTaskFactory;
+
enum HangAnnotationFlags
{
kInPluginCall = (1u << 0),
kHangUIShown = (1u << 1),
kHangUIContinued = (1u << 2),
kHangUIDontShow = (1u << 3)
};
Atomic<uint32_t> mHangAnnotationFlags;
- nsCString mPluginName;
- nsCString mPluginVersion;
#ifdef XP_WIN
InfallibleTArray<float> mPluginCpuUsageOnHang;
PluginHangUIParent *mHangUIParent;
bool mHangUIEnabled;
bool mIsTimerReset;
#ifdef MOZ_CRASHREPORTER
/**
* This mutex protects the crash reporter when the Plugin Hang UI event
@@ -344,25 +407,16 @@ private:
/**
* Finishes the Plugin Hang UI and cancels if it is being shown to the user.
*/
void
FinishHangUI();
#endif
- bool
- GetPluginDetails(nsACString& aPluginName, nsACString& aPluginVersion);
-
-#ifdef MOZ_X11
- // Dup of plugin's X socket, used to scope its resources to this
- // object instead of the plugin process's lifetime
- ScopedClose mPluginXSocketFdDup;
-#endif
-
friend class mozilla::dom::CrashReporterParent;
#ifdef MOZ_CRASHREPORTER_INJECTOR
void InitializeInjector();
void OnCrash(DWORD processID) MOZ_OVERRIDE;
DWORD mFlashProcess1;
--- a/dom/plugins/ipc/PluginProcessChild.cpp
+++ b/dom/plugins/ipc/PluginProcessChild.cpp
@@ -114,19 +114,19 @@ PluginProcessChild::Init()
# error Sorry
#endif
if (NS_FAILED(nsRegion::InitStatic())) {
NS_ERROR("Could not initialize nsRegion");
return false;
}
- return mPlugin.Init(pluginFilename, ParentHandle(),
- IOThreadChild::message_loop(),
- IOThreadChild::channel());
+ return mPlugin.InitForChrome(pluginFilename, ParentHandle(),
+ IOThreadChild::message_loop(),
+ IOThreadChild::channel());
}
void
PluginProcessChild::CleanUp()
{
#ifdef XP_WIN
::OleUninitialize();
#endif
--- a/dom/plugins/ipc/PluginProcessChild.h
+++ b/dom/plugins/ipc/PluginProcessChild.h
@@ -14,17 +14,18 @@ namespace mozilla {
namespace plugins {
//-----------------------------------------------------------------------------
class PluginProcessChild : public mozilla::ipc::ProcessChild {
protected:
typedef mozilla::ipc::ProcessChild ProcessChild;
public:
- explicit PluginProcessChild(ProcessHandle aParentHandle) : ProcessChild(aParentHandle)
+ explicit PluginProcessChild(ProcessHandle aParentHandle)
+ : ProcessChild(aParentHandle), mPlugin(true)
{ }
virtual ~PluginProcessChild()
{ }
virtual bool Init() MOZ_OVERRIDE;
virtual void CleanUp() MOZ_OVERRIDE;
--- a/dom/plugins/ipc/PluginScriptableObjectChild.cpp
+++ b/dom/plugins/ipc/PluginScriptableObjectChild.cpp
@@ -534,17 +534,17 @@ PluginScriptableObjectChild::PluginScrip
AssertPluginThread();
}
PluginScriptableObjectChild::~PluginScriptableObjectChild()
{
AssertPluginThread();
if (mObject) {
- PluginModuleChild::current()->UnregisterActorForNPObject(mObject);
+ UnregisterActor(mObject);
if (mObject->_class == GetClass()) {
NS_ASSERTION(mType == Proxy, "Wrong type!");
static_cast<ChildNPObject*>(mObject)->parent = nullptr;
}
else {
NS_ASSERTION(mType == LocalObject, "Wrong type!");
PluginModuleChild::sBrowserFuncs.releaseobject(mObject);
@@ -564,18 +564,18 @@ PluginScriptableObjectChild::InitializeP
NS_ASSERTION(mInstance, "Null manager?!");
NPObject* object = CreateProxyObject();
if (!object) {
NS_ERROR("Failed to create object!");
return false;
}
- if (!PluginModuleChild::current()->RegisterActorForNPObject(object, this)) {
- NS_ERROR("RegisterActorForNPObject failed");
+ if (!RegisterActor(object)) {
+ NS_ERROR("RegisterActor failed");
return false;
}
mObject = object;
return true;
}
void
@@ -589,18 +589,18 @@ PluginScriptableObjectChild::InitializeL
mInstance = static_cast<PluginInstanceChild*>(Manager());
NS_ASSERTION(mInstance, "Null manager?!");
PluginModuleChild::sBrowserFuncs.retainobject(aObject);
NS_ASSERTION(!mProtectCount, "Should be zero!");
mProtectCount++;
- if (!PluginModuleChild::current()->RegisterActorForNPObject(aObject, this)) {
- NS_ERROR("RegisterActorForNPObject failed");
+ if (!RegisterActor(aObject)) {
+ NS_ERROR("RegisterActor failed");
}
mObject = aObject;
}
NPObject*
PluginScriptableObjectChild::CreateProxyObject()
{
@@ -683,17 +683,17 @@ void
PluginScriptableObjectChild::DropNPObject()
{
NS_ASSERTION(mObject, "Invalidated object!");
NS_ASSERTION(mObject->_class == GetClass(), "Wrong type of object!");
NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!");
// We think we're about to be deleted, but we could be racing with the other
// process.
- PluginModuleChild::current()->UnregisterActorForNPObject(mObject);
+ UnregisterActor(mObject);
mObject = nullptr;
SendUnprotect();
}
void
PluginScriptableObjectChild::NPObjectDestroyed()
{
@@ -1176,8 +1176,110 @@ PluginScriptableObjectChild::Evaluate(NP
if (!success) {
return false;
}
ConvertToVariant(result, *aResult);
return true;
}
+
+nsTHashtable<PluginScriptableObjectChild::NPObjectData>* PluginScriptableObjectChild::sObjectMap;
+
+bool
+PluginScriptableObjectChild::RegisterActor(NPObject* aObject)
+{
+ AssertPluginThread();
+ MOZ_ASSERT(aObject, "Null pointer!");
+
+ NPObjectData* d = sObjectMap->GetEntry(aObject);
+ if (!d) {
+ NS_ERROR("NPObject not in object table");
+ return false;
+ }
+
+ d->actor = this;
+ return true;
+}
+
+void
+PluginScriptableObjectChild::UnregisterActor(NPObject* aObject)
+{
+ AssertPluginThread();
+ MOZ_ASSERT(aObject, "Null pointer!");
+
+ NPObjectData* d = sObjectMap->GetEntry(aObject);
+ MOZ_ASSERT(d, "NPObject not in object table");
+ if (d) {
+ d->actor = nullptr;
+ }
+}
+
+/* static */ PluginScriptableObjectChild*
+PluginScriptableObjectChild::GetActorForNPObject(NPObject* aObject)
+{
+ AssertPluginThread();
+ MOZ_ASSERT(aObject, "Null pointer!");
+
+ NPObjectData* d = sObjectMap->GetEntry(aObject);
+ if (!d) {
+ NS_ERROR("Plugin using object not created with NPN_CreateObject?");
+ return nullptr;
+ }
+
+ return d->actor;
+}
+
+/* static */ void
+PluginScriptableObjectChild::RegisterObject(NPObject* aObject, PluginInstanceChild* aInstance)
+{
+ AssertPluginThread();
+
+ if (!sObjectMap) {
+ sObjectMap = new nsTHashtable<PluginScriptableObjectChild::NPObjectData>();
+ }
+
+ NPObjectData* d = sObjectMap->PutEntry(aObject);
+ MOZ_ASSERT(!d->instance, "New NPObject already mapped?");
+ d->instance = aInstance;
+}
+
+/* static */ void
+PluginScriptableObjectChild::UnregisterObject(NPObject* aObject)
+{
+ AssertPluginThread();
+
+ sObjectMap->RemoveEntry(aObject);
+
+ if (!sObjectMap->Count()) {
+ delete sObjectMap;
+ sObjectMap = nullptr;
+ }
+}
+
+/* static */ PluginInstanceChild*
+PluginScriptableObjectChild::GetInstanceForNPObject(NPObject* aObject)
+{
+ AssertPluginThread();
+ NPObjectData* d = sObjectMap->GetEntry(aObject);
+ if (!d) {
+ return nullptr;
+ }
+ return d->instance;
+}
+
+/* static */ PLDHashOperator
+PluginScriptableObjectChild::CollectForInstance(NPObjectData* d, void* userArg)
+{
+ PluginInstanceChild* instance = static_cast<PluginInstanceChild*>(userArg);
+ if (d->instance == instance) {
+ NPObject* o = d->GetKey();
+ instance->mDeletingHash->PutEntry(o);
+ }
+ return PL_DHASH_NEXT;
+}
+
+/* static */ void
+PluginScriptableObjectChild::NotifyOfInstanceShutdown(PluginInstanceChild* aInstance)
+{
+ AssertPluginThread();
+ sObjectMap->EnumerateEntries(CollectForInstance, aInstance);
+}
--- a/dom/plugins/ipc/PluginScriptableObjectChild.h
+++ b/dom/plugins/ipc/PluginScriptableObjectChild.h
@@ -212,16 +212,32 @@ public:
DISALLOW_COPY_AND_ASSIGN(StackIdentifier);
PluginIdentifier mIdentifier;
nsRefPtr<StoredIdentifier> mStored;
};
static void ClearIdentifiers();
+ bool RegisterActor(NPObject* aObject);
+ void UnregisterActor(NPObject* aObject);
+
+ static PluginScriptableObjectChild* GetActorForNPObject(NPObject* aObject);
+
+ static void RegisterObject(NPObject* aObject, PluginInstanceChild* aInstance);
+ static void UnregisterObject(NPObject* aObject);
+
+ static PluginInstanceChild* GetInstanceForNPObject(NPObject* aObject);
+
+ /**
+ * Fill PluginInstanceChild.mDeletingHash with all the remaining NPObjects
+ * associated with that instance.
+ */
+ static void NotifyOfInstanceShutdown(PluginInstanceChild* aInstance);
+
private:
static NPObject*
ScriptableAllocate(NPP aInstance,
NPClass* aClass);
static void
ScriptableInvalidate(NPObject* aObject);
@@ -292,14 +308,37 @@ private:
static const NPClass sNPClass;
static StoredIdentifier* HashIdentifier(const nsCString& aIdentifier);
static void UnhashIdentifier(StoredIdentifier* aIdentifier);
typedef nsDataHashtable<nsCStringHashKey, nsRefPtr<StoredIdentifier>> IdentifierTable;
static IdentifierTable sIdentifiers;
+
+ struct NPObjectData : public nsPtrHashKey<NPObject>
+ {
+ explicit NPObjectData(const NPObject* key)
+ : nsPtrHashKey<NPObject>(key),
+ instance(nullptr),
+ actor(nullptr)
+ { }
+
+ // never nullptr
+ PluginInstanceChild* instance;
+
+ // sometimes nullptr (no actor associated with an NPObject)
+ PluginScriptableObjectChild* actor;
+ };
+
+ static PLDHashOperator CollectForInstance(NPObjectData* d, void* userArg);
+
+ /**
+ * mObjectMap contains all the currently active NPObjects (from NPN_CreateObject until the
+ * final release/dealloc, whether or not an actor is currently associated with the object.
+ */
+ static nsTHashtable<NPObjectData>* sObjectMap;
};
} /* namespace plugins */
} /* namespace mozilla */
#endif /* dom_plugins_PluginScriptableObjectChild_h */
--- a/dom/plugins/ipc/PluginScriptableObjectUtils-inl.h
+++ b/dom/plugins/ipc/PluginScriptableObjectUtils-inl.h
@@ -85,17 +85,17 @@ mozilla::plugins::ConvertToVariant(const
npn->retainobject(object);
OBJECT_TO_NPVARIANT(object, aVariant);
break;
}
case Variant::TPPluginScriptableObjectChild: {
NS_ASSERTION(!aInstance, "No instance should be given!");
- NS_ASSERTION(PluginModuleChild::current(),
+ NS_ASSERTION(XRE_GetProcessType() == GeckoProcessType_Plugin,
"Should be running on child only!");
NPObject* object = NPObjectFromVariant(aRemoteVariant);
NS_ASSERTION(object, "Null object?!");
PluginModuleChild::sBrowserFuncs.retainobject(object);
OBJECT_TO_NPVARIANT(object, aVariant);
break;
--- a/dom/plugins/ipc/PluginScriptableObjectUtils.h
+++ b/dom/plugins/ipc/PluginScriptableObjectUtils.h
@@ -107,17 +107,17 @@ ReleaseRemoteVariant(Variant& aVariant)
const_cast<PluginScriptableObjectParent*>(
reinterpret_cast<const PluginScriptableObjectParent*>(
aVariant.get_PPluginScriptableObjectParent()));
actor->Unprotect();
break;
}
case Variant::TPPluginScriptableObjectChild: {
- NS_ASSERTION(PluginModuleChild::current(),
+ NS_ASSERTION(XRE_GetProcessType() == GeckoProcessType_Plugin,
"Should only be running in the child!");
PluginScriptableObjectChild* actor =
const_cast<PluginScriptableObjectChild*>(
reinterpret_cast<const PluginScriptableObjectChild*>(
aVariant.get_PPluginScriptableObjectChild()));
actor->Unprotect();
break;
}
--- a/dom/plugins/ipc/PluginTypes.ipdlh
+++ b/dom/plugins/ipc/PluginTypes.ipdlh
@@ -1,16 +1,32 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
namespace mozilla {
namespace plugins {
+struct PluginTag
+{
+ uint32_t id;
+ nsCString name;
+ nsCString description;
+ nsCString[] mimeTypes;
+ nsCString[] mimeDescriptions;
+ nsCString[] extensions;
+ bool isJavaPlugin;
+ bool isFlashPlugin;
+ nsCString filename;
+ nsCString version;
+ int64_t lastModifiedTime;
+ bool isFromExtension;
+};
+
union PluginIdentifier
{
nsCString;
int32_t;
};
} // namespace plugins
} // namespace mozilla
--- a/dom/plugins/ipc/moz.build
+++ b/dom/plugins/ipc/moz.build
@@ -16,16 +16,17 @@ EXPORTS.mozilla.plugins += [
'BrowserStreamChild.h',
'BrowserStreamParent.h',
'ChildAsyncCall.h',
'ChildTimer.h',
'NPEventAndroid.h',
'NPEventOSX.h',
'NPEventUnix.h',
'NPEventWindows.h',
+ 'PluginBridge.h',
'PluginInstanceChild.h',
'PluginInstanceParent.h',
'PluginMessageUtils.h',
'PluginModuleChild.h',
'PluginModuleParent.h',
'PluginProcessChild.h',
'PluginProcessParent.h',
'PluginScriptableObjectChild.h',
--- a/dom/plugins/test/mochitest/mochitest.ini
+++ b/dom/plugins/test/mochitest/mochitest.ini
@@ -1,10 +1,10 @@
[DEFAULT]
-skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || e10s #b2g-desktop(tests that use plugins)
+skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || (e10s && debug) #b2g-desktop(tests that use plugins)
support-files =
307-xo-redirect.sjs
crashing_subpage.html
file_bug738396.html
file_bug771202.html
file_bug863792.html
large-pic.jpg
loremipsum.txt
@@ -58,35 +58,35 @@ skip-if = (!crashreporter) || true # Bug
[test_CrashService_crash.html]
skip-if = !crashreporter || e10s
[test_CrashService_hang.html]
skip-if = !crashreporter || e10s
[test_defaultValue.html]
[test_enumerate.html]
[test_fullpage.html]
[test_getauthenticationinfo.html]
+skip-if = e10s
[test_hanging.html]
-skip-if = !crashreporter
+skip-if = !crashreporter || e10s
[test_instance_re-parent.html]
[test_instance_unparent1.html]
[test_instance_unparent2.html]
[test_instance_unparent3.html]
[test_instantiation.html]
[test_mixed_case_mime.html]
[test_multipleinstanceobjects.html]
[test_newstreamondestroy.html]
[test_npn_asynccall.html]
[test_npn_timers.html]
[test_npobject_getters.html]
[test_npruntime_construct.html]
[test_npruntime_identifiers.html]
[test_npruntime_npnevaluate.html]
[test_npruntime_npninvoke.html]
[test_npruntime_npninvokedefault.html]
-[test_npruntime_npnsetexception.html]
[test_painting.html]
[test_plugin_scroll_painting.html]
skip-if = true # Bug 596491
[test_pluginstream_asfile.html]
[test_pluginstream_asfileonly.html]
[test_pluginstream_err.html]
[test_pluginstream_geturl.html]
[test_pluginstream_geturlnotify.html]
@@ -103,15 +103,16 @@ skip-if = true # Bug 596491
skip-if = true # disabled due to oddness, perhaps scrolling of the mochitest window?
[test_propertyAndMethod.html]
[test_queryContentsScaleFactor.html]
skip-if = toolkit != "cocoa"
[test_redirect_handling.html]
[test_secondPlugin.html]
[test_src_url_change.html]
[test_streamNotify.html]
+skip-if = e10s
[test_streamatclose.html]
[test_twostreams.html]
[test_windowed_invalidate.html]
skip-if = os != "win"
[test_visibility.html]
skip-if = toolkit == "cocoa"
[test_zero_opacity.html]
--- a/dom/plugins/test/mochitest/test_instance_re-parent.html
+++ b/dom/plugins/test/mochitest/test_instance_re-parent.html
@@ -5,17 +5,17 @@
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="utils.js"></script>
</head>
<body onload="begin()">
<script type="application/javascript;version=1.8">
SimpleTest.waitForExplicitFinish();
- getTestPlugin().enabledState = SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED;
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
var exceptionThrown = false;
var p = null;
var d1 = null;
var d2 = null;
var destroyed = false;
deleted file mode 100644
--- a/dom/plugins/test/mochitest/test_npruntime_npnsetexception.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<html>
-<head>
- <title>NPN_SetException Tests</title>
- <script type="text/javascript"
- src="/tests/SimpleTest/SimpleTest.js"></script>
- <script type="text/javascript" src="utils.js"></script>
- <link rel="stylesheet" type="text/css"
- href="/tests/SimpleTest/test.css" />
-</head>
-<body onload="runTests()">
- <script class="testbody" type="application/javascript">
- SimpleTest.waitForExplicitFinish();
- setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
-
- function runTests() {
- // test a single exception thrown in scriptable invoke
- var plugin = document.getElementById("plugin1");
- plugin.throwExceptionNextInvoke();
- try {
- plugin.npnInvokeTest("badFunction");
- ok(false, "exception not thrown");
- }
- catch (e) {
- is(e, "badFunction", "wrong exception thrown");
- }
-
- // test multiple exceptions thrown in scriptable invokedefault
- plugin.throwExceptionNextInvoke();
- try {
- plugin("first exception", "second exception");
- ok(false, "exception not thrown");
- }
- catch (e) {
- is(e, "second exception", "wrong exception thrown");
- }
-
- // test calling exception with NULL message
- plugin.throwExceptionNextInvoke();
- try {
- plugin();
- ok(false, "exception not thrown");
- }
- catch (e) {
- is(e.message, "Error calling method on NPObject!", "wrong exception thrown");
- }
-
- SimpleTest.finish();
- }
- </script>
-
- <p id="display"></p>
-
- <embed id="plugin1" type="application/x-test" width="400" height="100">
- </embed>
-
- <div id="verbose">
- </div>
- </body>
- </html>
--- a/dom/plugins/test/mochitest/test_secondPlugin.html
+++ b/dom/plugins/test/mochitest/test_secondPlugin.html
@@ -38,35 +38,36 @@
function run() {
// Add "Test Plug-in" (but not "Second Test Plug-in") to the list of
// unhidden plugins. This test must modify the "plugins.enumerable_names"
// pref BEFORE accessing the navigator.plugins or navigator.mimeTypes
// arrays because they only read the pref when they first initialize
// their internal arrays!
var prefs = SpecialPowers.Cc["@mozilla.org/preferences-service;1"].getService(SpecialPowers.Ci.nsIPrefBranch);
var defaultEnumerableNamesPref = prefs.getCharPref("plugins.enumerable_names");
- prefs.setCharPref("plugins.enumerable_names", defaultEnumerableNamesPref + ",Test Plug-in");
-
+ SpecialPowers.pushPrefEnv(
+ {'set': [["plugins.enumerable_names", defaultEnumerableNamesPref + ",Test Plug-in"]]},
+ finishRun
+ );
+ }
+ function finishRun() {
var pluginElement = document.getElementById("plugin");
is(pluginElement.identifierToStringTest("foo"), "foo", "Should be able to call a function provided by the plugin");
ok(navigator.plugins["Test Plug-in"], "Should have queried a non-hidden plugin named 'Test Plug-in'");
ok(navigator.plugins["Second Test Plug-in"], "Should have queried a hidden plugin named 'Test Plug-in'");
ok(findPlugin("Test Plug-in"), "Should have found a non-hidden plugin named 'Test Plug-in'");
ok(!findPlugin("Second Test Plug-in"), "Should NOT found a hidden plugin named 'Test Plug-in'");
ok(navigator.mimeTypes["application/x-test"], "Should have queried a non-hidden MIME type named 'application/x-test'");
ok(navigator.mimeTypes["application/x-second-test"], "Should have queried a MIME type named 'application/x-second-test'");
ok(findMimeType("application/x-test"), "Should have found a non-hidden MIME type named 'application/x-test'");
ok(!findMimeType("application/x-second-test"), "Should NOT have found a MIME type named 'application/x-second-test'");
- // Restore original pref to hide "Test Plug-in" and "Second Test Plug-in".
- prefs.setCharPref("plugins.enumerable_names", defaultEnumerableNamesPref);
-
SimpleTest.finish();
}
</script>
<object id="plugin" type="application/x-second-test" width=200 height=200></object>
</body>
</html>
--- a/dom/plugins/test/mochitest/utils.js
+++ b/dom/plugins/test/mochitest/utils.js
@@ -30,21 +30,29 @@ function getTestPlugin(pluginName) {
ok(false, "Could not find plugin tag with plugin name '" + name + "'");
return null;
}
// call this to set the test plugin(s) initially expected enabled state.
// it will automatically be reset to it's previous value after the test
// ends
function setTestPluginEnabledState(newEnabledState, pluginName) {
+ var oldEnabledState = SpecialPowers.setTestPluginEnabledState(newEnabledState, pluginName);
+ if (!oldEnabledState) {
+ ok(false, "Cannot find plugin '" + plugin + "'");
+ return;
+ }
var plugin = getTestPlugin(pluginName);
- var oldEnabledState = plugin.enabledState;
- plugin.enabledState = newEnabledState;
+ while (plugin.enabledState != newEnabledState) {
+ // Run a nested event loop to wait for the preference change to
+ // propagate to the child. Yuck!
+ SpecialPowers.Services.tm.currentThread.processNextEvent(true);
+ }
SimpleTest.registerCleanupFunction(function() {
- getTestPlugin(pluginName).enabledState = oldEnabledState;
+ SpecialPowers.setTestPluginEnabledState(oldEnabledState, pluginName);
});
}
function crashAndGetCrashServiceRecord(crashMethodName, callback) {
var crashMan =
SpecialPowers.Cu.import("resource://gre/modules/Services.jsm").
Services.crashmanager;
--- a/dom/plugins/test/testplugin/nptest.cpp
+++ b/dom/plugins/test/testplugin/nptest.cpp
@@ -1980,16 +1980,20 @@ scriptableSetProperty(NPObject* npobj, N
}
bool
scriptableRemoveProperty(NPObject* npobj, NPIdentifier name)
{
for (int i = 0; i < int(ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) {
if (name == sPluginPropertyIdentifiers[i]) {
NPN_ReleaseVariantValue(&sPluginPropertyValues[i]);
+
+ // Avoid double frees (see test_propertyAndMethod.html, which deletes a
+ // property that doesn't exist).
+ VOID_TO_NPVARIANT(sPluginPropertyValues[i]);
return true;
}
}
return false;
}
bool
scriptableEnumerate(NPObject* npobj, NPIdentifier** identifier, uint32_t* count)
--- a/dom/settings/SettingsManager.js
+++ b/dom/settings/SettingsManager.js
@@ -50,17 +50,22 @@ function SettingsLock(aSettingsManager)
this._settingsManager = aSettingsManager;
this._id = uuidgen.generateUUID().toString();
// DOMRequestIpcHelper.initHelper sets this._window
this.initDOMRequestHelper(this._settingsManager._window, ["Settings:Get:OK", "Settings:Get:KO",
"Settings:Clear:OK", "Settings:Clear:KO",
"Settings:Set:OK", "Settings:Set:KO",
"Settings:Finalize:OK", "Settings:Finalize:KO"]);
- this.sendMessage("Settings:CreateLock", {lockID: this._id, isServiceLock: false});
+ let createLockPayload = {
+ lockID: this._id,
+ isServiceLock: false,
+ windowID: this._settingsManager.innerWindowID
+ };
+ this.sendMessage("Settings:CreateLock", createLockPayload);
Services.tm.currentThread.dispatch(this._closeHelper.bind(this), Ci.nsIThread.DISPATCH_NORMAL);
// We only want to file closeHelper once per set of receiveMessage calls.
this._closeCalled = true;
}
SettingsLock.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
@@ -250,17 +255,16 @@ SettingsLock.prototype = {
Ci.nsIObserver,
Ci.nsISupportsWeakReference])
};
function SettingsManager() {
this._callbacks = null;
this._isRegistered = false;
this._locks = [];
- this._principal = null;
}
SettingsManager.prototype = {
_wrap: function _wrap(obj) {
return Cu.cloneInto(obj, this._window);
},
set onsettingchange(aHandler) {
@@ -371,27 +375,28 @@ SettingsManager.prototype = {
}
this.checkMessageRegistration();
},
init: function(aWindow) {
if (DEBUG) debug("SettingsManager init");
mrm.registerStrongReporter(this);
cpmm.addMessageListener("Settings:Change:Return:OK", this);
- this._window = aWindow;
- this._principal = this._window.document.nodePrincipal;
- Services.obs.addObserver(this, "dom-window-destroyed", false);
+ Services.obs.addObserver(this, "inner-window-destroyed", false);
let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
this.innerWindowID = util.currentInnerWindowID;
+ this._window = aWindow;
},
observe: function(aSubject, aTopic, aData) {
- if (aTopic == "dom-window-destroyed") {
- let window = aSubject.QueryInterface(Ci.nsIDOMWindow);
- if (window == this._window) {
+ if (DEBUG) debug("Topic: " + aTopic);
+ if (aTopic === "inner-window-destroyed") {
+ let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
+ if (wId === this.innerWindowID) {
+ if (DEBUG) debug("Received: inner-window-destroyed for valid innerWindowID=" + wId + ", cleanup.");
this.cleanup();
}
}
},
collectReports: function(aCallback, aData, aAnonymize) {
for (let topic in this._callbacks) {
let length = this._callbacks[topic].length;
@@ -412,31 +417,21 @@ SettingsManager.prototype = {
Ci.nsIMemoryReporter.UNITS_COUNT,
this._callbacks[topic].length,
"The number of settings observers for this topic.",
aData);
}
},
cleanup: function() {
- Services.obs.removeObserver(this, "dom-window-destroyed");
+ Services.obs.removeObserver(this, "inner-window-destroyed");
cpmm.removeMessageListener("Settings:Change:Return:OK", this);
mrm.unregisterStrongReporter(this);
- // At this point, the window is dying, so there's nothing left
- // that we could do with our lock. Go ahead and run finalize on
- // it to make sure changes are commited.
- for (let i = 0; i < this._locks.length; ++i) {
- if (DEBUG) debug("Lock alive at destroy, finalizing: " + this._locks[i]);
- cpmm.sendAsyncMessage("Settings:Finalize",
- {lockID: this._locks[i]},
- undefined,
- this._principal);
- }
+ this.innerWindowID = null;
this._window = null;
- this._innerWindowID = null;
},
classID: Components.ID("{c40b1c70-00fb-11e2-a21f-0800200c9a66}"),
contractID: "@mozilla.org/settingsManager;1",
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
Ci.nsIDOMGlobalPropertyInitializer,
Ci.nsIObserver,
Ci.nsIMemoryReporter]),
--- a/dom/settings/SettingsRequestManager.jsm
+++ b/dom/settings/SettingsRequestManager.jsm
@@ -13,16 +13,17 @@ const Cu = Components.utils;
this.EXPORTED_SYMBOLS = [];
Cu.import("resource://gre/modules/SettingsDB.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/PermissionsTable.jsm");
const kXpcomShutdownObserverTopic = "xpcom-shutdown";
+const kInnerWindowDestroyed = "inner-window-destroyed";
const kMozSettingsChangedObserverTopic = "mozsettings-changed";
const kSettingsReadSuffix = "-read";
const kSettingsWriteSuffix = "-write";
const kSettingsClearPermission = "settings-clear";
const kAllSettingsReadPermission = "settings" + kSettingsReadSuffix;
const kAllSettingsWritePermission = "settings" + kSettingsWriteSuffix;
// Any application with settings permissions, be it for all settings
// or a single one, will need to be able to access the settings API.
@@ -66,22 +67,24 @@ let SettingsPermissions = {
return this.hasAllReadPermission(aPrincipal) || this.checkPermission(aPrincipal, "settings:" + aSettingsName + kSettingsReadSuffix);
},
hasWritePermission: function(aPrincipal, aSettingsName) {
return this.hasAllWritePermission(aPrincipal) || this.checkPermission(aPrincipal, "settings:" + aSettingsName + kSettingsWriteSuffix);
}
};
-function SettingsLockInfo(aDB, aMsgMgr, aLockID, aIsServiceLock) {
+function SettingsLockInfo(aDB, aMsgMgr, aLockID, aIsServiceLock, aWindowID) {
return {
// ID Shared with the object on the child side
lockID: aLockID,
// Is this a content lock or a settings service lock?
isServiceLock: aIsServiceLock,
+ // Which inner window ID
+ windowID: aWindowID,
// Tasks to be run once the lock is at the head of the queue
tasks: [],
// This is set to true once a transaction is ready to run, but is not at the
// head of the lock queue.
consumable: false,
// Holds values that are requested to be set until the lock lifetime ends,
// then commits them to the DB.
queuedSets: {},
@@ -166,16 +169,17 @@ let SettingsRequestManager = {
init: function() {
if (DEBUG) debug("init");
this.settingsDB.init();
this.messages.forEach((function(msgName) {
ppmm.addMessageListener(msgName, this);
}).bind(this));
Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false);
+ Services.obs.addObserver(this, kInnerWindowDestroyed, false);
},
_serializePreservingBinaries: function _serializePreservingBinaries(aObject) {
function needsUUID(aValue) {
if (!aValue || !aValue.constructor) {
return false;
}
return (aValue.constructor.name == "Date") || (aValue instanceof Ci.nsIDOMFile) ||
@@ -643,25 +647,31 @@ let SettingsRequestManager = {
function(task) {
this.runTasks(lockID);
}.bind(this), function(ret) {
dump("-*- SettingsRequestManager: SETTINGS DATABASE ERROR: Cannot make DB connection!\n");
});
},
observe: function(aSubject, aTopic, aData) {
- if (DEBUG) debug("observe");
+ if (DEBUG) debug("observe: " + aTopic);
switch (aTopic) {
case kXpcomShutdownObserverTopic:
this.messages.forEach((function(msgName) {
ppmm.removeMessageListener(msgName, this);
}).bind(this));
Services.obs.removeObserver(this, kXpcomShutdownObserverTopic);
ppmm = null;
break;
+
+ case kInnerWindowDestroyed:
+ let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
+ this.forceFinalizeChildLocksNonOOP(wId);
+ break;
+
default:
if (DEBUG) debug("Wrong observer topic: " + aTopic);
break;
}
},
sendSettingsChange: function(aKey, aValue, aIsServiceLock) {
this.broadcastMessage("Settings:Change:Return:OK",
@@ -740,52 +750,69 @@ let SettingsRequestManager = {
// If index is 0, the lock we just removed was at the head of
// the queue, so possibly queue the next lock if it's
// consumable.
if (index == 0) {
this.queueConsume();
}
},
- removeMessageManager: function(aMsgMgr, aPrincipal) {
- if (DEBUG) debug("Removing message manager");
+ hasLockFinalizeTask: function(lock) {
+ // Go in reverse order because finalize should be the last one
+ for (let task_index = lock.tasks.length; task_index >= 0; task_index--) {
+ if (lock.tasks[task_index]
+ && lock.tasks[task_index].operation === "finalize") {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ enqueueForceFinalize: function(lock, principal) {
+ if (!this.hasLockFinalizeTask(lock)) {
+ if (DEBUG) debug("Alive lock has pending tasks: " + lock.lockID);
+ this.queueTask("finalize", {lockID: lock.lockID}, principal).then(
+ function() {
+ if (DEBUG) debug("Alive lock " + lockId + " succeeded to force-finalize");
+ },
+ function(error) {
+ if (DEBUG) debug("Alive lock " + lockId + " failed to force-finalize due to error: " + error);
+ }
+ );
+ }
+ },
+
+ forceFinalizeChildLocksNonOOP: function(windowId) {
+ if (DEBUG) debug("Forcing finalize on child locks, non OOP");
+
+ for (let lockId of Object.keys(this.lockInfo)) {
+ let lock = this.lockInfo[lockId];
+ if (lock.windowID === windowId) {
+ let principal = this.mmPrincipals.get(lock._mm);
+ this.enqueueForceFinalize(lock, principal);
+ }
+ }
+ },
+
+ forceFinalizeChildLocksOOP: function(aMsgMgr, aPrincipal) {
+ if (DEBUG) debug("Forcing finalize on child locks, OOP");
+
let msgMgrPrincipal = this.mmPrincipals.get(aMsgMgr);
this.removeObserver(aMsgMgr);
- let lockIDs = Object.keys(this.lockInfo);
- for (let i in lockIDs) {
- let lockId = lockIDs[i];
+ for (let lockId of Object.keys(this.lockInfo)) {
let lock = this.lockInfo[lockId];
if (lock._mm === aMsgMgr && msgMgrPrincipal === aPrincipal) {
- let is_finalizing = false;
- let task_index;
- // Go in reverse order because finalize should be the last one
- for (task_index = lock.tasks.length; task_index >= 0; task_index--) {
- if (lock.tasks[task_index]
- && lock.tasks[task_index].operation === "finalize") {
- is_finalizing = true;
- break;
- }
- }
- if (!is_finalizing) {
- this.queueTask("finalize", {lockID: lockId}, aPrincipal).then(
- function() {
- if (DEBUG) debug("Lock " + lockId + " with dead message manager finalized");
- },
- function(error) {
- if (DEBUG) debug("Lock " + lockId + " with dead message manager NOT FINALIZED due to error: " + error);
- }
- );
- }
+ this.enqueueForceFinalize(lock, aPrincipal);
}
}
},
receiveMessage: function(aMessage) {
- if (DEBUG) debug("receiveMessage " + aMessage.name);
+ if (DEBUG) debug("receiveMessage " + aMessage.name + ": " + JSON.stringify(aMessage.data));
let msg = aMessage.data;
let mm = aMessage.target;
function returnMessage(name, data) {
try {
mm.sendAsyncMessage(name, data);
} catch (e) {
@@ -827,42 +854,46 @@ let SettingsRequestManager = {
}
default:
break;
}
switch (aMessage.name) {
case "child-process-shutdown":
if (DEBUG) debug("Child process shutdown received.");
- this.removeMessageManager(mm, aMessage.principal);
+ this.forceFinalizeChildLocksOOP(mm, aMessage.principal);
break;
case "Settings:RegisterForMessages":
if (!SettingsPermissions.hasSomeReadPermission(aMessage.principal)) {
Cu.reportError("Settings message " + aMessage.name +
" from a content process with no 'settings-api-read' privileges.");
aMessage.target.assertPermission("message-manager-no-read-kill");
return;
}
this.addObserver(mm, aMessage.principal);
break;
case "Settings:UnregisterForMessages":
this.removeObserver(mm);
break;
case "Settings:CreateLock":
- if (DEBUG) debug("Received CreateLock for " + msg.lockID + " from " + aMessage.principal.origin);
+ if (DEBUG) debug("Received CreateLock for " + msg.lockID + " from " + aMessage.principal.origin + " window: " + msg.windowID);
// If we try to create a lock ID that collides with one
// already in the system, consider it a security violation and
// kill.
if (msg.lockID in this.settingsLockQueue) {
Cu.reportError("Trying to queue a lock with the same ID as an already queued lock. Killing app.");
aMessage.target.assertPermission("lock-id-duplicate-kill");
return;
}
this.settingsLockQueue.push(msg.lockID);
- this.lockInfo[msg.lockID] = SettingsLockInfo(this.settingsDB, mm, msg.lockID, msg.isServiceLock);
+ this.lockInfo[msg.lockID] = SettingsLockInfo(this.settingsDB,
+ mm,
+ msg.lockID,
+ msg.isServiceLock,
+ msg.windowID);
break;
case "Settings:Get":
if (DEBUG) debug("Received getRequest from " + msg.lockID);
this.queueTask("get", msg, aMessage.principal).then(function(settings) {
returnMessage("Settings:Get:OK", {
lockID: msg.lockID,
requestID: msg.requestID,
settings: settings
--- a/dom/system/gonk/NetworkUtils.cpp
+++ b/dom/system/gonk/NetworkUtils.cpp
@@ -104,16 +104,21 @@ typedef Tuple3<NetdCommand*, CommandChai
// A macro for native function call return value check.
// For native function call, non-zero return value means failure.
#define RETURN_IF_FAILED(rv) do { \
if (SUCCESS != rv) { \
return rv; \
} \
} while (0);
+#define WARN_IF_FAILED(rv) do { \
+ if (SUCCESS != rv) { \
+ WARN("Error (%d) occurred in %s (%s:%d)", rv, __PRETTY_FUNCTION__, __FILE__, __LINE__); \
+ } \
+} while (0);
static NetworkUtils* gNetworkUtils;
static nsTArray<QueueData> gCommandQueue;
static CurrentCommand gCurrentCommand;
static bool gPending = false;
static nsTArray<nsCString> gReason;
static NetworkParams *gWifiTetheringParms = 0;
@@ -1383,17 +1388,17 @@ CommandResult NetworkUtils::resetConnect
CommandResult NetworkUtils::setDefaultRoute(NetworkParams& aOptions)
{
NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
if (!aOptions.mOldIfname.IsEmpty()) {
// Remove IPv4's default route.
RETURN_IF_FAILED(mNetUtils->do_ifc_remove_default_route(GET_CHAR(mOldIfname)));
// Remove IPv6's default route.
- RETURN_IF_FAILED(mNetUtils->do_ifc_remove_route(GET_CHAR(mOldIfname), "::", 0, NULL));
+ WARN_IF_FAILED(mNetUtils->do_ifc_remove_route(GET_CHAR(mOldIfname), "::", 0, NULL));
}
uint32_t length = aOptions.mGateways.Length();
if (length > 0) {
for (uint32_t i = 0; i < length; i++) {
NS_ConvertUTF16toUTF8 autoGateway(aOptions.mGateways[i]);
int type = getIpType(autoGateway.get());
@@ -1439,19 +1444,19 @@ CommandResult NetworkUtils::removeDefaul
for (uint32_t i = 0; i < length; i++) {
NS_ConvertUTF16toUTF8 autoGateway(aOptions.mGateways[i]);
int type = getIpType(autoGateway.get());
if (type != AF_INET && type != AF_INET6) {
return EAFNOSUPPORT;
}
- RETURN_IF_FAILED(mNetUtils->do_ifc_remove_route(GET_CHAR(mIfname),
- type == AF_INET ? "0.0.0.0" : "::",
- 0, autoGateway.get()));
+ WARN_IF_FAILED(mNetUtils->do_ifc_remove_route(GET_CHAR(mIfname),
+ type == AF_INET ? "0.0.0.0" : "::",
+ 0, autoGateway.get()));
}
return SUCCESS;
}
/**
* Add host route for given network interface.
*/
@@ -1538,17 +1543,17 @@ CommandResult NetworkUtils::removeNetwor
}
char subnetStr[INET6_ADDRSTRLEN];
if (!inet_ntop(AF_INET6, &in6, subnetStr, sizeof subnetStr)) {
return EINVAL;
}
// Remove default route.
- RETURN_IF_FAILED(mNetUtils->do_ifc_remove_route(autoIfname.get(), "::", 0, NULL));
+ WARN_IF_FAILED(mNetUtils->do_ifc_remove_route(autoIfname.get(), "::", 0, NULL));
// Remove subnet route.
RETURN_IF_FAILED(mNetUtils->do_ifc_remove_route(autoIfname.get(), subnetStr, prefixLength, NULL));
return SUCCESS;
}
/* type == AF_INET */
uint32_t ip = inet_addr(autoIp.get());
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -528,16 +528,20 @@ this.ICC_EF_PNN = 0x6fc5;
this.ICC_EF_OPL = 0x6fc6;
this.ICC_EF_MBDN = 0x6fc7;
this.ICC_EF_EXT6 = 0x6fc8; // Ext record for EF[MBDN]
this.ICC_EF_MBI = 0x6fc9;
this.ICC_EF_MWIS = 0x6fca;
this.ICC_EF_CFIS = 0x6fcb;
this.ICC_EF_SPDI = 0x6fcd;
+// CPHS files to be supported
+this.ICC_EF_CPHS_INFO = 0x6f16; // CPHS Information
+this.ICC_EF_CPHS_MBN = 0x6f17; // Mailbox Numbers
+
// CSIM files
this.ICC_EF_CSIM_IMSI_M = 0x6f22;
this.ICC_EF_CSIM_CDMAHOME = 0x6f28;
this.ICC_EF_CSIM_CST = 0x6f32; // CDMA Service table
this.ICC_EF_CSIM_SPN = 0x6f41;
this.ICC_PHASE_1 = 0x00;
this.ICC_PHASE_2 = 0x02;
@@ -1304,16 +1308,28 @@ this.GECKO_ICC_SERVICES = {
MWIS: 48,
SPDI: 51
},
// @see 3GPP2 C.S0023-D 3.4.18 (RUIM).
ruim: {
ENHANCED_PHONEBOOK: 6,
SPN: 17,
SDN: 18
+ },
+ // @see B.3.1.1 CPHS Information in CPHS Phase 2:
+ // Indicates which of the CPHS 'optional' data-fields are present in the SIM card:
+ // EF_CPHS_CSP, EF_CPHS_SST, EF_CPHS_MBN, EF_CPHS_ONSF, EF_CPHS_INFO_NUM
+ // Note: Mandatory EFs are: (B.3.1 Enhanced SIM Requirements)
+ // EF_CPHS_CFF, EF_CPHS_VMI, EF_CPHS_ONS, EF_CPHS_INFO
+ cphs: {
+ CSP: 1,
+ SST: 2,
+ MBN: 3,
+ ONSF: 4,
+ INFO_NUM: 5
}
};
/**
* Cell Broadcast constants
*/
this.CB_FORMAT_GSM = 0;
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -12145,16 +12145,18 @@ ICCFileHelperObject.prototype = {
case ICC_EF_SST:
case ICC_EF_PHASE:
case ICC_EF_CBMI:
case ICC_EF_CBMID:
case ICC_EF_CBMIR:
case ICC_EF_OPL:
case ICC_EF_PNN:
case ICC_EF_GID1:
+ case ICC_EF_CPHS_INFO:
+ case ICC_EF_CPHS_MBN:
return EF_PATH_MF_SIM + EF_PATH_DF_GSM;
default:
return null;
}
},
/**
* This function handles EFs for USIM.
@@ -12171,16 +12173,22 @@ ICCFileHelperObject.prototype = {
case ICC_EF_SPDI:
case ICC_EF_CBMI:
case ICC_EF_CBMID:
case ICC_EF_CBMIR:
case ICC_EF_OPL:
case ICC_EF_PNN:
case ICC_EF_SMS:
case ICC_EF_GID1:
+ // CPHS spec was provided in 1997 based on SIM requirement, there is no
+ // detailed info about how these ICC_EF_CPHS_XXX are allocated in USIM.
+ // What we can do now is to follow what has been done in AOSP to have file
+ // path equal to MF_SIM/DF_GSM.
+ case ICC_EF_CPHS_INFO:
+ case ICC_EF_CPHS_MBN:
return EF_PATH_MF_SIM + EF_PATH_ADF_USIM;
default:
// The file ids in USIM phone book entries are decided by the
// card manufacturer. So if we don't match any of the cases
// above and if its a USIM return the phone book path.
return EF_PATH_MF_SIM + EF_PATH_DF_TELECOM + EF_PATH_DF_PHONEBOOK;
}
},
@@ -13096,17 +13104,28 @@ SimRecordHelperObject.prototype = {
context: null,
/**
* Fetch (U)SIM records.
*/
fetchSimRecords: function() {
this.context.RIL.getIMSI();
this.readAD();
- this.readSST();
+ // CPHS was widely introduced in Europe during GSM(2G) era to provide easier
+ // access to carrier's core service like voicemail, call forwarding, manual
+ // PLMN selection, and etc.
+ // Addition EF like EF_CPHS_MBN, EF_CPHS_CPHS_CFF, EF_CPHS_VWI, etc are
+ // introduced to support these feature.
+ // In USIM, the replancement of these EFs are provided. (EF_MBDN, EF_MWIS, ...)
+ // However, some carriers in Europe still rely on these EFs.
+ this.readCphsInfo(() => this.readSST(),
+ (aErrorMsg) => {
+ this.context.debug("Failed to read CPHS_INFO: " + aErrorMsg);
+ this.readSST();
+ });
},
/**
* Read EF_phase.
* This EF is only available in SIM.
*/
readSimPhase: function() {
function callback() {
@@ -13427,16 +13446,23 @@ SimRecordHelperObject.prototype = {
if (DEBUG) this.context.debug("SPN: SPN service is not available");
}
if (ICCUtilsHelper.isICCServiceAvailable("MDN")) {
if (DEBUG) this.context.debug("MDN: MDN available.");
this.readMBDN();
} else {
if (DEBUG) this.context.debug("MDN: MDN service is not available");
+
+ if (ICCUtilsHelper.isCphsServiceAvailable("MBN")) {
+ // read CPHS_MBN in advance if MBDN is not available.
+ this.readCphsMBN();
+ } else {
+ if (DEBUG) this.context.debug("CPHS_MBN: CPHS_MBN service is not available");
+ }
}
if (ICCUtilsHelper.isICCServiceAvailable("MWIS")) {
if (DEBUG) this.context.debug("MWIS: MWIS is available");
this.readMWIS();
} else {
if (DEBUG) this.context.debug("MWIS: MWIS is not available");
}
@@ -13499,16 +13525,25 @@ SimRecordHelperObject.prototype = {
*
* @see TS 131.102, clause 4.2.60
*/
readMBDN: function() {
function callback(options) {
let RIL = this.context.RIL;
let contact =
this.context.ICCPDUHelper.readAlphaIdDiallingNumber(options.recordSize);
+ if ((!contact ||
+ ((!contact.alphaId || contact.alphaId == "") &&
+ (!contact.number || contact.number == ""))) &&
+ this.context.ICCUtilsHelper.isCphsServiceAvailable("MBN")) {
+ // read CPHS_MBN in advance if MBDN is invalid or empty.
+ this.readCphsMBN();
+ return;
+ }
+
if (!contact ||
(RIL.iccInfoPrivate.mbdn !== undefined &&
RIL.iccInfoPrivate.mbdn === contact.number)) {
return;
}
RIL.iccInfoPrivate.mbdn = contact.number;
if (DEBUG) {
this.context.debug("MBDN, alphaId=" + contact.alphaId +
@@ -14076,16 +14111,127 @@ SimRecordHelperObject.prototype = {
}
}
this.context.ICCIOHelper.loadTransparentEF({
fileId: ICC_EF_GID1,
callback: callback.bind(this)
});
},
+
+ /**
+ * Read CPHS Phase & Service Table from CPHS Info.
+ *
+ * @See B.3.1.1 CPHS Information in CPHS Phase 2.
+ *
+ * @param onsuccess Callback to be called when success.
+ * @param onerror Callback to be called when error.
+ */
+ readCphsInfo: function(onsuccess, onerror) {
+ function callback() {
+ try {
+ let Buf = this.context.Buf;
+ let RIL = this.context.RIL;
+
+ let strLen = Buf.readInt32();
+ // Each octet is encoded into two chars.
+ let octetLen = strLen / 2;
+ let cphsInfo = this.context.GsmPDUHelper.readHexOctetArray(octetLen);
+ Buf.readStringDelimiter(strLen);
+ if (DEBUG) {
+ let str = "";
+ for (let i = 0; i < cphsInfo.length; i++) {
+ str += cphsInfo[i] + ", ";
+ }
+ this.context.debug("CPHS INFO: " + str);
+ }
+
+ /**
+ * CPHS INFORMATION
+ *
+ * Byte 1: CPHS Phase
+ * 01 phase 1
+ * 02 phase 2
+ * etc.
+ *
+ * Byte 2: CPHS Service Table
+ * +----+----+----+----+----+----+----+----+
+ * | b8 | b7 | b6 | b5 | b4 | b3 | b2 | b1 |
+ * +----+----+----+----+----+----+----+----+
+ * | ONSF | MBN | SST | CSP |
+ * | Phase 2 | ALL | Phase 1 | All |
+ * +----+----+----+----+----+----+----+----+
+ *
+ * Byte 3: CPHS Service Table continued
+ * +----+----+----+----+----+----+----+----+
+ * | b8 | b7 | b6 | b5 | b4 | b3 | b2 | b1 |
+ * +----+----+----+----+----+----+----+----+
+ * | RFU | RFU | RFU | INFO_NUM|
+ * | | | | Phase 2 |
+ * +----+----+----+----+----+----+----+----+
+ */
+ let cphsPhase = cphsInfo[0];
+ if (cphsPhase == 1) {
+ // Clear 'Phase 2 only' services.
+ cphsInfo[1] &= 0x3F;
+ // We don't know whether Byte 3 is available in CPHS phase 1 or not.
+ // Add boundary check before accessing it.
+ if (cphsInfo.length > 2) {
+ cphsInfo[2] = 0x00;
+ }
+ } else if (cphsPhase == 2) {
+ // Clear 'Phase 1 only' services.
+ cphsInfo[1] &= 0xF3;
+ } else {
+ throw new Error("Unknown CPHS phase: " + cphsPhase);
+ }
+
+ RIL.iccInfoPrivate.cphsSt = cphsInfo.subarray(1);
+ onsuccess();
+ } catch(e) {
+ onerror(e.toString());
+ }
+ }
+
+ this.context.ICCIOHelper.loadTransparentEF({
+ fileId: ICC_EF_CPHS_INFO,
+ callback: callback.bind(this),
+ onerror: onerror
+ });
+ },
+
+ /**
+ * Read CPHS MBN. (Mailbox Numbers)
+ *
+ * @See B.4.2.2 Voice Message Retrieval and Indicator Clearing
+ */
+ readCphsMBN: function() {
+ function callback(options) {
+ let RIL = this.context.RIL;
+ let contact =
+ this.context.ICCPDUHelper.readAlphaIdDiallingNumber(options.recordSize);
+ if (!contact ||
+ (RIL.iccInfoPrivate.mbdn !== undefined &&
+ RIL.iccInfoPrivate.mbdn === contact.number)) {
+ return;
+ }
+ RIL.iccInfoPrivate.mbdn = contact.number;
+ if (DEBUG) {
+ this.context.debug("CPHS_MDN, alphaId=" + contact.alphaId +
+ " number=" + contact.number);
+ }
+ contact.rilMessageType = "iccmbdn";
+ RIL.sendChromeMessage(contact);
+ }
+
+ this.context.ICCIOHelper.loadLinearFixedEF({
+ fileId: ICC_EF_CPHS_MBN,
+ callback: callback.bind(this)
+ });
+ }
};
function RuimRecordHelperObject(aContext) {
this.context = aContext;
}
RuimRecordHelperObject.prototype = {
context: null,
@@ -14657,18 +14803,18 @@ ICCUtilsHelperObject.prototype = {
* Service id is valid in 1..N, and 2 bits are used to code each service.
*
* +----+-- --+----+----+
* | b8 | ... | b2 | b1 |
* +----+-- --+----+----+
*
* b1 = 0, service not allocated.
* 1, service allocated.
- * b2 = 0, service not activatd.
- * 1, service allocated.
+ * b2 = 0, service not activated.
+ * 1, service activated.
*
* @see 3GPP TS 51.011 10.3.7.
*/
let simService;
if (RIL.appType == CARD_APPTYPE_SIM) {
simService = GECKO_ICC_SERVICES.sim[geckoService];
} else {
simService = GECKO_ICC_SERVICES.ruim[geckoService];
@@ -14684,18 +14830,16 @@ ICCUtilsHelperObject.prototype = {
* Service id is valid in 1..N, and 1 bit is used to code each service.
*
* +----+-- --+----+----+
* | b8 | ... | b2 | b1 |
* +----+-- --+----+----+
*
* b1 = 0, service not avaiable.
* 1, service available.
- * b2 = 0, service not avaiable.
- * 1, service available.
*
* @see 3GPP TS 31.102 4.2.8.
*/
let usimService = GECKO_ICC_SERVICES.usim[geckoService];
if (!usimService) {
return false;
}
usimService -= 1;
@@ -14704,16 +14848,59 @@ ICCUtilsHelperObject.prototype = {
}
return (serviceTable !== null) &&
(index < serviceTable.length) &&
((serviceTable[index] & bitmask) !== 0);
},
/**
+ * Get whether specificed CPHS service is available.
+ *
+ * @param geckoService
+ * Service name like "MDN", etc.
+ *
+ * @return true if the service is enabled, false otherwise.
+ */
+ isCphsServiceAvailable: function(geckoService) {
+ let RIL = this.context.RIL;
+ let serviceTable = RIL.iccInfoPrivate.cphsSt;
+
+ if (!(serviceTable instanceof Uint8Array)) {
+ return false;
+ }
+
+ /**
+ * Service id is valid in 1..N, and 2 bits are used to code each service.
+ *
+ * +----+-- --+----+----+
+ * | b8 | ... | b2 | b1 |
+ * +----+-- --+----+----+
+ *
+ * b1 = 0, service not allocated.
+ * 1, service allocated.
+ * b2 = 0, service not activated.
+ * 1, service activated.
+ *
+ * @See B.3.1.1 CPHS Information in CPHS Phase 2.
+ */
+ let cphsService = GECKO_ICC_SERVICES.cphs[geckoService];
+
+ if (!cphsService) {
+ return false;
+ }
+ cphsService -= 1;
+ let index = Math.floor(cphsService / 4);
+ let bitmask = 2 << ((cphsService % 4) << 1);
+
+ return (index < serviceTable.length) &&
+ ((serviceTable[index] & bitmask) !== 0);
+ },
+
+ /**
* Check if the string is of GSM default 7-bit coded alphabets with bit 8
* set to 0.
*
* @param str String to be checked.
*/
isGsm8BitAlphabet: function(str) {
if (!str) {
return false;
--- a/dom/system/gonk/tests/test_ril_worker_icc_ICCUtilsHelper.js
+++ b/dom/system/gonk/tests/test_ril_worker_icc_ICCUtilsHelper.js
@@ -291,8 +291,36 @@ add_test(function test_get_network_name_
// Current PLMN is not HPLMN, and according to LAC, we should get
// the 8th PNN record after wild char (ie: ';') handling.
testGetNetworkNameFromICC({mcc: "002", mnc: "03", lac: 0x0001},
{longName: "PNN8Long", shortName: "PNN8Short"});
run_next_test();
});
+
+/**
+ * Verify ICCUtilsHelper.isCphsServiceAvailable.
+ */
+add_test(function test_is_cphs_service_available() {
+ let worker = newUint8Worker();
+ let context = worker.ContextPool._contexts[0];
+ let ICCUtilsHelper = context.ICCUtilsHelper;
+ let RIL = context.RIL;
+ RIL.iccInfoPrivate.cphsSt = Uint8Array(2);
+
+ function test_table(cphsSt, geckoService) {
+ RIL.iccInfoPrivate.cphsSt.set(cphsSt);
+
+ for (let service in GECKO_ICC_SERVICES.cphs) {
+ do_check_eq(ICCUtilsHelper.isCphsServiceAvailable(service),
+ geckoService == service);
+ }
+ }
+
+ test_table([0x03, 0x00], "CSP");
+ test_table([0x0C, 0x00], "SST");
+ test_table([0x30, 0x00], "MBN");
+ test_table([0xC0, 0x00], "ONSF");
+ test_table([0x00, 0x03], "INFO_NUM");
+
+ run_next_test();
+});
--- a/dom/system/gonk/tests/test_ril_worker_icc_SimRecordHelper.js
+++ b/dom/system/gonk/tests/test_ril_worker_icc_SimRecordHelper.js
@@ -155,50 +155,60 @@ add_test(function test_reading_optional_
do_test(buildSST(supportedEf), supportedEf);
run_next_test();
});
/**
* Verify fetchSimRecords.
*/
-add_test(function test_fetch_sim_recodes() {
+add_test(function test_fetch_sim_records() {
let worker = newWorker();
let context = worker.ContextPool._contexts[0];
let RIL = context.RIL;
let iccRecord = context.ICCRecordHelper;
let simRecord = context.SimRecordHelper;
- function testFetchSimRecordes(expectCalled) {
+ function testFetchSimRecordes(expectCalled, expectCphsSuccess) {
let ifCalled = [];
RIL.getIMSI = function() {
ifCalled.push("getIMSI");
};
simRecord.readAD = function() {
ifCalled.push("readAD");
};
+ simRecord.readCphsInfo = function(onsuccess, onerror) {
+ ifCalled.push("readCphsInfo");
+ if (expectCphsSuccess) {
+ onsuccess();
+ } else {
+ onerror();
+ }
+ };
+
simRecord.readSST = function() {
ifCalled.push("readSST");
};
simRecord.fetchSimRecords();
for (let i = 0; i < expectCalled.length; i++ ) {
if (ifCalled[i] != expectCalled[i]) {
do_print(expectCalled[i] + " is not called.");
do_check_true(false);
}
}
}
- let expectCalled = ["getIMSI", "readAD", "readSST"];
- testFetchSimRecordes(expectCalled);
+ let expectCalled = ["getIMSI", "readAD", "readCphsInfo", "readSST"];
+ testFetchSimRecordes(expectCalled, true);
+ testFetchSimRecordes(expectCalled, false);
run_next_test();
});
/**
* Verify SimRecordHelper.readMWIS
*/
add_test(function test_read_mwis() {
@@ -777,17 +787,17 @@ add_test(function test_reading_img_basic
add_test(function test_reading_img_length_error() {
let worker = newUint8Worker();
let context = worker.ContextPool._contexts[0];
let record = context.SimRecordHelper;
let helper = context.GsmPDUHelper;
let ril = context.RIL;
let buf = context.Buf;
let io = context.ICCIOHelper;
-
+
let test_data = [
{/* Offset length not enough, should be 4. */
img: [0x01, 0x05, 0x05, 0x11, 0x4f, 0x00, 0x00, 0x04, 0x00, 0x06],
iidf: [0xff, 0xff, 0xff, // Offset.
0x05, 0x05, 0x11, 0x22, 0x33, 0xfe]},
{/* iidf data length not enough, should be 6. */
img: [0x01, 0x05, 0x05, 0x11, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x06],
iidf: [0x05, 0x05, 0x11, 0x22, 0x33]}];
@@ -1287,8 +1297,209 @@ add_test(function test_reading_img_color
record.readIMG(0, onsuccess);
}
for (let i = 0; i< test_data.length; i++) {
do_test(test_data[i].img, test_data[i].iidf, test_data[i].expected);
}
run_next_test();
});
+
+/**
+ * Verify SimRecordHelper.readCphsInfo
+ */
+add_test(function test_read_cphs_info() {
+ let worker = newUint8Worker();
+ let context = worker.ContextPool._contexts[0];
+ let RIL = context.RIL;
+ let pduHelper = context.GsmPDUHelper;
+ let recordHelper = context.SimRecordHelper;
+ let buf = context.Buf;
+ let io = context.ICCIOHelper;
+ let cphsPDU = Uint8Array(3);
+
+ io.loadTransparentEF = function(options) {
+ if (cphsPDU) {
+ // Write data size
+ buf.writeInt32(cphsPDU.length * 2);
+
+ // Write CPHS INFO
+ for (let i = 0; i < cphsPDU.length; i++) {
+ pduHelper.writeHexOctet(cphsPDU[i]);
+ }
+
+ // Write string delimiter
+ buf.writeStringDelimiter(cphsPDU.length * 2);
+
+ if (options.callback) {
+ options.callback(options);
+ }
+ } else {
+ do_print("cphsPDU[] is not set.");
+ }
+ };
+
+ function do_test(cphsInfo, cphsSt) {
+ let onsuccess = false;
+ let onerror = false;
+
+ delete RIL.iccInfoPrivate.cphsSt;
+ cphsPDU.set(cphsInfo);
+ recordHelper.readCphsInfo(() => { onsuccess = true; },
+ () => { onerror = true; });
+
+ do_check_true((cphsSt) ? onsuccess : onerror);
+ do_check_false((cphsSt) ? onerror : onsuccess);
+ if (cphsSt) {
+ do_check_eq(RIL.iccInfoPrivate.cphsSt.length, cphsSt.length);
+ for (let i = 0; i < cphsSt.length; i++) {
+ do_check_eq(RIL.iccInfoPrivate.cphsSt[i], cphsSt[i]);
+ }
+ } else {
+ do_check_eq(RIL.iccInfoPrivate.cphsSt, cphsSt);
+ }
+ }
+
+ do_test([
+ 0x01, // Phase 1
+ 0xFF, // All available & activated
+ 0x03 // All available & activated
+ ],
+ [
+ 0x3F, // All services except ONSF(bit 8-7) are available and activated.
+ 0x00 // INFO_NUM shall not be available & activated.
+ ]);
+
+ do_test([
+ 0x02, // Phase 2
+ 0xFF, // All available & activated
+ 0x03 // All available & activated
+ ],
+ [
+ 0xF3, // All services except ONSF are available and activated.
+ 0x03 // INFO_NUM shall not be available & activated.
+ ]);
+
+ do_test([
+ 0x03, // Phase 3
+ 0xFF, // All available & activated
+ 0x03 // All available & activated
+ ],
+ undefined); // RIL.iccInfoPrivate.cphsSt shall be remained as 'undefined'.
+
+ run_next_test();
+});
+
+/**
+ * Verify SimRecordHelper.readMBDN/SimRecordHelper.readCphsMBN
+ */
+add_test(function test_read_voicemail_number() {
+ let worker = newUint8Worker();
+ let context = worker.ContextPool._contexts[0];
+ let RIL = context.RIL;
+ let pduHelper = context.GsmPDUHelper;
+ let recordHelper = context.SimRecordHelper;
+ let buf = context.Buf;
+ let io = context.ICCIOHelper;
+ let postedMessage;
+
+ worker.postMessage = function(message) {
+ postedMessage = message;
+ };
+
+ io.loadLinearFixedEF = function(options) {
+ let mbnData = [
+ 0x56, 0x6F, 0x69, 0x63, 0x65, 0x6D, 0x61, 0x69,
+ 0x6C, 0xFF, // Alpha Identifier: Voicemail
+ 0x03, // Length of BCD number: 3
+ 0x80, // TOA: Unknown
+ 0x11, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, // Dialing Number: 111
+ 0xFF, // Capability/Configuration Record Identifier
+ 0xFF // Extension Record Identifier
+ ];
+
+ // Write data size
+ buf.writeInt32(mbnData.length * 2);
+
+ // Write MBN
+ for (let i = 0; i < mbnData.length; i++) {
+ pduHelper.writeHexOctet(mbnData[i]);
+ }
+
+ // Write string delimiter
+ buf.writeStringDelimiter(mbnData.length * 2);
+
+ options.recordSize = mbnData.length;
+ if (options.callback) {
+ options.callback(options);
+ }
+ };
+
+ function do_test(funcName, msgCount) {
+ postedMessage = null;
+ delete RIL.iccInfoPrivate.mbdn;
+ recordHelper[funcName]();
+
+ do_check_eq("iccmbdn", postedMessage.rilMessageType);
+ do_check_eq("Voicemail", postedMessage.alphaId);
+ do_check_eq("111", postedMessage.number);
+ }
+
+ do_test("readMBDN");
+ do_test("readCphsMBN");
+
+ run_next_test();
+});
+
+/**
+ * Verify the recovery from SimRecordHelper.readCphsMBN() if MBDN is not valid
+ * or is empty after SimRecordHelper.readMBDN().
+ */
+add_test(function test_read_mbdn_recovered_from_cphs_mbn() {
+ let worker = newUint8Worker();
+ let context = worker.ContextPool._contexts[0];
+ let RIL = context.RIL;
+ let pduHelper = context.GsmPDUHelper;
+ let recordHelper = context.SimRecordHelper;
+ let iccUtilsHelper = context.ICCUtilsHelper;
+ let buf = context.Buf;
+ let io = context.ICCIOHelper;
+
+ io.loadLinearFixedEF = function(options) {
+ let mbnData = [
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ ];
+
+ // Write data size
+ buf.writeInt32(mbnData.length * 2);
+
+ // Write MBN
+ for (let i = 0; i < mbnData.length; i++) {
+ pduHelper.writeHexOctet(mbnData[i]);
+ }
+
+ // Write string delimiter
+ buf.writeStringDelimiter(mbnData.length * 2);
+
+ options.recordSize = mbnData.length;
+ if (options.callback) {
+ options.callback(options);
+ }
+ };
+
+ iccUtilsHelper.isCphsServiceAvailable = function(geckoService) {
+ return geckoService == "MBN";
+ };
+
+ let isRecovered = false;
+ recordHelper.readCphsMBN = function(onComplete) {
+ isRecovered = true;
+ };
+
+ recordHelper.readMBDN();
+
+ do_check_eq(RIL.iccInfoPrivate.mbdn, undefined);
+ do_check_true(isRecovered);
+
+ run_next_test();
+});
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -1229,16 +1229,34 @@ var interfaceNamesInGlobalScope =
{name: "TreeColumns", xbl: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "TreeContentView", xbl: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "TreeSelection", xbl: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"TreeWalker",
// IMPORTANT: Do not change this list without review from a DOM peer!
+ {name: "TVChannel", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+ {name: "TVCurrentChannelChangedEvent", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+ {name: "TVCurrentSourceChangedEvent", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+ {name: "TVEITBroadcastedEvent", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+ {name: "TVManager", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+ {name: "TVProgram", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+ {name: "TVScanningStateChangedEvent", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+ {name: "TVSource", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+ {name: "TVTuner", b2g: true, pref: "dom.tv.enabled", permission: "tv"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "UDPMessageEvent", pref: "dom.udpsocket.enabled", permission: "udp-socket"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "UDPSocket", pref: "dom.udpsocket.enabled", permission: "udp-socket"},
// IMPORTANT: Do not change this list without review from a DOM peer!
"UIEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
"UndoManager",
// IMPORTANT: Do not change this list without review from a DOM peer!
new file mode 100644
--- /dev/null
+++ b/dom/tv/FakeTVService.cpp
@@ -0,0 +1,196 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/TVServiceRunnables.h"
+#include "nsCOMPtr.h"
+#include "nsIMutableArray.h"
+#include "nsServiceManagerUtils.h"
+#include "FakeTVService.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS(FakeTVService, nsITVService)
+
+/* virtual */ NS_IMETHODIMP
+FakeTVService::GetSourceListener(nsITVSourceListener** aSourceListener)
+{
+ *aSourceListener = mSourceListener;
+ NS_ADDREF(*aSourceListener);
+ return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+FakeTVService::SetSourceListener(nsITVSourceListener* aSourceListener)
+{
+ mSourceListener = aSourceListener;
+ return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+FakeTVService::GetTuners(nsITVServiceCallback* aCallback)
+{
+ if (!aCallback) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsCOMPtr<nsIMutableArray> tunerDataList = do_CreateInstance(NS_ARRAY_CONTRACTID);
+ if (!tunerDataList) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // TODO Implement in follow-up patches.
+
+ nsCOMPtr<nsIRunnable> runnable =
+ new TVServiceNotifyRunnable(aCallback, tunerDataList);
+ return NS_DispatchToCurrentThread(runnable);
+}
+
+/* virtual */ NS_IMETHODIMP
+FakeTVService::SetSource(const nsAString& aTunerId,
+ const nsAString& aSourceType,
+ nsITVServiceCallback* aCallback)
+{
+ if (!aCallback) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // TODO Implement in follow-up patches.
+
+ nsCOMPtr<nsIRunnable> runnable =
+ new TVServiceNotifyRunnable(aCallback, nullptr);
+ return NS_DispatchToCurrentThread(runnable);
+}
+
+/* virtual */ NS_IMETHODIMP
+FakeTVService::StartScanningChannels(const nsAString& aTunerId,
+ const nsAString& aSourceType,
+ nsITVServiceCallback* aCallback)
+{
+ if (!aCallback) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // TODO Implement in follow-up patches.
+
+ nsCOMPtr<nsIRunnable> runnable =
+ new TVServiceNotifyRunnable(aCallback, nullptr);
+ return NS_DispatchToCurrentThread(runnable);
+}
+
+/* virtual */ NS_IMETHODIMP
+FakeTVService::StopScanningChannels(const nsAString& aTunerId,
+ const nsAString& aSourceType,
+ nsITVServiceCallback* aCallback)
+{
+ if (!aCallback) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // TODO Implement in follow-up patches.
+
+ nsCOMPtr<nsIRunnable> runnable =
+ new TVServiceNotifyRunnable(aCallback, nullptr);
+ return NS_DispatchToCurrentThread(runnable);
+}
+
+/* virtual */ NS_IMETHODIMP
+FakeTVService::ClearScannedChannelsCache()
+{
+ // TODO Implement in follow-up patches.
+
+ return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+FakeTVService::SetChannel(const nsAString& aTunerId,
+ const nsAString& aSourceType,
+ const nsAString& aChannelNumber,
+ nsITVServiceCallback* aCallback)
+{
+ if (!aCallback) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsCOMPtr<nsIMutableArray> channelDataList = do_CreateInstance(NS_ARRAY_CONTRACTID);
+ if (!channelDataList) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // TODO Implement in follow-up patches.
+
+ nsCOMPtr<nsIRunnable> runnable =
+ new TVServiceNotifyRunnable(aCallback, channelDataList);
+ return NS_DispatchToCurrentThread(runnable);
+}
+
+/* virtual */ NS_IMETHODIMP
+FakeTVService::GetChannels(const nsAString& aTunerId,
+ const nsAString& aSourceType,
+ nsITVServiceCallback* aCallback)
+{
+ if (!aCallback) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsCOMPtr<nsIMutableArray> channelDataList = do_CreateInstance(NS_ARRAY_CONTRACTID);
+ if (!channelDataList) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // TODO Implement in follow-up patches.
+
+ nsCOMPtr<nsIRunnable> runnable =
+ new TVServiceNotifyRunnable(aCallback, channelDataList);
+ return NS_DispatchToCurrentThread(runnable);
+}
+
+/* virtual */ NS_IMETHODIMP
+FakeTVService::GetPrograms(const nsAString& aTunerId,
+ const nsAString& aSourceType,
+ const nsAString& aChannelNumber,
+ uint64_t startTime,
+ uint64_t endTime,
+ nsITVServiceCallback* aCallback)
+{
+ if (!aCallback) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsCOMPtr<nsIMutableArray> programDataList = do_CreateInstance(NS_ARRAY_CONTRACTID);
+ if (!programDataList) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // TODO Implement in follow-up patches.
+
+ nsCOMPtr<nsIRunnable> runnable =
+ new TVServiceNotifyRunnable(aCallback, programDataList);
+ return NS_DispatchToCurrentThread(runnable);
+}
+
+/* virtual */ NS_IMETHODIMP
+FakeTVService::GetOverlayId(const nsAString& aTunerId,
+ nsITVServiceCallback* aCallback)
+{
+ if (!aCallback) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsCOMPtr<nsIMutableArray> overlayIds = do_CreateInstance(NS_ARRAY_CONTRACTID);
+ if (!overlayIds) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // TODO Implement in follow-up patches.
+
+ nsCOMPtr<nsIRunnable> runnable =
+ new TVServiceNotifyRunnable(aCallback, overlayIds);
+ return NS_DispatchToCurrentThread(runnable);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/tv/FakeTVService.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_FakeTVService_h
+#define mozilla_dom_FakeTVService_h
+
+#include "nsCOMPtr.h"
+#include "nsITVService.h"
+
+#define FAKE_TV_SERVICE_CONTRACTID \
+ "@mozilla.org/tv/faketvservice;1"
+#define FAKE_TV_SERVICE_CID \
+ { 0x60fb3c53, 0x017f, 0x4340, { 0x91, 0x1b, 0xd5, 0x5c, 0x31, 0x28, 0x88, 0xb6 } }
+
+namespace mozilla {
+namespace dom {
+
+class FakeTVService MOZ_FINAL : public nsITVService
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSITVSERVICE
+
+ FakeTVService() {}
+
+ // TODO More members might be added in follow-up patches to help testing.
+
+private:
+ ~FakeTVService() {}
+
+ nsCOMPtr<nsITVSourceListener> mSourceListener;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_FakeTVService_h
new file mode 100644
--- /dev/null
+++ b/dom/tv/TVChannel.cpp
@@ -0,0 +1,213 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/TVServiceCallbacks.h"
+#include "mozilla/dom/TVServiceFactory.h"
+#include "mozilla/dom/TVSource.h"
+#include "mozilla/dom/TVTuner.h"
+#include "mozilla/dom/TVUtils.h"
+#include "nsITVService.h"
+#include "nsServiceManagerUtils.h"
+#include "TVChannel.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(TVChannel, DOMEventTargetHelper,
+ mTVService, mSource)
+
+NS_IMPL_ADDREF_INHERITED(TVChannel, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(TVChannel, DOMEventTargetHelper)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TVChannel)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+TVChannel::TVChannel(nsPIDOMWindow* aWindow,
+ TVSource* aSource)
+ : DOMEventTargetHelper(aWindow)
+ , mSource(aSource)
+{
+ MOZ_ASSERT(mSource);
+}
+
+TVChannel::~TVChannel()
+{
+}
+
+/* static */ already_AddRefed<TVChannel>
+TVChannel::Create(nsPIDOMWindow* aWindow,
+ TVSource* aSource,
+ nsITVChannelData* aData)
+{
+ nsRefPtr<TVChannel> channel = new TVChannel(aWindow, aSource);
+ return (channel->Init(aData)) ? channel.forget() : nullptr;
+}
+
+bool
+TVChannel::Init(nsITVChannelData* aData)
+{
+ NS_ENSURE_TRUE(aData, false);
+
+ nsString channelType;
+ aData->GetType(channelType);
+ mType = ToTVChannelType(channelType);
+ if (NS_WARN_IF(mType == TVChannelType::EndGuard_)) {
+ return false;
+ }
+
+ aData->GetNetworkId(mNetworkId);
+ aData->GetTransportStreamId(mTransportStreamId);
+ aData->GetServiceId(mServiceId);
+ aData->GetName(mName);
+ aData->GetNumber(mNumber);
+ aData->GetIsEmergency(&mIsEmergency);
+ aData->GetIsFree(&mIsFree);
+
+ mTVService = TVServiceFactory::AutoCreateTVService();
+ NS_ENSURE_TRUE(mTVService, false);
+
+ return true;
+}
+
+/* virtual */ JSObject*
+TVChannel::WrapObject(JSContext* aCx)
+{
+ return TVChannelBinding::Wrap(aCx, this);
+}
+
+nsresult
+TVChannel::DispatchTVEvent(nsIDOMEvent* aEvent)
+{
+ return DispatchTrustedEvent(aEvent);
+}
+
+already_AddRefed<Promise>
+TVChannel::GetPrograms(const TVGetProgramsOptions& aOptions, ErrorResult& aRv)
+{
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
+ MOZ_ASSERT(global);
+
+ nsRefPtr<Promise> promise = Promise::Create(global, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ nsRefPtr<TVTuner> tuner = mSource->Tuner();
+ nsString tunerId;
+ tuner->GetId(tunerId);
+
+ uint64_t startTime = aOptions.mStartTime.WasPassed() ?
+ aOptions.mStartTime.Value() :
+ PR_Now();
+ uint64_t endTime = aOptions.mDuration.WasPassed() ?
+ (startTime + aOptions.mDuration.Value()) :
+ std::numeric_limits<uint64_t>::max();
+ nsCOMPtr<nsITVServiceCallback> callback =
+ new TVServiceProgramGetterCallback(this, promise, false);
+ nsresult rv =
+ mTVService->GetPrograms(tunerId,
+ ToTVSourceTypeStr(mSource->Type()),
+ mNumber,
+ startTime,
+ endTime,
+ callback);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+ }
+
+ return promise.forget();
+}
+
+void
+TVChannel::GetNetworkId(nsAString& aNetworkId) const
+{
+ aNetworkId = mNetworkId;
+}
+
+void
+TVChannel::GetTransportStreamId(nsAString& aTransportStreamId) const
+{
+ aTransportStreamId = mTransportStreamId;
+}
+
+void
+TVChannel::GetServiceId(nsAString& aServiceId) const
+{
+ aServiceId = mServiceId;
+}
+
+already_AddRefed<TVSource>
+TVChannel::Source() const
+{
+ nsRefPtr<TVSource> source = mSource;
+ return source.forget();
+}
+
+TVChannelType
+TVChannel::Type() const
+{
+ return mType;
+}
+
+void
+TVChannel::GetName(nsAString& aName) const
+{
+ aName = mName;
+}
+
+void
+TVChannel::GetNumber(nsAString& aNumber) const
+{
+ aNumber = mNumber;
+}
+
+bool
+TVChannel::IsEmergency() const
+{
+ return mIsEmergency;
+}
+
+bool
+TVChannel::IsFree() const
+{
+ return mIsFree;
+}
+
+already_AddRefed<Promise>
+TVChannel::GetCurrentProgram(ErrorResult& aRv)
+{
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
+ MOZ_ASSERT(global);
+
+ nsRefPtr<Promise> promise = Promise::Create(global, aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ nsRefPtr<TVTuner> tuner = mSource->Tuner();
+ nsString tunerId;
+ tuner->GetId(tunerId);
+
+ // Get only one program from now on.
+ nsCOMPtr<nsITVServiceCallback> callback =
+ new TVServiceProgramGetterCallback(this, promise, true);
+ nsresult rv =
+ mTVService->GetPrograms(tunerId,
+ ToTVSourceTypeStr(mSource->Type()),
+ mNumber,
+ PR_Now(),
+ std::numeric_limits<uint64_t>::max(),
+ callback);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+ }
+
+ return promise.forget();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/tv/TVChannel.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_TVChannel_h
+#define mozilla_dom_TVChannel_h
+
+#include "mozilla/DOMEventTargetHelper.h"
+// Include TVChannelBinding.h since enum TVChannelType can't be forward declared.
+#include "mozilla/dom/TVChannelBinding.h"
+
+class nsITVChannelData;
+class nsITVService;
+
+namespace mozilla {
+namespace dom {
+
+class Promise;
+class TVProgram;
+class TVSource;
+
+class TVChannel MOZ_FINAL : public DOMEventTargetHelper
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TVChannel, DOMEventTargetHelper)
+
+ static already_AddRefed<TVChannel> Create(nsPIDOMWindow* aWindow,
+ TVSource* aSource,
+ nsITVChannelData* aData);
+
+ // WebIDL (internal functions)
+
+ virtual JSObject* WrapObject(JSContext *aCx) MOZ_OVERRIDE;
+
+ nsresult DispatchTVEvent(nsIDOMEvent* aEvent);
+
+ // WebIDL (public APIs)
+
+ already_AddRefed<Promise> GetPrograms(const TVGetProgramsOptions& aOptions,
+ ErrorResult& aRv);
+
+ already_AddRefed<Promise> GetCurrentProgram(ErrorResult& aRv);
+
+ void GetNetworkId(nsAString& aNetworkId) const;
+
+ void GetTransportStreamId(nsAString& aTransportStreamId) const;
+
+ void GetServiceId(nsAString& aServiceId) const;
+
+ already_AddRefed<TVSource> Source() const;
+
+ TVChannelType Type() const;
+
+ void GetName(nsAString& aName) const;
+
+ void GetNumber(nsAString& aNumber) const;
+
+ bool IsEmergency() const;
+
+ bool IsFree() const;
+
+private:
+ TVChannel(nsPIDOMWindow* aWindow,
+ TVSource* aSource);
+
+ ~TVChannel();
+
+ bool Init(nsITVChannelData* aData);
+
+ nsCOMPtr<nsITVService> mTVService;
+ nsRefPtr<TVSource> mSource;
+ nsString mNetworkId;
+ nsString mTransportStreamId;
+ nsString mServiceId;
+ TVChannelType mType;
+ nsString mNumber;
+ nsString mName;
+ bool mIsEmergency;
+ bool mIsFree;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TVChannel_h
new file mode 100644
--- /dev/null
+++ b/dom/tv/TVListeners.cpp
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/TVSource.h"
+#include "mozilla/dom/TVTuner.h"
+#include "mozilla/dom/TVUtils.h"
+#include "TVListeners.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS(TVSourceListener, nsITVSourceListener)
+
+void
+TVSourceListener::RegisterSource(TVSource* aSource)
+{
+ nsString tunerId;
+ nsRefPtr<TVTuner> tuner = aSource->Tuner();
+ tuner->GetId(tunerId);
+
+ nsRefPtrHashtable<nsStringHashKey, TVSource>* tunerSources = nullptr;
+ if (!mSources.Get(tunerId, &tunerSources)) {
+ tunerSources = new nsRefPtrHashtable<nsStringHashKey, TVSource>();
+ mSources.Put(tunerId, tunerSources);
+ }
+
+ nsString sourceType = ToTVSourceTypeStr(aSource->Type());
+ tunerSources->Put(sourceType, aSource);
+}
+
+void
+TVSourceListener::UnregisterSource(TVSource* aSource)
+{
+ nsString tunerId;
+ nsRefPtr<TVTuner> tuner = aSource->Tuner();
+ tuner->GetId(tunerId);
+
+ nsRefPtrHashtable<nsStringHashKey, TVSource>* tunerSources = nullptr;
+ if (!mSources.Get(tunerId, &tunerSources)) {
+ return;
+ }
+
+ nsString sourceType = ToTVSourceTypeStr(aSource->Type());
+ tunerSources->Remove(sourceType);
+}
+
+/* virtual */ NS_IMETHODIMP
+TVSourceListener::NotifyChannelScanned(const nsAString& aTunerId,
+ const nsAString& aSourceType,
+ nsITVChannelData* aChannelData)
+{
+ nsRefPtr<TVSource> source = GetSource(aTunerId, aSourceType);
+ source->NotifyChannelScanned(aChannelData);
+ return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVSourceListener::NotifyChannelScanComplete(const nsAString& aTunerId,
+ const nsAString& aSourceType)
+{
+ nsRefPtr<TVSource> source = GetSource(aTunerId, aSourceType);
+ source->NotifyChannelScanComplete();
+ return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVSourceListener::NotifyChannelScanStopped(const nsAString& aTunerId,
+ const nsAString& aSourceType)
+{
+ nsRefPtr<TVSource> source = GetSource(aTunerId, aSourceType);
+ source->NotifyChannelScanStopped();
+ return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVSourceListener::NotifyEITBroadcasted(const nsAString& aTunerId,
+ const nsAString& aSourceType,
+ nsITVChannelData* aChannelData,
+ nsITVProgramData** aProgramDataList,
+ const uint32_t aCount)
+{
+ nsRefPtr<TVSource> source = GetSource(aTunerId, aSourceType);
+ source->NotifyEITBroadcasted(aChannelData, aProgramDataList, aCount);
+ return NS_OK;
+}
+
+already_AddRefed<TVSource>
+TVSourceListener::GetSource(const nsAString& aTunerId,
+ const nsAString& aSourceType)
+{
+ nsRefPtrHashtable<nsStringHashKey, TVSource>* tunerSources = nullptr;
+ if (!mSources.Get(aTunerId, &tunerSources)) {
+ return nullptr;
+ }
+
+ nsRefPtr<TVSource> source;
+ tunerSources->Get(aSourceType, getter_AddRefs(source));
+ return source.forget();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/tv/TVListeners.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_TVListeners_h
+#define mozilla_dom_TVListeners_h
+
+#include "nsClassHashtable.h"
+#include "nsITVService.h"
+#include "nsRefPtrHashtable.h"
+
+namespace mozilla {
+namespace dom {
+
+class TVSource;
+
+class TVSourceListener : public nsITVSourceListener
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSITVSOURCELISTENER
+
+ void RegisterSource(TVSource* aSource);
+
+ void UnregisterSource(TVSource* aSource);
+
+private:
+ ~TVSourceListener() {}
+
+ already_AddRefed<TVSource> GetSource(const nsAString& aTunerId,
+ const nsAString& aSourceType);
+
+ // The tuner ID acts as the key of the outer table; whereas the source type is
+ // the key for the inner one.
+ nsClassHashtable<nsStringHashKey, nsRefPtrHashtable<nsStringHashKey, TVSource>> mSources;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TVListeners_h
new file mode 100644
--- /dev/null
+++ b/dom/tv/TVManager.cpp
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/TVManagerBinding.h"
+#include "mozilla/dom/TVServiceCallbacks.h"
+#include "mozilla/dom/TVServiceFactory.h"
+#include "mozilla/dom/TVTuner.h"
+#include "nsITVService.h"
+#include "nsServiceManagerUtils.h"
+#include "TVManager.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(TVManager, DOMEventTargetHelper, mTVService,
+ mTuners, mPendingGetTunersPromises)
+
+NS_IMPL_ADDREF_INHERITED(TVManager, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(TVManager, DOMEventTargetHelper)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TVManager)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+TVManager::TVManager(nsPIDOMWindow* aWindow)
+ : DOMEventTargetHelper(aWindow)
+ , mIsReady(false)
+{
+}
+
+TVManager::~TVManager()
+{
+}
+
+/* static */ already_AddRefed<TVManager>
+TVManager::Create(nsPIDOMWindow* aWindow)
+{
+ nsRefPtr<TVManager> manager = new TVManager(aWindow);
+ return (manager->Init()) ? manager.forget() : nullptr;
+}
+
+bool
+TVManager::Init()
+{
+ mTVService = TVServiceFactory::AutoCreateTVService();
+ NS_ENSURE_TRUE(mTVService, false);
+
+ nsCOMPtr<nsITVServiceCallback> callback = new TVServiceTunerGetterCallback(this);
+ nsresult rv = mTVService->GetTuners(callback);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ return true;
+}
+
+/* virtual */ JSObject*
+TVManager::WrapObject(JSContext* aCx)
+{
+ return TVManagerBinding::Wrap(aCx, this);
+}
+
+nsresult
+TVManager::SetTuners(const nsTArray<nsRefPtr<TVTuner>>& aTuners)
+{
+ // Should be called only when TV Manager hasn't been ready yet.
+ if (mIsReady) {
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ mTuners = aTuners;
+ mIsReady = true;
+
+ // Resolve pending promises.
+ uint32_t length = mPendingGetTunersPromises.Length();
+ for(uint32_t i = 0; i < length; i++) {
+ mPendingGetTunersPromises[i]->MaybeResolve(mTuners);
+ }
+ mPendingGetTunersPromises.Clear();
+ return NS_OK;
+}
+
+void
+TVManager::RejectPendingGetTunersPromises(nsresult aRv)
+{
+ // Reject pending promises.
+ uint32_t length = mPendingGetTunersPromises.Length();
+ for(uint32_t i = 0; i < length; i++) {
+ mPendingGetTunersPromises[i]->MaybeReject(aRv);
+ }
+ mPendingGetTunersPromises.Clear();
+}
+
+nsresult
+TVManager::DispatchTVEvent(nsIDOMEvent* aEvent)
+{
+ return DispatchTrustedEvent(aEvent);
+}
+
+already_AddRefed<Promise>
+TVManager::GetTuners(ErrorResult& aRv)
+{
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
+ MOZ_ASSERT(global);
+
+ nsRefPtr<Promise> promise = Promise::Create(global, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ // Keep track of the promise when the manager hasn't been ready yet.
+ if (mIsReady) {
+ promise->MaybeResolve(mTuners);
+ } else {
+ mPendingGetTunersPromises.AppendElement(promise);
+ }
+
+ return promise.forget();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/tv/TVManager.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_TVManager_h
+#define mozilla_dom_TVManager_h
+
+#include "mozilla/DOMEventTargetHelper.h"
+
+class nsITVService;
+
+namespace mozilla {
+namespace dom {
+
+class Promise;
+class TVTuner;
+
+class TVManager MOZ_FINAL : public DOMEventTargetHelper
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TVManager, DOMEventTargetHelper)
+
+ static already_AddRefed<TVManager> Create(nsPIDOMWindow* aWindow);
+
+ // WebIDL (internal functions)
+
+ virtual JSObject* WrapObject(JSContext *aCx) MOZ_OVERRIDE;
+
+ nsresult SetTuners(const nsTArray<nsRefPtr<TVTuner>>& aTuners);
+
+ void RejectPendingGetTunersPromises(nsresult aRv);
+
+ nsresult DispatchTVEvent(nsIDOMEvent* aEvent);
+
+ // WebIDL (public APIs)
+
+ already_AddRefed<Promise> GetTuners(ErrorResult& aRv);
+
+private:
+ explicit TVManager(nsPIDOMWindow* aWindow);
+
+ ~TVManager();
+
+ bool Init();
+
+ nsCOMPtr<nsITVService> mTVService;
+ nsTArray<nsRefPtr<TVTuner>> mTuners;
+ bool mIsReady;
+ nsTArray<nsRefPtr<Promise>> mPendingGetTunersPromises;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TVManager_h
new file mode 100644
--- /dev/null
+++ b/dom/tv/TVProgram.cpp
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/TVChannel.h"
+#include "mozilla/dom/TVProgramBinding.h"
+#include "nsITVService.h"
+#include "TVProgram.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TVProgram, mOwner, mChannel)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TVProgram)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TVProgram)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TVProgram)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+TVProgram::TVProgram(nsISupports* aOwner,
+ TVChannel* aChannel,
+ nsITVProgramData* aData)
+ : mOwner(aOwner)
+ , mChannel(aChannel)
+{
+ MOZ_ASSERT(mChannel);
+ MOZ_ASSERT(aData);
+
+ aData->GetEventId(mEventId);
+ aData->GetTitle(mTitle);
+ aData->GetStartTime(&mStartTime);
+ aData->GetDuration(&mDuration);
+ aData->GetDescription(mDescription);
+ aData->GetRating(mRating);
+
+ uint32_t count;
+ char** languages;
+ aData->GetAudioLanguages(&count, &languages);
+ SetLanguages(count, languages, mAudioLanguages);
+ aData->GetSubtitleLanguages(&count, &languages);
+ SetLanguages(count, languages, mSubtitleLanguages);
+}
+
+TVProgram::~TVProgram()
+{
+}
+
+/* virtual */ JSObject*
+TVProgram::WrapObject(JSContext* aCx)
+{
+ return TVProgramBinding::Wrap(aCx, this);
+}
+
+void
+TVProgram::GetAudioLanguages(nsTArray<nsString>& aLanguages) const
+{
+ aLanguages = mAudioLanguages;
+}
+
+void
+TVProgram::GetSubtitleLanguages(nsTArray<nsString>& aLanguages) const
+{
+ aLanguages = mSubtitleLanguages;
+}
+
+void
+TVProgram::GetEventId(nsAString& aEventId) const
+{
+ aEventId = mEventId;
+}
+
+already_AddRefed<TVChannel>
+TVProgram::Channel() const
+{
+ nsRefPtr<TVChannel> channel = mChannel;
+ return channel.forget();
+}
+
+void
+TVProgram::GetTitle(nsAString& aTitle) const
+{
+ aTitle = mTitle;
+}
+
+uint64_t
+TVProgram::StartTime() const
+{
+ return mStartTime;
+}
+
+uint64_t
+TVProgram::Duration() const
+{
+ return mDuration;
+}
+
+void
+TVProgram::GetDescription(nsAString& aDescription) const
+{
+ aDescription = mDescription;
+}
+
+void
+TVProgram::GetRating(nsAString& aRating) const
+{
+ aRating = mRating;
+}
+
+void
+TVProgram::SetLanguages(uint32_t aCount,
+ char** aLanguages,
+ nsTArray<nsString>& aLanguageList)
+{
+ for (uint32_t i = 0; i < aCount; i++) {
+ nsString language;
+ language.AssignASCII(aLanguages[i]);
+ aLanguageList.AppendElement(language);
+ }
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(aCount, aLanguages);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/tv/TVProgram.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_TVProgram_h
+#define mozilla_dom_TVProgram_h
+
+#include "nsWrapperCache.h"
+
+class nsITVProgramData;
+
+namespace mozilla {
+namespace dom {
+
+class TVChannel;
+
+class TVProgram MOZ_FINAL : public nsISupports
+ , public nsWrapperCache
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TVProgram)
+
+ TVProgram(nsISupports* aOwner,
+ TVChannel* aChannel,
+ nsITVProgramData* aData);
+
+ // WebIDL (internal functions)
+
+ nsISupports* GetParentObject() const
+ {
+ return mOwner;
+ }
+
+ virtual JSObject* WrapObject(JSContext *aCx) MOZ_OVERRIDE;
+
+ // WebIDL (public APIs)
+
+ void GetAudioLanguages(nsTArray<nsString>& aLanguages) const;
+
+ void GetSubtitleLanguages(nsTArray<nsString>& aLanguages) const;
+
+ void GetEventId(nsAString& aEventId) const;
+
+ already_AddRefed<TVChannel> Channel() const;
+
+ void GetTitle(nsAString& aTitle) const;
+
+ uint64_t StartTime() const;
+
+ uint64_t Duration() const;
+
+ void GetDescription(nsAString& aDescription) const;
+
+ void GetRating(nsAString& aRating) const;
+
+private:
+ ~TVProgram();
+
+ void SetLanguages(uint32_t aCount,
+ char** aLanguages,
+ nsTArray<nsString>& aLanguageList);
+
+ nsCOMPtr<nsISupports> mOwner;
+ nsRefPtr<TVChannel> mChannel;
+ nsString mEventId;
+ nsString mTitle;
+ uint64_t mStartTime;
+ uint64_t mDuration;
+ nsString mDescription;
+ nsString mRating;
+ bool mIsInterrupting;
+ nsTArray<nsString> mAudioLanguages;
+ nsTArray<nsString> mSubtitleLanguages;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TVProgram_h__
new file mode 100644
--- /dev/null
+++ b/dom/tv/TVServiceCallbacks.cpp
@@ -0,0 +1,492 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/TVManager.h"
+#include "mozilla/dom/TVProgram.h"
+#include "mozilla/dom/TVSource.h"
+#include "mozilla/dom/TVTuner.h"
+#include "nsArrayUtils.h"
+#include "TVServiceCallbacks.h"
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * Implementation of TVServiceSourceSetterCallback
+ */
+
+NS_IMPL_CYCLE_COLLECTION(TVServiceSourceSetterCallback, mTuner, mPromise)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TVServiceSourceSetterCallback)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TVServiceSourceSetterCallback)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TVServiceSourceSetterCallback)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+ NS_INTERFACE_MAP_ENTRY(nsITVServiceCallback)
+NS_INTERFACE_MAP_END
+
+TVServiceSourceSetterCallback::TVServiceSourceSetterCallback(TVTuner* aTuner,
+ Promise* aPromise,
+ TVSourceType aSourceType)
+ : mTuner(aTuner)
+ , mPromise(aPromise)
+ , mSourceType(aSourceType)
+{
+ MOZ_ASSERT(mTuner);
+ MOZ_ASSERT(mPromise);
+}
+
+TVServiceSourceSetterCallback::~TVServiceSourceSetterCallback()
+{
+}
+
+/* virtual */ NS_IMETHODIMP
+TVServiceSourceSetterCallback::NotifySuccess(nsIArray* aDataList)
+{
+ // |aDataList| is expected to be null for setter callbacks.
+ if (aDataList) {
+ mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsresult rv = mTuner->SetCurrentSource(mSourceType);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ mPromise->MaybeReject(rv);
+ return rv;
+ }
+
+ mPromise->MaybeResolve(JS::UndefinedHandleValue);
+ return NS_OK;
+}
+
+/* virtual */ NS_IMETHODIMP
+TVServiceSourceSetterCallback::NotifyError(uint16_t aErrorCode)
+{
+ switch (aErrorCode) {
+ case nsITVServiceCallback::TV_ERROR_FAILURE:
+ case nsITVServiceCallback::TV_ERROR_INVALID_ARG:
+ mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+ return NS_OK;
+ case nsITVServiceCallback::TV_ERROR_NO_SIGNAL:
+ mPromise->MaybeReject(NS_ERROR_DOM_NETWORK_ERR);
+ return NS_OK;
+ case nsITVServiceCallback::TV_ERROR_NOT_SUPPORTED:
+ mPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return NS_OK;
+ }
+
+ mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+ return NS_ERROR_ILLEGAL_VALUE;
+}
+
+
+/*
+ * Implementation of TVServiceChannelScanCallback
+ */
+
+NS_IMPL_CYCLE_COLLECTION(TVServiceChannelScanCallback, mSource, mPromise)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TVServiceChannelScanCallback)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TVServiceChannelScanCallback)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TVServiceChannelScanCallback)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)