Merge m-c to fx-team. a=merge
Merge m-c to fx-team. a=merge
--- 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="470826d13ae130a5c3d572d1029e595105485fb0"/>
+ <project name="gaia" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
<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="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
--- 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="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
<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="470826d13ae130a5c3d572d1029e595105485fb0"/>
+ <project name="gaia.git" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
<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="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
<!-- 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="470826d13ae130a5c3d572d1029e595105485fb0"/>
+ <project name="gaia" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
<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="470826d13ae130a5c3d572d1029e595105485fb0"/>
+ <project name="gaia" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
<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="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
--- 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="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
<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="470826d13ae130a5c3d572d1029e595105485fb0"/>
+ <project name="gaia.git" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
<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="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
<!--original fetch url was git://codeaurora.org/-->
<remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
<!--original fetch url was https://git.mozilla.org/releases-->
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
<!-- B2G specific things. -->
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
- <project name="gaia" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
+ <project name="gaia" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
<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="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
--- 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="470826d13ae130a5c3d572d1029e595105485fb0"/>
+ <project name="gaia" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
<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": "842fd7448790cfaeaf0bf22164e599c74e9be0e9",
+ "revision": "1e5360b21cca807b2ea63ea8fb878b451131c9bd",
"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="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
<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="470826d13ae130a5c3d572d1029e595105485fb0"/>
+ <project name="gaia.git" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
<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="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
<!-- 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="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
<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="470826d13ae130a5c3d572d1029e595105485fb0"/>
+ <project name="gaia.git" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
<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="470826d13ae130a5c3d572d1029e595105485fb0"/>
+ <project name="gaia" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
<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="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
<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="470826d13ae130a5c3d572d1029e595105485fb0"/>
+ <project name="gaia.git" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
<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="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/content/base/src/ImportManager.cpp
+++ b/content/base/src/ImportManager.cpp
@@ -86,16 +86,20 @@ ImportLoader::Updater::GetReferrerChain(
for (uint32_t i = 0; i < l / 2; i++) {
Swap(aResult[i], aResult[l - i - 1]);
}
}
bool
ImportLoader::Updater::ShouldUpdate(nsTArray<nsINode*>& aNewPath)
{
+ if (mLoader->Manager()->GetNearestPredecessor(mLoader->GetMainReferrer()) !=
+ mLoader->mBlockingPredecessor) {
+ return true;
+ }
// Let's walk down on the main referrer chains of both the current main and
// the new link, and find the last pair of links that are from the same
// document. This is the junction point between the two referrer chain. Their
// order in the subimport list of that document will determine if we have to
// update the spanning tree or this new edge changes nothing in the script
// execution order.
nsTArray<nsINode*> oldPath;
GetReferrerChain(mLoader->mLinks[mLoader->mMainReferrer], oldPath);
@@ -737,32 +741,40 @@ ImportManager::AddLoaderWithNewURI(Impor
}
nsRefPtr<ImportLoader> ImportManager::GetNearestPredecessor(nsINode* aNode)
{
// Return the previous link if there is any in the same document.
nsIDocument* doc = aNode->OwnerDoc();
int32_t idx = doc->IndexOfSubImportLink(aNode);
MOZ_ASSERT(idx != -1, "aNode must be a sub import link of its owner document");
+
+ for (; idx > 0; idx--) {
+ HTMLLinkElement* link =
+ static_cast<HTMLLinkElement*>(doc->GetSubImportLink(idx - 1));
+ nsCOMPtr<nsIURI> uri = link->GetHrefURI();
+ nsRefPtr<ImportLoader> ret;
+ mImports.Get(uri, getter_AddRefs(ret));
+ // Only main referrer links are interesting.
+ if (ret->GetMainReferrer() == link) {
+ return ret;
+ }
+ }
+
if (idx == 0) {
if (doc->IsMasterDocument()) {
// If there is no previous one, and it was the master document, then
// there is no predecessor.
return nullptr;
}
// Else we find the main referrer of the import parent of the link's document.
// And do a recursion.
ImportLoader* owner = Find(doc);
MOZ_ASSERT(owner);
nsCOMPtr<nsINode> mainReferrer = owner->GetMainReferrer();
return GetNearestPredecessor(mainReferrer);
}
- MOZ_ASSERT(idx > 0);
- HTMLLinkElement* link =
- static_cast<HTMLLinkElement*>(doc->GetSubImportLink(idx - 1));
- nsCOMPtr<nsIURI> uri = link->GetHrefURI();
- nsRefPtr<ImportLoader> ret;
- mImports.Get(uri, getter_AddRefs(ret));
- return ret;
+
+ return nullptr;
}
} // namespace dom
} // namespace mozilla
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -3515,16 +3515,20 @@ nsDocument::GetBaseTarget(nsAString &aBa
void
nsDocument::SetDocumentCharacterSet(const nsACString& aCharSetID)
{
// XXX it would be a good idea to assert the sanity of the argument,
// but before we figure out what to do about non-Encoding Standard
// encodings in the charset menu and in mailnews, assertions are futile.
if (!mCharacterSet.Equals(aCharSetID)) {
+ if (mMasterDocument && !aCharSetID.EqualsLiteral("UTF-8")) {
+ // Imports are always UTF-8
+ return;
+ }
mCharacterSet = aCharSetID;
int32_t n = mCharSetObservers.Length();
for (int32_t i = 0; i < n; i++) {
nsIObserver* observer = mCharSetObservers.ElementAt(i);
observer->Observe(static_cast<nsIDocument *>(this), "charset",
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_1_A.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_1_B.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("A");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_1_B.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_1_C.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_1_A.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("B");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_1_C.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_1_B.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_1_A.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("C");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_2_A.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_2_B.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("A");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_2_B.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_2_A.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_2_C.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_2_D.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("B");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_2_C.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+</head>
+<body>
+ <script>
+ order.push("C");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_2_D.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+</head>
+<body>
+ <script>
+ order.push("D");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_3_A.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_3_C.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("A");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_3_B.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_3_C.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("B");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_3_C.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_3_A.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_3_B.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("C");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_4_A.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_4_B.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("A");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_4_B.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_4_C.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("B");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_4_C.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_4_D.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("C");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_4_D.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_4_B.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_4_E.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("D");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_4_E.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_4_C.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("E");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_5_A.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_5_B.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_5_D.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_5_C.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("A");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_5_B.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_5_A.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_5_C.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_5_D.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("B");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_5_C.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_5_A.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_5_B.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_5_D.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("C");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_5_D.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+ <link rel="import" href="file_cycle_5_A.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_5_B.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_5_C.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+ <script>
+ order.push("D");
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_encoding.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<head>
+<meta charset="EUC-KR">
+</head>
+<body>Ignore my encoding</body>
\ No newline at end of file
--- a/content/html/content/test/imports/mochitest.ini
+++ b/content/html/content/test/imports/mochitest.ini
@@ -11,10 +11,40 @@ support-files =
file_importC5.html
file_importC6.html
file_importC7.html
file_importC8.html
file_importC9.html
file_importC10.html
file_importD.html
file_importE.html
+ file_cycle_1_A.html
+ file_cycle_1_B.html
+ file_cycle_1_C.html
+ file_cycle_2_A.html
+ file_cycle_2_B.html
+ file_cycle_2_C.html
+ file_cycle_2_D.html
+ file_cycle_3_A.html
+ file_cycle_3_B.html
+ file_cycle_3_C.html
+ file_cycle_4_A.html
+ file_cycle_4_B.html
+ file_cycle_4_C.html
+ file_cycle_4_D.html
+ file_cycle_4_E.html
+ file_cycle_5_A.html
+ file_cycle_5_B.html
+ file_cycle_5_C.html
+ file_cycle_5_D.html
+ file_encoding.html
-
+[test_cycle_1.html]
+skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
+[test_cycle_2.html]
+skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
+[test_cycle_3.html]
+skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
+[test_cycle_4.html]
+skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
+[test_cycle_5.html]
+skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
+[test_encoding.html]
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/test_cycle_1.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1061469
+-->
+<head>
+ <title>Test for Bug 1061469</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1061469">Mozilla Bug 1061469</a>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ var counter = 0;
+ var fcounter = 0;
+ var order = [];
+ function loaded() {
+ counter++;
+ }
+ function failed() {
+ fcounter++;
+ }
+ </script>
+ <link rel="import" href="file_cycle_1_A.html" onload="loaded()" onerror="failed()"></link>
+ <script type="text/javascript">
+ is(counter, 6, "Imports are loaded");
+ is(fcounter, 0, "No error in imports");
+ var expected = ["C","B","A"];
+ for (i in expected)
+ is(order[i], expected[i], "import " + i + " should be " + expected[i]);
+ SimpleTest.finish();
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/test_cycle_2.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1061469
+-->
+<head>
+ <title>Test for Bug 1061469</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1061469">Mozilla Bug 1061469</a>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ var counter = 0;
+ var fcounter = 0;
+ var order = [];
+ function loaded() {
+ counter++;
+ }
+ function failed() {
+ fcounter++;
+ }
+ </script>
+ <link rel="import" href="file_cycle_2_A.html" onload="loaded()" onerror="failed()"></link>
+ <script type="text/javascript">
+ is(counter, 5, "Imports are loaded");
+ is(fcounter, 0, "No error in imports");
+ var expected = ["C","D","B","A"];
+ for (i in expected)
+ is(order[i], expected[i], "import " + i + " should be " + expected[i]);
+ SimpleTest.finish();
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/test_cycle_3.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1061469
+-->
+<head>
+ <title>Test for Bug 1061469</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1061469">Mozilla Bug 1061469</a>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ var counter = 0;
+ var fcounter = 0;
+ var order = [];
+ function loaded() {
+ counter++;
+ }
+ function failed() {
+ fcounter++;
+ }
+ </script>
+ <link rel="import" href="file_cycle_3_A.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_3_B.html" onload="loaded()" onerror="failed()"></link>
+ <script type="text/javascript">
+ is(counter, 6, "Imports are loaded");
+ is(fcounter, 0, "No error in imports");
+ var expected = ["B","C","A"];
+ for (i in expected)
+ is(order[i], expected[i], "import " + i + " should be " + expected[i]);
+ SimpleTest.finish();
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/test_cycle_4.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1061469
+-->
+<head>
+ <title>Test for Bug 1061469</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1061469">Mozilla Bug 1061469</a>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ var counter = 0;
+ var fcounter = 0;
+ var order = [];
+ function loaded() {
+ counter++;
+ }
+ function failed() {
+ fcounter++;
+ }
+ </script>
+ <link rel="import" href="file_cycle_4_A.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_4_D.html" onload="loaded()" onerror="failed()"></link>
+ <script type="text/javascript">
+ is(counter, 8, "Imports are loaded");
+ is(fcounter, 0, "No error in imports");
+ var expected = ["E","D","C","B","A"];
+ for (i in expected)
+ is(order[i], expected[i], "import " + i + " should be " + expected[i]);
+ SimpleTest.finish();
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/test_cycle_5.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1061469
+-->
+<head>
+ <title>Test for Bug 1061469</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1061469">Mozilla Bug 1061469</a>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ var counter = 0;
+ var fcounter = 0;
+ var order = [];
+ function loaded() {
+ counter++;
+ }
+ function failed() {
+ fcounter++;
+ }
+ </script>
+ <link rel="import" href="file_cycle_5_A.html" onload="loaded()" onerror="failed()"></link>
+ <link rel="import" href="file_cycle_5_C.html" onload="loaded()" onerror="failed()"></link>
+ <script type="text/javascript">
+ is(counter, 14, "Imports are loaded");
+ is(fcounter, 0, "No error in imports");
+ var expected = ["D","C","B","A"];
+ for (i in expected)
+ is(order[i], expected[i], "import " + i + " should be " + expected[i]);
+ SimpleTest.finish();
+ </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/test_encoding.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1061469
+-->
+<head>
+ <title>Test for Bug 1061469</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1061469">Mozilla Bug 1061469</a>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ var success = false;
+ function loaded() {
+ success = true;
+ }
+ function failed() {
+ ok(false, "Import loading failed");
+ }
+ </script>
+ <link rel="import" href="file_encoding.html" id="import" onload="loaded()" onerror="failed()"></link>
+ <script type="text/javascript">
+ is(document.getElementById("import").import.characterSet, "UTF-8", "characterSet should be UTF-8 for imports");
+ SimpleTest.finish();
+ </script>
+</body>
+</html>
\ No newline at end of file
--- a/content/html/content/test/test_video_wakelock.html
+++ b/content/html/content/test/test_video_wakelock.html
@@ -16,64 +16,70 @@ https://bugzilla.mozilla.org/show_bug.cg
<div id="content">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 868943 **/
function testVideoPlayPause() {
+ info("#1 testVideoPlayPause");
+
var lockState_cpu = true;
var lockState_screen = true;
var count_cpu = 0;
var count_screen = 0;
var content = document.getElementById('content');
var video = document.createElement('video');
video.src = "wakelock.ogv";
content.appendChild(video);
var startDate;
- video.addEventListener('playing', function() {
- startDate = new Date();
+ function testVideoPlayPauseListener(topic, state) {
+ info("#1 topic=" + topic + ", state=" + state);
- // The next step is to unlock the resource.
- lockState_cpu = false;
- lockState_screen = false;
- video.pause();
- });
-
- function testVideoPlayPauseListener(topic, state) {
var locked = state == "locked-foreground" ||
state == "locked-background";
if (topic == "cpu") {
- is(locked, lockState_cpu, "Video element locked the cpu - paused");
+ is(locked, lockState_cpu, "#1 Video element locked the cpu");
count_cpu++;
} else if (topic == "screen") {
- is(locked, lockState_screen, "Video element locked the screen - paused");
+ is(locked, lockState_screen, "#1 Video element locked the screen");
count_screen++;
}
+ if (count_cpu == 1 && count_screen == 1) {
+ info("#1 Both cpu and screen are locked");
+ // The next step is to unlock the resource.
+ lockState_cpu = false;
+ lockState_screen = false;
+ video.pause();
+ startDate = new Date();
+ }
+
if (count_cpu == 2 && count_screen == 2) {
var diffDate = (new Date() - startDate);
- ok(diffDate > 200, "There was at least 200 milliseconds between the stop and the wakelock release");
+ ok(diffDate > 200, "#1 There was at least 200 milliseconds between the stop and the wakelock release");
content.removeChild(video);
navigator.mozPower.removeWakeLockListener(testVideoPlayPauseListener);
runTests();
}
}
navigator.mozPower.addWakeLockListener(testVideoPlayPauseListener);
video.play();
}
function testVideoPlay() {
+ info("#2 testVideoPlay");
+
var lockState_cpu = true;
var lockState_screen = true;
var count_cpu = 0;
var count_screen = 0;
var content = document.getElementById('content');
var video = document.createElement('video');
@@ -81,34 +87,37 @@ function testVideoPlay() {
content.appendChild(video);
var startDate;
video.addEventListener('progress', function() {
startDate = new Date();
});
function testVideoPlayListener(topic, state) {
+ info("#2 topic=" + topic + ", state=" + state);
+
var locked = state == "locked-foreground" ||
state == "locked-background";
if (topic == "cpu") {
- is(locked, lockState_cpu, "Video element locked the cpu - paused");
+ is(locked, lockState_cpu, "#2 Video element locked the cpu");
count_cpu++;
} else if (topic == "screen") {
- is(locked, lockState_screen, "Video element locked the screen - paused");
+ is(locked, lockState_screen, "#2 Video element locked the screen");
count_screen++;
}
if (count_cpu == 1 && count_screen == 1) {
+ info("#2 Both cpu and screen are locked");
// The next step is to unlock the resource.
lockState_cpu = false;
lockState_screen = false;
} else if (count_cpu == 2 && count_screen == 2) {
var diffDate = (new Date() - startDate);
- ok(diffDate > 200, "There was at least milliseconds between the stop and the wakelock release");
+ ok(diffDate > 200, "#2 There was at least milliseconds between the stop and the wakelock release");
content.removeChild(video);
navigator.mozPower.removeWakeLockListener(testVideoPlayListener);
runTests();
}
}
navigator.mozPower.addWakeLockListener(testVideoPlayListener);
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -693,33 +693,47 @@ DOMInterfaces = {
'nativeType': 'mozilla::dom::HTMLCanvasPrintState',
},
'MozChannel': {
'nativeType': 'nsIChannel',
'notflattened': True
},
+'MozCdmaIccInfo': {
+ 'headerFile': 'mozilla/dom/IccInfo.h',
+ 'nativeType': 'mozilla::dom::CdmaIccInfo',
+},
+
'MozCellBroadcast': {
'nativeType': 'mozilla::dom::CellBroadcast',
},
'MozCellBroadcastEtwsInfo': {
'nativeType': 'mozilla::dom::CellBroadcastEtwsInfo',
'headerFile': 'CellBroadcastMessage.h'
},
'MozCellBroadcastMessage': {
'nativeType': 'mozilla::dom::CellBroadcastMessage',
},
+'MozGsmIccInfo': {
+ 'headerFile': 'mozilla/dom/IccInfo.h',
+ 'nativeType': 'mozilla::dom::GsmIccInfo',
+},
+
'MozIcc': {
'nativeType': 'mozilla::dom::Icc',
},
+'MozIccInfo': {
+ 'nativeType': 'mozilla::dom::IccInfo',
+},
+
'MozIccManager': {
'nativeType': 'mozilla::dom::IccManager',
},
'MozMobileCellInfo': {
'nativeType': 'mozilla::dom::MobileCellInfo',
},
@@ -1789,17 +1803,16 @@ addExternalIface('HitRegionOptions', nat
addExternalIface('imgINotificationObserver', nativeType='imgINotificationObserver')
addExternalIface('imgIRequest', nativeType='imgIRequest', notflattened=True)
addExternalIface('MenuBuilder', nativeType='nsIMenuBuilder', notflattened=True)
addExternalIface('MozBoxObject', nativeType='nsIBoxObject')
addExternalIface('MozControllers', nativeType='nsIControllers')
addExternalIface('MozFrameLoader', nativeType='nsIFrameLoader', notflattened=True)
addExternalIface('MozFrameRequestCallback', nativeType='nsIFrameRequestCallback',
notflattened=True)
-addExternalIface('MozIccInfo', headerFile='nsIDOMIccInfo.h')
addExternalIface('MozMmsMessage')
addExternalIface('MozObserver', nativeType='nsIObserver', notflattened=True)
addExternalIface('MozRDFCompositeDataSource', nativeType='nsIRDFCompositeDataSource',
notflattened=True)
addExternalIface('MozRDFResource', nativeType='nsIRDFResource', notflattened=True)
addExternalIface('MozSmsMessage')
addExternalIface('MozTreeBoxObject', nativeType='nsITreeBoxObject',
notflattened=True)
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -11,17 +11,17 @@
#include "BluetoothUtils.h"
#include "jsapi.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "nsContentUtils.h"
#include "nsIAudioManager.h"
-#include "nsIDOMIccInfo.h"
+#include "nsIIccInfo.h"
#include "nsIIccProvider.h"
#include "nsIMobileConnectionInfo.h"
#include "nsIMobileConnectionService.h"
#include "nsIMobileNetworkInfo.h"
#include "nsIObserverService.h"
#include "nsISettingsService.h"
#include "nsITelephonyService.h"
#include "nsRadioInterfaceLayer.h"
@@ -668,21 +668,21 @@ BluetoothHfpManager::HandleVoiceConnecti
void
BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId)
{
nsCOMPtr<nsIIccProvider> icc =
do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
NS_ENSURE_TRUE_VOID(icc);
- nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
+ nsCOMPtr<nsIIccInfo> iccInfo;
icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo));
NS_ENSURE_TRUE_VOID(iccInfo);
- nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
+ nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
NS_ENSURE_TRUE_VOID(gsmIccInfo);
gsmIccInfo->GetMsisdn(mMsisdn);
}
void
BluetoothHfpManager::HandleShutdown()
{
MOZ_ASSERT(NS_IsMainThread());
--- a/dom/bluetooth/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.cpp
@@ -22,17 +22,17 @@
#include "nsContentUtils.h"
#include "nsIObserverService.h"
#include "nsISettingsService.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/SettingChangeNotificationBinding.h"
#ifdef MOZ_B2G_RIL
-#include "nsIDOMIccInfo.h"
+#include "nsIIccInfo.h"
#include "nsIIccProvider.h"
#include "nsIMobileConnectionInfo.h"
#include "nsIMobileConnectionService.h"
#include "nsIMobileNetworkInfo.h"
#include "nsITelephonyService.h"
#include "nsRadioInterfaceLayer.h"
#endif
@@ -669,21 +669,21 @@ BluetoothHfpManager::HandleVoiceConnecti
void
BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId)
{
nsCOMPtr<nsIIccProvider> icc =
do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
NS_ENSURE_TRUE_VOID(icc);
- nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
+ nsCOMPtr<nsIIccInfo> iccInfo;
icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo));
NS_ENSURE_TRUE_VOID(iccInfo);
- nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
+ nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
NS_ENSURE_TRUE_VOID(gsmIccInfo);
gsmIccInfo->GetMsisdn(mMsisdn);
}
#endif // MOZ_B2G_RIL
void
BluetoothHfpManager::HandleShutdown()
{
--- a/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -11,17 +11,17 @@
#include "BluetoothUtils.h"
#include "jsapi.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "nsContentUtils.h"
#include "nsIAudioManager.h"
-#include "nsIDOMIccInfo.h"
+#include "nsIIccInfo.h"
#include "nsIIccProvider.h"
#include "nsIMobileConnectionInfo.h"
#include "nsIMobileConnectionService.h"
#include "nsIMobileNetworkInfo.h"
#include "nsIObserverService.h"
#include "nsISettingsService.h"
#include "nsITelephonyService.h"
#include "nsRadioInterfaceLayer.h"
@@ -673,21 +673,21 @@ BluetoothHfpManager::HandleVoiceConnecti
void
BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId)
{
nsCOMPtr<nsIIccProvider> icc =
do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
NS_ENSURE_TRUE_VOID(icc);
- nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
+ nsCOMPtr<nsIIccInfo> iccInfo;
icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo));
NS_ENSURE_TRUE_VOID(iccInfo);
- nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
+ nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
NS_ENSURE_TRUE_VOID(gsmIccInfo);
gsmIccInfo->GetMsisdn(mMsisdn);
}
void
BluetoothHfpManager::HandleShutdown()
{
MOZ_ASSERT(NS_IsMainThread());
--- a/dom/bluetooth2/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth2/bluez/BluetoothHfpManager.cpp
@@ -22,17 +22,17 @@
#include "nsContentUtils.h"
#include "nsIObserverService.h"
#include "nsISettingsService.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/SettingChangeNotificationBinding.h"
#ifdef MOZ_B2G_RIL
-#include "nsIDOMIccInfo.h"
+#include "nsIIccInfo.h"
#include "nsIIccProvider.h"
#include "nsIMobileConnectionInfo.h"
#include "nsIMobileConnectionService.h"
#include "nsIMobileNetworkInfo.h"
#include "nsITelephonyService.h"
#include "nsRadioInterfaceLayer.h"
#endif
@@ -669,21 +669,21 @@ BluetoothHfpManager::HandleVoiceConnecti
void
BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId)
{
nsCOMPtr<nsIIccProvider> icc =
do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
NS_ENSURE_TRUE_VOID(icc);
- nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
+ nsCOMPtr<nsIIccInfo> iccInfo;
icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo));
NS_ENSURE_TRUE_VOID(iccInfo);
- nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
+ nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
NS_ENSURE_TRUE_VOID(gsmIccInfo);
gsmIccInfo->GetMsisdn(mMsisdn);
}
#endif // MOZ_B2G_RIL
void
BluetoothHfpManager::HandleShutdown()
{
--- a/dom/browser-element/BrowserElementParent.jsm
+++ b/dom/browser-element/BrowserElementParent.jsm
@@ -139,16 +139,18 @@ function BrowserElementParent(frameLoade
defineDOMRequestMethod('getCanGoBack', 'get-can-go-back');
defineDOMRequestMethod('getCanGoForward', 'get-can-go-forward');
defineDOMRequestMethod('getContentDimensions', 'get-contentdimensions');
}
defineMethod('addNextPaintListener', this._addNextPaintListener);
defineMethod('removeNextPaintListener', this._removeNextPaintListener);
+ defineNoReturnMethod('setActive', this._setActive);
+ defineMethod('getActive', 'this._getActive');
let principal = this._frameElement.ownerDocument.nodePrincipal;
let perm = Services.perms
.testExactPermissionFromPrincipal(principal, "input-manage");
if (perm === Ci.nsIPermissionManager.ALLOW_ACTION) {
defineMethod('setInputMethodActive', this._setInputMethodActive);
}
@@ -582,16 +584,24 @@ BrowserElementParent.prototype = {
}
},
_setVisible: function(visible) {
this._sendAsyncMsg('set-visible', {visible: visible});
this._frameLoader.visible = visible;
},
+ _setActive: function(active) {
+ this._frameLoader.visible = active;
+ },
+
+ _getActive: function() {
+ return this._frameLoader.visible;
+ },
+
_sendMouseEvent: function(type, x, y, button, clickCount, modifiers) {
this._sendAsyncMsg("send-mouse-event", {
"type": type,
"x": x,
"y": y,
"button": button,
"clickCount": clickCount,
"modifiers": modifiers
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -3087,21 +3087,28 @@ CanvasRenderingContext2D::GetHitRegionRe
*/
struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcessor
{
typedef CanvasRenderingContext2D::ContextState ContextState;
virtual void SetText(const char16_t* text, int32_t length, nsBidiDirection direction)
{
mFontgrp->UpdateUserFonts(); // ensure user font generation is current
+ // adjust flags for current direction run
+ uint32_t flags = mTextRunFlags;
+ if (direction & 1) {
+ flags |= gfxTextRunFactory::TEXT_IS_RTL;
+ } else {
+ flags &= ~gfxTextRunFactory::TEXT_IS_RTL;
+ }
mTextRun = mFontgrp->MakeTextRun(text,
length,
mThebes,
mAppUnitsPerDevPixel,
- direction==NSBIDI_RTL ? gfxTextRunFactory::TEXT_IS_RTL : 0);
+ flags);
}
virtual nscoord GetWidth()
{
gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(0,
mTextRun->GetLength(),
mDoMeasureBoundingBox ?
gfxFont::TIGHT_INK_EXTENTS :
@@ -3117,34 +3124,38 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
}
return NSToCoordRound(textRunMetrics.mAdvanceWidth);
}
virtual void DrawText(nscoord xOffset, nscoord width)
{
gfxPoint point = mPt;
- point.x += xOffset;
+ bool rtl = mTextRun->IsRightToLeft();
+ bool verticalRun = mTextRun->IsVertical();
+
+ gfxFloat& inlineCoord = verticalRun ? point.y : point.x;
+ inlineCoord += xOffset;
// offset is given in terms of left side of string
- if (mTextRun->IsRightToLeft()) {
+ if (rtl) {
// Bug 581092 - don't use rounded pixel width to advance to
// right-hand end of run, because this will cause different
// glyph positioning for LTR vs RTL drawing of the same
// glyph string on OS X and DWrite where textrun widths may
// involve fractional pixels.
gfxTextRun::Metrics textRunMetrics =
mTextRun->MeasureText(0,
mTextRun->GetLength(),
mDoMeasureBoundingBox ?
gfxFont::TIGHT_INK_EXTENTS :
gfxFont::LOOSE_INK_EXTENTS,
mThebes,
nullptr);
- point.x += textRunMetrics.mAdvanceWidth;
+ inlineCoord += textRunMetrics.mAdvanceWidth;
// old code was:
// point.x += width * mAppUnitsPerDevPixel;
// TODO: restore this if/when we move to fractional coords
// throughout the text layout process
}
uint32_t numRuns;
const gfxTextRun::GlyphRun *runs = mTextRun->GetGlyphRuns(&numRuns);
@@ -3153,16 +3164,25 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
Point baselineOrigin =
Point(point.x * devUnitsPerAppUnit, point.y * devUnitsPerAppUnit);
float advanceSum = 0;
mCtx->EnsureTarget();
for (uint32_t c = 0; c < numRuns; c++) {
gfxFont *font = runs[c].mFont;
+
+ bool verticalFont =
+ runs[c].mOrientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+
+ const float& baselineOriginInline =
+ verticalFont ? baselineOrigin.y : baselineOrigin.x;
+ const float& baselineOriginBlock =
+ verticalFont ? baselineOrigin.x : baselineOrigin.y;
+
uint32_t endRun = 0;
if (c + 1 < numRuns) {
endRun = runs[c + 1].mCharacterOffset;
} else {
endRun = mTextRun->GetLength();
}
const gfxTextRun::CompressedGlyph *glyphs = mTextRun->GetCharacterGlyphs();
@@ -3170,70 +3190,100 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
RefPtr<ScaledFont> scaledFont =
gfxPlatform::GetPlatform()->GetScaledFontForFont(mCtx->mTarget, font);
if (!scaledFont) {
// This can occur when something switched DirectWrite off.
return;
}
+ AutoRestoreTransform sidewaysRestore;
+ if (runs[c].mOrientation ==
+ gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT) {
+ sidewaysRestore.Init(mCtx->mTarget);
+ // TODO: The baseline adjustment here is kinda ad-hoc; eventually
+ // perhaps we should check for horizontal and vertical baseline data
+ // in the font, and adjust accordingly.
+ // (The same will be true for HTML text layout.)
+ const gfxFont::Metrics& metrics = mTextRun->GetFontGroup()->
+ GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal);
+ mCtx->mTarget->SetTransform(mCtx->mTarget->GetTransform().Copy().
+ PreTranslate(baselineOrigin). // translate origin for rotation
+ PreRotate(gfx::Float(M_PI / 2.0)). // turn 90deg clockwise
+ PreTranslate(-baselineOrigin). // undo the translation
+ PreTranslate(Point(0, metrics.emAscent - metrics.emDescent) / 2));
+ // and offset the (alphabetic) baseline of the
+ // horizontally-shaped text from the (centered)
+ // default baseline used for vertical
+ }
+
RefPtr<GlyphRenderingOptions> renderingOptions = font->GetGlyphRenderingOptions();
GlyphBuffer buffer;
std::vector<Glyph> glyphBuf;
+ // TODO:
+ // This more-or-less duplicates the code found in gfxTextRun::Draw
+ // and the gfxFont methods that uses (Draw, DrawGlyphs, DrawOneGlyph);
+ // it would be nice to refactor and share that code.
for (uint32_t i = runs[c].mCharacterOffset; i < endRun; i++) {
Glyph newGlyph;
+
+ float& inlinePos =
+ verticalFont ? newGlyph.mPosition.y : newGlyph.mPosition.x;
+ float& blockPos =
+ verticalFont ? newGlyph.mPosition.x : newGlyph.mPosition.y;
+
if (glyphs[i].IsSimpleGlyph()) {
newGlyph.mIndex = glyphs[i].GetSimpleGlyph();
- if (mTextRun->IsRightToLeft()) {
- newGlyph.mPosition.x = baselineOrigin.x - advanceSum -
+ if (rtl) {
+ inlinePos = baselineOriginInline - advanceSum -
glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
} else {
- newGlyph.mPosition.x = baselineOrigin.x + advanceSum;
+ inlinePos = baselineOriginInline + advanceSum;
}
- newGlyph.mPosition.y = baselineOrigin.y;
+ blockPos = baselineOriginBlock;
advanceSum += glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
glyphBuf.push_back(newGlyph);
continue;
}
if (!glyphs[i].GetGlyphCount()) {
continue;
}
- gfxTextRun::DetailedGlyph *detailedGlyphs =
- mTextRun->GetDetailedGlyphs(i);
+ const gfxTextRun::DetailedGlyph *d = mTextRun->GetDetailedGlyphs(i);
if (glyphs[i].IsMissing()) {
newGlyph.mIndex = 0;
- if (mTextRun->IsRightToLeft()) {
- newGlyph.mPosition.x = baselineOrigin.x - advanceSum -
- detailedGlyphs[0].mAdvance * devUnitsPerAppUnit;
+ if (rtl) {
+ inlinePos = baselineOriginInline - advanceSum -
+ d->mAdvance * devUnitsPerAppUnit;
} else {
- newGlyph.mPosition.x = baselineOrigin.x + advanceSum;
+ inlinePos = baselineOriginInline + advanceSum;
}
- newGlyph.mPosition.y = baselineOrigin.y;
- advanceSum += detailedGlyphs[0].mAdvance * devUnitsPerAppUnit;
+ blockPos = baselineOriginBlock;
+ advanceSum += d->mAdvance * devUnitsPerAppUnit;
glyphBuf.push_back(newGlyph);
continue;
}
- for (uint32_t c = 0; c < glyphs[i].GetGlyphCount(); c++) {
- newGlyph.mIndex = detailedGlyphs[c].mGlyphID;
- if (mTextRun->IsRightToLeft()) {
- newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit -
- advanceSum - detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
+ for (uint32_t c = 0; c < glyphs[i].GetGlyphCount(); c++, d++) {
+ newGlyph.mIndex = d->mGlyphID;
+ if (rtl) {
+ inlinePos = baselineOriginInline - advanceSum -
+ d->mAdvance * devUnitsPerAppUnit;
} else {
- newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit + advanceSum;
+ inlinePos = baselineOriginInline + advanceSum;
}
- newGlyph.mPosition.y = baselineOrigin.y + detailedGlyphs[c].mYOffset * devUnitsPerAppUnit;
+ inlinePos += d->mXOffset * devUnitsPerAppUnit;
+ blockPos = baselineOriginBlock + d->mYOffset * devUnitsPerAppUnit;
glyphBuf.push_back(newGlyph);
- advanceSum += detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
+ advanceSum += d->mAdvance * devUnitsPerAppUnit;
}
}
if (!glyphBuf.size()) {
// This may happen for glyph runs for a 0 size font.
continue;
}
@@ -3298,16 +3348,19 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
CanvasRenderingContext2D::TextDrawOperation mOp;
// context state
ContextState *mState;
// union of bounding boxes of all runs, needed for shadows
gfxRect mBoundingBox;
+ // flags to use when creating textrun, based on CSS style
+ uint32_t mTextRunFlags;
+
// true iff the bounding box should be measured
bool mDoMeasureBoundingBox;
};
nsresult
CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
float aX,
float aY,
@@ -3337,19 +3390,20 @@ CanvasRenderingContext2D::DrawOrMeasureT
// replace all the whitespace characters with U+0020 SPACE
nsAutoString textToDraw(aRawText);
TextReplaceWhitespaceCharacters(textToDraw);
// for now, default to ltr if not in doc
bool isRTL = false;
+ nsRefPtr<nsStyleContext> canvasStyle;
if (mCanvasElement && mCanvasElement->IsInDoc()) {
// try to find the closest context
- nsRefPtr<nsStyleContext> canvasStyle =
+ canvasStyle =
nsComputedDOMStyle::GetStyleContextForElement(mCanvasElement,
nullptr,
presShell);
if (!canvasStyle) {
return NS_ERROR_FAILURE;
}
isRTL = canvasStyle->StyleVisibility()->mDirection ==
@@ -3374,16 +3428,24 @@ CanvasRenderingContext2D::DrawOrMeasureT
const ContextState &state = CurrentState();
// This is only needed to know if we can know the drawing bounding box easily.
bool doCalculateBounds = NeedToCalculateBounds();
CanvasBidiProcessor processor;
+ // If we don't have a style context, we can't set up vertical-text flags
+ // (for now, at least; perhaps we need new Canvas API to control this).
+ processor.mTextRunFlags = canvasStyle ?
+ nsLayoutUtils::GetTextRunFlagsForStyle(canvasStyle,
+ canvasStyle->StyleFont(),
+ canvasStyle->StyleText(),
+ 0) : 0;
+
GetAppUnitsValues(&processor.mAppUnitsPerDevPixel, nullptr);
processor.mPt = gfxPoint(aX, aY);
processor.mThebes =
new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget());
// If we don't have a target then we don't have a transform. A target won't
// be needed in the case where we're measuring the text size. This allows
// to avoid creating a target if it's only being used to measure text sizes.
@@ -3440,17 +3502,19 @@ CanvasRenderingContext2D::DrawOrMeasureT
anchorX = 1;
}
processor.mPt.x -= anchorX * totalWidth;
// offset pt.y based on text baseline
processor.mFontgrp->UpdateUserFonts(); // ensure user font generation is current
const gfxFont::Metrics& fontMetrics =
- processor.mFontgrp->GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal); // XXX vertical?
+ processor.mFontgrp->GetFirstValidFont()->GetMetrics(
+ processor.mTextRun->IsVertical() ? gfxFont::eVertical :
+ gfxFont::eHorizontal);
gfxFloat anchorY;
switch (state.textBaseline)
{
case TextBaseline::HANGING:
// fall through; best we can do with the information available
case TextBaseline::TOP:
--- a/dom/icc/Icc.cpp
+++ b/dom/icc/Icc.cpp
@@ -1,45 +1,60 @@
/* 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 "Icc.h"
+#include "mozilla/dom/Icc.h"
+#include "mozilla/dom/DOMRequest.h"
+#include "mozilla/dom/IccInfo.h"
#include "mozilla/dom/MozIccBinding.h"
#include "mozilla/dom/MozStkCommandEvent.h"
-#include "mozilla/dom/DOMRequest.h"
#include "mozilla/dom/ScriptSettings.h"
-#include "nsIDOMIccInfo.h"
+#include "nsIIccInfo.h"
+#include "nsIIccProvider.h"
#include "nsJSON.h"
#include "nsRadioInterfaceLayer.h"
#include "nsServiceManagerUtils.h"
using namespace mozilla::dom;
-Icc::Icc(nsPIDOMWindow* aWindow, long aClientId, const nsAString& aIccId)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(Icc, DOMEventTargetHelper, mIccInfo)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Icc)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(Icc, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(Icc, DOMEventTargetHelper)
+
+Icc::Icc(nsPIDOMWindow* aWindow, long aClientId, nsIIccInfo* aIccInfo)
: mLive(true)
, mClientId(aClientId)
- , mIccId(aIccId)
{
SetIsDOMBinding();
BindToOwner(aWindow);
mProvider = do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+ if (aIccInfo) {
+ aIccInfo->GetIccid(mIccId);
+ UpdateIccInfo(aIccInfo);
+ }
+
// Not being able to acquire the provider isn't fatal since we check
// for it explicitly below.
if (!mProvider) {
NS_WARNING("Could not acquire nsIIccProvider!");
}
}
void
Icc::Shutdown()
{
+ mIccInfo.SetNull();
mProvider = nullptr;
mLive = false;
}
nsresult
Icc::NotifyEvent(const nsAString& aName)
{
return DispatchTrustedEvent(aName);
@@ -69,40 +84,62 @@ Icc::NotifyStkEvent(const nsAString& aNa
init.mCommand = value;
nsRefPtr<MozStkCommandEvent> event =
MozStkCommandEvent::Constructor(this, aName, init);
return DispatchTrustedEvent(event);
}
+void
+Icc::UpdateIccInfo(nsIIccInfo* aIccInfo)
+{
+ if (!aIccInfo) {
+ mIccInfo.SetNull();
+ return;
+ }
+
+ nsCOMPtr<nsIGsmIccInfo> gsmIccInfo(do_QueryInterface(aIccInfo));
+ if (gsmIccInfo) {
+ if (mIccInfo.IsNull() || !mIccInfo.Value().IsMozGsmIccInfo()) {
+ mIccInfo.SetValue().SetAsMozGsmIccInfo() = new GsmIccInfo(GetOwner());
+ }
+ mIccInfo.Value().GetAsMozGsmIccInfo().get()->Update(gsmIccInfo);
+ return;
+ }
+
+ nsCOMPtr<nsICdmaIccInfo> cdmaIccInfo(do_QueryInterface(aIccInfo));
+ if (cdmaIccInfo) {
+ if (mIccInfo.IsNull() || !mIccInfo.Value().IsMozCdmaIccInfo()) {
+ mIccInfo.SetValue().SetAsMozCdmaIccInfo() = new CdmaIccInfo(GetOwner());
+ }
+ mIccInfo.Value().GetAsMozCdmaIccInfo().get()->Update(cdmaIccInfo);
+ return;
+ }
+
+ if (mIccInfo.IsNull() || !mIccInfo.Value().IsMozIccInfo()) {
+ mIccInfo.SetValue().SetAsMozIccInfo() = new IccInfo(GetOwner());
+ }
+ mIccInfo.Value().GetAsMozIccInfo().get()->Update(aIccInfo);
+}
+
// WrapperCache
JSObject*
Icc::WrapObject(JSContext* aCx)
{
return MozIccBinding::Wrap(aCx, this);
}
// MozIcc WebIDL
-already_AddRefed<nsIDOMMozIccInfo>
-Icc::GetIccInfo() const
+void
+Icc::GetIccInfo(Nullable<OwningMozIccInfoOrMozGsmIccInfoOrMozCdmaIccInfo>& aIccInfo) const
{
- if (!mProvider) {
- return nullptr;
- }
-
- nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
- nsresult rv = mProvider->GetIccInfo(mClientId, getter_AddRefs(iccInfo));
- if (NS_FAILED(rv)) {
- return nullptr;
- }
-
- return iccInfo.forget();
+ aIccInfo = mIccInfo;
}
void
Icc::GetCardState(nsString& aCardState) const
{
aCardState.SetIsVoid(true);
if (!mProvider) {
--- a/dom/icc/Icc.h
+++ b/dom/icc/Icc.h
@@ -1,58 +1,66 @@
/* 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_Icc_h
#define mozilla_dom_Icc_h
+#include "mozilla/dom/UnionTypes.h"
#include "mozilla/DOMEventTargetHelper.h"
-#include "nsIIccProvider.h"
+
+class nsIIccInfo;
+class nsIIccProvider;
namespace mozilla {
namespace dom {
class DOMRequest;
class Icc MOZ_FINAL : public DOMEventTargetHelper
{
public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Icc, DOMEventTargetHelper)
NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
- Icc(nsPIDOMWindow* aWindow, long aClientId, const nsAString& aIccId);
+ Icc(nsPIDOMWindow* aWindow, long aClientId, nsIIccInfo* aIccInfo);
void
Shutdown();
nsresult
NotifyEvent(const nsAString& aName);
nsresult
NotifyStkEvent(const nsAString& aName, const nsAString& aMessage);
nsString
GetIccId()
{
return mIccId;
}
+ void
+ UpdateIccInfo(nsIIccInfo* aIccInfo);
+
nsPIDOMWindow*
GetParentObject() const
{
return GetOwner();
}
// WrapperCache
virtual JSObject*
WrapObject(JSContext* aCx) MOZ_OVERRIDE;
// MozIcc WebIDL
- already_AddRefed<nsIDOMMozIccInfo>
- GetIccInfo() const;
+ void
+ GetIccInfo(Nullable<OwningMozIccInfoOrMozGsmIccInfoOrMozCdmaIccInfo>& aIccInfo) const;
void
GetCardState(nsString& aCardState) const;
void
SendStkResponse(const JSContext* aCx, JS::Handle<JS::Value> aCommand,
JS::Handle<JS::Value> aResponse, ErrorResult& aRv);
@@ -111,14 +119,15 @@ public:
private:
bool mLive;
uint32_t mClientId;
nsString mIccId;
// mProvider is a xpcom service and will be released at shutdown, so it
// doesn't need to be cycle collected.
nsCOMPtr<nsIIccProvider> mProvider;
+ Nullable<OwningMozIccInfoOrMozGsmIccInfoOrMozCdmaIccInfo> mIccInfo;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_icc_Icc_h
new file mode 100644
--- /dev/null
+++ b/dom/icc/IccInfo.cpp
@@ -0,0 +1,230 @@
+/* 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/IccInfo.h"
+
+#include "nsPIDOMWindow.h"
+
+#define CONVERT_STRING_TO_NULLABLE_ENUM(_string, _enumType, _enum) \
+{ \
+ uint32_t i = 0; \
+ for (const EnumEntry* entry = _enumType##Values::strings; \
+ entry->value; \
+ ++entry, ++i) { \
+ if (_string.EqualsASCII(entry->value)) { \
+ _enum.SetValue(static_cast<_enumType>(i)); \
+ } \
+ } \
+}
+
+using namespace mozilla::dom;
+
+// IccInfo
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(IccInfo, mWindow, mIccInfo)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(IccInfo)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(IccInfo)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IccInfo)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+IccInfo::IccInfo(nsPIDOMWindow* aWindow)
+ : mWindow(aWindow)
+{
+ SetIsDOMBinding();
+}
+
+void
+IccInfo::Update(nsIIccInfo* aInfo)
+{
+ mIccInfo = aInfo;
+}
+
+JSObject*
+IccInfo::WrapObject(JSContext* aCx)
+{
+ return MozIccInfoBinding::Wrap(aCx, this);
+}
+
+Nullable<IccType>
+IccInfo::GetIccType() const
+{
+ if (!mIccInfo) {
+ return Nullable<IccType>();
+ }
+
+ nsAutoString type;
+ Nullable<IccType> iccType;
+
+ mIccInfo->GetIccType(type);
+ CONVERT_STRING_TO_NULLABLE_ENUM(type, IccType, iccType);
+
+ return iccType;
+}
+
+void
+IccInfo::GetIccid(nsAString& aIccId) const
+{
+ if (!mIccInfo) {
+ aIccId.SetIsVoid(true);
+ return;
+ }
+
+ mIccInfo->GetIccid(aIccId);
+}
+
+void
+IccInfo::GetMcc(nsAString& aMcc) const
+{
+ if (!mIccInfo) {
+ aMcc.SetIsVoid(true);
+ return;
+ }
+
+ mIccInfo->GetMcc(aMcc);
+}
+
+void
+IccInfo::GetMnc(nsAString& aMnc) const
+{
+ if (!mIccInfo) {
+ aMnc.SetIsVoid(true);
+ return;
+ }
+
+ mIccInfo->GetMnc(aMnc);
+}
+
+void
+IccInfo::GetSpn(nsAString& aSpn) const
+{
+ if (!mIccInfo) {
+ aSpn.SetIsVoid(true);
+ return;
+ }
+
+ mIccInfo->GetSpn(aSpn);
+}
+
+bool
+IccInfo::IsDisplayNetworkNameRequired() const
+{
+ if (!mIccInfo) {
+ return false;
+ }
+
+ bool isDisplayNetworkNameRequired;
+ mIccInfo->GetIsDisplayNetworkNameRequired(&isDisplayNetworkNameRequired);
+
+ return isDisplayNetworkNameRequired;
+}
+
+bool
+IccInfo::IsDisplaySpnRequired() const
+{
+ if (!mIccInfo) {
+ return false;
+ }
+
+ bool isDisplaySpnRequired;
+ mIccInfo->GetIsDisplaySpnRequired(&isDisplaySpnRequired);
+
+ return isDisplaySpnRequired;
+}
+
+// GsmIccInfo
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(GsmIccInfo, IccInfo, mGsmIccInfo)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(GsmIccInfo)
+NS_INTERFACE_MAP_END_INHERITING(IccInfo)
+
+NS_IMPL_ADDREF_INHERITED(GsmIccInfo, IccInfo)
+NS_IMPL_RELEASE_INHERITED(GsmIccInfo, IccInfo)
+
+GsmIccInfo::GsmIccInfo(nsPIDOMWindow* aWindow)
+ : IccInfo(aWindow)
+{
+ SetIsDOMBinding();
+}
+
+void
+GsmIccInfo::Update(nsIGsmIccInfo* aInfo)
+{
+ IccInfo::Update(aInfo);
+ mGsmIccInfo = aInfo;
+}
+
+JSObject*
+GsmIccInfo::WrapObject(JSContext* aCx)
+{
+ return MozGsmIccInfoBinding::Wrap(aCx, this);
+}
+
+void
+GsmIccInfo::GetMsisdn(nsAString& aMsisdn) const
+{
+ if (!mGsmIccInfo) {
+ aMsisdn.SetIsVoid(true);
+ return;
+ }
+
+ mGsmIccInfo->GetMsisdn(aMsisdn);
+}
+
+// CdmaIccInfo
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(CdmaIccInfo, IccInfo, mCdmaIccInfo)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(CdmaIccInfo)
+NS_INTERFACE_MAP_END_INHERITING(IccInfo)
+
+NS_IMPL_ADDREF_INHERITED(CdmaIccInfo, IccInfo)
+NS_IMPL_RELEASE_INHERITED(CdmaIccInfo, IccInfo)
+
+CdmaIccInfo::CdmaIccInfo(nsPIDOMWindow* aWindow)
+ : IccInfo(aWindow)
+{
+ SetIsDOMBinding();
+}
+
+void
+CdmaIccInfo::Update(nsICdmaIccInfo* aInfo)
+{
+ IccInfo::Update(aInfo);
+ mCdmaIccInfo = aInfo;
+}
+
+JSObject*
+CdmaIccInfo::WrapObject(JSContext* aCx)
+{
+ return MozCdmaIccInfoBinding::Wrap(aCx, this);
+}
+
+void
+CdmaIccInfo::GetMdn(nsAString& aMdn) const
+{
+ if (!mCdmaIccInfo) {
+ aMdn.SetIsVoid(true);
+ return;
+ }
+
+ mCdmaIccInfo->GetMdn(aMdn);
+}
+
+int32_t
+CdmaIccInfo::PrlVersion() const
+{
+ if (!mCdmaIccInfo) {
+ return 0;
+ }
+
+ int32_t prlVersion;
+ mCdmaIccInfo->GetPrlVersion(&prlVersion);
+
+ return prlVersion;
+}
new file mode 100644
--- /dev/null
+++ b/dom/icc/IccInfo.h
@@ -0,0 +1,128 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_IccInfo_h
+#define mozilla_dom_IccInfo_h
+
+#include "MozIccInfoBinding.h"
+#include "nsIIccInfo.h"
+#include "nsWrapperCache.h"
+
+class nsPIDOMWindow;
+
+namespace mozilla {
+namespace dom {
+
+class IccInfo : public nsISupports
+ , public nsWrapperCache
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IccInfo)
+
+ explicit IccInfo(nsPIDOMWindow* aWindow);
+
+ void
+ Update(nsIIccInfo* aInfo);
+
+ nsPIDOMWindow*
+ GetParentObject() const
+ {
+ return mWindow;
+ }
+
+ // WrapperCache
+ virtual JSObject*
+ WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+ // WebIDL interface
+ Nullable<IccType>
+ GetIccType() const;
+
+ void
+ GetIccid(nsAString& aIccId) const;
+
+ void
+ GetMcc(nsAString& aMcc) const;
+
+ void
+ GetMnc(nsAString& aMnc) const;
+
+ void
+ GetSpn(nsAString& aSpn) const;
+
+ bool
+ IsDisplayNetworkNameRequired() const;
+
+ bool
+ IsDisplaySpnRequired() const;
+
+protected:
+ ~IccInfo() {}
+
+protected:
+ nsCOMPtr<nsPIDOMWindow> mWindow;
+ nsCOMPtr<nsIIccInfo> mIccInfo;
+};
+
+class GsmIccInfo MOZ_FINAL : public IccInfo
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(GsmIccInfo, IccInfo)
+
+ explicit GsmIccInfo(nsPIDOMWindow* aWindow);
+
+ void
+ Update(nsIGsmIccInfo* aInfo);
+
+ // WrapperCache
+ virtual JSObject*
+ WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+ // MozCdmaIccInfo WebIDL
+ void
+ GetMsisdn(nsAString& aMsisdn) const;
+
+private:
+ ~GsmIccInfo() {}
+
+private:
+ nsCOMPtr<nsIGsmIccInfo> mGsmIccInfo;
+};
+
+class CdmaIccInfo MOZ_FINAL : public IccInfo
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CdmaIccInfo, IccInfo)
+
+ explicit CdmaIccInfo(nsPIDOMWindow* aWindow);
+
+ void
+ Update(nsICdmaIccInfo* aInfo);
+
+ // WrapperCache
+ virtual JSObject*
+ WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+ // MozCdmaIccInfo WebIDL
+ void
+ GetMdn(nsAString& aMdn) const;
+
+ int32_t
+ PrlVersion() const;
+
+private:
+ ~CdmaIccInfo() {}
+
+private:
+ nsCOMPtr<nsICdmaIccInfo> mCdmaIccInfo;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_IccInfo_h
+
--- a/dom/icc/IccListener.cpp
+++ b/dom/icc/IccListener.cpp
@@ -2,17 +2,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "IccListener.h"
#include "Icc.h"
#include "IccManager.h"
#include "nsIDOMClassInfo.h"
-#include "nsIDOMIccInfo.h"
+#include "nsIIccInfo.h"
#include "nsRadioInterfaceLayer.h"
using namespace mozilla::dom;
NS_IMPL_ISUPPORTS(IccListener, nsIIccListener)
IccListener::IccListener(IccManager* aIccManager, uint32_t aClientId)
: mClientId(aClientId)
@@ -22,23 +22,23 @@ IccListener::IccListener(IccManager* aIc
mProvider = do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
if (!mProvider) {
NS_WARNING("Could not acquire nsIIccProvider!");
return;
}
- nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
+ nsCOMPtr<nsIIccInfo> iccInfo;
mProvider->GetIccInfo(mClientId, getter_AddRefs(iccInfo));
if (iccInfo) {
nsString iccId;
iccInfo->GetIccid(iccId);
if (!iccId.IsEmpty()) {
- mIcc = new Icc(mIccManager->GetOwner(), mClientId, iccId);
+ mIcc = new Icc(mIccManager->GetOwner(), mClientId, iccInfo);
}
}
DebugOnly<nsresult> rv = mProvider->RegisterIccMsg(mClientId, this);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
"Failed registering icc messages with provider");
}
@@ -93,35 +93,36 @@ IccListener::NotifyCardStateChanged()
}
return mIcc->NotifyEvent(NS_LITERAL_STRING("cardstatechange"));
}
NS_IMETHODIMP
IccListener::NotifyIccInfoChanged()
{
- nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
+ nsCOMPtr<nsIIccInfo> iccInfo;
mProvider->GetIccInfo(mClientId, getter_AddRefs(iccInfo));
// Create/delete icc object based on current iccInfo.
// 1. If the mIcc is nullptr and iccInfo has valid data, create icc object and
// notify mIccManager a new icc is added.
// 2. If the mIcc is not nullptr and iccInfo becomes to null, delete existed
// icc object and notify mIccManager the icc is removed.
if (!mIcc) {
if (iccInfo) {
nsString iccId;
iccInfo->GetIccid(iccId);
if (!iccId.IsEmpty()) {
- mIcc = new Icc(mIccManager->GetOwner(), mClientId, iccId);
+ mIcc = new Icc(mIccManager->GetOwner(), mClientId, iccInfo);
mIccManager->NotifyIccAdd(iccId);
mIcc->NotifyEvent(NS_LITERAL_STRING("iccinfochange"));
}
}
} else {
+ mIcc->UpdateIccInfo(iccInfo);
mIcc->NotifyEvent(NS_LITERAL_STRING("iccinfochange"));
if (!iccInfo) {
nsString iccId = mIcc->GetIccId();
mIcc->Shutdown();
mIcc = nullptr;
mIccManager->NotifyIccRemove(iccId);
}
}
--- a/dom/icc/IccManager.cpp
+++ b/dom/icc/IccManager.cpp
@@ -3,17 +3,17 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "IccManager.h"
#include "mozilla/dom/MozIccManagerBinding.h"
#include "Icc.h"
#include "IccListener.h"
#include "mozilla/dom/IccChangeEvent.h"
#include "mozilla/Preferences.h"
-#include "nsIDOMIccInfo.h"
+#include "nsIIccInfo.h"
using namespace mozilla::dom;
NS_IMPL_CYCLE_COLLECTION_CLASS(IccManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IccManager,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
--- a/dom/icc/interfaces/moz.build
+++ b/dom/icc/interfaces/moz.build
@@ -1,12 +1,12 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPIDL_SOURCES += [
- 'nsIDOMIccInfo.idl',
+ 'nsIIccInfo.idl',
'nsIIccProvider.idl',
]
XPIDL_MODULE = 'dom_icc'
rename from dom/icc/interfaces/nsIDOMIccInfo.idl
rename to dom/icc/interfaces/nsIIccInfo.idl
--- a/dom/icc/interfaces/nsIDOMIccInfo.idl
+++ b/dom/icc/interfaces/nsIIccInfo.idl
@@ -1,16 +1,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
-[scriptable, uuid(dd9f229c-e5a6-453a-8388-950af0ff9918)]
-interface nsIDOMMozIccInfo : nsISupports
+[scriptable, uuid(3ba11a90-34e0-11e4-8c21-0800200c9a66)]
+interface nsIIccInfo : nsISupports
{
/**
* Integrated Circuit Card Type.
*
* Possible values: null(unknown), "sim", "usim", "csim", ruim".
*/
readonly attribute DOMString iccType;
@@ -40,28 +40,28 @@ interface nsIDOMMozIccInfo : nsISupports
readonly attribute boolean isDisplayNetworkNameRequired;
/**
* Service provider name must be a part of displayed carrier name.
*/
readonly attribute boolean isDisplaySpnRequired;
};
-[scriptable, uuid(3c237e39-7af3-4748-baf4-4a3b6c3e0e66)]
-interface nsIDOMMozGsmIccInfo : nsIDOMMozIccInfo
+[scriptable, uuid(6c9c78b0-34e0-11e4-8c21-0800200c9a66)]
+interface nsIGsmIccInfo : nsIIccInfo
{
/**
* Mobile Station ISDN Number (MSISDN) of the subscriber, aka
* his phone number.
*/
readonly attribute DOMString msisdn;
};
-[scriptable, uuid(7e937d09-4d1d-43c5-96d8-c91396022809)]
-interface nsIDOMMozCdmaIccInfo : nsIDOMMozIccInfo
+[scriptable, uuid(7452f570-34e0-11e4-8c21-0800200c9a66)]
+interface nsICdmaIccInfo : nsIIccInfo
{
/**
* Mobile Directory Number (MDN) of the subscriber, aka his phone number.
*/
readonly attribute DOMString mdn;
/**
* Preferred Roaming List (PRL) version of the subscriber.
--- a/dom/icc/interfaces/nsIIccProvider.idl
+++ b/dom/icc/interfaces/nsIIccProvider.idl
@@ -1,45 +1,45 @@
/* 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 "nsISupports.idl"
interface nsIDOMDOMRequest;
-interface nsIDOMMozIccInfo;
interface nsIDOMWindow;
+interface nsIIccInfo;
[scriptable, uuid(7c0ada3d-d8d4-493e-9243-fa3df39855e4)]
interface nsIIccListener : nsISupports
{
void notifyStkCommand(in DOMString aMessage);
void notifyStkSessionEnd();
void notifyCardStateChanged();
void notifyIccInfoChanged();
};
/**
* XPCOM component (in the content process) that provides the ICC information.
*/
-[scriptable, uuid(7c67ab92-52a3-4e11-995c-c0ad2f66c4cb)]
+[scriptable, uuid(1afa72d0-3d70-11e4-916c-0800200c9a66)]
interface nsIIccProvider : nsISupports
{
/**
* Called when a content process registers receiving unsolicited messages from
* RadioInterfaceLayer in the chrome process. Only a content process that has
* the 'mobileconnection' permission is allowed to register.
*/
void registerIccMsg(in unsigned long clientId, in nsIIccListener listener);
void unregisterIccMsg(in unsigned long clientId, in nsIIccListener listener);
/**
* UICC Information
*/
- nsIDOMMozIccInfo getIccInfo(in unsigned long clientId);
+ nsIIccInfo getIccInfo(in unsigned long clientId);
/**
* Card State
*/
DOMString getCardState(in unsigned long clientId);
/**
* STK interfaces.
--- a/dom/icc/moz.build
+++ b/dom/icc/moz.build
@@ -3,21 +3,23 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += ['interfaces']
EXPORTS.mozilla.dom += [
'Icc.h',
+ 'IccInfo.h',
'IccManager.h',
]
UNIFIED_SOURCES += [
'Icc.cpp',
+ "IccInfo.cpp",
'IccListener.cpp',
'IccManager.cpp',
]
FAIL_ON_WARNINGS = True
include('/ipc/chromium/chromium-config.mozbuild')
--- a/dom/icc/tests/marionette/test_icc_info.js
+++ b/dom/icc/tests/marionette/test_icc_info.js
@@ -30,17 +30,17 @@ function setEmulatorMccMnc(mcc, mnc) {
/* Basic test */
taskHelper.push(function basicTest() {
let iccInfo = icc.iccInfo;
// The emulator's hard coded iccid value.
// See it here {B2G_HOME}/external/qemu/telephony/sim_card.c#L299.
is(iccInfo.iccid, 89014103211118510720);
- if (iccInfo instanceof Ci.nsIDOMMozGsmIccInfo) {
+ if (iccInfo instanceof MozGsmIccInfo) {
log("Test Gsm IccInfo");
is(iccInfo.iccType, "sim");
is(iccInfo.spn, "Android");
// The emulator's hard coded mcc and mnc codes.
// See it here {B2G_HOME}/external/qemu/telephony/android_modem.c#L2465.
is(iccInfo.mcc, 310);
is(iccInfo.mnc, 260);
// Phone number is hardcoded in MSISDN
--- a/dom/mobilemessage/gonk/MmsService.js
+++ b/dom/mobilemessage/gonk/MmsService.js
@@ -334,32 +334,32 @@ MmsConnection.prototype = {
if (DEBUG) debug("isVoiceRoaming = " + isRoaming);
return isRoaming;
},
/**
* Get phone number from iccInfo.
*
* If the icc card is gsm card, the phone number is in msisdn.
- * @see nsIDOMMozGsmIccInfo
+ * @see nsIGsmIccInfo
*
* Otherwise, the phone number is in mdn.
- * @see nsIDOMMozCdmaIccInfo
+ * @see nsICdmaIccInfo
*/
getPhoneNumber: function() {
let number;
// Get the proper IccInfo based on the current card type.
try {
let iccInfo = null;
let baseIccInfo = this.radioInterface.rilContext.iccInfo;
if (baseIccInfo.iccType === 'ruim' || baseIccInfo.iccType === 'csim') {
- iccInfo = baseIccInfo.QueryInterface(Ci.nsIDOMMozCdmaIccInfo);
+ iccInfo = baseIccInfo.QueryInterface(Ci.nsICdmaIccInfo);
number = iccInfo.mdn;
} else {
- iccInfo = baseIccInfo.QueryInterface(Ci.nsIDOMMozGsmIccInfo);
+ iccInfo = baseIccInfo.QueryInterface(Ci.nsIGsmIccInfo);
number = iccInfo.msisdn;
}
} catch (e) {
if (DEBUG) {
debug("Exception - QueryInterface failed on iccinfo for GSM/CDMA info");
}
return null;
}
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -29,17 +29,17 @@
#include "nsIObserverService.h"
#include "nsJSUtils.h"
#include "nsPrintfCString.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "prtime.h"
#ifdef MOZ_B2G_RIL
-#include "nsIDOMIccInfo.h"
+#include "nsIIccInfo.h"
#include "nsIMobileConnectionInfo.h"
#include "nsIMobileConnectionService.h"
#include "nsIMobileCellInfo.h"
#include "nsIRadioInterfaceLayer.h"
#endif
#ifdef AGPS_TYPE_INVALID
#define AGPS_HAVE_DUAL_APN
@@ -454,20 +454,20 @@ GonkGPSGeolocationProvider::RequestSetID
if (rilCtx) {
nsAutoString id;
if (flags & AGPS_RIL_REQUEST_SETID_IMSI) {
type = AGPS_SETID_TYPE_IMSI;
rilCtx->GetImsi(id);
}
if (flags & AGPS_RIL_REQUEST_SETID_MSISDN) {
- nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
+ nsCOMPtr<nsIIccInfo> iccInfo;
rilCtx->GetIccInfo(getter_AddRefs(iccInfo));
if (iccInfo) {
- nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
+ nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
if (gsmIccInfo) {
type = AGPS_SETID_TYPE_MSISDN;
gsmIccInfo->GetMsisdn(id);
}
}
}
NS_ConvertUTF16toUTF8 idBytes(id);
@@ -489,17 +489,17 @@ GonkGPSGeolocationProvider::SetReference
mRadioInterface->GetRilContext(getter_AddRefs(rilCtx));
AGpsRefLocation location;
// TODO: Bug 772750 - get mobile connection technology from rilcontext
location.type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
if (rilCtx) {
- nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
+ nsCOMPtr<nsIIccInfo> iccInfo;
rilCtx->GetIccInfo(getter_AddRefs(iccInfo));
if (iccInfo) {
nsresult result;
nsAutoString mcc, mnc;
iccInfo->GetMcc(mcc);
iccInfo->GetMnc(mnc);
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -36,22 +36,16 @@ const kPrefRilDebuggingEnabled = "ril.de
let DEBUG;
function debug(s) {
dump("-*- RILContentHelper: " + s + "\n");
}
const RILCONTENTHELPER_CID =
Components.ID("{472816e1-1fd6-4405-996c-806f9ea68174}");
-const ICCINFO_CID =
- Components.ID("{39d64d90-26a6-11e4-8c21-0800200c9a66}");
-const GSMICCINFO_CID =
- Components.ID("{e0fa785b-ad3f-46ed-bc56-fcb0d6fe4fa8}");
-const CDMAICCINFO_CID =
- Components.ID("{3d1f844f-9ec5-48fb-8907-aed2e5421709}");
const ICCCARDLOCKERROR_CID =
Components.ID("{08a71987-408c-44ff-93fd-177c0a85c3dd}");
const RIL_IPC_MSG_NAMES = [
"RIL:CardStateChanged",
"RIL:IccInfoChanged",
"RIL:CardLockResult",
"RIL:CardLockRetryCount",
@@ -103,66 +97,45 @@ function MobileIccCardLockRetryCount(opt
MobileIccCardLockRetryCount.prototype = {
__exposedProps__ : {lockType: 'r',
retryCount: 'r',
success: 'r'}
};
function IccInfo() {}
IccInfo.prototype = {
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozIccInfo]),
- classID: ICCINFO_CID,
- classInfo: XPCOMUtils.generateCI({
- classID: ICCINFO_CID,
- classDescription: "MozIccInfo",
- flags: Ci.nsIClassInfo.DOM_OBJECT,
- interfaces: [Ci.nsIDOMMozIccInfo]
- }),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccInfo]),
- // nsIDOMMozIccInfo
+ // nsIIccInfo
iccType: null,
iccid: null,
mcc: null,
mnc: null,
spn: null,
isDisplayNetworkNameRequired: null,
isDisplaySpnRequired: null
};
function GsmIccInfo() {}
GsmIccInfo.prototype = {
- __proto__: IccInfo.prototype,
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozGsmIccInfo]),
- classID: GSMICCINFO_CID,
- classInfo: XPCOMUtils.generateCI({
- classID: GSMICCINFO_CID,
- classDescription: "MozGsmIccInfo",
- flags: Ci.nsIClassInfo.DOM_OBJECT,
- interfaces: [Ci.nsIDOMMozGsmIccInfo]
- }),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
+ Ci.nsIIccInfo]),
- // nsIDOMMozGsmIccInfo
+ // nsIGsmIccInfo
msisdn: null
};
function CdmaIccInfo() {}
CdmaIccInfo.prototype = {
- __proto__: IccInfo.prototype,
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozCdmaIccInfo]),
- classID: CDMAICCINFO_CID,
- classInfo: XPCOMUtils.generateCI({
- classID: CDMAICCINFO_CID,
- classDescription: "MozCdmaIccInfo",
- flags: Ci.nsIClassInfo.DOM_OBJECT,
- interfaces: [Ci.nsIDOMMozCdmaIccInfo]
- }),
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsICdmaIccInfo,
+ Ci.nsIIccInfo]),
- // nsIDOMMozCdmaIccInfo
+ // nsICdmaIccInfo
mdn: null,
prlVersion: 0
};
function IccCardLockError() {
}
IccCardLockError.prototype = {
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -48,22 +48,16 @@ let RILQUIRKS_HAVE_IPV6 =
libcutils.property_get("ro.moz.ril.ipv6", "false") == "true";
const RADIOINTERFACELAYER_CID =
Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
const RADIOINTERFACE_CID =
Components.ID("{6a7c91f0-a2b3-4193-8562-8969296c0b54}");
const RILNETWORKINTERFACE_CID =
Components.ID("{3bdd52a9-3965-4130-b569-0ac5afed045e}");
-const ICCINFO_CID =
- Components.ID("{52eec7f0-26a4-11e4-8c21-0800200c9a66}");
-const GSMICCINFO_CID =
- Components.ID("{d90c4261-a99d-47bc-8b05-b057bb7e8f8a}");
-const CDMAICCINFO_CID =
- Components.ID("{39ba3c08-aacc-46d0-8c04-9b619c387061}");
const NEIGHBORINGCELLINFO_CID =
Components.ID("{f9dfe26a-851e-4a8b-a769-cbb1baae7ded}");
const GSMCELLINFO_CID =
Components.ID("{41f6201e-7263-42e3-b31f-38a9dc8a280a}");
const WCDMACELLINFO_CID =
Components.ID("{eeaaf307-df6e-4c98-b121-e3302b1fc468}");
const CDMACELLINFO_CID =
Components.ID("{b497d6e4-4cb8-4d6e-b673-840c7d5ddf25}");
@@ -936,66 +930,47 @@ try {
} catch (e) {}
return 1;
})());
} catch (e) {}
function IccInfo() {}
IccInfo.prototype = {
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozIccInfo]),
- classID: ICCINFO_CID,
- classInfo: XPCOMUtils.generateCI({
- classID: ICCINFO_CID,
- classDescription: "MozIccInfo",
- flags: Ci.nsIClassInfo.DOM_OBJECT,
- interfaces: [Ci.nsIDOMMozIccInfo]
- }),
-
- // nsIDOMMozIccInfo
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccInfo]),
+
+ // nsIIccInfo
iccType: null,
iccid: null,
mcc: null,
mnc: null,
spn: null,
isDisplayNetworkNameRequired: null,
isDisplaySpnRequired: null
};
function GsmIccInfo() {}
GsmIccInfo.prototype = {
__proto__: IccInfo.prototype,
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozGsmIccInfo]),
- classID: GSMICCINFO_CID,
- classInfo: XPCOMUtils.generateCI({
- classID: GSMICCINFO_CID,
- classDescription: "MozGsmIccInfo",
- flags: Ci.nsIClassInfo.DOM_OBJECT,
- interfaces: [Ci.nsIDOMMozGsmIccInfo]
- }),
-
- // nsIDOMMozGsmIccInfo
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
+ Ci.nsIIccInfo]),
+
+ // nsIGsmIccInfo
msisdn: null
};
function CdmaIccInfo() {}
CdmaIccInfo.prototype = {
__proto__: IccInfo.prototype,
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozCdmaIccInfo]),
- classID: CDMAICCINFO_CID,
- classInfo: XPCOMUtils.generateCI({
- classID: CDMAICCINFO_CID,
- classDescription: "MozCdmaIccInfo",
- flags: Ci.nsIClassInfo.DOM_OBJECT,
- interfaces: [Ci.nsIDOMMozCdmaIccInfo]
- }),
-
- // nsIDOMMozCdmaIccInfo
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsICdmaIccInfo,
+ Ci.nsIIccInfo]),
+
+ // nsICdmaIccInfo
mdn: null,
prlVersion: 0
};
function NeighboringCellInfo() {}
NeighboringCellInfo.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsINeighboringCellInfo]),
@@ -2136,30 +2111,30 @@ RadioInterface.prototype = {
message.rilMessageType);
}
},
/**
* Get phone number from iccInfo.
*
* If the icc card is gsm card, the phone number is in msisdn.
- * @see nsIDOMMozGsmIccInfo
+ * @see nsIGsmIccInfo
*
* Otherwise, the phone number is in mdn.
- * @see nsIDOMMozCdmaIccInfo
+ * @see nsICdmaIccInfo
*/
getPhoneNumber: function() {
let iccInfo = this.rilContext.iccInfo;
if (!iccInfo) {
return null;
}
// After moving SMS code out of RadioInterfaceLayer, we could use
- // |iccInfo instanceof Ci.nsIDOMMozGsmIccInfo| here.
+ // |iccInfo instanceof Ci.nsIGsmIccInfo| here.
// TODO: Bug 873351 - B2G SMS: move SMS code out of RadioInterfaceLayer to
// SmsService
let number = (iccInfo instanceof GsmIccInfo) ? iccInfo.msisdn : iccInfo.mdn;
// Workaround an xpconnect issue with undefined string objects.
// See bug 808220
if (number === undefined || number === "undefined") {
return null;
--- a/dom/system/gonk/nsIRadioInterfaceLayer.idl
+++ b/dom/system/gonk/nsIRadioInterfaceLayer.idl
@@ -1,41 +1,41 @@
/* 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 "nsISupports.idl"
#include "nsINetworkManager.idl"
-interface nsIDOMMozIccInfo;
+interface nsICellInfoListCallback;
+interface nsIIccInfo;
interface nsIMobileConnectionInfo;
interface nsIMobileMessageCallback;
interface nsINeighboringCellIdsCallback;
-interface nsICellInfoListCallback;
[scriptable, uuid(6e0f45b8-410e-11e3-8c8e-b715b2cd0128)]
interface nsIRilNetworkInterface : nsINetworkInterface
{
readonly attribute unsigned long serviceId;
readonly attribute DOMString iccId;
/* The following attributes are for MMS proxy settings. */
readonly attribute DOMString mmsc; // Empty string if not set.
readonly attribute DOMString mmsProxy; // Empty string if not set.
readonly attribute long mmsPort; // -1 if not set.
};
-[scriptable, uuid(0226a2c1-a3b9-416a-92cb-c89e4dad4be0)]
+[scriptable, uuid(08a69c70-34b1-11e4-8c21-0800200c9a66)]
interface nsIRilContext : nsISupports
{
readonly attribute DOMString cardState;
readonly attribute DOMString imsi;
- readonly attribute nsIDOMMozIccInfo iccInfo;
+ readonly attribute nsIIccInfo iccInfo;
};
[scriptable, function, uuid(3bc96351-53b0-47a1-a888-c74c64b60f25)]
interface nsIRilSendWorkerMessageCallback : nsISupports
{
boolean handleResponse(in jsval response);
};
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -2720,16 +2720,23 @@ RilObject.prototype = {
* @param program
* One of CALL_BARRING_PROGRAM_* constants.
* @param serviceClass
* One of ICC_SERVICE_CLASS_* constants.
*/
queryCallBarringStatus: function(options) {
options.facility = CALL_BARRING_PROGRAM_TO_FACILITY[options.program];
options.password = ""; // For query no need to provide it.
+
+ // For some operators, querying specific serviceClass doesn't work. We use
+ // serviceClass 0 instead, and then process the response to extract the
+ // answer for queryServiceClass.
+ options.queryServiceClass = options.serviceClass;
+ options.serviceClass = 0;
+
this.queryICCFacilityLock(options);
},
/**
* Configures call barring rule.
*
* @param program
* One of CALL_BARRING_PROGRAM_* constants.
@@ -5925,30 +5932,33 @@ RilObject.prototype[REQUEST_DEACTIVATE_D
RilObject.prototype[REQUEST_QUERY_FACILITY_LOCK] = function REQUEST_QUERY_FACILITY_LOCK(length, options) {
options.success = (options.rilRequestError === 0);
if (!options.success) {
options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
this.sendChromeMessage(options);
return;
}
- let services;
- if (length) {
- // Buf.readInt32List()[0] for Call Barring is a bit vector of services.
- services = this.context.Buf.readInt32List()[0];
- } else {
+ if (!length) {
options.success = false;
options.errorMsg = GECKO_ERROR_GENERIC_FAILURE;
this.sendChromeMessage(options);
return;
}
- options.enabled = services === 0 ? false : true;
-
- if (options.success && (options.rilMessageType === "sendMMI")) {
+ // Buf.readInt32List()[0] for Call Barring is a bit vector of services.
+ let services = this.context.Buf.readInt32List()[0];
+
+ if (options.queryServiceClass) {
+ options.enabled = (services & options.queryServiceClass) ? true : false;
+ } else {
+ options.enabled = services ? true : false;
+ }
+
+ if (options.rilMessageType === "sendMMI") {
if (!options.enabled) {
options.statusMessage = MMI_SM_KS_SERVICE_DISABLED;
} else {
options.statusMessage = MMI_SM_KS_SERVICE_ENABLED_FOR;
let serviceClass = [];
for (let serviceClassMask = 1;
serviceClassMask <= ICC_SERVICE_CLASS_MAX;
serviceClassMask <<= 1) {
--- a/dom/tests/mochitest/general/mochitest.ini
+++ b/dom/tests/mochitest/general/mochitest.ini
@@ -1,10 +1,9 @@
[DEFAULT]
-skip-if = e10s
support-files =
497633.html
file_MozEnteredDomFullscreen.html
file_bug628069.html
file_clonewrapper.html
file_domWindowUtils_scrollbarSize.html
file_frameElementWrapping.html
file_interfaces.xml
@@ -30,62 +29,62 @@ support-files =
res6.resource^headers^
res7.resource
res7.resource^headers^
res8.resource
res8.resource^headers^
resource_timing.js
[test_497898.html]
-skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #Bug 931116, b2g desktop specific, initial triage
+skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || toolkit == 'android' #Bug 931116, b2g desktop specific, initial triage
[test_bug504220.html]
[test_bug628069_1.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
[test_bug628069_2.html]
[test_bug631440.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
[test_bug653364.html]
[test_bug861217.html]
[test_clientRects.html]
[test_clipboard_events.html]
-skip-if = buildapp == 'b2g' # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined)
+skip-if = e10s || buildapp == 'b2g' # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined)
[test_consoleAPI.html]
[test_DOMMatrix.html]
[test_domWindowUtils.html]
[test_domWindowUtils_scrollXY.html]
[test_domWindowUtils_scrollbarSize.html]
[test_donottrack.html]
skip-if = buildapp == 'mulet'
[test_focus_legend_noparent.html]
[test_focusrings.xul]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
+skip-if = e10s || buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
[test_for_of.html]
[test_frameElementWrapping.html]
[test_framedhistoryframes.html]
[test_idleapi_permissions.html]
-skip-if = buildapp == 'b2g' || buildapp == 'mulet'
+skip-if = e10s || buildapp == 'b2g' || buildapp == 'mulet'
[test_interfaces.html]
skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
# [test_network_events.html]
# Disable this test until bug 795711 is fixed.
[test_offsets.html]
[test_offsets.js]
[test_outerHTML.html]
[test_outerHTML.xhtml]
skip-if = buildapp == 'mulet'
[test_paste_selection.html]
skip-if = buildapp == 'mulet'
[test_picture_pref.html]
[test_resource_timing.html]
-skip-if = buildapp == 'b2g' || buildapp == 'mulet' # b2g(No clipboard) b2g-debug(No clipboard) b2g-desktop(No clipboard)
+skip-if = buildapp == 'b2g' || buildapp == 'mulet'
[test_resource_timing_cross_origin.html]
skip-if = buildapp == 'b2g' || buildapp == 'mulet'
[test_performance_now.html]
[test_srcset_pref.html]
[test_showModalDialog.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #Don't run modal tests on Android # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog)
+skip-if = e10s || buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #Don't run modal tests on Android # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog)
[test_stylesheetPI.html]
[test_vibrator.html]
skip-if = buildapp == 'mulet' || toolkit == 'android' #CRASH_SUTAGENT
[test_windowProperties.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
[test_windowedhistoryframes.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -696,26 +696,32 @@ var interfaceNamesInGlobalScope =
"mozContact",
// IMPORTANT: Do not change this list without review from a DOM peer!
"MozContactChangeEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
"MozCSSKeyframeRule",
// IMPORTANT: Do not change this list without review from a DOM peer!
"MozCSSKeyframesRule",
// IMPORTANT: Do not change this list without review from a DOM peer!
+ {name: "MozCdmaIccInfo", b2g: true, pref: "dom.icc.enabled"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozEmergencyCbModeEvent", b2g: true, pref: "dom.mobileconnection.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozIcc", b2g: true, pref: "dom.icc.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
+ {name: "MozIccInfo", b2g: true, pref: "dom.icc.enabled"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozIccManager", b2g: true, pref: "dom.icc.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozInputContext", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozInputMethodManager", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
+ {name: "MozGsmIccInfo", b2g: true, pref: "dom.icc.enabled"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozMessageDeletedEvent", b2g: true, pref: "dom.sms.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
"MozMmsEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
"MozMmsMessage",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "MozMobileCellInfo", b2g: true, pref: "dom.mobileconnection.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/tests/mochitest/general/test_resource_timing.html
+++ b/dom/tests/mochitest/general/test_resource_timing.html
@@ -15,21 +15,24 @@ https://bugzilla.mozilla.org/show_bug.cg
<body>
<pre id="test">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
// Resource timing is prefed off by default, so we had to use this workaround
-SpecialPowers.setBoolPref("dom.enable_resource_timing", true);
-var subwindow = window.open("resource_timing_main_test.html");
+SpecialPowers.pushPrefEnv({"set": [["dom.enable_resource_timing", true]]}, start);
+var subwindow = null;
+
+function start() {
+ subwindow = window.open("resource_timing_main_test.html");
+}
function finishTests() {
subwindow.close();
- SpecialPowers.setBoolPref("dom.enable_resource_timing", false);
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>
--- a/dom/webidl/MozIcc.webidl
+++ b/dom/webidl/MozIcc.webidl
@@ -1,27 +1,25 @@
/* 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/. */
-interface MozIccInfo;
-
[Pref="dom.icc.enabled"]
interface MozIcc : EventTarget
{
// Integrated Circuit Card Information.
/**
* Information stored in the device's ICC.
*
* Once the ICC becomes undetectable, iccinfochange event will be notified.
* Also, the attribute is set to null and this MozIcc object becomes invalid.
* Calling asynchronous functions raises exception then.
*/
- readonly attribute MozIccInfo? iccInfo;
+ readonly attribute (MozIccInfo or MozGsmIccInfo or MozCdmaIccInfo)? iccInfo;
/**
* The 'iccinfochange' event is notified whenever the icc info object
* changes.
*/
attribute EventHandler oniccinfochange;
// Integrated Circuit Card State.
new file mode 100644
--- /dev/null
+++ b/dom/webidl/MozIccInfo.webidl
@@ -0,0 +1,65 @@
+/* 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/. */
+
+enum IccType {"sim", "usim", "csim", "ruim"};
+
+[Pref="dom.icc.enabled"]
+interface MozIccInfo {
+ /**
+ * Integrated Circuit Card Type.
+ */
+ readonly attribute IccType? iccType;
+
+ /**
+ * Integrated Circuit Card Identifier.
+ */
+ readonly attribute DOMString? iccid;
+
+ /**
+ * Mobile Country Code (MCC) of the subscriber's home network.
+ */
+ readonly attribute DOMString? mcc;
+
+ /**
+ * Mobile Network Code (MNC) of the subscriber's home network.
+ */
+ readonly attribute DOMString? mnc;
+
+ /**
+ * Service Provider Name (SPN) of the subscriber's home network.
+ */
+ readonly attribute DOMString? spn;
+
+ /**
+ * Network name must be a part of displayed carrier name.
+ */
+ readonly attribute boolean isDisplayNetworkNameRequired;
+
+ /**
+ * Service provider name must be a part of displayed carrier name.
+ */
+ readonly attribute boolean isDisplaySpnRequired;
+};
+
+[Pref="dom.icc.enabled"]
+interface MozGsmIccInfo : MozIccInfo {
+ /**
+ * Mobile Station ISDN Number (MSISDN) of the subscriber, aka
+ * his phone number.
+ */
+ readonly attribute DOMString? msisdn;
+};
+
+[Pref="dom.icc.enabled"]
+interface MozCdmaIccInfo : MozIccInfo {
+ /**
+ * Mobile Directory Number (MDN) of the subscriber, aka his phone number.
+ */
+ readonly attribute DOMString? mdn;
+
+ /**
+ * Preferred Roaming List (PRL) version of the subscriber.
+ */
+ readonly attribute long prlVersion;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -611,16 +611,17 @@ if CONFIG['MOZ_B2G_BT']:
'BluetoothDevice.webidl',
'BluetoothManager.webidl',
]
if CONFIG['MOZ_B2G_RIL']:
WEBIDL_FILES += [
'IccCardLockError.webidl',
'MozIcc.webidl',
+ 'MozIccInfo.webidl',
'MozIccManager.webidl',
]
if CONFIG['MOZ_NFC']:
WEBIDL_FILES += [
'MozNDEFRecord.webidl',
'MozNFC.webidl',
'MozNFCPeer.webidl',
--- a/gfx/2d/Helpers.h
+++ b/gfx/2d/Helpers.h
@@ -9,16 +9,20 @@
#include "2D.h"
namespace mozilla {
namespace gfx {
class AutoRestoreTransform
{
public:
+ AutoRestoreTransform()
+ {
+ }
+
explicit AutoRestoreTransform(DrawTarget *aTarget)
: mDrawTarget(aTarget),
mOldTransform(aTarget->GetTransform())
{
}
void Init(DrawTarget *aTarget)
{
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -5,16 +5,20 @@
#ifndef MOZILLA_GFX_LOGGING_H_
#define MOZILLA_GFX_LOGGING_H_
#include <string>
#include <sstream>
#include <stdio.h>
+#ifdef MOZ_LOGGING
+#include <prlog.h>
+#endif
+
#if defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID)
#include "nsDebug.h"
#endif
#include "Point.h"
#include "BaseRect.h"
#include "Matrix.h"
#include "mozilla/TypedEnum.h"
@@ -24,30 +28,28 @@
// thing we need from windows.h, we just declare it here directly.
// Note: the function's documented signature is
// WINBASEAPI void WINAPI OutputDebugStringA(LPCSTR lpOutputString)
// but if we don't include windows.h, the macros WINBASEAPI, WINAPI, and
// LPCSTR are not defined, so we need to replace them with their expansions.
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* lpOutputString);
#endif
-#if defined(DEBUG) || defined(PR_LOGGING)
-#include <prlog.h>
-
+#if defined(PR_LOGGING)
extern GFX2D_API PRLogModuleInfo *GetGFX2DLog();
#endif
namespace mozilla {
namespace gfx {
const int LOG_DEBUG = 1;
const int LOG_WARNING = 2;
const int LOG_CRITICAL = 3;
-#if defined(DEBUG) || defined(PR_LOGGING)
+#if defined(PR_LOGGING)
inline PRLogModuleLevel PRLogLevelForLevel(int aLevel) {
switch (aLevel) {
case LOG_DEBUG:
return PR_LOG_DEBUG;
case LOG_WARNING:
return PR_LOG_WARNING;
}
--- a/gfx/angle/src/common/debug.h
+++ b/gfx/angle/src/common/debug.h
@@ -119,17 +119,17 @@ namespace gl
// A macro that determines whether an object has a given runtime type.
#if !defined(NDEBUG) && (!defined(_MSC_VER) || defined(_CPPRTTI)) && (!defined(__GNUC__) || __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) || defined(__GXX_RTTI))
#define HAS_DYNAMIC_TYPE(type, obj) (dynamic_cast<type >(obj) != NULL)
#else
#define HAS_DYNAMIC_TYPE(type, obj) true
#endif
// A macro functioning as a compile-time assert to validate constant conditions
-#if defined(_MSC_VER) && _MSC_VER >= 1600
+#if (defined(_MSC_VER) && _MSC_VER >= 1600) || (defined(__GNUC__) && (__GNUC__ > 4 || __GNUC_MINOR__ >= 3))
#define META_ASSERT_MSG(condition, msg) static_assert(condition, msg)
#else
#define META_ASSERT_CONCAT(a, b) a ## b
#define META_ASSERT_CONCAT2(a, b) META_ASSERT_CONCAT(a, b)
#define META_ASSERT_MSG(condition, msg) typedef int META_ASSERT_CONCAT2(COMPILE_TIME_ASSERT_, __LINE__)[static_cast<bool>(condition)?1:-1]
#endif
#define META_ASSERT(condition) META_ASSERT_MSG(condition, "compile time assertion failed.")
--- a/gfx/angle/src/libEGL/Display.cpp
+++ b/gfx/angle/src/libEGL/Display.cpp
@@ -9,16 +9,17 @@
// [EGL 1.4] section 2.1.2 page 3.
#include "libEGL/Display.h"
#include <algorithm>
#include <map>
#include <vector>
#include <sstream>
+#include <iterator>
#include "common/debug.h"
#include "common/mathutil.h"
#include "libGLESv2/main.h"
#include "libGLESv2/Context.h"
#include "libGLESv2/renderer/SwapChain.h"
#include "libEGL/main.h"
--- a/gfx/angle/src/libEGL/moz.build
+++ b/gfx/angle/src/libEGL/moz.build
@@ -49,14 +49,13 @@ DEFINES['GL_GLEXT_PROTOTYPES'] = ""
DEFINES['EGLAPI'] = ""
# ANGLE uses the STL, so we can't use our derpy STL wrappers.
DISABLE_STL_WRAPPING = True
LOCAL_INCLUDES += [ '../../include', '../../src' ]
USE_LIBS += [ 'libGLESv2' ]
-EXTRA_DSO_LDOPTS += [ '../libGLESv2/libGLESv2.lib' ]
SharedLibrary('libEGL')
RCFILE = SRCDIR + '/libEGL.rc'
DEFFILE = SRCDIR + '/libEGL.def'
--- a/gfx/angle/src/libGLESv2.gypi
+++ b/gfx/angle/src/libGLESv2.gypi
@@ -41,16 +41,17 @@
'common/utilities.cpp',
'common/utilities.h',
'common/version.h',
'libGLESv2/BinaryStream.h',
'libGLESv2/Buffer.cpp',
'libGLESv2/Buffer.h',
'libGLESv2/Caps.cpp',
'libGLESv2/Caps.h',
+ 'libGLESv2/Constants.h',
'libGLESv2/Context.cpp',
'libGLESv2/Context.h',
'libGLESv2/Error.cpp',
'libGLESv2/Error.h',
'libGLESv2/Fence.cpp',
'libGLESv2/Fence.h',
'libGLESv2/Float16ToFloat32.cpp',
'libGLESv2/Framebuffer.cpp',
@@ -82,17 +83,16 @@
'libGLESv2/Uniform.cpp',
'libGLESv2/Uniform.h',
'libGLESv2/VertexArray.cpp',
'libGLESv2/VertexArray.h',
'libGLESv2/VertexAttribute.cpp',
'libGLESv2/VertexAttribute.h',
'libGLESv2/angletypes.cpp',
'libGLESv2/angletypes.h',
- 'libGLESv2/constants.h',
'libGLESv2/formatutils.cpp',
'libGLESv2/formatutils.h',
'libGLESv2/libGLESv2.cpp',
'libGLESv2/libGLESv2.def',
'libGLESv2/libGLESv2.rc',
'libGLESv2/main.cpp',
'libGLESv2/main.h',
'libGLESv2/queryconversions.cpp',
rename from gfx/angle/src/libGLESv2/constants.h
rename to gfx/angle/src/libGLESv2/Constants.h
--- a/gfx/angle/src/libGLESv2/Context.cpp
+++ b/gfx/angle/src/libGLESv2/Context.cpp
@@ -28,16 +28,17 @@
#include "libGLESv2/VertexArray.h"
#include "libGLESv2/Sampler.h"
#include "libGLESv2/validationES.h"
#include "libGLESv2/TransformFeedback.h"
#include "libEGL/Surface.h"
#include <sstream>
+#include <iterator>
namespace gl
{
Context::Context(int clientVersion, const gl::Context *shareContext, rx::Renderer *renderer, bool notifyResets, bool robustAccess)
: mRenderer(renderer)
{
ASSERT(robustAccess == false); // Unimplemented
@@ -336,17 +337,17 @@ void Context::deleteRenderbuffer(GLuint
}
void Context::deleteFenceSync(GLsync fenceSync)
{
// The spec specifies the underlying Fence object is not deleted until all current
// wait commands finish. However, since the name becomes invalid, we cannot query the fence,
// and since our API is currently designed for being called from a single thread, we can delete
// the fence immediately.
- mResourceManager->deleteFenceSync(reinterpret_cast<GLuint>(fenceSync));
+ mResourceManager->deleteFenceSync(reinterpret_cast<uintptr_t>(fenceSync));
}
void Context::deleteVertexArray(GLuint vertexArray)
{
auto vertexArrayObject = mVertexArrayMap.find(vertexArray);
if (vertexArrayObject != mVertexArrayMap.end())
{
@@ -442,17 +443,17 @@ Texture *Context::getTexture(GLuint hand
Renderbuffer *Context::getRenderbuffer(GLuint handle)
{
return mResourceManager->getRenderbuffer(handle);
}
FenceSync *Context::getFenceSync(GLsync handle) const
{
- return mResourceManager->getFenceSync(reinterpret_cast<GLuint>(handle));
+ return mResourceManager->getFenceSync(reinterpret_cast<uintptr_t>(handle));
}
VertexArray *Context::getVertexArray(GLuint handle) const
{
auto vertexArray = mVertexArrayMap.find(handle);
if (vertexArray == mVertexArrayMap.end())
{
--- a/gfx/angle/src/libGLESv2/Framebuffer.h
+++ b/gfx/angle/src/libGLESv2/Framebuffer.h
@@ -7,17 +7,17 @@
// Framebuffer.h: Defines the gl::Framebuffer class. Implements GL framebuffer
// objects and related functionality. [OpenGL ES 2.0.24] section 4.4 page 105.
#ifndef LIBGLESV2_FRAMEBUFFER_H_
#define LIBGLESV2_FRAMEBUFFER_H_
#include "common/angleutils.h"
#include "common/RefCountObject.h"
-#include "constants.h"
+#include "Constants.h"
namespace rx
{
class Renderer;
}
namespace gl
{
--- a/gfx/angle/src/libGLESv2/Texture.h
+++ b/gfx/angle/src/libGLESv2/Texture.h
@@ -9,17 +9,17 @@
// related functionality. [OpenGL ES 2.0.24] section 3.7 page 63.
#ifndef LIBGLESV2_TEXTURE_H_
#define LIBGLESV2_TEXTURE_H_
#include "common/debug.h"
#include "common/RefCountObject.h"
#include "libGLESv2/angletypes.h"
-#include "libGLESv2/constants.h"
+#include "libGLESv2/Constants.h"
#include "libGLESv2/renderer/TextureImpl.h"
#include "libGLESv2/Caps.h"
#include "angle_gl.h"
#include <vector>
namespace egl
--- a/gfx/angle/src/libGLESv2/VertexArray.h
+++ b/gfx/angle/src/libGLESv2/VertexArray.h
@@ -9,17 +9,17 @@
// together to form a vertex array object. All state related to the definition of data used
// by the vertex processor is encapsulated in a vertex array object.
//
#ifndef LIBGLESV2_VERTEXARRAY_H_
#define LIBGLESV2_VERTEXARRAY_H_
#include "common/RefCountObject.h"
-#include "libGLESv2/constants.h"
+#include "libGLESv2/Constants.h"
#include "libGLESv2/VertexAttribute.h"
#include <vector>
namespace rx
{
class Renderer;
class VertexArrayImpl;
--- a/gfx/angle/src/libGLESv2/angletypes.h
+++ b/gfx/angle/src/libGLESv2/angletypes.h
@@ -4,17 +4,17 @@
// found in the LICENSE file.
//
// angletypes.h : Defines a variety of structures and enum types that are used throughout libGLESv2
#ifndef LIBGLESV2_ANGLETYPES_H_
#define LIBGLESV2_ANGLETYPES_H_
-#include "libGLESv2/constants.h"
+#include "libGLESv2/Constants.h"
#include "common/RefCountObject.h"
namespace gl
{
class Buffer;
class ProgramBinary;
struct VertexAttribute;
struct VertexAttribCurrentValueData;
--- a/gfx/angle/src/libGLESv2/moz.build
+++ b/gfx/angle/src/libGLESv2/moz.build
@@ -112,17 +112,16 @@ UNIFIED_SOURCES += [
'renderer/d3d/d3d9/renderer9_utils.cpp',
'renderer/d3d/d3d9/RenderTarget9.cpp',
'renderer/d3d/d3d9/ShaderExecutable9.cpp',
'renderer/d3d/d3d9/SwapChain9.cpp',
'renderer/d3d/d3d9/TextureStorage9.cpp',
'renderer/d3d/d3d9/VertexBuffer9.cpp',
'renderer/d3d/d3d9/VertexDeclarationCache.cpp',
'renderer/d3d/DynamicHLSL.cpp',
- 'renderer/d3d/HLSLCompiler.cpp',
'renderer/d3d/ImageD3D.cpp',
'renderer/d3d/IndexBuffer.cpp',
'renderer/d3d/IndexDataManager.cpp',
'renderer/d3d/MemoryBuffer.cpp',
'renderer/d3d/ShaderD3D.cpp',
'renderer/d3d/TextureD3D.cpp',
'renderer/d3d/TextureStorage.cpp',
'renderer/d3d/TransformFeedbackD3D.cpp',
@@ -143,16 +142,17 @@ UNIFIED_SOURCES += [
'validationES2.cpp',
'validationES3.cpp',
'VertexArray.cpp',
'VertexAttribute.cpp',
]
SOURCES += [
'../compiler/translator/glslang_lex.cpp',
'../compiler/translator/glslang_tab.cpp',
+ 'renderer/d3d/HLSLCompiler.cpp',
'renderer/loadimageSSE2.cpp',
]
SOURCES['renderer/loadimageSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
UNIFIED_SOURCES += [
'renderer/d3d/d3d11/Blit11.cpp',
'renderer/d3d/d3d11/Buffer11.cpp',
'renderer/d3d/d3d11/Clear11.cpp',
@@ -208,22 +208,22 @@ DEFINES['EGLAPI'] = ""
# ANGLE uses the STL, so we can't use our derpy STL wrappers.
DISABLE_STL_WRAPPING = True
LOCAL_INCLUDES += [ '../../include', '../../src' ]
if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
- EXTRA_DSO_LDOPTS += [ 'd3d9.lib', 'dxguid.lib' ]
+ OS_LIBS += [ 'd3d9', 'dxguid' ]
else:
EXTRA_DSO_LDOPTS += [
'\'%s/lib/%s/d3d9.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
'\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
]
SharedLibrary('libGLESv2')
RCFILE = SRCDIR + '/libGLESv2.rc'
DEFFILE = SRCDIR + '/libGLESv2.def'
-DEFINES['ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES'] = '{ TEXT("d3dcompiler_47.dll"), TEXT("d3dcompiler_46.dll"), TEXT("d3dcompiler_43.dll") }'
+SOURCES['renderer/d3d/HLSLCompiler.cpp'].flags += ['-DANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES=\'{ TEXT("d3dcompiler_47.dll"), TEXT("d3dcompiler_46.dll"), TEXT("d3dcompiler_43.dll") }\'']
--- a/gfx/angle/src/libGLESv2/renderer/copyimage.cpp
+++ b/gfx/angle/src/libGLESv2/renderer/copyimage.cpp
@@ -1,17 +1,17 @@
//
// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// copyimage.cpp: Defines image copying functions
-#include "libGLESv2/renderer/copyImage.h"
+#include "libGLESv2/renderer/copyimage.h"
namespace rx
{
void CopyBGRA8ToRGBA8(const uint8_t *source, uint8_t *dest)
{
uint32_t argb = *reinterpret_cast<const uint32_t*>(source);
*reinterpret_cast<uint32_t*>(dest) = (argb & 0xFF00FF00) | // Keep alpha and green
--- a/gfx/angle/src/libGLESv2/renderer/copyimage.inl
+++ b/gfx/angle/src/libGLESv2/renderer/copyimage.inl
@@ -19,14 +19,14 @@ template <typename destType, typename co
inline void WriteColor(const uint8_t *source, uint8_t *dest)
{
destType::writeColor(reinterpret_cast<destType*>(dest), reinterpret_cast<const gl::Color<colorDataType>*>(source));
}
template <typename sourceType, typename destType, typename colorDataType>
inline void CopyPixel(const uint8_t *source, uint8_t *dest)
{
- colorType temp;
+ colorDataType temp;
ReadColor<sourceType, colorDataType>(source, &temp);
WriteColor<destType, colorDataType>(&temp, dest);
}
}
--- a/gfx/angle/src/libGLESv2/renderer/d3d/DynamicHLSL.h
+++ b/gfx/angle/src/libGLESv2/renderer/d3d/DynamicHLSL.h
@@ -5,17 +5,17 @@
//
// DynamicHLSL.h: Interface for link and run-time HLSL generation
//
#ifndef LIBGLESV2_RENDERER_DYNAMIC_HLSL_H_
#define LIBGLESV2_RENDERER_DYNAMIC_HLSL_H_
#include "common/angleutils.h"
-#include "libGLESv2/constants.h"
+#include "libGLESv2/Constants.h"
#include "angle_gl.h"
#include <vector>
#include <map>
namespace rx
{
--- a/gfx/angle/src/libGLESv2/renderer/d3d/TextureD3D.cpp
+++ b/gfx/angle/src/libGLESv2/renderer/d3d/TextureD3D.cpp
@@ -124,17 +124,17 @@ bool TextureD3D::subImage(GLint xoffset,
GLenum format, GLenum type, const gl::PixelUnpackState &unpack, const void *pixels, Image *image)
{
const void *pixelData = pixels;
// CPU readback & copy where direct GPU copy is not supported
if (unpack.pixelBuffer.id() != 0)
{
gl::Buffer *pixelBuffer = unpack.pixelBuffer.get();
- unsigned int offset = reinterpret_cast<unsigned int>(pixels);
+ uintptr_t offset = reinterpret_cast<uintptr_t>(pixels);
// TODO: setImage/subImage is the only place outside of renderer that asks for a buffers raw data.
// This functionality should be moved into renderer and the getData method of BufferImpl removed.
const void *bufferData = pixelBuffer->getImplementation()->getData();
pixelData = static_cast<const unsigned char *>(bufferData) + offset;
}
if (pixelData != NULL)
{
@@ -178,17 +178,17 @@ bool TextureD3D::fastUnpackPixels(const
{
return true;
}
// In order to perform the fast copy through the shader, we must have the right format, and be able
// to create a render target.
ASSERT(mRenderer->supportsFastCopyBufferToTexture(sizedInternalFormat));
- unsigned int offset = reinterpret_cast<unsigned int>(pixels);
+ uintptr_t offset = reinterpret_cast<uintptr_t>(pixels);
return mRenderer->fastCopyBufferToTexture(unpack, offset, destRenderTarget, sizedInternalFormat, type, destArea);
}
GLint TextureD3D::creationLevels(GLsizei width, GLsizei height, GLsizei depth) const
{
if ((gl::isPow2(width) && gl::isPow2(height) && gl::isPow2(depth)) || mRenderer->getRendererExtensions().textureNPOT)
{
--- a/gfx/angle/src/libGLESv2/renderer/d3d/TextureD3D.h
+++ b/gfx/angle/src/libGLESv2/renderer/d3d/TextureD3D.h
@@ -6,17 +6,17 @@
// TextureD3D.h: Implementations of the Texture interfaces shared betweeen the D3D backends.
#ifndef LIBGLESV2_RENDERER_TEXTURED3D_H_
#define LIBGLESV2_RENDERER_TEXTURED3D_H_
#include "libGLESv2/renderer/TextureImpl.h"
#include "libGLESv2/angletypes.h"
-#include "libGLESv2/constants.h"
+#include "libGLESv2/Constants.h"
namespace gl
{
class Framebuffer;
}
namespace rx
{
--- a/gfx/angle/src/libGLESv2/validationES.cpp
+++ b/gfx/angle/src/libGLESv2/validationES.cpp
@@ -1641,17 +1641,17 @@ bool ValidateDrawElements(Context *conte
return false;
}
// Use max index to validate if our vertex buffers are large enough for the pull.
// TODO: offer fast path, with disabled index validation.
// TODO: also disable index checking on back-ends that are robust to out-of-range accesses.
if (elementArrayBuffer)
{
- unsigned int offset = reinterpret_cast<unsigned int>(indices);
+ uintptr_t offset = reinterpret_cast<uintptr_t>(indices);
if (!elementArrayBuffer->getIndexRangeCache()->findRange(type, offset, count, indexRangeOut, NULL))
{
const void *dataPointer = elementArrayBuffer->getImplementation()->getData();
const uint8_t *offsetPointer = static_cast<const uint8_t *>(dataPointer) + offset;
*indexRangeOut = rx::IndexRangeCache::ComputeRange(type, offsetPointer, count);
}
}
else
--- a/gfx/angle/src/third_party/trace_event/trace_event.h
+++ b/gfx/angle/src/third_party/trace_event/trace_event.h
@@ -582,17 +582,17 @@ const int zeroNumArgs = 0;
const unsigned long long noEventId = 0;
// TraceID encapsulates an ID that can either be an integer or pointer. Pointers
// are mangled with the Process ID so that they are unlikely to collide when the
// same pointer is used on different processes.
class TraceID {
public:
explicit TraceID(const void* id, unsigned char* flags) :
- m_data(static_cast<unsigned long long>(reinterpret_cast<unsigned long>(id)))
+ m_data(static_cast<unsigned long long>(reinterpret_cast<uintptr_t>(id)))
{
*flags |= TRACE_EVENT_FLAG_MANGLE_ID;
}
explicit TraceID(unsigned long long id, unsigned char* flags) : m_data(id) { (void)flags; }
explicit TraceID(unsigned long id, unsigned char* flags) : m_data(id) { (void)flags; }
explicit TraceID(unsigned int id, unsigned char* flags) : m_data(id) { (void)flags; }
explicit TraceID(unsigned short id, unsigned char* flags) : m_data(id) { (void)flags; }
explicit TraceID(unsigned char id, unsigned char* flags) : m_data(id) { (void)flags; }
@@ -783,44 +783,13 @@ private:
struct Data {
const unsigned char* categoryEnabled;
const char* name;
};
Data* m_pdata;
Data m_data;
};
-// TraceEventSamplingStateScope records the current sampling state
-// and sets a new sampling state. When the scope exists, it restores
-// the sampling state having recorded.
-template<size_t BucketNumber>
-class SamplingStateScope {
-public:
- SamplingStateScope(const char* categoryAndName)
- {
- m_previousState = SamplingStateScope<BucketNumber>::current();
- SamplingStateScope<BucketNumber>::set(categoryAndName);
- }
-
- ~SamplingStateScope()
- {
- SamplingStateScope<BucketNumber>::set(m_previousState);
- }
-
- // FIXME: Make load/store to traceSamplingState[] thread-safe and atomic.
- static inline const char* current()
- {
- return reinterpret_cast<const char*>(*gl::traceSamplingState[BucketNumber]);
- }
- static inline void set(const char* categoryAndName)
- {
- *gl::traceSamplingState[BucketNumber] = reinterpret_cast<long>(const_cast<char*>(categoryAndName));
- }
-
-private:
- const char* m_previousState;
-};
-
} // namespace TraceEvent
} // namespace gl
#endif
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -723,16 +723,22 @@ RotatedContentBuffer::BorrowDrawTargetFo
}
if (result->GetBackendType() == BackendType::DIRECT2D ||
result->GetBackendType() == BackendType::DIRECT2D1_1) {
drawPtr->SimplifyOutwardByArea(100 * 100);
}
if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
MOZ_ASSERT(mDTBuffer && mDTBufferOnWhite);
+ if (!mDTBuffer || !mDTBufferOnWhite) {
+ // This can happen in release builds if allocating one of the two buffers
+ // failed. This is pretty bad and the reason for the failure is already
+ // reported through gfxCriticalError.
+ return nullptr;
+ }
nsIntRegionRectIterator iter(*drawPtr);
const nsIntRect *iterRect;
while ((iterRect = iter.Next())) {
mDTBuffer->FillRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height),
ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
mDTBufferOnWhite->FillRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height),
ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
}
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -620,17 +620,17 @@ nsEventStatus
APZCTreeManager::ProcessTouchInput(MultiTouchInput& aInput,
ScrollableLayerGuid* aOutTargetGuid)
{
if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
// If we are in an overscrolled state and a second finger goes down,
// ignore that second touch point completely. The touch-start for it is
// dropped completely; subsequent touch events until the touch-end for it
// will have this touch point filtered out.
- if (mApzcForInputBlock && mApzcForInputBlock->IsOverscrolled()) {
+ if (mApzcForInputBlock && BuildOverscrollHandoffChain(mApzcForInputBlock)->HasOverscrolledApzc()) {
if (mRetainedTouchIdentifier == -1) {
mRetainedTouchIdentifier = mApzcForInputBlock->GetLastTouchIdentifier();
}
return nsEventStatus_eConsumeNoDefault;
}
// NS_TOUCH_START event contains all active touches of the current
// session thus resetting mTouchCount.
--- a/gfx/layers/apz/src/OverscrollHandoffState.cpp
+++ b/gfx/layers/apz/src/OverscrollHandoffState.cpp
@@ -64,16 +64,28 @@ void
OverscrollHandoffChain::ForEachApzc(APZCMethod aMethod) const
{
MOZ_ASSERT(Length() > 0);
for (uint32_t i = 0; i < Length(); ++i) {
(mChain[i]->*aMethod)();
}
}
+bool
+OverscrollHandoffChain::AnyApzc(APZCPredicate aPredicate) const
+{
+ MOZ_ASSERT(Length() > 0);
+ for (uint32_t i = 0; i < Length(); ++i) {
+ if ((mChain[i]->*aPredicate)()) {
+ return true;
+ }
+ }
+ return false;
+}
+
void
OverscrollHandoffChain::FlushRepaints() const
{
ForEachApzc(&AsyncPanZoomController::FlushRepaintForOverscrollHandoff);
}
void
OverscrollHandoffChain::CancelAnimations() const
@@ -124,10 +136,17 @@ OverscrollHandoffChain::CanBePanned(cons
if (mChain[j]->IsPannable()) {
return true;
}
}
return false;
}
+bool
+OverscrollHandoffChain::HasOverscrolledApzc() const
+{
+ return AnyApzc(&AsyncPanZoomController::IsOverscrolled);
+}
+
+
} // namespace layers
} // namespace mozilla
--- a/gfx/layers/apz/src/OverscrollHandoffState.h
+++ b/gfx/layers/apz/src/OverscrollHandoffState.h
@@ -100,21 +100,26 @@ public:
// Snap back the APZC that is overscrolled on the subset of the chain from
// |aStart| onwards, if any.
void SnapBackOverscrolledApzc(const AsyncPanZoomController* aStart) const;
// Determine whether the given APZC, or any APZC further in the chain,
// has room to be panned.
bool CanBePanned(const AsyncPanZoomController* aApzc) const;
+
+ // Determine whether any APZC along this handoff chain is overscrolled.
+ bool HasOverscrolledApzc() const;
private:
std::vector<nsRefPtr<AsyncPanZoomController>> mChain;
typedef void (AsyncPanZoomController::*APZCMethod)();
+ typedef bool (AsyncPanZoomController::*APZCPredicate)() const;
void ForEachApzc(APZCMethod aMethod) const;
+ bool AnyApzc(APZCPredicate aPredicate) const;
};
/**
* This class groups the state maintained during overscroll handoff.
*/
struct OverscrollHandoffState {
OverscrollHandoffState(const OverscrollHandoffChain& aChain,
const ScreenPoint& aPanDistance)
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -548,17 +548,20 @@ CompositorD3D11::ClearRect(const gfx::Re
mContext->VSSetShader(mAttachments->mVSQuadShader[MaskType::MaskNone], nullptr, 0);
mContext->PSSetShader(mAttachments->mSolidColorShader[MaskType::MaskNone], nullptr, 0);
mPSConstants.layerColor[0] = 0;
mPSConstants.layerColor[1] = 0;
mPSConstants.layerColor[2] = 0;
mPSConstants.layerColor[3] = 0;
- UpdateConstantBuffers();
+ if (!UpdateConstantBuffers()) {
+ NS_WARNING("Failed to update shader constant buffers");
+ return;
+ }
mContext->Draw(4, 0);
mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF);
}
void
CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
@@ -734,17 +737,20 @@ CompositorD3D11::DrawQuad(const gfx::Rec
mContext->OMSetBlendState(mAttachments->mComponentBlendState, sBlendFactor, 0xFFFFFFFF);
restoreBlendMode = true;
}
break;
default:
NS_WARNING("Unknown shader type");
return;
}
- UpdateConstantBuffers();
+ if (!UpdateConstantBuffers()) {
+ NS_WARNING("Failed to update shader constant buffers");
+ return;
+ }
mContext->Draw(4, 0);
if (restoreBlendMode) {
mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF);
}
}
void
@@ -961,33 +967,72 @@ CompositorD3D11::CreateShaders()
byRef(mAttachments->mRGBAShader[MaskType::Mask3d]));
if (FAILED(hr)) {
return false;
}
return true;
}
-void
+static
+bool ShouldRecoverFromMapFailure(HRESULT hr, ID3D11Device* device)
+{
+ // XXX - it would be nice to use gfxCriticalError, but it needs to
+ // be made to work off the main thread first.
+ if (SUCCEEDED(hr)) {
+ return true;
+ }
+ if (hr == DXGI_ERROR_DEVICE_REMOVED) {
+ switch (device->GetDeviceRemovedReason()) {
+ case DXGI_ERROR_DEVICE_HUNG:
+ case DXGI_ERROR_DEVICE_REMOVED:
+ case DXGI_ERROR_DEVICE_RESET:
+ case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
+ return true;
+ case DXGI_ERROR_INVALID_CALL:
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+bool
CompositorD3D11::UpdateConstantBuffers()
{
+ HRESULT hr;
D3D11_MAPPED_SUBRESOURCE resource;
- mContext->Map(mAttachments->mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
+
+ hr = mContext->Map(mAttachments->mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
+ if (FAILED(hr)) {
+ if (ShouldRecoverFromMapFailure(hr, GetDevice())) {
+ return false;
+ }
+ MOZ_CRASH();
+ }
*(VertexShaderConstants*)resource.pData = mVSConstants;
mContext->Unmap(mAttachments->mVSConstantBuffer, 0);
- mContext->Map(mAttachments->mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
+
+ hr = mContext->Map(mAttachments->mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
+ if (FAILED(hr)) {
+ if (ShouldRecoverFromMapFailure(hr, GetDevice())) {
+ return false;
+ }
+ MOZ_CRASH();
+ }
*(PixelShaderConstants*)resource.pData = mPSConstants;
mContext->Unmap(mAttachments->mPSConstantBuffer, 0);
ID3D11Buffer *buffer = mAttachments->mVSConstantBuffer;
mContext->VSSetConstantBuffers(0, 1, &buffer);
buffer = mAttachments->mPSConstantBuffer;
mContext->PSSetConstantBuffers(0, 1, &buffer);
+ return true;
}
void
CompositorD3D11::SetSamplerForFilter(Filter aFilter)
{
ID3D11SamplerState *sampler;
switch (aFilter) {
default:
--- a/gfx/layers/d3d11/CompositorD3D11.h
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -143,17 +143,17 @@ public:
ID3D11DeviceContext* GetDC() { return mContext; }
private:
// ensure mSize is up to date with respect to mWidget
void EnsureSize();
void VerifyBufferSize();
void UpdateRenderTarget();
bool CreateShaders();
- void UpdateConstantBuffers();
+ bool UpdateConstantBuffers();
void SetSamplerForFilter(gfx::Filter aFilter);
void SetPSForEffect(Effect *aEffect, MaskType aMaskType, gfx::SurfaceFormat aFormat);
void PaintToTarget();
virtual gfx::IntSize GetWidgetSize() const MOZ_OVERRIDE { return gfx::ToIntSize(mSize); }
RefPtr<ID3D11DeviceContext> mContext;
RefPtr<ID3D11Device> mDevice;
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -1607,16 +1607,65 @@ protected:
SetScrollableFrameMetrics(layers[0], FrameMetrics::START_SCROLL_ID);
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1);
SetScrollableFrameMetrics(layers[5], FrameMetrics::START_SCROLL_ID + 1);
SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2);
SetScrollableFrameMetrics(layers[6], FrameMetrics::START_SCROLL_ID + 3);
}
};
+// A version of ApzcPan() that routes the pan through the tree manager,
+// so that the tree manager has the appropriate state for testing.
+static void
+ApzctmPan(APZCTreeManager* aTreeManager,
+ int& aTime,
+ int aTouchStartY,
+ int aTouchEndY,
+ bool aKeepFingerDown = false)
+{
+ // TODO: Reuse some code between this and ApzcPan().
+
+ // Reduce the touch start tolerance to a tiny value.
+ // We can't do what ApzcPan() does to overcome the tolerance (send the
+ // touch-start at (aTouchStartY + some_large_value)) because the tree manager
+ // does hit testing based on the touch-start coordinates, and a different
+ // APZC than the one we intend might be hit.
+ SCOPED_GFX_PREF(APZTouchStartTolerance, float, 1.0f / 1000.0f);
+ const int OVERCOME_TOUCH_TOLERANCE = 1;
+
+ const int TIME_BETWEEN_TOUCH_EVENT = 100;
+
+ // Make sure the move is large enough to not be handled as a tap
+ MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, TimeStamp(), 0);
+ mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY), ScreenSize(0, 0), 0, 0));
+ aTreeManager->ReceiveInputEvent(mti, nullptr);
+
+ aTime += TIME_BETWEEN_TOUCH_EVENT;
+
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, TimeStamp(), 0);
+ mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY + OVERCOME_TOUCH_TOLERANCE), ScreenSize(0, 0), 0, 0));
+ aTreeManager->ReceiveInputEvent(mti, nullptr);
+
+ aTime += TIME_BETWEEN_TOUCH_EVENT;
+
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, TimeStamp(), 0);
+ mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
+ aTreeManager->ReceiveInputEvent(mti, nullptr);
+
+ aTime += TIME_BETWEEN_TOUCH_EVENT;
+
+ if (!aKeepFingerDown) {
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, TimeStamp(), 0);
+ mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
+ aTreeManager->ReceiveInputEvent(mti, nullptr);
+ }
+
+ aTime += TIME_BETWEEN_TOUCH_EVENT;
+}
+
class APZHitTestingTester : public APZCTreeManagerTester {
protected:
Matrix4x4 transformToApzc;
Matrix4x4 transformToGecko;
already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint) {
nsRefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(aPoint, nullptr);
if (hit) {
@@ -2184,16 +2233,53 @@ TEST_F(APZOverscrollHandoffTester, Layer
// Make sure things have scrolled according to the handoff chain in
// place at the time the touch-start of the second pan was queued.
EXPECT_EQ(0, childApzc->GetFrameMetrics().GetScrollOffset().y);
EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
EXPECT_EQ(-10, middleApzc->GetFrameMetrics().GetScrollOffset().y);
}
+// Test that putting a second finger down on an APZC while a down-chain APZC
+// is overscrolled doesn't result in being stuck in overscroll.
+TEST_F(APZOverscrollHandoffTester, StuckInOverscroll_Bug1073250) {
+ // Enable overscrolling.
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ CreateOverscrollHandoffLayerTree1();
+
+ TestAsyncPanZoomController* child = ApzcOf(layers[1]);
+
+ // Pan, causing the parent APZC to overscroll.
+ int time = 0;
+ ApzctmPan(manager, time, 10, 40, true /* keep finger down */);
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_TRUE(rootApzc->IsOverscrolled());
+
+ // Put a second finger down.
+ MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ // Use the same touch identifier for the first touch (0) as ApzctmPan(). (A bit hacky.)
+ secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0));
+ secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0));
+ manager->ReceiveInputEvent(secondFingerDown, nullptr);
+
+ // Release the fingers.
+ MultiTouchInput fingersUp = secondFingerDown;
+ fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
+ manager->ReceiveInputEvent(fingersUp, nullptr);
+
+ // Allow any animations to run their course.
+ child->AdvanceAnimationsUntilEnd(testStartTime);
+ rootApzc->AdvanceAnimationsUntilEnd(testStartTime);
+
+ // Make sure nothing is overscrolled.
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_FALSE(rootApzc->IsOverscrolled());
+}
+
// Here we test that if two flings are happening simultaneously, overscroll
// is handed off correctly for each.
TEST_F(APZOverscrollHandoffTester, SimultaneousFlings) {
// Set up an initial APZC tree.
CreateOverscrollHandoffLayerTree3();
TestAsyncPanZoomController* parent1 = ApzcOf(layers[1]);
TestAsyncPanZoomController* child1 = ApzcOf(layers[2]);
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -1,31 +1,26 @@
/* -*- 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 "ImageLogging.h"
-#include "nsPNGDecoder.h"
-
+#include "ImageLogging.h" // Must appear first
+#include "gfxColor.h"
+#include "gfxPlatform.h"
+#include "nsColor.h"
+#include "nsIInputStream.h"
#include "nsMemory.h"
+#include "nsPNGDecoder.h"
#include "nsRect.h"
-
-#include "nsIInputStream.h"
-
+#include "nspr.h"
+#include "png.h"
#include "RasterImage.h"
-#include "gfxColor.h"
-#include "nsColor.h"
-
-#include "nspr.h"
-#include "png.h"
-
-#include "gfxPlatform.h"
#include <algorithm>
namespace mozilla {
namespace image {
#ifdef PR_LOGGING
static PRLogModuleInfo *
GetPNGLog()
@@ -41,17 +36,17 @@ GetPNGDecoderAccountingLog()
{
static PRLogModuleInfo *sPNGDecoderAccountingLog;
if (!sPNGDecoderAccountingLog)
sPNGDecoderAccountingLog = PR_NewLogModule("PNGDecoderAccounting");
return sPNGDecoderAccountingLog;
}
#endif
-/* limit image dimensions (bug #251381, #591822, and #967656) */
+// Limit image dimensions (bug #251381, #591822, and #967656)
#ifndef MOZ_PNG_MAX_DIMENSION
# define MOZ_PNG_MAX_DIMENSION 32767
#endif
// For size decodes
#define WIDTH_OFFSET 16
#define HEIGHT_OFFSET (WIDTH_OFFSET + 4)
#define BYTES_NEEDED_FOR_DIMENSIONS (HEIGHT_OFFSET + 4)
@@ -64,33 +59,34 @@ nsPNGDecoder::AnimFrameInfo::AnimFrameIn
#ifdef PNG_APNG_SUPPORTED
nsPNGDecoder::AnimFrameInfo::AnimFrameInfo(png_structp aPNG, png_infop aInfo)
: mDispose(FrameBlender::kDisposeKeep)
, mBlend(FrameBlender::kBlendOver)
, mTimeout(0)
{
png_uint_16 delay_num, delay_den;
- /* delay, in seconds is delay_num/delay_den */
+ // delay, in seconds is delay_num/delay_den
png_byte dispose_op;
png_byte blend_op;
delay_num = png_get_next_frame_delay_num(aPNG, aInfo);
delay_den = png_get_next_frame_delay_den(aPNG, aInfo);
dispose_op = png_get_next_frame_dispose_op(aPNG, aInfo);
blend_op = png_get_next_frame_blend_op(aPNG, aInfo);
if (delay_num == 0) {
mTimeout = 0; // SetFrameTimeout() will set to a minimum
} else {
if (delay_den == 0)
delay_den = 100; // so says the APNG spec
// Need to cast delay_num to float to have a proper division and
// the result to int to avoid compiler warning
- mTimeout = static_cast<int32_t>(static_cast<double>(delay_num) * 1000 / delay_den);
+ mTimeout = static_cast<int32_t>(static_cast<double>(delay_num) *
+ 1000 / delay_den);
}
if (dispose_op == PNG_DISPOSE_OP_PREVIOUS) {
mDispose = FrameBlender::kDisposeRestorePrevious;
} else if (dispose_op == PNG_DISPOSE_OP_BACKGROUND) {
mDispose = FrameBlender::kDisposeClear;
} else {
mDispose = FrameBlender::kDisposeKeep;
@@ -126,17 +122,17 @@ nsPNGDecoder::~nsPNGDecoder()
png_destroy_read_struct(&mPNG, mInfo ? &mInfo : nullptr, nullptr);
if (mCMSLine)
nsMemory::Free(mCMSLine);
if (interlacebuf)
nsMemory::Free(interlacebuf);
if (mInProfile) {
qcms_profile_release(mInProfile);
- /* mTransform belongs to us only if mInProfile is non-null */
+ // mTransform belongs to us only if mInProfile is non-null
if (mTransform)
qcms_transform_release(mTransform);
}
}
// CreateFrame() is used for both simple and animated images
void nsPNGDecoder::CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
int32_t width, int32_t height,
@@ -190,55 +186,57 @@ void nsPNGDecoder::EndImageFrame()
uint32_t numFrames = GetFrameCount();
// We can't use mPNG->num_frames_read as it may be one ahead.
if (numFrames > 1) {
PostInvalidation(mFrameRect);
}
#endif
- PostFrameStop(alpha, mAnimInfo.mDispose, mAnimInfo.mTimeout, mAnimInfo.mBlend);
+ PostFrameStop(alpha, mAnimInfo.mDispose, mAnimInfo.mTimeout,
+ mAnimInfo.mBlend);
}
void
nsPNGDecoder::InitInternal()
{
// For size decodes, we don't need to initialize the png decoder
if (IsSizeDecode()) {
return;
}
mCMSMode = gfxPlatform::GetCMSMode();
if ((mDecodeFlags & DECODER_NO_COLORSPACE_CONVERSION) != 0)
mCMSMode = eCMSMode_Off;
- mDisablePremultipliedAlpha = (mDecodeFlags & DECODER_NO_PREMULTIPLY_ALPHA) != 0;
+ mDisablePremultipliedAlpha = (mDecodeFlags & DECODER_NO_PREMULTIPLY_ALPHA)
+ != 0;
#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
static png_byte color_chunks[]=
- { 99, 72, 82, 77, '\0', /* cHRM */
- 105, 67, 67, 80, '\0'}; /* iCCP */
+ { 99, 72, 82, 77, '\0', // cHRM
+ 105, 67, 67, 80, '\0'}; // iCCP
static png_byte unused_chunks[]=
- { 98, 75, 71, 68, '\0', /* bKGD */
- 104, 73, 83, 84, '\0', /* hIST */
- 105, 84, 88, 116, '\0', /* iTXt */
- 111, 70, 70, 115, '\0', /* oFFs */
- 112, 67, 65, 76, '\0', /* pCAL */
- 115, 67, 65, 76, '\0', /* sCAL */
- 112, 72, 89, 115, '\0', /* pHYs */
- 115, 66, 73, 84, '\0', /* sBIT */
- 115, 80, 76, 84, '\0', /* sPLT */
- 116, 69, 88, 116, '\0', /* tEXt */
- 116, 73, 77, 69, '\0', /* tIME */
- 122, 84, 88, 116, '\0'}; /* zTXt */
+ { 98, 75, 71, 68, '\0', // bKGD
+ 104, 73, 83, 84, '\0', // hIST
+ 105, 84, 88, 116, '\0', // iTXt
+ 111, 70, 70, 115, '\0', // oFFs
+ 112, 67, 65, 76, '\0', // pCAL
+ 115, 67, 65, 76, '\0', // sCAL
+ 112, 72, 89, 115, '\0', // pHYs
+ 115, 66, 73, 84, '\0', // sBIT
+ 115, 80, 76, 84, '\0', // sPLT
+ 116, 69, 88, 116, '\0', // tEXt
+ 116, 73, 77, 69, '\0', // tIME
+ 122, 84, 88, 116, '\0'}; // zTXt
#endif
- /* For full decodes, do png init stuff */
+ // For full decodes, do png init stuff
- /* Initialize the container's source image header. */
- /* Always decode to 24 bit pixdepth */
+ // Initialize the container's source image header
+ // Always decode to 24 bit pixdepth
mPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING,
nullptr, nsPNGDecoder::error_callback,
nsPNGDecoder::warning_callback);
if (!mPNG) {
PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
return;
}
@@ -246,57 +244,57 @@ nsPNGDecoder::InitInternal()
mInfo = png_create_info_struct(mPNG);
if (!mInfo) {
PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
png_destroy_read_struct(&mPNG, nullptr, nullptr);
return;
}
#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
- /* Ignore unused chunks */
+ // Ignore unused chunks
if (mCMSMode == eCMSMode_Off)
png_set_keep_unknown_chunks(mPNG, 1, color_chunks, 2);
png_set_keep_unknown_chunks(mPNG, 1, unused_chunks,
(int)sizeof(unused_chunks)/5);
#endif
#ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED
if (mCMSMode != eCMSMode_Off)
png_set_chunk_malloc_max(mPNG, 4000000L);
#endif
#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
#ifndef PR_LOGGING
- /* Disallow palette-index checking, for speed; we would ignore the warning
- * anyhow unless we have defined PR_LOGGING. This feature was added at
- * libpng version 1.5.10 and is disabled in the embedded libpng but enabled
- * by default in the system libpng. This call also disables it in the
- * system libpng, for decoding speed. Bug #745202.
- */
+ // Disallow palette-index checking, for speed; we would ignore the warning
+ // anyhow unless we have defined PR_LOGGING. This feature was added at
+ // libpng version 1.5.10 and is disabled in the embedded libpng but enabled
+ // by default in the system libpng. This call also disables it in the
+ // system libpng, for decoding speed. Bug #745202.
png_set_check_for_invalid_index(mPNG, 0);
#endif
#endif
#if defined(PNG_SET_OPTION_SUPPORTED) && defined(PNG_sRGB_PROFILE_CHECKS) && \
PNG_sRGB_PROFILE_CHECKS >= 0
- /* Skip checking of sRGB ICC profiles */
+ // Skip checking of sRGB ICC profiles
png_set_option(mPNG, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
#endif
- /* use this as libpng "progressive pointer" (retrieve in callbacks) */
+ // use this as libpng "progressive pointer" (retrieve in callbacks)
png_set_progressive_read_fn(mPNG, static_cast<png_voidp>(this),
nsPNGDecoder::info_callback,
nsPNGDecoder::row_callback,
nsPNGDecoder::end_callback);
}
void
-nsPNGDecoder::WriteInternal(const char *aBuffer, uint32_t aCount, DecodeStrategy)
+nsPNGDecoder::WriteInternal(const char *aBuffer, uint32_t aCount,
+ DecodeStrategy)
{
NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");
// If we only want width/height, we don't need to go through libpng
if (IsSizeDecode()) {
// Are we done?
if (mHeaderBytesRead == BYTES_NEEDED_FOR_DIMENSIONS)
@@ -388,34 +386,24 @@ PNGGetColorProfile(png_structp png_ptr,
int color_type, qcms_data_type *inType, uint32_t *intent)
{
qcms_profile *profile = nullptr;
*intent = QCMS_INTENT_PERCEPTUAL; // Our default
// First try to see if iCCP chunk is present
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) {
png_uint_32 profileLen;
-#if (PNG_LIBPNG_VER < 10500)
- char *profileData, *profileName;
-#else
png_bytep profileData;
png_charp profileName;
-#endif
int compression;
png_get_iCCP(png_ptr, info_ptr, &profileName, &compression,
&profileData, &profileLen);
- profile = qcms_profile_from_memory(
-#if (PNG_LIBPNG_VER < 10500)
- profileData,
-#else
- (char *)profileData,
-#endif
- profileLen);
+ profile = qcms_profile_from_memory((char *)profileData, profileLen);
if (profile) {
uint32_t profileSpace = qcms_profile_get_color_space(profile);
bool mismatch = false;
if (color_type & PNG_COLOR_MASK_COLOR) {
if (profileSpace != icSigRgbData)
mismatch = true;
} else {
@@ -493,81 +481,81 @@ PNGGetColorProfile(png_structp png_ptr,
}
return profile;
}
void
nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr)
{
-/* int number_passes; NOT USED */
+// int number_passes; NOT USED
png_uint_32 width, height;
int bit_depth, color_type, interlace_type, compression_type, filter_type;
unsigned int channels;
png_bytep trans = nullptr;
int num_trans = 0;
nsPNGDecoder *decoder =
static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));
- /* always decode to 24-bit RGB or 32-bit RGBA */
+ // Always decode to 24-bit RGB or 32-bit RGBA
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
&interlace_type, &compression_type, &filter_type);
- /* Are we too big? */
+ // Are we too big?
if (width > MOZ_PNG_MAX_DIMENSION || height > MOZ_PNG_MAX_DIMENSION)
- longjmp(png_jmpbuf(decoder->mPNG), 1);
+ png_longjmp(decoder->mPNG, 1);
// Post our size to the superclass
decoder->PostSize(width, height);
if (decoder->HasError()) {
// Setting the size led to an error.
- longjmp(png_jmpbuf(decoder->mPNG), 1);
+ png_longjmp(decoder->mPNG, 1);
}
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_expand(png_ptr);
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand(png_ptr);
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
int sample_max = (1 << bit_depth);
png_color_16p trans_values;
png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values);
- /* libpng doesn't reject a tRNS chunk with out-of-range samples
- so we check it here to avoid setting up a useless opacity
- channel or producing unexpected transparent pixels when using
- libpng-1.2.19 through 1.2.26 (bug #428045) */
+ // libpng doesn't reject a tRNS chunk with out-of-range samples
+ // so we check it here to avoid setting up a useless opacity
+ // channel or producing unexpected transparent pixels when using
+ // libpng-1.2.19 through 1.2.26 (bug #428045)
if ((color_type == PNG_COLOR_TYPE_GRAY &&
(int)trans_values->gray > sample_max) ||
(color_type == PNG_COLOR_TYPE_RGB &&
((int)trans_values->red > sample_max ||
(int)trans_values->green > sample_max ||
(int)trans_values->blue > sample_max)))
{
- /* clear the tRNS valid flag and release tRNS memory */
+ // clear the tRNS valid flag and release tRNS memory
png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0);
}
else
png_set_expand(png_ptr);
}
if (bit_depth == 16)
png_set_scale_16(png_ptr);
qcms_data_type inType = QCMS_DATA_RGBA_8;
uint32_t intent = -1;
uint32_t pIntent;
if (decoder->mCMSMode != eCMSMode_Off) {
intent = gfxPlatform::GetRenderingIntent();
decoder->mInProfile = PNGGetColorProfile(png_ptr, info_ptr,
color_type, &inType, &pIntent);
- /* If we're not mandating an intent, use the one from the image. */
+ // If we're not mandating an intent, use the one from the image.
if (intent == uint32_t(-1))
intent = pIntent;
}
if (decoder->mInProfile && gfxPlatform::GetCMSOutputProfile()) {
qcms_data_type outType;
if (color_type & PNG_COLOR_MASK_ALPHA || num_trans)
outType = QCMS_DATA_RGBA_8;
@@ -589,52 +577,30 @@ nsPNGDecoder::info_callback(png_structp
if (decoder->mCMSMode == eCMSMode_All) {
if (color_type & PNG_COLOR_MASK_ALPHA || num_trans)
decoder->mTransform = gfxPlatform::GetCMSRGBATransform();
else
decoder->mTransform = gfxPlatform::GetCMSRGBTransform();
}
}
- /* let libpng expand interlaced images */
+ // let libpng expand interlaced images
if (interlace_type == PNG_INTERLACE_ADAM7) {
- /* number_passes = */
+ // number_passes =
png_set_interlace_handling(png_ptr);
}
- /* now all of those things we set above are used to update various struct
- * members and whatnot, after which we can get channels, rowbytes, etc. */
+ // now all of those things we set above are used to update various struct
+ // members and whatnot, after which we can get channels, rowbytes, etc.
png_read_update_info(png_ptr, info_ptr);
decoder->mChannels = channels = png_get_channels(png_ptr, info_ptr);
- /*---------------------------------------------------------------*/
- /* copy PNG info into imagelib structs (formerly png_set_dims()) */
- /*---------------------------------------------------------------*/
-
- // This code is currently unused, but it will be needed for bug 517713.
-#if 0
- int32_t alpha_bits = 1;
-
- if (channels == 2 || channels == 4) {
- /* check if alpha is coming from a tRNS chunk and is binary */
- if (num_trans) {
- /* if it's not an indexed color image, tRNS means binary */
- if (color_type == PNG_COLOR_TYPE_PALETTE) {
- for (int i=0; i<num_trans; i++) {
- if ((trans[i] != 0) && (trans[i] != 255)) {
- alpha_bits = 8;
- break;
- }
- }
- }
- } else {
- alpha_bits = 8;
- }
- }
-#endif
+ //---------------------------------------------------------------//
+ // copy PNG info into imagelib structs (formerly png_set_dims()) //
+ //---------------------------------------------------------------//
if (channels == 1 || channels == 3)
decoder->format = gfx::SurfaceFormat::B8G8R8X8;
else if (channels == 2 || channels == 4)
decoder->format = gfx::SurfaceFormat::B8G8R8A8;
#ifdef PNG_APNG_SUPPORTED
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL))
@@ -651,32 +617,31 @@ nsPNGDecoder::info_callback(png_structp
#endif
if (decoder->mTransform &&
(channels <= 2 || interlace_type == PNG_INTERLACE_ADAM7)) {
uint32_t bpp[] = { 0, 3, 4, 3, 4 };
decoder->mCMSLine =
(uint8_t *)moz_malloc(bpp[channels] * width);
if (!decoder->mCMSLine) {
- longjmp(png_jmpbuf(decoder->mPNG), 5); // NS_ERROR_OUT_OF_MEMORY
+ png_longjmp(decoder->mPNG, 5); // NS_ERROR_OUT_OF_MEMORY
}
}
if (interlace_type == PNG_INTERLACE_ADAM7) {
if (height < INT32_MAX / (width * channels))
decoder->interlacebuf = (uint8_t *)moz_malloc(channels * width * height);
if (!decoder->interlacebuf) {
- longjmp(png_jmpbuf(decoder->mPNG), 5); // NS_ERROR_OUT_OF_MEMORY
+ png_longjmp(decoder->mPNG, 5); // NS_ERROR_OUT_OF_MEMORY
}
}
if (decoder->NeedsNewFrame()) {
- /* We know that we need a new frame, so pause input so the decoder
- * infrastructure can give it to us.
- */
+ // We know that we need a new frame, so pause input so the decoder
+ // infrastructure can give it to us.
png_process_data_pause(png_ptr, /* save = */ 1);
}
}
void
nsPNGDecoder::row_callback(png_structp png_ptr, png_bytep new_row,
png_uint_32 row_num, int pass)
{
@@ -730,17 +695,17 @@ nsPNGDecoder::row_callback(png_structp p
uint32_t bpr = width * sizeof(uint32_t);
uint32_t *cptr32 = (uint32_t*)(decoder->mImageData + (row_num*bpr));
bool rowHasNoAlpha = true;
if (decoder->mTransform) {
if (decoder->mCMSLine) {
qcms_transform_data(decoder->mTransform, line, decoder->mCMSLine,
iwidth);
- /* copy alpha over */
+ // copy alpha over
uint32_t channels = decoder->mChannels;
if (channels == 2 || channels == 4) {
for (uint32_t i = 0; i < iwidth; i++)
decoder->mCMSLine[4 * i + 3] = line[channels * i + channels - 1];
}
line = decoder->mCMSLine;
} else {
qcms_transform_data(decoder->mTransform, line, line, iwidth);
@@ -781,26 +746,27 @@ nsPNGDecoder::row_callback(png_structp p
for (uint32_t x=width; x>0; --x) {
*cptr32++ = gfxPackedPixel(line[3], line[0], line[1], line[2]);
if (line[3] != 0xff)
rowHasNoAlpha = false;
line += 4;
}
} else {
for (uint32_t x=width; x>0; --x) {
- *cptr32++ = gfxPackedPixelNoPreMultiply(line[3], line[0], line[1], line[2]);
+ *cptr32++ = gfxPackedPixelNoPreMultiply(line[3], line[0], line[1],
+ line[2]);
if (line[3] != 0xff)
rowHasNoAlpha = false;
line += 4;
}
}
}
break;
default:
- longjmp(png_jmpbuf(decoder->mPNG), 1);
+ png_longjmp(decoder->mPNG, 1);
}
if (!rowHasNoAlpha)
decoder->mFrameHasNoAlpha = false;
if (decoder->mNumFrames <= 1) {
// Only do incremental image display for the first frame
// XXXbholley - this check should be handled in the superclass
@@ -830,19 +796,18 @@ nsPNGDecoder::frame_info_callback(png_st
x_offset = png_get_next_frame_x_offset(png_ptr, decoder->mInfo);
y_offset = png_get_next_frame_y_offset(png_ptr, decoder->mInfo);
width = png_get_next_frame_width(png_ptr, decoder->mInfo);
height = png_get_next_frame_height(png_ptr, decoder->mInfo);
decoder->CreateFrame(x_offset, y_offset, width, height, decoder->format);
if (decoder->NeedsNewFrame()) {
- /* We know that we need a new frame, so pause input so the decoder
- * infrastructure can give it to us.
- */
+ // We know that we need a new frame, so pause input so the decoder
+ // infrastructure can give it to us.
png_process_data_pause(png_ptr, /* save = */ 1);
}
}
#endif
void
nsPNGDecoder::end_callback(png_structp png_ptr, png_infop info_ptr)
{
@@ -877,17 +842,17 @@ nsPNGDecoder::end_callback(png_structp p
decoder->PostDecodeDone(loop_count);
}
void
nsPNGDecoder::error_callback(png_structp png_ptr, png_const_charp error_msg)
{
PR_LOG(GetPNGLog(), PR_LOG_ERROR, ("libpng error: %s\n", error_msg));
- longjmp(png_jmpbuf(png_ptr), 1);
+ png_longjmp(png_ptr, 1);
}
void
nsPNGDecoder::warning_callback(png_structp png_ptr, png_const_charp warning_msg)
{
PR_LOG(GetPNGLog(), PR_LOG_WARNING, ("libpng warning: %s\n", warning_msg));
}
--- a/image/decoders/nsPNGDecoder.h
+++ b/image/decoders/nsPNGDecoder.h
@@ -1,16 +1,16 @@
/* -*- 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/. */
-#ifndef nsPNGDecoder_h__
-#define nsPNGDecoder_h__
+#ifndef nsPNGDecoder_h
+#define nsPNGDecoder_h
#include "Decoder.h"
#include "gfxTypes.h"
#include "nsCOMPtr.h"
#include "png.h"
@@ -128,9 +128,9 @@ public:
// This is defined in the PNG spec as an invariant. We use it to
// do manual validation without libpng.
static const uint8_t pngSignatureBytes[];
};
} // namespace image
} // namespace mozilla
-#endif // nsPNGDecoder_h__
+#endif // nsPNGDecoder_h
--- a/image/encoders/png/nsPNGEncoder.cpp
+++ b/image/encoders/png/nsPNGEncoder.cpp
@@ -1,31 +1,45 @@
/* -*- 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 "ImageLogging.h"
#include "nsCRT.h"
#include "nsPNGEncoder.h"
-#include "prprf.h"
+#include "nsStreamUtils.h"
#include "nsString.h"
-#include "nsStreamUtils.h"
+#include "prprf.h"
using namespace mozilla;
-NS_IMPL_ISUPPORTS(nsPNGEncoder, imgIEncoder, nsIInputStream, nsIAsyncInputStream)
+#ifdef PR_LOGGING
+static PRLogModuleInfo *
+GetPNGEncoderLog()
+{
+ static PRLogModuleInfo *sPNGEncoderLog;
+ if (!sPNGEncoderLog)
+ sPNGEncoderLog = PR_NewLogModule("PNGEncoder");
+ return sPNGEncoderLog;
+}
+#endif
+
+NS_IMPL_ISUPPORTS(nsPNGEncoder, imgIEncoder, nsIInputStream,
+ nsIAsyncInputStream)
nsPNGEncoder::nsPNGEncoder() : mPNG(nullptr), mPNGinfo(nullptr),
mIsAnimation(false),
mFinished(false),
mImageBuffer(nullptr), mImageBufferSize(0),
mImageBufferUsed(0), mImageBufferReadPoint(0),
mCallback(nullptr),
mCallbackTarget(nullptr), mNotifyThreshold(0),
- mReentrantMonitor("nsPNGEncoder.mReentrantMonitor")
+ mReentrantMonitor(
+ "nsPNGEncoder.mReentrantMonitor")
{
}
nsPNGEncoder::~nsPNGEncoder()
{
if (mImageBuffer) {
moz_free(mImageBuffer);
mImageBuffer = nullptr;
@@ -67,17 +81,17 @@ NS_IMETHODIMP nsPNGEncoder::InitFromData
rv = EndImageEncode();
return rv;
}
// nsPNGEncoder::StartImageEncode
//
-//
+//
// See ::InitFromData for other info.
NS_IMETHODIMP nsPNGEncoder::StartImageEncode(uint32_t aWidth,
uint32_t aHeight,
uint32_t aInputFormat,
const nsAString& aOutputOptions)
{
bool useTransparency = true, skipFirstFrame = false;
uint32_t numFrames = 1;
@@ -236,17 +250,17 @@ NS_IMETHODIMP nsPNGEncoder::AddImageFram
if (mIsAnimation) {
// XXX the row pointers arg (#3) is unused, can it be removed?
png_write_frame_head(mPNG, mPNGinfo, nullptr,
aWidth, aHeight, x_offset, y_offset,
delay_ms, 1000, dispose_op, blend_op);
}
#endif
- // Stride is the padded width of each row, so it better be longer
+ // Stride is the padded width of each row, so it better be longer
// (I'm afraid people will not understand what stride means, so
// check it well)
if ((aInputFormat == INPUT_FORMAT_RGB &&
aStride < aWidth * 3) ||
((aInputFormat == INPUT_FORMAT_RGBA ||
aInputFormat == INPUT_FORMAT_HOSTARGB) &&
aStride < aWidth * 4)) {
NS_WARNING("Invalid stride for InitFromData/AddImageFrame");
@@ -562,17 +576,18 @@ NS_IMETHODIMP nsPNGEncoder::AsyncWait(ns
// 0 means "any number of bytes except 0"
mNotifyThreshold = aRequestedCount;
if (!aRequestedCount)
mNotifyThreshold = 1024; // We don't want to notify incessantly
// We set the callback absolutely last, because NotifyListener uses it to
// determine if someone needs to be notified. If we don't set it last,
// NotifyListener might try to fire off a notification to a null target
- // which will generally cause non-threadsafe objects to be used off the main thread
+ // which will generally cause non-threadsafe objects to be used off the main
+ // thread
mCallback = aCallback;
// What we are being asked for may be present already
NotifyListener();
return NS_OK;
}
NS_IMETHODIMP nsPNGEncoder::CloseWithStatus(nsresult aStatus)
@@ -626,47 +641,35 @@ nsPNGEncoder::StripAlpha(const uint8_t*
pixelOut[1] = pixelIn[1];
pixelOut[2] = pixelIn[2];
}
}
// nsPNGEncoder::WarningCallback
-void // static
+void
nsPNGEncoder::WarningCallback(png_structp png_ptr,
png_const_charp warning_msg)
{
-#ifdef DEBUG
- // XXX: these messages are probably useful callers...
- // use nsIConsoleService?
- PR_fprintf(PR_STDERR, "PNG Encoder: %s\n", warning_msg);;
-#endif
+ PR_LOG(GetPNGEncoderLog(), PR_LOG_WARNING,
+ ("libpng warning: %s\n", warning_msg));
}
// nsPNGEncoder::ErrorCallback
-void // static
+void
nsPNGEncoder::ErrorCallback(png_structp png_ptr,
png_const_charp error_msg)
{
-#ifdef DEBUG
- // XXX: these messages are probably useful callers...
- // use nsIConsoleService?
- PR_fprintf(PR_STDERR, "PNG Encoder: %s\n", error_msg);;
-#endif
-#if PNG_LIBPNG_VER < 10500
- longjmp(png_ptr->jmpbuf, 1);
-#else
- png_longjmp(png_ptr, 1);
-#endif
+ PR_LOG(GetPNGEncoderLog(), PR_LOG_ERROR, ("libpng error: %s\n", error_msg));
+ png_longjmp(png_ptr, 1);
}
-
// nsPNGEncoder::WriteCallback
void // static
nsPNGEncoder::WriteCallback(png_structp png, png_bytep data,
png_size_t size)
{
nsPNGEncoder* that = static_cast<nsPNGEncoder*>(png_get_io_ptr(png));
if (! that->mImageBuffer)
--- a/image/encoders/png/nsPNGEncoder.h
+++ b/image/encoders/png/nsPNGEncoder.h
@@ -1,21 +1,22 @@
/* -*- 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 "mozilla/Attributes.h"
-#include "mozilla/ReentrantMonitor.h"
+#ifndef nsPNGEncoder_h
+
+#include <png.h>
#include "imgIEncoder.h"
-
#include "nsCOMPtr.h"
-#include <png.h>
+#include "mozilla/Attributes.h"
+#include "mozilla/ReentrantMonitor.h"
#define NS_PNGENCODER_CID \
{ /* 38d1592e-b81e-432b-86f8-471878bbfe07 */ \
0x38d1592e, \
0xb81e, \
0x432b, \
{0x86, 0xf8, 0x47, 0x18, 0x78, 0xbb, 0xfe, 0x07} \
}
@@ -67,16 +68,16 @@ protected:
uint32_t mImageBufferUsed;
uint32_t mImageBufferReadPoint;
nsCOMPtr<nsIInputStreamCallback> mCallback;
nsCOMPtr<nsIEventTarget> mCallbackTarget;
uint32_t mNotifyThreshold;
- /*
- nsPNGEncoder is designed to allow one thread to pump data into it while another
- reads from it. We lock to ensure that the buffer remains append-only while
- we read from it (that it is not realloced) and to ensure that only one thread
- dispatches a callback for each call to AsyncWait.
- */
+ // nsPNGEncoder is designed to allow one thread to pump data into it while
+ // another reads from it. We lock to ensure that the buffer remains
+ // append-only while we read from it (that it is not realloced) and to
+ // ensure that only one thread dispatches a callback for each call to
+ // AsyncWait.
ReentrantMonitor mReentrantMonitor;
};
+#endif // nsPNGEncoder_h
--- a/js/src/builtin/SymbolObject.cpp
+++ b/js/src/builtin/SymbolObject.cpp
@@ -5,16 +5,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "builtin/SymbolObject.h"
#include "vm/StringBuffer.h"
#include "jsobjinlines.h"
+#include "vm/ObjectImpl-inl.h"
#include "vm/Symbol-inl.h"
using JS::Symbol;
using namespace js;
const Class SymbolObject::class_ = {
"Symbol",
JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Symbol),
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -31,38 +31,33 @@ namespace gc {
typedef Vector<JS::Zone *, 4, SystemAllocPolicy> ZoneVector;
class MarkingValidator;
struct AutoPrepareForTracing;
class AutoTraceSession;
class ChunkPool
{
- Chunk *emptyChunkListHead;
- size_t emptyCount;
+ Chunk *head_;
+ size_t count_;
public:
- ChunkPool()
- : emptyChunkListHead(nullptr),
- emptyCount(0)
- {}
+ ChunkPool() : head_(nullptr), count_(0) {}
- size_t getEmptyCount() const {
- return emptyCount;
- }
+ size_t count() const { return count_; }
/* Must be called with the GC lock taken. */
inline Chunk *get(JSRuntime *rt);
/* Must be called either during the GC or with the GC lock taken. */
inline void put(Chunk *chunk);
class Enum {
public:
- explicit Enum(ChunkPool &pool) : pool(pool), chunkp(&pool.emptyChunkListHead) {}
+ explicit Enum(ChunkPool &pool) : pool(pool), chunkp(&pool.head_) {}
bool empty() { return !*chunkp; }
Chunk *front();
inline void popFront();
inline void removeAndPopFront();
private:
ChunkPool &pool;
Chunk **chunkp;
};
@@ -590,17 +585,17 @@ class GCRuntime
* Doubly-linked lists of chunks from user and system compartments. The GC
* allocates its arenas from the corresponding list and when all arenas
* in the list head are taken, then the chunk is removed from the list.
* During the GC when all arenas in a chunk become free, that chunk is
* removed from the list and scheduled for release.
*/
js::gc::Chunk *systemAvailableChunkListHead;
js::gc::Chunk *userAvailableChunkListHead;
- js::gc::ChunkPool chunkPool;
+ js::gc::ChunkPool emptyChunks;
js::RootedValueMap rootsHash;
size_t maxMallocBytes;
/*
* Number of the committed arenas in all GC chunks including empty chunks.
*/
@@ -693,16 +688,22 @@ class GCRuntime
JS::Zone *zoneGroups;
JS::Zone *currentZoneGroup;
int finalizePhase;
JS::Zone *sweepZone;
int sweepKindIndex;
bool abortSweepAfterCurrentGroup;
/*
+ * Concurrent sweep infrastructure.
+ */
+ void startTask(GCParallelTask &task, gcstats::Phase phase);
+ void joinTask(GCParallelTask &task, gcstats::Phase phase);
+
+ /*
* List head of arenas allocated during the sweep phase.
*/
js::gc::ArenaHeader *arenasAllocatedDuringSweep;
#ifdef JS_GC_MARKING_VALIDATION
js::gc::MarkingValidator *markingValidator;
#endif
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -467,16 +467,25 @@ IsMarked(T **thingp)
#endif
return (*thingp)->asTenured().isMarked();
}
template <typename T>
static bool
IsAboutToBeFinalized(T **thingp)
{
+ MOZ_ASSERT_IF(!ThingIsPermanentAtom(*thingp),
+ CurrentThreadCanAccessRuntime((*thingp)->runtimeFromMainThread()));
+ return IsAboutToBeFinalizedFromAnyThread(thingp);
+}
+
+template <typename T>
+static bool
+IsAboutToBeFinalizedFromAnyThread(T **thingp)
+{
MOZ_ASSERT(thingp);
MOZ_ASSERT(*thingp);
T *thing = *thingp;
JSRuntime *rt = thing->runtimeFromAnyThread();
/* Permanent atoms are never finalized by non-owning runtimes. */
if (ThingIsPermanentAtom(thing) && !TlsPerThreadData.get()->associatedWith(rt))
@@ -498,17 +507,17 @@ IsAboutToBeFinalized(T **thingp)
if (rt->isHeapMinorCollecting()) {
if (IsInsideNursery(thing))
return !nursery.getForwardedPointer(thingp);
return false;
}
}
#endif // JSGC_GENERATIONAL
- Zone *zone = thing->asTenured().zone();
+ Zone *zone = thing->asTenured().zoneFromAnyThread();
if (zone->isGCSweeping()) {
/*
* We should return false for things that have been allocated during
* incremental sweeping, but this possibility doesn't occur at the moment
* because this function is only called at the very start of the sweeping a
* compartment group and during minor gc. Rather than do the extra check,
* we just assert that it's not necessary.
*/
@@ -611,16 +620,22 @@ Is##base##Marked(BarrieredBase<type*> *t
\
bool \
Is##base##AboutToBeFinalized(type **thingp) \
{ \
return IsAboutToBeFinalized<type>(thingp); \
} \
\
bool \
+Is##base##AboutToBeFinalizedFromAnyThread(type **thingp) \
+{ \
+ return IsAboutToBeFinalizedFromAnyThread<type>(thingp); \
+} \
+ \
+bool \
Is##base##AboutToBeFinalized(BarrieredBase<type*> *thingp) \
{ \
return IsAboutToBeFinalized<type>(thingp->unsafeGet()); \
} \
\
type * \
Update##base##IfRelocated(JSRuntime *rt, BarrieredBase<type*> *thingp) \
{ \
@@ -905,16 +920,38 @@ gc::IsValueAboutToBeFinalized(Value *v)
MOZ_ASSERT(v->isSymbol());
JS::Symbol *sym = v->toSymbol();
rv = IsAboutToBeFinalized<JS::Symbol>(&sym);
v->setSymbol(sym);
}
return rv;
}
+bool
+gc::IsValueAboutToBeFinalizedFromAnyThread(Value *v)
+{
+ MOZ_ASSERT(v->isMarkable());
+ bool rv;
+ if (v->isString()) {
+ JSString *str = (JSString *)v->toGCThing();
+ rv = IsAboutToBeFinalizedFromAnyThread<JSString>(&str);
+ v->setString(str);
+ } else if (v->isObject()) {
+ JSObject *obj = (JSObject *)v->toGCThing();
+ rv = IsAboutToBeFinalizedFromAnyThread<JSObject>(&obj);
+ v->setObject(*obj);
+ } else {
+ MOZ_ASSERT(v->isSymbol());
+ JS::Symbol *sym = v->toSymbol();
+ rv = IsAboutToBeFinalizedFromAnyThread<JS::Symbol>(&sym);
+ v->setSymbol(sym);
+ }
+ return rv;
+}
+
/*** Slot Marking ***/
bool
gc::IsSlotMarked(HeapSlot *s)
{
return IsMarked(s);
}
@@ -1026,16 +1063,22 @@ gc::IsCellMarked(Cell **thingp)
}
bool
gc::IsCellAboutToBeFinalized(Cell **thingp)
{
return IsAboutToBeFinalized<Cell>(thingp);
}
+bool
+gc::IsCellAboutToBeFinalizedFromAnyThread(Cell **thingp)
+{
+ return IsAboutToBeFinalizedFromAnyThread<Cell>(thingp);
+}
+
/*** Push Mark Stack ***/
#define JS_COMPARTMENT_ASSERT(rt, thing) \
MOZ_ASSERT((thing)->zone()->isGCMarking())
#define JS_COMPARTMENT_ASSERT_STR(rt, thing) \
MOZ_ASSERT((thing)->zone()->isGCMarking() || \
(rt)->isAtomsZone((thing)->zone()));
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -93,16 +93,17 @@ namespace gc {
void Mark##base(JSTracer *trc, BarrieredBase<type*> *thing, const char *name); \
void Mark##base##Root(JSTracer *trc, type **thingp, const char *name); \
void Mark##base##Unbarriered(JSTracer *trc, type **thingp, const char *name); \
void Mark##base##Range(JSTracer *trc, size_t len, HeapPtr<type*> *thing, const char *name); \
void Mark##base##RootRange(JSTracer *trc, size_t len, type **thing, const char *name); \
bool Is##base##Marked(type **thingp); \
bool Is##base##Marked(BarrieredBase<type*> *thingp); \
bool Is##base##AboutToBeFinalized(type **thingp); \
+bool Is##base##AboutToBeFinalizedFromAnyThread(type **thingp); \
bool Is##base##AboutToBeFinalized(BarrieredBase<type*> *thingp); \
type *Update##base##IfRelocated(JSRuntime *rt, BarrieredBase<type*> *thingp); \
type *Update##base##IfRelocated(JSRuntime *rt, type **thingp);
DeclMarker(BaseShape, BaseShape)
DeclMarker(BaseShape, UnownedBaseShape)
DeclMarker(JitCode, jit::JitCode)
DeclMarker(Object, NativeObject)
@@ -214,16 +215,19 @@ void
MarkTypeRoot(JSTracer *trc, types::Type *v, const char *name);
bool
IsValueMarked(Value *v);
bool
IsValueAboutToBeFinalized(Value *v);
+bool
+IsValueAboutToBeFinalizedFromAnyThread(Value *v);
+
/*** Slot Marking ***/
bool
IsSlotMarked(HeapSlot *s);
void
MarkSlot(JSTracer *trc, HeapSlot *s, const char *name);
@@ -322,16 +326,19 @@ Mark(JSTracer *trc, ScopeObject **obj, c
}
bool
IsCellMarked(Cell **thingp);
bool
IsCellAboutToBeFinalized(Cell **thing);
+bool
+IsCellAboutToBeFinalizedFromAnyThread(Cell **thing);
+
inline bool
IsMarked(BarrieredBase<Value> *v)
{
if (!v->isMarkable())
return true;
return IsValueMarked(v->unsafeGet());
}
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -14,16 +14,17 @@
#include <stdio.h>
#include "jscrashreport.h"
#include "jsprf.h"
#include "jsutil.h"
#include "prmjtime.h"
#include "gc/Memory.h"
+#include "vm/HelperThreads.h"
#include "vm/Runtime.h"
using namespace js;
using namespace js::gc;
using namespace js::gcstats;
using mozilla::PodArrayZero;
@@ -832,16 +833,26 @@ Statistics::endPhase(Phase phase)
phaseNestingDepth--;
int64_t t = PRMJ_Now() - phaseStartTimes[phase];
slices.back().phaseTimes[phase] += t;
phaseTimes[phase] += t;
phaseStartTimes[phase] = 0;
}
+void
+Statistics::endParallelPhase(Phase phase, const GCParallelTask *task)
+{
+ phaseNestingDepth--;
+
+ slices.back().phaseTimes[phase] += task->duration();
+ phaseTimes[phase] += task->duration();
+ phaseStartTimes[phase] = 0;
+}
+
int64_t
Statistics::beginSCC()
{
return PRMJ_Now();
}
void
Statistics::endSCC(unsigned scc, int64_t start)
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -15,16 +15,19 @@
#include "jspubtd.h"
#include "js/GCAPI.h"
#include "js/Vector.h"
struct JSCompartment;
namespace js {
+
+class GCParallelTask;
+
namespace gcstats {
enum Phase {
PHASE_GC_BEGIN,
PHASE_WAIT_BACKGROUND_THREAD,
PHASE_MARK_DISCARD_CODE,
PHASE_PURGE,
PHASE_MARK,
@@ -104,16 +107,17 @@ struct ZoneGCStats
struct Statistics
{
explicit Statistics(JSRuntime *rt);
~Statistics();
void beginPhase(Phase phase);
void endPhase(Phase phase);
+ void endParallelPhase(Phase phase, const GCParallelTask *task);
void beginSlice(const ZoneGCStats &zoneStats, JS::gcreason::Reason reason);
void endSlice();
void reset(const char *reason) { slices.back().resetReason = reason; }
void nonincremental(const char *reason) { nonincrementalReason = reason; }
void count(Stat s) {
@@ -229,35 +233,51 @@ struct AutoGCSlice
Statistics &stats;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
struct AutoPhase
{
AutoPhase(Statistics &stats, Phase phase
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
- : stats(stats), phase(phase), enabled(true)
+ : stats(stats), task(nullptr), phase(phase), enabled(true)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
stats.beginPhase(phase);
}
+
AutoPhase(Statistics &stats, bool condition, Phase phase
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
- : stats(stats), phase(phase), enabled(condition)
+ : stats(stats), task(nullptr), phase(phase), enabled(condition)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
if (enabled)
stats.beginPhase(phase);
}
- ~AutoPhase() {
+
+ AutoPhase(Statistics &stats, const GCParallelTask &task, Phase phase
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : stats(stats), task(&task), phase(phase), enabled(true)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
if (enabled)
- stats.endPhase(phase);
+ stats.beginPhase(phase);
+ }
+
+ ~AutoPhase() {
+ if (enabled) {
+ if (task)
+ stats.endParallelPhase(phase, task);
+ else
+ stats.endPhase(phase);
+ }
}
Statistics &stats;
+ const GCParallelTask *task;
Phase phase;
bool enabled;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
struct AutoSCC
{
AutoSCC(Statistics &stats, unsigned scc
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -1211,16 +1211,20 @@ Instruction *
BOffImm::getDest(Instruction *src)
{
// TODO: It is probably worthwhile to verify that src is actually a branch.
// NOTE: This does not explicitly shift the offset of the destination left by 2,
// since it is indexing into an array of instruction sized objects.
return &src[(((int32_t)data << 8) >> 8) + 2];
}
+const js::jit::DoubleEncoder::DoubleEntry js::jit::DoubleEncoder::table[256] = {
+#include "jit/arm/DoubleEntryTable.tbl"
+};
+
// VFPRegister implementation
VFPRegister
VFPRegister::doubleOverlay(unsigned int which) const
{
MOZ_ASSERT(!_isInvalid);
MOZ_ASSERT(which == 0);
if (kind != Double)
return VFPRegister(code_ >> 1, Double);
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -445,36 +445,33 @@ struct Imm8VFPOffData
MOZ_ASSERT((imm & ~(0xff)) == 0);
}
};
// ARM can magically encode 256 very special immediates to be moved into a
// register.
struct Imm8VFPImmData
{
- private:
+ // This structure's members are public and it has no constructor to
+ // initialize them, for a very special reason. Were this structure to
+ // have a constructor, the initialization for DoubleEncoder's internal
+ // table (see below) would require a rather large static constructor on
+ // some of our supported compilers. The known solution to this is to mark
+ // the constructor MOZ_CONSTEXPR, but, again, some of our supported
+ // compilers don't support MOZ_CONSTEXPR! So we are reduced to public
+ // members and eschewing a constructor in hopes that the initialization
+ // of DoubleEncoder's table is correct.
uint32_t imm4L : 4;
- uint32_t pad : 12;
uint32_t imm4H : 4;
- int32_t isInvalid : 12;
-
- public:
- Imm8VFPImmData()
- : imm4L(-1U & 0xf), imm4H(-1U & 0xf), isInvalid(-1)
- { }
-
- Imm8VFPImmData(uint32_t imm)
- : imm4L(imm&0xf), imm4H(imm >> 4), isInvalid(0)
- {
- MOZ_ASSERT(imm <= 0xff);
- }
+ int32_t isInvalid : 24;
uint32_t encode() {
- if (isInvalid != 0)
- return -1;
+ // This assert is an attempting at ensuring that we don't create random
+ // instances of this structure and then asking to encode() it.
+ MOZ_ASSERT(isInvalid == 0);
return imm4L | (imm4H << 16);
};
};
struct Imm12Data
{
uint32_t data : 12;
uint32_t encode() {
@@ -2185,60 +2182,25 @@ GetDoubleArgStackDisp(uint32_t usedIntAr
return (intSlots + doubleSlots + *padding) * sizeof(intptr_t);
}
#endif
class DoubleEncoder {
- uint32_t rep(bool b, uint32_t count) {
- uint32_t ret = 0;
- for (uint32_t i = 0; i < count; i++)
- ret = (ret << 1) | b;
- return ret;
- }
-
- uint32_t encode(uint8_t value) {
- // ARM ARM "VFP modified immediate constants"
- // aBbbbbbb bbcdefgh 000...
- // We want to return the top 32 bits of the double the rest are 0.
- bool a = value >> 7;
- bool b = value >> 6 & 1;
- bool B = !b;
- uint32_t cdefgh = value & 0x3f;
- return a << 31 |
- B << 30 |
- rep(b, 8) << 22 |
- cdefgh << 16;
- }
-
struct DoubleEntry
{
uint32_t dblTop;
datastore::Imm8VFPImmData data;
-
- DoubleEntry()
- : dblTop(-1)
- { }
- DoubleEntry(uint32_t dblTop_, datastore::Imm8VFPImmData data_)
- : dblTop(dblTop_), data(data_)
- { }
};
- mozilla::Array<DoubleEntry, 256> table;
+ static const DoubleEntry table[256];
public:
- DoubleEncoder()
- {
- for (int i = 0; i < 256; i++) {
- table[i] = DoubleEntry(encode(i), datastore::Imm8VFPImmData(i));
- }
- }
-
bool lookup(uint32_t top, datastore::Imm8VFPImmData *ret) {
for (int i = 0; i < 256; i++) {
if (table[i].dblTop == top) {
*ret = table[i].data;
return true;
}
}
return false;
new file mode 100644
--- /dev/null
+++ b/js/src/jit/arm/DoubleEntryTable.tbl
@@ -0,0 +1,257 @@
+/* THIS FILE IS AUTOMATICALLY GENERATED BY gen-double-encode-table.py. */
+ { 0x40000000, { 0, 0, 0 } },
+ { 0x40010000, { 1, 0, 0 } },
+ { 0x40020000, { 2, 0, 0 } },
+ { 0x40030000, { 3, 0, 0 } },
+ { 0x40040000, { 4, 0, 0 } },
+ { 0x40050000, { 5, 0, 0 } },
+ { 0x40060000, { 6, 0, 0 } },
+ { 0x40070000, { 7, 0, 0 } },
+ { 0x40080000, { 8, 0, 0 } },
+ { 0x40090000, { 9, 0, 0 } },
+ { 0x400a0000, { 10, 0, 0 } },
+ { 0x400b0000, { 11, 0, 0 } },
+ { 0x400c0000, { 12, 0, 0 } },
+ { 0x400d0000, { 13, 0, 0 } },
+ { 0x400e0000, { 14, 0, 0 } },
+ { 0x400f0000, { 15, 0, 0 } },
+ { 0x40100000, { 0, 1, 0 } },
+ { 0x40110000, { 1, 1, 0 } },
+ { 0x40120000, { 2, 1, 0 } },
+ { 0x40130000, { 3, 1, 0 } },
+ { 0x40140000, { 4, 1, 0 } },
+ { 0x40150000, { 5, 1, 0 } },
+ { 0x40160000, { 6, 1, 0 } },
+ { 0x40170000, { 7, 1, 0 } },
+ { 0x40180000, { 8, 1, 0 } },
+ { 0x40190000, { 9, 1, 0 } },
+ { 0x401a0000, { 10, 1, 0 } },
+ { 0x401b0000, { 11, 1, 0 } },
+ { 0x401c0000, { 12, 1, 0 } },
+ { 0x401d0000, { 13, 1, 0 } },
+ { 0x401e0000, { 14, 1, 0 } },
+ { 0x401f0000, { 15, 1, 0 } },
+ { 0x40200000, { 0, 2, 0 } },
+ { 0x40210000, { 1, 2, 0 } },
+ { 0x40220000, { 2, 2, 0 } },
+ { 0x40230000, { 3, 2, 0 } },
+ { 0x40240000, { 4, 2, 0 } },
+ { 0x40250000, { 5, 2, 0 } },
+ { 0x40260000, { 6, 2, 0 } },
+ { 0x40270000, { 7, 2, 0 } },
+ { 0x40280000, { 8, 2, 0 } },
+ { 0x40290000, { 9, 2, 0 } },
+ { 0x402a0000, { 10, 2, 0 } },
+ { 0x402b0000, { 11, 2, 0 } },
+ { 0x402c0000, { 12, 2, 0 } },
+ { 0x402d0000, { 13, 2, 0 } },
+ { 0x402e0000, { 14, 2, 0 } },
+ { 0x402f0000, { 15, 2, 0 } },
+ { 0x40300000, { 0, 3, 0 } },
+ { 0x40310000, { 1, 3, 0 } },
+ { 0x40320000, { 2, 3, 0 } },
+ { 0x40330000, { 3, 3, 0 } },
+ { 0x40340000, { 4, 3, 0 } },
+ { 0x40350000, { 5, 3, 0 } },
+ { 0x40360000, { 6, 3, 0 } },
+ { 0x40370000, { 7, 3, 0 } },
+ { 0x40380000, { 8, 3, 0 } },
+ { 0x40390000, { 9, 3, 0 } },
+ { 0x403a0000, { 10, 3, 0 } },
+ { 0x403b0000, { 11, 3, 0 } },
+ { 0x403c0000, { 12, 3, 0 } },
+ { 0x403d0000, { 13, 3, 0 } },
+ { 0x403e0000, { 14, 3, 0 } },
+ { 0x403f0000, { 15, 3, 0 } },
+ { 0x3fc00000, { 0, 4, 0 } },
+ { 0x3fc10000, { 1, 4, 0 } },
+ { 0x3fc20000, { 2, 4, 0 } },
+ { 0x3fc30000, { 3, 4, 0 } },
+ { 0x3fc40000, { 4, 4, 0 } },
+ { 0x3fc50000, { 5, 4, 0 } },
+ { 0x3fc60000, { 6, 4, 0 } },
+ { 0x3fc70000, { 7, 4, 0 } },
+ { 0x3fc80000, { 8, 4, 0 } },
+ { 0x3fc90000, { 9, 4, 0 } },
+ { 0x3fca0000, { 10, 4, 0 } },
+ { 0x3fcb0000, { 11, 4, 0 } },
+ { 0x3fcc0000, { 12, 4, 0 } },
+ { 0x3fcd0000, { 13, 4, 0 } },
+ { 0x3fce0000, { 14, 4, 0 } },
+ { 0x3fcf0000, { 15, 4, 0 } },
+ { 0x3fd00000, { 0, 5, 0 } },
+ { 0x3fd10000, { 1, 5, 0 } },
+ { 0x3fd20000, { 2, 5, 0 } },
+ { 0x3fd30000, { 3, 5, 0 } },
+ { 0x3fd40000, { 4, 5, 0 } },
+ { 0x3fd50000, { 5, 5, 0 } },
+ { 0x3fd60000, { 6, 5, 0 } },
+ { 0x3fd70000, { 7, 5, 0 } },
+ { 0x3fd80000, { 8, 5, 0 } },
+ { 0x3fd90000, { 9, 5, 0 } },
+ { 0x3fda0000, { 10, 5, 0 } },
+ { 0x3fdb0000, { 11, 5, 0 } },
+ { 0x3fdc0000, { 12, 5, 0 } },
+ { 0x3fdd0000, { 13, 5, 0 } },
+ { 0x3fde0000, { 14, 5, 0 } },
+ { 0x3fdf0000, { 15, 5, 0 } },
+ { 0x3fe00000, { 0, 6, 0 } },
+ { 0x3fe10000, { 1, 6, 0 } },
+ { 0x3fe20000, { 2, 6, 0 } },
+ { 0x3fe30000, { 3, 6, 0 } },
+ { 0x3fe40000, { 4, 6, 0 } },
+ { 0x3fe50000, { 5, 6, 0 } },
+ { 0x3fe60000, { 6, 6, 0 } },
+ { 0x3fe70000, { 7, 6, 0 } },
+ { 0x3fe80000, { 8, 6, 0 } },
+ { 0x3fe90000, { 9, 6, 0 } },
+ { 0x3fea0000, { 10, 6, 0 } },
+ { 0x3feb0000, { 11, 6, 0 } },
+ { 0x3fec0000, { 12, 6, 0 } },
+ { 0x3fed0000, { 13, 6, 0 } },
+ { 0x3fee0000, { 14, 6, 0 } },
+ { 0x3fef0000, { 15, 6, 0 } },
+ { 0x3ff00000, { 0, 7, 0 } },
+ { 0x3ff10000, { 1, 7, 0 } },
+ { 0x3ff20000, { 2, 7, 0 } },
+ { 0x3ff30000, { 3, 7, 0 } },
+ { 0x3ff40000, { 4, 7, 0 } },
+ { 0x3ff50000, { 5, 7, 0 } },
+ { 0x3ff60000, { 6, 7, 0 } },
+ { 0x3ff70000, { 7, 7, 0 } },
+ { 0x3ff80000, { 8, 7, 0 } },
+ { 0x3ff90000, { 9, 7, 0 } },
+ { 0x3ffa0000, { 10, 7, 0 } },
+ { 0x3ffb0000, { 11, 7, 0 } },
+ { 0x3ffc0000, { 12, 7, 0 } },
+ { 0x3ffd0000, { 13, 7, 0 } },
+ { 0x3ffe0000, { 14, 7, 0 } },
+ { 0x3fff0000, { 15, 7, 0 } },
+ { 0xc0000000, { 0, 8, 0 } },
+ { 0xc0010000, { 1, 8, 0 } },
+ { 0xc0020000, { 2, 8, 0 } },
+ { 0xc0030000, { 3, 8, 0 } },
+ { 0xc0040000, { 4, 8, 0 } },
+ { 0xc0050000, { 5, 8, 0 } },
+ { 0xc0060000, { 6, 8, 0 } },
+ { 0xc0070000, { 7, 8, 0 } },
+ { 0xc0080000, { 8, 8, 0 } },
+ { 0xc0090000, { 9, 8, 0 } },
+ { 0xc00a0000, { 10, 8, 0 } },
+ { 0xc00b0000, { 11, 8, 0 } },
+ { 0xc00c0000, { 12, 8, 0 } },
+ { 0xc00d0000, { 13, 8, 0 } },
+ { 0xc00e0000, { 14, 8, 0 } },
+ { 0xc00f0000, { 15, 8, 0 } },
+ { 0xc0100000, { 0, 9, 0 } },
+ { 0xc0110000, { 1, 9, 0 } },
+ { 0xc0120000, { 2, 9, 0 } },
+ { 0xc0130000, { 3, 9, 0 } },
+ { 0xc0140000, { 4, 9, 0 } },
+ { 0xc0150000, { 5, 9, 0 } },
+ { 0xc0160000, { 6, 9, 0 } },
+ { 0xc0170000, { 7, 9, 0 } },
+ { 0xc0180000, { 8, 9, 0 } },
+ { 0xc0190000, { 9, 9, 0 } },
+ { 0xc01a0000, { 10, 9, 0 } },
+ { 0xc01b0000, { 11, 9, 0 } },
+ { 0xc01c0000, { 12, 9, 0 } },
+ { 0xc01d0000, { 13, 9, 0 } },
+ { 0xc01e0000, { 14, 9, 0 } },
+ { 0xc01f0000, { 15, 9, 0 } },
+ { 0xc0200000, { 0, 10, 0 } },
+ { 0xc0210000, { 1, 10, 0 } },
+ { 0xc0220000, { 2, 10, 0 } },
+ { 0xc0230000, { 3, 10, 0 } },
+ { 0xc0240000, { 4, 10, 0 } },
+ { 0xc0250000, { 5, 10, 0 } },
+ { 0xc0260000, { 6, 10, 0 } },
+ { 0xc0270000, { 7, 10, 0 } },
+ { 0xc0280000, { 8, 10, 0 } },
+ { 0xc0290000, { 9, 10, 0 } },
+ { 0xc02a0000, { 10, 10, 0 } },
+ { 0xc02b0000, { 11, 10, 0 } },
+ { 0xc02c0000, { 12, 10, 0 } },
+ { 0xc02d0000, { 13, 10, 0 } },
+ { 0xc02e0000, { 14, 10, 0 } },
+ { 0xc02f0000, { 15, 10, 0 } },
+ { 0xc0300000, { 0, 11, 0 } },
+ { 0xc0310000, { 1, 11, 0 } },
+ { 0xc0320000, { 2, 11, 0 } },
+ { 0xc0330000, { 3, 11, 0 } },
+ { 0xc0340000, { 4, 11, 0 } },
+ { 0xc0350000, { 5, 11, 0 } },
+ { 0xc0360000, { 6, 11, 0 } },
+ { 0xc0370000, { 7, 11, 0 } },
+ { 0xc0380000, { 8, 11, 0 } },
+ { 0xc0390000, { 9, 11, 0 } },
+ { 0xc03a0000, { 10, 11, 0 } },
+ { 0xc03b0000, { 11, 11, 0 } },
+ { 0xc03c0000, { 12, 11, 0 } },
+ { 0xc03d0000, { 13, 11, 0 } },
+ { 0xc03e0000, { 14, 11, 0 } },
+ { 0xc03f0000, { 15, 11, 0 } },
+ { 0xbfc00000, { 0, 12, 0 } },
+ { 0xbfc10000, { 1, 12, 0 } },
+ { 0xbfc20000, { 2, 12, 0 } },
+ { 0xbfc30000, { 3, 12, 0 } },
+ { 0xbfc40000, { 4, 12, 0 } },
+ { 0xbfc50000, { 5, 12, 0 } },
+ { 0xbfc60000, { 6, 12, 0 } },
+ { 0xbfc70000, { 7, 12, 0 } },
+ { 0xbfc80000, { 8, 12, 0 } },
+ { 0xbfc90000, { 9, 12, 0 } },
+ { 0xbfca0000, { 10, 12, 0 } },
+ { 0xbfcb0000, { 11, 12, 0 } },
+ { 0xbfcc0000, { 12, 12, 0 } },
+ { 0xbfcd0000, { 13, 12, 0 } },
+ { 0xbfce0000, { 14, 12, 0 } },
+ { 0xbfcf0000, { 15, 12, 0 } },
+ { 0xbfd00000, { 0, 13, 0 } },
+ { 0xbfd10000, { 1, 13, 0 } },
+ { 0xbfd20000, { 2, 13, 0 } },
+ { 0xbfd30000, { 3, 13, 0 } },
+ { 0xbfd40000, { 4, 13, 0 } },
+ { 0xbfd50000, { 5, 13, 0 } },
+ { 0xbfd60000, { 6, 13, 0 } },
+ { 0xbfd70000, { 7, 13, 0 } },
+ { 0xbfd80000, { 8, 13, 0 } },
+ { 0xbfd90000, { 9, 13, 0 } },
+ { 0xbfda0000, { 10, 13, 0 } },
+ { 0xbfdb0000, { 11, 13, 0 } },
+ { 0xbfdc0000, { 12, 13, 0 } },
+ { 0xbfdd0000, { 13, 13, 0 } },
+ { 0xbfde0000, { 14, 13, 0 } },
+ { 0xbfdf0000, { 15, 13, 0 } },
+ { 0xbfe00000, { 0, 14, 0 } },
+ { 0xbfe10000, { 1, 14, 0 } },
+ { 0xbfe20000, { 2, 14, 0 } },
+ { 0xbfe30000, { 3, 14, 0 } },
+ { 0xbfe40000, { 4, 14, 0 } },
+ { 0xbfe50000, { 5, 14, 0 } },
+ { 0xbfe60000, { 6, 14, 0 } },
+ { 0xbfe70000, { 7, 14, 0 } },
+ { 0xbfe80000, { 8, 14, 0 } },
+ { 0xbfe90000, { 9, 14, 0 } },
+ { 0xbfea0000, { 10, 14, 0 } },
+ { 0xbfeb0000, { 11, 14, 0 } },
+ { 0xbfec0000, { 12, 14, 0 } },
+ { 0xbfed0000, { 13, 14, 0 } },
+ { 0xbfee0000, { 14, 14, 0 } },
+ { 0xbfef0000, { 15, 14, 0 } },
+ { 0xbff00000, { 0, 15, 0 } },
+ { 0xbff10000, { 1, 15, 0 } },
+ { 0xbff20000, { 2, 15, 0 } },
+ { 0xbff30000, { 3, 15, 0 } },
+ { 0xbff40000, { 4, 15, 0 } },
+ { 0xbff50000, { 5, 15, 0 } },
+ { 0xbff60000, { 6, 15, 0 } },
+ { 0xbff70000, { 7, 15, 0 } },
+ { 0xbff80000, { 8, 15, 0 } },
+ { 0xbff90000, { 9, 15, 0 } },
+ { 0xbffa0000, { 10, 15, 0 } },
+ { 0xbffb0000, { 11, 15, 0 } },
+ { 0xbffc0000, { 12, 15, 0 } },
+ { 0xbffd0000, { 13, 15, 0 } },
+ { 0xbffe0000, { 14, 15, 0 } },
+ { 0xbfff0000, { 15, 15, 0 } },
new file mode 100644
--- /dev/null
+++ b/js/src/jit/arm/gen-double-encoder-table.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+"""Generate tables of immediately-encodable VFP doubles.
+
+DOES NOT get automatically run during the build process. If you need to
+modify this file (which is unlikely), you must re-run this script:
+
+python gen-double-encode-table.py > $(topsrcdir)/path/to/DoubleEntryTable.tbl
+"""
+
+import operator
+
+def rep(bit, count):
+ return reduce(operator.ior, [bit << c for c in range(count)])
+
+def encodeDouble(value):
+ """Generate an ARM ARM 'VFP modified immediate constant' with format:
+ aBbbbbbb bbcdefgh 000...
+
+ We will return the top 32 bits of the double; the rest are 0."""
+ assert (0 <= value) and (value <= 255)
+ a = value >> 7
+ b = (value >> 6) & 1
+ B = int(b == 0)
+ cdefgh = value & 0x3f
+ return (a << 31) | (B << 30) | (rep(b, 8) << 22) | cdefgh << 16
+
+print '/* THIS FILE IS AUTOMATICALLY GENERATED BY gen-double-encode-table.py. */'
+for i in range(256):
+ print ' { 0x%08x, { %d, %d, 0 } },' % (encodeDouble(i), i & 0xf, i >> 4)
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -249,17 +249,17 @@ void
JSRuntime::sweepAtoms()
{
if (!atoms_)
return;
for (AtomSet::Enum e(*atoms_); !e.empty(); e.popFront()) {
AtomStateEntry entry = e.front();
JSAtom *atom = entry.asPtr();
- bool isDying = IsStringAboutToBeFinalized(&atom);
+ bool isDying = IsStringAboutToBeFinalizedFromAnyThread(&atom);
/* Pinned or interned key cannot be finalized. */
MOZ_ASSERT_IF(hasContexts() && entry.isTagged(), !isDying);
if (isDying)
e.removeFront();
}
}
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -96,18 +96,19 @@ js::TraceCycleDetectionSet(JSTracer *trc
}
void
JSCompartment::sweepCallsiteClones()
{
if (callsiteClones.initialized()) {
for (CallsiteCloneTable::Enum e(callsiteClones); !e.empty(); e.popFront()) {
CallsiteCloneKey key = e.front().key();
- if (IsObjectAboutToBeFinalized(&key.original) || IsScriptAboutToBeFinalized(&key.script) ||
- IsObjectAboutToBeFinalized(e.front().value().unsafeGet()))
+ if (IsObjectAboutToBeFinalizedFromAnyThread(&key.original) ||
+ IsScriptAboutToBeFinalizedFromAnyThread(&key.script) ||
+ IsObjectAboutToBeFinalizedFromAnyThread(e.front().value().unsafeGet()))
{
e.removeFront();
} else if (key != e.front().key()) {
e.rekeyFront(key);
}
}
}
}
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -530,54 +530,54 @@ JSCompartment::markRoots(JSTracer *trc)
if (jitCompartment_)
jitCompartment_->mark(trc, this);
/*
* If a compartment is on-stack, we mark its global so that
* JSContext::global() remains valid.
*/
- if (enterCompartmentDepth && global_)
+ if (enterCompartmentDepth && global_.unbarrieredGet())
MarkObjectRoot(trc, global_.unsafeGet(), "on-stack compartment global");
}
void
JSCompartment::sweepInnerViews()
{
- innerViews.sweep(runtimeFromMainThread());
+ innerViews.sweep(runtimeFromAnyThread());
}
void
JSCompartment::sweepTypeObjectTables()
{
sweepNewTypeObjectTable(newTypeObjects);
sweepNewTypeObjectTable(lazyTypeObjects);
}
void
JSCompartment::sweepSavedStacks()
{
- savedStacks_.sweep(runtimeFromMainThread());
+ savedStacks_.sweep(runtimeFromAnyThread());
}
void
JSCompartment::sweepGlobalObject(FreeOp *fop)
{
- if (global_ && IsObjectAboutToBeFinalized(global_.unsafeGet())) {
+ if (global_.unbarrieredGet() && IsObjectAboutToBeFinalizedFromAnyThread(global_.unsafeGet())) {
if (debugMode())
Debugger::detachAllDebuggersFromGlobal(fop, global_);
global_.set(nullptr);
}
}
void
JSCompartment::sweepSelfHostingScriptSource()
{
- if (selfHostingScriptSource &&
- IsObjectAboutToBeFinalized((JSObject **) selfHostingScriptSource.unsafeGet()))
+ if (selfHostingScriptSource.unbarrieredGet() &&
+ IsObjectAboutToBeFinalizedFromAnyThread((JSObject **) selfHostingScriptSource.unsafeGet()))
{
selfHostingScriptSource.set(nullptr);
}
}
void
JSCompartment::sweepJitCompartment(FreeOp *fop)
{
@@ -588,23 +588,23 @@ JSCompartment::sweepJitCompartment(FreeO
void
JSCompartment::sweepRegExps()
{
/*
* JIT code increments activeWarmUpCounter for any RegExpShared used by jit
* code for the lifetime of the JIT script. Thus, we must perform
* sweeping after clearing jit code.
*/
- regExps.sweep(runtimeFromMainThread());
+ regExps.sweep(runtimeFromAnyThread());
}
void
JSCompartment::sweepDebugScopes()
{
- JSRuntime *rt = runtimeFromMainThread();
+ JSRuntime *rt = runtimeFromAnyThread();
if (debugScopes)
debugScopes->sweep(rt);
}
void
JSCompartment::sweepWeakMaps()
{
/* Finalize unreachable (key,value) pairs in all weak maps. */
@@ -614,36 +614,36 @@ JSCompartment::sweepWeakMaps()
void
JSCompartment::sweepNativeIterators()
{
/* Sweep list of native iterators. */
NativeIterator *ni = enumerators->next();
while (ni != enumerators) {
JSObject *iterObj = ni->iterObj();
NativeIterator *next = ni->next();
- if (gc::IsObjectAboutToBeFinalized(&iterObj))
+ if (gc::IsObjectAboutToBeFinalizedFromAnyThread(&iterObj))
ni->unlink();
ni = next;
}
}
/*
* Remove dead wrappers from the table. We must sweep all compartments, since
* string entries in the crossCompartmentWrappers table are not marked during
* markCrossCompartmentWrappers.
*/
void
JSCompartment::sweepCrossCompartmentWrappers()
{
/* Remove dead wrappers from the table. */
for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
CrossCompartmentKey key = e.front().key();
- bool keyDying = IsCellAboutToBeFinalized(&key.wrapped);
- bool valDying = IsValueAboutToBeFinalized(e.front().value().unsafeGet());
- bool dbgDying = key.debugger && IsObjectAboutToBeFinalized(&key.debugger);
+ bool keyDying = IsCellAboutToBeFinalizedFromAnyThread(&key.wrapped);
+ bool valDying = IsValueAboutToBeFinalizedFromAnyThread(e.front().value().unsafeGet());
+ bool dbgDying = key.debugger && IsObjectAboutToBeFinalizedFromAnyThread(&key.debugger);
if (keyDying || valDying || dbgDying) {
MOZ_ASSERT(key.kind != CrossCompartmentKey::StringWrapper);
e.removeFront();
} else if (key.wrapped != e.front().key().wrapped ||
key.debugger != e.front().key().debugger)
{
e.rekeyFront(key);
}
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -249,16 +249,17 @@ struct JSCompartment
/* Set of initial shapes in the compartment. */
js::InitialShapeSet initialShapes;
void sweepInitialShapeTable();
/* Set of default 'new' or lazy types in the compartment. */
js::types::TypeObjectWithNewScriptSet newTypeObjects;
js::types::TypeObjectWithNewScriptSet lazyTypeObjects;
void sweepNewTypeObjectTable(js::types::TypeObjectWithNewScriptSet &table);
+
#ifdef JSGC_HASH_TABLE_CHECKS
void checkTypeObjectTablesAfterMovingGC();
void checkTypeObjectTableAfterMovingGC(js::types::TypeObjectWithNewScriptSet &table);
void checkInitialShapesTableAfterMovingGC();
void checkWrapperMapAfterMovingGC();
#endif
/*
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2145,39 +2145,16 @@ js::ReportIncompatible(JSContext *cx, Ca
JSAutoByteString funNameBytes;
if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_METHOD,
funName, "method", InformalValueTypeName(call.thisv()));
}
}
}
-bool
-JSObject::hasIdempotentProtoChain() const
-{
- // Return false if obj (or an object on its proto chain) is non-native or
- // has a resolve or lookup hook.
- JSObject *obj = const_cast<JSObject *>(this);
- while (true) {
- if (!obj->isNative())
- return false;
-
- JSResolveOp resolve = obj->getClass()->resolve;
- if (resolve != JS_ResolveStub && resolve != (JSResolveOp) js::fun_resolve)
- return false;
-
- if (obj->getOps()->lookupProperty || obj->getOps()->lookupGeneric || obj->getOps()->lookupElement)
- return false;
-
- obj = obj->getProto();
- if (!obj)
- return true;
- }
-}
-
namespace JS {
namespace detail {
JS_PUBLIC_API(void)
CheckIsValidConstructible(Value calleev)
{
JSObject *callee = &calleev.toObject();
if (callee->is<JSFunction>())
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -658,74 +658,74 @@ FreeChunk(JSRuntime *rt, Chunk *p)
{
UnmapPages(static_cast<void *>(p), ChunkSize);
}
/* Must be called with the GC lock taken. */
inline Chunk *
ChunkPool::get(JSRuntime *rt)
{
- Chunk *chunk = emptyChunkListHead;
+ Chunk *chunk = head_;
if (!chunk) {
- MOZ_ASSERT(!emptyCount);
+ MOZ_ASSERT(!count_);
return nullptr;
}
- MOZ_ASSERT(emptyCount);
- emptyChunkListHead = chunk->info.next;
- --emptyCount;
+ MOZ_ASSERT(count_);
+ head_ = chunk->info.next;
+ --count_;
return chunk;
}
/* Must be called either during the GC or with the GC lock taken. */
inline void
ChunkPool::put(Chunk *chunk)
{
chunk->info.age = 0;
- chunk->info.next = emptyChunkListHead;
- emptyChunkListHead = chunk;
- emptyCount++;
+ chunk->info.next = head_;
+ head_ = chunk;
+ count_++;
}
inline Chunk *
ChunkPool::Enum::front()
{
Chunk *chunk = *chunkp;
- MOZ_ASSERT_IF(chunk, pool.getEmptyCount() != 0);
+ MOZ_ASSERT_IF(chunk, pool.count() != 0);
return chunk;
}
inline void
ChunkPool::Enum::popFront()
{
MOZ_ASSERT(!empty());
chunkp = &front()->info.next;
}
inline void
ChunkPool::Enum::removeAndPopFront()
{
MOZ_ASSERT(!empty());
*chunkp = front()->info.next;
- --pool.emptyCount;
+ --pool.count_;
}
/* Must be called either during the GC or with the GC lock taken. */
Chunk *
GCRuntime::expireChunkPool(bool shrinkBuffers, bool releaseAll)
{
/*
* Return old empty chunks to the system while preserving the order of
* other chunks in the list. This way, if the GC runs several times
* without emptying the list, the older chunks will stay at the tail
* and are more likely to reach the max age.
*/
Chunk *freeList = nullptr;
unsigned freeChunkCount = 0;
- for (ChunkPool::Enum e(chunkPool); !e.empty(); ) {
+ for (ChunkPool::Enum e(emptyChunks); !e.empty(); ) {
Chunk *chunk = e.front();
MOZ_ASSERT(chunk->unused());
MOZ_ASSERT(!chunkSet.has(chunk));
if (releaseAll || freeChunkCount >= tunables.maxEmptyChunkCount() ||
(freeChunkCount >= tunables.minEmptyChunkCount() &&
(shrinkBuffers || chunk->info.age == MAX_EMPTY_CHUNK_AGE)))
{
e.removeAndPopFront();
@@ -734,19 +734,19 @@ GCRuntime::expireChunkPool(bool shrinkBu
freeList = chunk;
} else {
/* Keep the chunk but increase its age. */
++freeChunkCount;
++chunk->info.age;
e.popFront();
}
}
- MOZ_ASSERT(chunkPool.getEmptyCount() <= tunables.maxEmptyChunkCount());
- MOZ_ASSERT_IF(shrinkBuffers, chunkPool.getEmptyCount() <= tunables.minEmptyChunkCount());
- MOZ_ASSERT_IF(releaseAll, chunkPool.getEmptyCount() == 0);
+ MOZ_ASSERT(emptyChunks.count() <= tunables.maxEmptyChunkCount());
+ MOZ_ASSERT_IF(shrinkBuffers, emptyChunks.count() <= tunables.minEmptyChunkCount());
+ MOZ_ASSERT_IF(releaseAll, emptyChunks.count() == 0);
return freeList;
}
void
GCRuntime::freeChunkList(Chunk *chunkListHead)
{
while (Chunk *chunk = chunkListHead) {
MOZ_ASSERT(!chunk->info.numArenasFreeCommitted);
@@ -1032,29 +1032,29 @@ Chunk::releaseArena(ArenaHeader *aheader
}
void
GCRuntime::moveChunkToFreePool(Chunk *chunk)
{
MOZ_ASSERT(chunk->unused());
MOZ_ASSERT(chunkSet.has(chunk));
chunkSet.remove(chunk);
- chunkPool.put(chunk);
+ emptyChunks.put(chunk);
}
inline bool
GCRuntime::wantBackgroundAllocation() const
{
/*
* To minimize memory waste we do not want to run the background chunk
* allocation if we have empty chunks or when the runtime needs just few
* of them.
*/
return helperState.canBackgroundAllocate() &&
- chunkPool.getEmptyCount() < tunables.minEmptyChunkCount() &&
+ emptyChunks.count() < tunables.minEmptyChunkCount() &&
chunkSet.count() >= 4;
}
class js::gc::AutoMaybeStartBackgroundAllocation
{
private:
JSRuntime *runtime;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
@@ -1083,17 +1083,17 @@ class js::gc::AutoMaybeStartBackgroundAl
Chunk *
GCRuntime::pickChunk(Zone *zone, AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation)
{
Chunk **listHeadp = getAvailableChunkList(zone);
Chunk *chunk = *listHeadp;
if (chunk)
return chunk;
- chunk = chunkPool.get(rt);
+ chunk = emptyChunks.get(rt);
if (!chunk) {
chunk = Chunk::allocate(rt);
if (!chunk)
return nullptr;
MOZ_ASSERT(chunk->info.numArenasFreeCommitted == 0);
}
MOZ_ASSERT(chunk->unused());
@@ -1507,19 +1507,19 @@ GCRuntime::getParameter(JSGCParamKey key
return uint32_t(tunables.gcMaxBytes());
case JSGC_MAX_MALLOC_BYTES:
return maxMallocBytes;
case JSGC_BYTES:
return uint32_t(usage.gcBytes());
case JSGC_MODE:
return uint32_t(mode);
case JSGC_UNUSED_CHUNKS:
- return uint32_t(chunkPool.getEmptyCount());
+ return uint32_t(emptyChunks.count());
case JSGC_TOTAL_CHUNKS:
- return uint32_t(chunkSet.count() + chunkPool.getEmptyCount());
+ return uint32_t(chunkSet.count() + emptyChunks.count());
case JSGC_SLICE_TIME_BUDGET:
return uint32_t(sliceBudget > 0 ? sliceBudget / PRMJ_USEC_PER_MSEC : 0);
case JSGC_MARK_STACK_LIMIT:
return marker.maxCapacity();
case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
return tunables.highFrequencyThresholdUsec();
case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
return tunables.highFrequencyLowLimitBytes() / 1024 / 1024;
@@ -2193,25 +2193,26 @@ RelocateCell(Zone *zone, TenuredCell *sr
// Copy source cell contents to destination.
memcpy(dst, src, thingSize);
if (thingKind <= FINALIZE_OBJECT_LAST) {
JSObject *srcObj = static_cast<JSObject *>(static_cast<Cell *>(src));
JSObject *dstObj = static_cast<JSObject *>(static_cast<Cell *>(dst));
// Fixup the pointer to inline object elements if necessary.
- if (srcObj->hasFixedElements())
- dstObj->setFixedElements();
+ if (srcObj->isNative() && srcObj->as<NativeObject>().hasFixedElements())
+ dstObj->as<NativeObject>().setFixedElements();
// Call object moved hook if present.
if (JSObjectMovedOp op = srcObj->getClass()->ext.objectMovedOp)
op(dstObj, srcObj);
MOZ_ASSERT_IF(dstObj->isNative(),
- !PtrIsInRange((const Value*)dstObj->getDenseElements(), src, thingSize));
+ !PtrIsInRange((const Value*)dstObj->as<NativeObject>().getDenseElements(),
+ src, thingSize));
}
// Copy the mark bits.
dst->copyMarkBitsFrom(src);
// Mark source cell as forwarded and leave a pointer to the destination.
RelocationOverlay* overlay = RelocationOverlay::fromCell(src);
overlay->forwardTo(dst);
@@ -3249,17 +3250,19 @@ GCHelperState::startBackgroundThread(Sta
HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER);
}
void
GCHelperState::waitForBackgroundThread()
{
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
+#ifdef DEBUG
rt->gc.lockOwner = nullptr;
+#endif
PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT);
#ifdef DEBUG
rt->gc.lockOwner = PR_GetCurrentThread();
#endif
}
void
GCHelperState::work()
@@ -3294,17 +3297,17 @@ GCHelperState::work()
AutoUnlockGC unlock(rt);
chunk = Chunk::allocate(rt);
}
/* OOM stops the background allocation. */
if (!chunk)
break;
MOZ_ASSERT(chunk->info.numArenasFreeCommitted == 0);
- rt->gc.chunkPool.put(chunk);
+ rt->gc.emptyChunks.put(chunk);
} while (state() == ALLOCATING && rt->gc.wantBackgroundAllocation());
MOZ_ASSERT(state() == ALLOCATING || state() == CANCEL_ALLOCATION);
break;
}
case CANCEL_ALLOCATION:
break;
@@ -4604,16 +4607,107 @@ GCRuntime::endMarkingZoneGroup()
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
MOZ_ASSERT(zone->isGCMarkingGray());
zone->setGCState(Zone::Mark);
}
MOZ_ASSERT(marker.isDrained());
marker.setMarkColorBlack();
}
+#define MAKE_GC_PARALLEL_TASK(name) \
+ class name : public GCParallelTask {\
+ JSRuntime *runtime;\
+ virtual void run() MOZ_OVERRIDE;\
+ public:\
+ name (JSRuntime *rt) : runtime(rt) {}\
+ }
+MAKE_GC_PARALLEL_TASK(SweepAtomsTask);
+MAKE_GC_PARALLEL_TASK(SweepInnerViewsTask);
+MAKE_GC_PARALLEL_TASK(SweepCCWrappersTask);
+MAKE_GC_PARALLEL_TASK(SweepBaseShapesTask);
+MAKE_GC_PARALLEL_TASK(SweepInitialShapesTask);
+MAKE_GC_PARALLEL_TASK(SweepTypeObjectsTask);
+MAKE_GC_PARALLEL_TASK(SweepRegExpsTask);
+MAKE_GC_PARALLEL_TASK(SweepMiscTask);
+
+/* virtual */ void
+SweepAtomsTask::run()
+{
+ runtime->sweepAtoms();
+}
+
+/* virtual */ void
+SweepInnerViewsTask::run()
+{
+ for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
+ c->sweepInnerViews();
+}
+
+/* virtual */ void
+SweepCCWrappersTask::run()
+{
+ for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
+ c->sweepCrossCompartmentWrappers();
+}
+
+/* virtual */ void
+SweepBaseShapesTask::run()
+{
+ for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
+ c->sweepBaseShapeTable();
+}
+
+/* virtual */ void
+SweepInitialShapesTask::run()
+{
+ for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
+ c->sweepInitialShapeTable();
+}
+
+/* virtual */ void
+SweepTypeObjectsTask::run()
+{
+ for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
+ c->sweepTypeObjectTables();
+}
+
+/* virtual */ void
+SweepRegExpsTask::run()
+{
+ for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
+ c->sweepRegExps();
+}
+
+/* virtual */ void
+SweepMiscTask::run()
+{
+ for (GCCompartmentGroupIter c(runtime); !c.done(); c.next()) {
+ c->sweepCallsiteClones();
+ c->sweepSavedStacks();
+ c->sweepSelfHostingScriptSource();
+ c->sweepNativeIterators();
+ }
+}
+
+void
+GCRuntime::startTask(GCParallelTask &task, gcstats::Phase phase)
+{
+ if (!task.startWithLockHeld()) {
+ gcstats::AutoPhase ap(stats, phase);
+ task.runFromMainThread(rt);
+ }
+}
+
+void
+GCRuntime::joinTask(GCParallelTask &task, gcstats::Phase phase)
+{
+ gcstats::AutoPhase ap(stats, task, phase);
+ task.joinWithLockHeld();
+}
+
void
GCRuntime::beginSweepingZoneGroup()
{
/*
* Begin sweeping the group of zones in gcCurrentZoneGroup,
* performing actions that must be done before yielding to caller.
*/
@@ -4633,85 +4727,61 @@ GCRuntime::beginSweepingZoneGroup()
rt->sweepZoneCallback(zone);
zone->gcLastZoneGroupIndex = zoneGroupIndex;
}
validateIncrementalMarking();
FreeOp fop(rt);
+ SweepAtomsTask sweepAtomsTask(rt);
+ SweepInnerViewsTask sweepInnerViewsTask(rt);
+ SweepCCWrappersTask sweepCCWrappersTask(rt);
+ SweepBaseShapesTask sweepBaseShapesTask(rt);
+ SweepInitialShapesTask sweepInitialShapesTask(rt);
+ SweepTypeObjectsTask sweepTypeObjectsTask(rt);
+ SweepRegExpsTask sweepRegExpsTask(rt);
+ SweepMiscTask sweepMiscTask(rt);
{
gcstats::AutoPhase ap(stats, gcstats::PHASE_FINALIZE_START);
callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_START);
callWeakPointerCallbacks();
}
if (sweepingAtoms) {
- gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_ATOMS);
- rt->sweepAtoms();
+ AutoLockHelperThreadState helperLock;
+ startTask(sweepAtomsTask, gcstats::PHASE_SWEEP_ATOMS);
}
{
gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS);
gcstats::AutoSCC scc(stats, zoneGroupIndex);
{
- gcstats::AutoPhase apiv(stats, gcstats::PHASE_SWEEP_INNER_VIEWS);
- for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
- c->sweepInnerViews();
- }
+ AutoLockHelperThreadState helperLock;
+ startTask(sweepInnerViewsTask, gcstats::PHASE_SWEEP_INNER_VIEWS);
+ startTask(sweepCCWrappersTask, gcstats::PHASE_SWEEP_CC_WRAPPER);
+ startTask(sweepBaseShapesTask, gcstats::PHASE_SWEEP_BASE_SHAPE);
+ startTask(sweepInitialShapesTask, gcstats::PHASE_SWEEP_INITIAL_SHAPE);
+ startTask(sweepTypeObjectsTask, gcstats::PHASE_SWEEP_TYPE_OBJECT);
+ startTask(sweepRegExpsTask, gcstats::PHASE_SWEEP_REGEXP);
+ startTask(sweepMiscTask, gcstats::PHASE_SWEEP_MISC);
}
+ // The remainder of the of the tasks run in parallel on the main
+ // thread until we join, below.
{
- gcstats::AutoPhase apccw(stats, gcstats::PHASE_SWEEP_CC_WRAPPER);
- for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
- c->sweepCrossCompartmentWrappers();
- }
- }
-
- {
- gcstats::AutoPhase apbs(stats, gcstats::PHASE_SWEEP_BASE_SHAPE);
- for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
- c->sweepBaseShapeTable();
- }
- }
-
- {
- gcstats::AutoPhase apis(stats, gcstats::PHASE_SWEEP_INITIAL_SHAPE);
+ gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_MISC);
+
for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
- c->sweepInitialShapeTable();
- }
- }
-
- {
- gcstats::AutoPhase apto(stats, gcstats::PHASE_SWEEP_TYPE_OBJECT);
- for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
- c->sweepTypeObjectTables();
- }
- }
-
- {
- gcstats::AutoPhase apre(stats, gcstats::PHASE_SWEEP_REGEXP);
- for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
- c->sweepRegExps();
- }
- }
-
- {
- gcstats::AutoPhase apmisc(stats, gcstats::PHASE_SWEEP_MISC);
- for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
- c->sweepCallsiteClones();
- c->sweepSavedStacks();
c->sweepGlobalObject(&fop);
- c->sweepSelfHostingScriptSource();
c->sweepDebugScopes();
c->sweepJitCompartment(&fop);
c->sweepWeakMaps();
- c->sweepNativeIterators();
}
// Bug 1071218: the following two methods have not yet been
// refactored to work on a single zone-group at once.
// Collect watch points associated with unreachable objects.
WatchpointMap::sweepAll(rt);
@@ -4741,16 +4811,36 @@ GCRuntime::beginSweepingZoneGroup()
}
}
if (sweepingAtoms) {
gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_SYMBOL_REGISTRY);
rt->symbolRegistry().sweep();
}
+ // Rejoin our off-main-thread tasks.
+ if (sweepingAtoms) {
+ AutoLockHelperThreadState helperLock;
+ joinTask(sweepAtomsTask, gcstats::PHASE_SWEEP_ATOMS);
+ }
+
+ {
+ gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS);
+ gcstats::AutoSCC scc(stats, zoneGroupIndex);
+
+ AutoLockHelperThreadState helperLock;
+ joinTask(sweepInnerViewsTask, gcstats::PHASE_SWEEP_INNER_VIEWS);
+ joinTask(sweepCCWrappersTask, gcstats::PHASE_SWEEP_CC_WRAPPER);
+ joinTask(sweepBaseShapesTask, gcstats::PHASE_SWEEP_BASE_SHAPE);
+ joinTask(sweepInitialShapesTask, gcstats::PHASE_SWEEP_INITIAL_SHAPE);
+ joinTask(sweepTypeObjectsTask, gcstats::PHASE_SWEEP_TYPE_OBJECT);
+ joinTask(sweepRegExpsTask, gcstats::PHASE_SWEEP_REGEXP);
+ joinTask(sweepMiscTask, gcstats::PHASE_SWEEP_MISC);
+ }
+
/*
* Queue all GC things in all zones for sweeping, either in the
* foreground or on the background thread.
*
* Note that order is important here for the background case.
*
* Objects are finalized immediately but this may change in the future.
*/
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1065,16 +1065,57 @@ class GCHelperState
}
bool shouldShrink() const {
MOZ_ASSERT(isBackgroundSweeping());
return shrinkFlag;
}
};
+// A generic task used to dispatch work to the helper thread system.
+// Users should derive from GCParallelTask add what data they need and
+// override |run|.
+class GCParallelTask
+{
+ // The state of the parallel computation.
+ enum TaskState {
+ NotStarted,
+ Dispatched,
+ Finished,
+ } state;
+
+ // Amount of time this task took to execute.
+ uint64_t duration_;
+
+ protected:
+ virtual void run() = 0;
+
+ public:
+ GCParallelTask() : state(NotStarted), duration_(0) {}
+
+ int64_t duration() const { return duration_; }
+
+ // The simple interface to a parallel task works exactly like pthreads.
+ bool start();
+ void join();
+
+ // If multiple tasks are to be started or joined at once, it is more
+ // efficient to take the helper thread lock once and use these methods.
+ bool startWithLockHeld();
+ void joinWithLockHeld();
+
+ // Instead of dispatching to a helper, run the task on the main thread.
+ void runFromMainThread(JSRuntime *rt);
+
+ // This should be friended to HelperThread, but cannot be because it
+ // would introduce several circular dependencies.
+ public:
+ void runFromHelperThread();
+};
+
struct GCChunkHasher {
typedef gc::Chunk *Lookup;
/*
* Strip zeros for better distribution after multiplying by the golden
* ratio.
*/
static HashNumber hash(gc::Chunk *chunk) {
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -4779,27 +4779,27 @@ TypeCompartment::sweep(FreeOp *fop)
e.rekeyFront(key);
}
}
}
void
JSCompartment::sweepNewTypeObjectTable(TypeObjectWithNewScriptSet &table)
{
- MOZ_ASSERT(zone()->isCollecting());
+ MOZ_ASSERT(zone()->runtimeFromAnyThread()->isHeapCollecting());
if (table.initialized()) {
for (TypeObjectWithNewScriptSet::Enum e(table); !e.empty(); e.popFront()) {
TypeObjectWithNewScriptEntry entry = e.front();
- if (IsTypeObjectAboutToBeFinalized(entry.object.unsafeGet()) ||
- (entry.newFunction && IsObjectAboutToBeFinalized(&entry.newFunction)))
+ if (IsTypeObjectAboutToBeFinalizedFromAnyThread(entry.object.unsafeGet()) ||
+ (entry.newFunction && IsObjectAboutToBeFinalizedFromAnyThread(&entry.newFunction)))
{
e.removeFront();
} else {
/* Any rekeying necessary is handled by fixupNewTypeObjectTable() below. */
- MOZ_ASSERT(entry.object == e.front().object);
+ MOZ_ASSERT(entry.object.unbarrieredGet() == e.front().object.unbarrieredGet());
MOZ_ASSERT(entry.newFunction == e.front().newFunction);
}
}
}
}
#ifdef JSGC_COMPACTING
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -30,16 +30,17 @@
#include "vm/GlobalObject.h"
#include "vm/NumericConversions.h"
#include "vm/StringBuffer.h"
#include "jsatominlines.h"
#include "vm/NumberObject-inl.h"
+#include "vm/ObjectImpl-inl.h"
#include "vm/String-inl.h"
using namespace js;
using namespace js::types;
using mozilla::Abs;
using mozilla::ArrayLength;
using mozilla::MinNumberValue;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -6,17 +6,16 @@
/*
* JS object implementation.
*/
#include "jsobjinlines.h"
#include "mozilla/ArrayUtils.h"
-#include "mozilla/CheckedInt.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/TemplateLib.h"
#include <string.h>
#include "jsapi.h"
#include "jsarray.h"
@@ -68,20 +67,16 @@
#include "vm/StringObject-inl.h"
using namespace js;
using namespace js::gc;
using namespace js::types;
using mozilla::DebugOnly;
using mozilla::Maybe;
-using mozilla::RoundUpPow2;
-
-JS_STATIC_ASSERT(int32_t((NativeObject::NELEMENTS_LIMIT - 1) * sizeof(Value)) ==
- int64_t((NativeObject::NELEMENTS_LIMIT - 1) * sizeof(Value)));
static JSObject *
CreateObjectConstructor(JSContext *cx, JSProtoKey key)
{
Rooted<GlobalObject*> self(cx, cx->global());
if (!GlobalObject::ensureConstructor(cx, self, JSProto_Function))
return nullptr;
@@ -651,59 +646,16 @@ Reject(JSContext *cx, HandleId id, unsig
if (throwError)
return Throw(cx, id, errorNumber);
*rval = false;
return true;
}
static unsigned
-ApplyAttributes(unsigned attrs, bool enumerable, bool writable, bool configurable)
-{
- /*
- * Respect the fact that some callers may want to preserve existing attributes as much as
- * possible, or install defaults otherwise.
- */
- if (attrs & JSPROP_IGNORE_ENUMERATE) {
- attrs &= ~JSPROP_IGNORE_ENUMERATE;
- if (enumerable)
- attrs |= JSPROP_ENUMERATE;
- else
- attrs &= ~JSPROP_ENUMERATE;
- }
- if (attrs & JSPROP_IGNORE_READONLY) {
- attrs &= ~JSPROP_IGNORE_READONLY;
- // Only update the writability if it's relevant
- if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
- if (!writable)
- attrs |= JSPROP_READONLY;
- else
- attrs &= ~JSPROP_READONLY;
- }
- }
- if (attrs & JSPROP_IGNORE_PERMANENT) {
- attrs &= ~JSPROP_IGNORE_PERMANENT;
- if (!configurable)
- attrs |= JSPROP_PERMANENT;
- else
- attrs &= ~JSPROP_PERMANENT;
- }
- return attrs;
-}
-
-static unsigned
-ApplyOrDefaultAttributes(unsigned attrs, const Shape *shape = nullptr)
-{
- bool enumerable = shape ? shape->enumerable() : false;
- bool writable = shape ? shape->writable() : false;
- bool configurable = shape ? shape->configurable() : false;
- return ApplyAttributes(attrs, enumerable, writable, configurable);
-}
-
-static unsigned
ApplyOrDefaultAttributes(unsigned attrs, Handle<PropertyDescriptor> desc)
{
bool present = !!desc.object();
bool enumerable = present ? desc.isEnumerable() : false;
bool writable = present ? !desc.isReadonly() : false;
bool configurable = present ? !desc.isPermanent() : false;
return ApplyAttributes(attrs, enumerable, writable, configurable);
}
@@ -1860,62 +1812,16 @@ js::CreateThisForFunction(JSContext *cx,
TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(nobj));
return nobj;
}
return obj;
}
-/*
- * Given pc pointing after a property accessing bytecode, return true if the
- * access is "object-detecting" in the sense used by web scripts, e.g., when
- * checking whether document.all is defined.
- */
-static bool
-Detecting(JSContext *cx, JSScript *script, jsbytecode *pc)
-{
- MOZ_ASSERT(script->containsPC(pc));
-
- /* General case: a branch or equality op follows the access. */
- JSOp op = JSOp(*pc);
- if (js_CodeSpec[op].format & JOF_DETECTING)
- return true;
-
- jsbytecode *endpc = script->codeEnd();
-
- if (op == JSOP_NULL) {
- /*
- * Special case #1: handle (document.all == null). Don't sweat
- * about JS1.2's revision of the equality operators here.
- */
- if (++pc < endpc) {
- op = JSOp(*pc);
- return op == JSOP_EQ || op == JSOP_NE;
- }
- return false;
- }
-
- if (op == JSOP_GETGNAME || op == JSOP_NAME) {
- /*
- * Special case #2: handle (document.all == undefined). Don't worry
- * about a local variable named |undefined| shadowing the immutable
- * global binding...because, really?
- */
- JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc));
- if (atom == cx->names().undefined &&
- (pc += js_CodeSpec[op].length) < endpc) {
- op = JSOp(*pc);
- return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
- }
- }
-
- return false;
-}
-
/* static */ bool
JSObject::nonNativeSetProperty(JSContext *cx, HandleObject obj,
HandleId id, MutableHandleValue vp, bool strict)
{
if (MOZ_UNLIKELY(obj->watched())) {
WatchpointMap *wpmap = cx->compartment()->watchpointMap;
if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
return false;
@@ -2936,673 +2842,16 @@ js_InitClass(JSContext *cx, HandleObject
{
return nullptr;
}
return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs,
ps, fs, static_ps, static_fs, ctorp, ctorKind);
}
-/* static */ inline bool
-NativeObject::updateSlotsForSpan(ThreadSafeContext *cx,
- HandleNativeObject obj, size_t oldSpan, size_t newSpan)
-{
- MOZ_ASSERT(cx->isThreadLocal(obj));
- MOZ_ASSERT(oldSpan != newSpan);
-
- size_t oldCount = dynamicSlotsCount(obj->numFixedSlots(), oldSpan, obj->getClass());
- size_t newCount = dynamicSlotsCount(obj->numFixedSlots(), newSpan, obj->getClass());
-
- if (oldSpan < newSpan) {
- if (oldCount < newCount && !growSlots(cx, obj, oldCount, newCount))
- return false;
-
- if (newSpan == oldSpan + 1)
- obj->initSlotUnchecked(oldSpan, UndefinedValue());
- else
- obj->initializeSlotRange(oldSpan, newSpan - oldSpan);
- } else {
- /* Trigger write barriers on the old slots before reallocating. */
- obj->prepareSlotRangeForOverwrite(newSpan, oldSpan);
- obj->invalidateSlotRange(newSpan, oldSpan - newSpan);
-
- if (oldCount > newCount)
- shrinkSlots(cx, obj, oldCount, newCount);
- }
-
- return true;
-}
-
-/* static */ bool
-NativeObject::setLastProperty(ThreadSafeContext *cx, HandleNativeObject obj, HandleShape shape)
-{
- MOZ_ASSERT(cx->isThreadLocal(obj));
- MOZ_ASSERT(!obj->inDictionaryMode());
- MOZ_ASSERT(!shape->inDictionary());
- MOZ_ASSERT(shape->compartment() == obj->compartment());
- MOZ_ASSERT(shape->numFixedSlots() == obj->numFixedSlots());
-
- size_t oldSpan = obj->lastProperty()->slotSpan();
- size_t newSpan = shape->slotSpan();
-
- if (oldSpan == newSpan) {
- obj->shape_ = shape;
- return true;
- }
-
- if (!updateSlotsForSpan(cx, obj, oldSpan, newSpan))
- return false;
-
- obj->shape_ = shape;
- return true;
-}
-
-void
-NativeObject::setLastPropertyShrinkFixedSlots(Shape *shape)
-{
- MOZ_ASSERT(!inDictionaryMode());
- MOZ_ASSERT(!shape->inDictionary());
- MOZ_ASSERT(shape->compartment() == compartment());
- MOZ_ASSERT(lastProperty()->slotSpan() == shape->slotSpan());
-
- DebugOnly<size_t> oldFixed = numFixedSlots();
- DebugOnly<size_t> newFixed = shape->numFixedSlots();
- MOZ_ASSERT(newFixed < oldFixed);
- MOZ_ASSERT(shape->slotSpan() <= oldFixed);
- MOZ_ASSERT(shape->slotSpan() <= newFixed);
- MOZ_ASSERT(dynamicSlotsCount(oldFixed, shape->slotSpan(), getClass()) == 0);
- MOZ_ASSERT(dynamicSlotsCount(newFixed, shape->slotSpan(), getClass()) == 0);
-
- shape_ = shape;
-}
-
-/* static */ bool
-NativeObject::setSlotSpan(ThreadSafeContext *cx, HandleNativeObject obj, uint32_t span)
-{
- MOZ_ASSERT(cx->isThreadLocal(obj));
- MOZ_ASSERT(obj->inDictionaryMode());
-
- size_t oldSpan = obj->lastProperty()->base()->slotSpan();
- if (oldSpan == span)
- return true;
-
- if (!updateSlotsForSpan(cx, obj, oldSpan, span))
- return false;
-
- obj->lastProperty()->base()->setSlotSpan(span);
- return true;
-}
-
-// This will not run the garbage collector. If a nursery cannot accomodate the slot array
-// an attempt will be made to place the array in the tenured area.
-static HeapSlot *
-AllocateSlots(ThreadSafeContext *cx, JSObject *obj, uint32_t nslots)
-{
-#ifdef JSGC_GENERATIONAL
- if (cx->isJSContext())
- return cx->asJSContext()->runtime()->gc.nursery.allocateSlots(obj, nslots);
-#endif
-#ifdef JSGC_FJGENERATIONAL
- if (cx->isForkJoinContext())
- return cx->asForkJoinContext()->nursery().allocateSlots(obj, nslots);
-#endif
- return obj->zone()->pod_malloc<HeapSlot>(nslots);
-}
-
-// This will not run the garbage collector. If a nursery cannot accomodate the slot array
-// an attempt will be made to place the array in the tenured area.
-//
-// If this returns null then the old slots will be left alone.
-static HeapSlot *
-ReallocateSlots(ThreadSafeContext *cx, JSObject *obj, HeapSlot *oldSlots,
- uint32_t oldCount, uint32_t newCount)
-{
-#ifdef JSGC_GENERATIONAL
- if (cx->isJSContext()) {
- return cx->asJSContext()->runtime()->gc.nursery.reallocateSlots(obj, oldSlots,
- oldCount, newCount);
- }
-#endif
-#ifdef JSGC_FJGENERATIONAL
- if (cx->isForkJoinContext()) {
- return cx->asForkJoinContext()->nursery().reallocateSlots(obj, oldSlots,
- oldCount, newCount);
- }
-#endif
- return obj->zone()->pod_realloc<HeapSlot>(oldSlots, oldCount, newCount);
-}
-
-/* static */ bool
-NativeObject::growSlots(ThreadSafeContext *cx, HandleNativeObject obj, uint32_t oldCount, uint32_t newCount)
-{
- MOZ_ASSERT(cx->isThreadLocal(obj));
- MOZ_ASSERT(newCount > oldCount);
- MOZ_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
-
- /*
- * Slot capacities are determined by the span of allocated objects. Due to
- * the limited number of bits to store shape slots, object growth is
- * throttled well before the slot capacity can overflow.
- */
- MOZ_ASSERT(newCount < NELEMENTS_LIMIT);
-
- if (!oldCount) {
- obj->slots = AllocateSlots(cx, obj, newCount);
- if (!obj->slots)
- return false;
- Debug_SetSlotRangeToCrashOnTouch(obj->slots, newCount);
- return true;
- }
-
- HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount);
- if (!newslots)
- return false; /* Leave slots at its old size. */
-
- obj->slots = newslots;
-
- Debug_SetSlotRangeToCrashOnTouch(obj->slots + oldCount, newCount - oldCount);
-
- return true;
-}
-
-static void
-FreeSlots(ThreadSafeContext *cx, HeapSlot *slots)
-{
-#ifdef JSGC_GENERATIONAL
- // Note: threads without a JSContext do not have access to GGC nursery allocated things.
- if (cx->isJSContext())
- return cx->asJSContext()->runtime()->gc.nursery.freeSlots(slots);
-#endif
-#ifdef JSGC_FJGENERATIONAL
- if (cx->isForkJoinContext())
- return cx->asForkJoinContext()->nursery().freeSlots(slots);
-#endif
- js_free(slots);
-}
-
-/* static */ void
-NativeObject::shrinkSlots(ThreadSafeContext *cx, HandleNativeObject obj,
- uint32_t oldCount, uint32_t newCount)
-{
- MOZ_ASSERT(cx->isThreadLocal(obj));
- MOZ_ASSERT(newCount < oldCount);
-
- if (newCount == 0) {
- FreeSlots(cx, obj->slots);
- obj->slots = nullptr;
- return;
- }
-
- MOZ_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
-
- HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount);
- if (!newslots)
- return; /* Leave slots at its old size. */
-
- obj->slots = newslots;
-}
-
-/* static */ bool
-NativeObject::sparsifyDenseElement(ExclusiveContext *cx, HandleNativeObject obj, uint32_t index)
-{
- if (!obj->maybeCopyElementsForWrite(cx))
- return false;
-
- RootedValue value(cx, obj->getDenseElement(index));
- MOZ_ASSERT(!value.isMagic(JS_ELEMENTS_HOLE));
-
- removeDenseElementForSparseIndex(cx, obj, index);
-
- uint32_t slot = obj->slotSpan();
- if (!obj->addDataProperty(cx, INT_TO_JSID(index), slot, JSPROP_ENUMERATE)) {
- obj->setDenseElement(index, value);
- return false;
- }
-
- MOZ_ASSERT(slot == obj->slotSpan() - 1);
- obj->initSlot(slot, value);
-
- return true;
-}
-
-/* static */ bool
-NativeObject::sparsifyDenseElements(js::ExclusiveContext *cx, HandleNativeObject obj)
-{
- if (!obj->maybeCopyElementsForWrite(cx))
- return false;
-
- uint32_t initialized = obj->getDenseInitializedLength();
-
- /* Create new properties with the value of non-hole dense elements. */
- for (uint32_t i = 0; i < initialized; i++) {
- if (obj->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE))
- continue;
-
- if (!sparsifyDenseElement(cx, obj, i))
- return false;
- }
-
- if (initialized)
- obj->setDenseInitializedLength(0);
-
- /*
- * Reduce storage for dense elements which are now holes. Explicitly mark
- * the elements capacity as zero, so that any attempts to add dense
- * elements will be caught in ensureDenseElements.
- */
- if (obj->getDenseCapacity()) {
- obj->shrinkElements(cx, 0);
- obj->getElementsHeader()->capacity = 0;
- }
-
- return true;
-}
-
-bool
-NativeObject::willBeSparseElements(uint32_t requiredCapacity, uint32_t newElementsHint)
-{
- MOZ_ASSERT(isNative());
- MOZ_ASSERT(requiredCapacity > MIN_SPARSE_INDEX);
-
- uint32_t cap = getDenseCapacity();
- MOZ_ASSERT(requiredCapacity >= cap);
-
- if (requiredCapacity >= NELEMENTS_LIMIT)
- return true;
-
- uint32_t minimalDenseCount = requiredCapacity / SPARSE_DENSITY_RATIO;
- if (newElementsHint >= minimalDenseCount)
- return false;
- minimalDenseCount -= newElementsHint;
-
- if (minimalDenseCount > cap)
- return true;
-
- uint32_t len = getDenseInitializedLength();
- const Value *elems = getDenseElements();
- for (uint32_t i = 0; i < len; i++) {
- if (!elems[i].isMagic(JS_ELEMENTS_HOLE) && !--minimalDenseCount)
- return false;
- }
- return true;
-}
-
-/* static */ NativeObject::EnsureDenseResult
-NativeObject::maybeDensifySparseElements(js::ExclusiveContext *cx, HandleNativeObject obj)
-{
- /*
- * Wait until after the object goes into dictionary mode, which must happen
- * when sparsely packing any array with more than MIN_SPARSE_INDEX elements
- * (see PropertyTree::MAX_HEIGHT).
- */
- if (!obj->inDictionaryMode())
- return ED_SPARSE;
-
- /*
- * Only measure the number of indexed properties every log(n) times when
- * populating the object.
- */
- uint32_t slotSpan = obj->slotSpan();
- if (slotSpan != RoundUpPow2(slotSpan))
- return ED_SPARSE;
-
- /* Watch for conditions under which an object's elements cannot be dense. */
- if (!obj->nonProxyIsExtensible() || obj->watched())
- return ED_SPARSE;
-
- /*
- * The indexes in the object need to be sufficiently dense before they can
- * be converted to dense mode.
- */
- uint32_t numDenseElements = 0;
- uint32_t newInitializedLength = 0;
-
- RootedShape shape(cx, obj->lastProperty());
- while (!shape->isEmptyShape()) {
- uint32_t index;
- if (js_IdIsIndex(shape->propid(), &index)) {
- if (shape->attributes() == JSPROP_ENUMERATE &&
- shape->hasDefaultGetter() &&
- shape->hasDefaultSetter())
- {
- numDenseElements++;
- newInitializedLength = Max(newInitializedLength, index + 1);
- } else {
- /*
- * For simplicity, only densify the object if all indexed
- * properties can be converted to dense elements.
- */
- return ED_SPARSE;
- }
- }
- shape = shape->previous();
- }
-
- if (numDenseElements * SPARSE_DENSITY_RATIO < newInitializedLength)
- return ED_SPARSE;
-
- if (newInitializedLength >= NELEMENTS_LIMIT)
- return ED_SPARSE;
-
- /*
- * This object meets all necessary restrictions, convert all indexed
- * properties into dense elements.
- */
-
- if (!obj->maybeCopyElementsForWrite(cx))
- return ED_FAILED;
-
- if (newInitializedLength > obj->getDenseCapacity()) {
- if (!obj->growElements(cx, newInitializedLength))
- return ED_FAILED;
- }
-
- obj->ensureDenseInitializedLength(cx, newInitializedLength, 0);
-
- RootedValue value(cx);
-
- shape = obj->lastProperty();
- while (!shape->isEmptyShape()) {
- jsid id = shape->propid();
- uint32_t index;
- if (js_IdIsIndex(id, &index)) {
- value = obj->getSlot(shape->slot());
-
- /*
- * When removing a property from a dictionary, the specified
- * property will be removed from the dictionary list and the
- * last property will then be changed due to reshaping the object.
- * Compute the next shape in the traverse, watching for such
- * removals from the list.
- */
- if (shape != obj->lastProperty()) {
- shape = shape->previous();
- if (!obj->removeProperty(cx, id))
- return ED_FAILED;
- } else {
- if (!obj->removeProperty(cx, id))
- return ED_FAILED;
- shape = obj->lastProperty();
- }
-
- obj->setDenseElement(index, value);
- } else {
- shape = shape->previous();
- }
- }
-
- /*
- * All indexed properties on the object are now dense, clear the indexed
- * flag so that we will not start using sparse indexes again if we need
- * to grow the object.
- */
- if (!obj->clearFlag(cx, BaseShape::INDEXED))
- return ED_FAILED;
-
- return ED_OK;
-}
-
-// This will not run the garbage collector. If a nursery cannot accomodate the element array
-// an attempt will be made to place the array in the tenured area.
-static ObjectElements *
-AllocateElements(ThreadSafeContext *cx, JSObject *obj, uint32_t nelems)
-{
-#ifdef JSGC_GENERATIONAL
- if (cx->isJSContext())
- return cx->asJSContext()->runtime()->gc.nursery.allocateElements(obj, nelems);
-#endif
-#ifdef JSGC_FJGENERATIONAL
- if (cx->isForkJoinContext())
- return cx->asForkJoinContext()->nursery().allocateElements(obj, nelems);
-#endif
-
- return reinterpret_cast<js::ObjectElements *>(obj->zone()->pod_malloc<HeapSlot>(nelems));
-}
-
-// This will not run the garbage collector. If a nursery cannot accomodate the element array
-// an attempt will be made to place the array in the tenured area.
-static ObjectElements *
-ReallocateElements(ThreadSafeContext *cx, JSObject *obj, ObjectElements *oldHeader,
- uint32_t oldCount, uint32_t newCount)
-{
-#ifdef JSGC_GENERATIONAL
- if (cx->isJSContext()) {
- return cx->asJSContext()->runtime()->gc.nursery.reallocateElements(obj, oldHeader,
- oldCount, newCount);
- }
-#endif
-#ifdef JSGC_FJGENERATIONAL
- if (cx->isForkJoinContext()) {
- return cx->asForkJoinContext()->nursery().reallocateElements(obj, oldHeader,
- oldCount, newCount);
- }
-#endif
-
- return reinterpret_cast<js::ObjectElements *>(
- obj->zone()->pod_realloc<HeapSlot>(reinterpret_cast<HeapSlot *>(oldHeader),
- oldCount, newCount));
-}
-
-// Round up |reqAllocated| to a good size. Up to 1 Mebi (i.e. 1,048,576) the
-// slot count is usually a power-of-two:
-//
-// 8, 16, 32, 64, ..., 256 Ki, 512 Ki, 1 Mi
-//
-// Beyond that, we use this formula:
-//
-// count(n+1) = Math.ceil(count(n) * 1.125)
-//
-// where |count(n)| is the size of the nth bucket measured in MiSlots.
-//
-// These counts lets us add N elements to an array in amortized O(N) time.
-// Having the second class means that for bigger arrays the constant factor is
-// higher, but we waste less space.
-//
-// There is one exception to the above rule: for the power-of-two cases, if the
-// chosen capacity would be 2/3 or more of the array's length, the chosen
-// capacity is adjusted (up or down) to be equal to the array's length
-// (assuming length is at least as large as the required capacity). This avoids
-// the allocation of excess elements which are unlikely to be needed, either in
-// this resizing or a subsequent one. The 2/3 factor is chosen so that
-// exceptional resizings will at most triple the capacity, as opposed to the
-// usual doubling.
-//
-/* static */ uint32_t
-NativeObject::goodAllocated(uint32_t reqAllocated, uint32_t length = 0)
-{
- static const uint32_t Mebi = 1024 * 1024;
-
- // This table was generated with this JavaScript code and a small amount
- // subsequent reformatting:
- //
- // for (let n = 1, i = 0; i < 57; i++) {
- // print((n * 1024 * 1024) + ', ');
- // n = Math.ceil(n * 1.125);
- // }
- // print('0');
- //
- // The final element is a sentinel value.
- static const uint32_t BigBuckets[] = {
- 1048576, 2097152, 3145728, 4194304, 5242880, 6291456, 7340032, 8388608,
- 9437184, 11534336, 13631488, 15728640, 17825792, 20971520, 24117248,
- 27262976, 31457280, 35651584, 40894464, 46137344, 52428800, 59768832,
- 68157440, 77594624, 88080384, 99614720, 112197632, 126877696,
- 143654912, 162529280, 183500800, 206569472, 232783872, 262144000,
- 295698432, 333447168, 375390208, 422576128, 476053504, 535822336,
- 602931200, 678428672, 763363328, 858783744, 966787072, 1088421888,
- 1224736768, 1377828864, 1550843904, 1744830464, 1962934272, 2208301056,
- 2485125120, 2796552192, 3146776576, 3541041152, 3984588800, 0
- };
-
- // This code relies very much on |goodAllocated| being a uint32_t.
- uint32_t goodAllocated = reqAllocated;
- if (goodAllocated < Mebi) {
- goodAllocated = RoundUpPow2(goodAllocated);
-
- // Look for the abovementioned exception.
- uint32_t goodCapacity = goodAllocated - ObjectElements::VALUES_PER_HEADER;
- uint32_t reqCapacity = reqAllocated - ObjectElements::VALUES_PER_HEADER;
- if (length >= reqCapacity && goodCapacity > (length / 3) * 2)
- goodAllocated = length + ObjectElements::VALUES_PER_HEADER;
-
- if (goodAllocated < SLOT_CAPACITY_MIN)
- goodAllocated = SLOT_CAPACITY_MIN;
-
- } else {
- uint32_t i = 0;
- while (true) {
- uint32_t b = BigBuckets[i++];
- if (b >= goodAllocated) {
- // Found the first bucket greater than or equal to
- // |goodAllocated|.
- goodAllocated = b;
- break;
- } else if (b == 0) {
- // Hit the end; return the maximum possible goodAllocated.
- goodAllocated = 0xffffffff;
- break;
- }
- }
- }
-
- return goodAllocated;
-}
-
-bool
-NativeObject::growElements(ThreadSafeContext *cx, uint32_t reqCapacity)
-{
- MOZ_ASSERT(nonProxyIsExtensible());
- MOZ_ASSERT(canHaveNonEmptyElements());
- if (denseElementsAreCopyOnWrite())
- MOZ_CRASH();
-
- uint32_t oldCapacity = getDenseCapacity();
- MOZ_ASSERT(oldCapacity < reqCapacity);
-
- using mozilla::CheckedInt;
-
- CheckedInt<uint32_t> checkedOldAllocated =
- CheckedInt<uint32_t>(oldCapacity) + ObjectElements::VALUES_PER_HEADER;
- CheckedInt<uint32_t> checkedReqAllocated =
- CheckedInt<uint32_t>(reqCapacity) + ObjectElements::VALUES_PER_HEADER;
- if (!checkedOldAllocated.isValid() || !checkedReqAllocated.isValid())
- return false;
-
- uint32_t reqAllocated = checkedReqAllocated.value();
- uint32_t oldAllocated = checkedOldAllocated.value();
-
- uint32_t newAllocated;
- if (is<ArrayObject>() && !as<ArrayObject>().lengthIsWritable()) {
- MOZ_ASSERT(reqCapacity <= as<ArrayObject>().length());
- // Preserve the |capacity <= length| invariant for arrays with
- // non-writable length. See also js::ArraySetLength which initially
- // enforces this requirement.
- newAllocated = reqAllocated;
- } else {
- newAllocated = goodAllocated(reqAllocated, getElementsHeader()->length);
- }
-
- uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
- MOZ_ASSERT(newCapacity > oldCapacity && newCapacity >= reqCapacity);
-
- // Don't let nelements get close to wrapping around uint32_t.
- if (newCapacity >= NELEMENTS_LIMIT)
- return false;
-
- uint32_t initlen = getDenseInitializedLength();
-
- ObjectElements *newheader;
- if (hasDynamicElements()) {
- newheader = ReallocateElements(cx, this, getElementsHeader(), oldAllocated, newAllocated);
- if (!newheader)
- return false; // Leave elements at its old size.
- } else {
- newheader = AllocateElements(cx, this, newAllocated);
- if (!newheader)
- return false; // Leave elements at its old size.
- js_memcpy(newheader, getElementsHeader(),
- (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
- }
-
- newheader->capacity = newCapacity;
- elements = newheader->elements();
-
- Debug_SetSlotRangeToCrashOnTouch(elements + initlen, newCapacity - initlen);
-
- return true;
-}
-
-void
-NativeObject::shrinkElements(ThreadSafeContext *cx, uint32_t reqCapacity)
-{
- MOZ_ASSERT(cx->isThreadLocal(this));
- MOZ_ASSERT(canHaveNonEmptyElements());
- if (denseElementsAreCopyOnWrite())
- MOZ_CRASH();
-
- if (!hasDynamicElements())
- return;
-
- uint32_t oldCapacity = getDenseCapacity();
- MOZ_ASSERT(reqCapacity < oldCapacity);
-
- uint32_t oldAllocated = oldCapacity + ObjectElements::VALUES_PER_HEADER;
- uint32_t reqAllocated = reqCapacity + ObjectElements::VALUES_PER_HEADER;
- uint32_t newAllocated = goodAllocated(reqAllocated);
- if (newAllocated == oldAllocated)
- return; // Leave elements at its old size.
-
- MOZ_ASSERT(newAllocated > ObjectElements::VALUES_PER_HEADER);
- uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
-
- ObjectElements *newheader = ReallocateElements(cx, this, getElementsHeader(),
- oldAllocated, newAllocated);
- if (!newheader) {
- cx->recoverFromOutOfMemory();
- return; // Leave elements at its old size.
- }
-
- newheader->capacity = newCapacity;
- elements = newheader->elements();
-}
-
-/* static */ bool
-NativeObject::CopyElementsForWrite(ThreadSafeContext *cx, NativeObject *obj)
-{
- MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());
-
- // The original owner of a COW elements array should never be modified.
- MOZ_ASSERT(obj->getElementsHeader()->ownerObject() != obj);
-
- uint32_t initlen = obj->getDenseInitializedLength();
- uint32_t allocated = initlen + ObjectElements::VALUES_PER_HEADER;
- uint32_t newAllocated = goodAllocated(allocated);
-
- uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
-
- if (newCapacity >= NELEMENTS_LIMIT)
- return false;
-
- JSObject::writeBarrierPre(obj->getElementsHeader()->ownerObject());
-
- ObjectElements *newheader = AllocateElements(cx, obj, newAllocated);
- if (!newheader)
- return false;
- js_memcpy(newheader, obj->getElementsHeader(),
- (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
-
- newheader->capacity = newCapacity;
- newheader->clearCopyOnWrite();
- obj->elements = newheader->elements();
-
- Debug_SetSlotRangeToCrashOnTouch(obj->elements + initlen, newCapacity - initlen);
-
- return true;
-}
-
void
JSObject::fixupAfterMovingGC()
{
/*
* If this is a copy-on-write elements we may need to fix up both the
* elements' pointer back to the owner object, and the elements pointer
* itself if it points to inline elements in another object.
*/
@@ -3872,159 +3121,16 @@ JSObject::constructHook() const
const js::ProxyObject &p = as<js::ProxyObject>();
if (p.handler()->isConstructor(const_cast<JSObject*>(this)))
return js::proxy_Construct;
}
return nullptr;
}
/* static */ bool
-NativeObject::allocSlot(ThreadSafeContext *cx, HandleNativeObject obj, uint32_t *slotp)
-{
- MOZ_ASSERT(cx->isThreadLocal(obj));
-
- uint32_t slot = obj->slotSpan();
- MOZ_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
-
- /*
- * If this object is in dictionary mode, try to pull a free slot from the
- * shape table's slot-number freelist.
- */
- if (obj->inDictionaryMode()) {
- ShapeTable &table = obj->lastProperty()->table();
- uint32_t last = table.freelist;
- if (last != SHAPE_INVALID_SLOT) {
-#ifdef DEBUG
- MOZ_ASSERT(last < slot);
- uint32_t next = obj->getSlot(last).toPrivateUint32();
- MOZ_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot);
-#endif
-
- *slotp = last;
-
- const Value &vref = obj->getSlot(last);
- table.freelist = vref.toPrivateUint32();
- obj->setSlot(last, UndefinedValue());
- return true;
- }
- }
-
- if (slot >= SHAPE_MAXIMUM_SLOT) {
- js_ReportOutOfMemory(cx);
- return false;
- }
-
- *slotp = slot;
-
- if (obj->inDictionaryMode() && !setSlotSpan(cx, obj, slot + 1))
- return false;
-
- return true;
-}
-
-void
-NativeObject::freeSlot(uint32_t slot)
-{
- MOZ_ASSERT(slot < slotSpan());
-
- if (inDictionaryMode()) {
- uint32_t &last = lastProperty()->table().freelist;
-
- /* Can't afford to check the whole freelist, but let's check the head. */
- MOZ_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan() && last != slot);
-
- /*
- * Place all freed slots other than reserved slots (bug 595230) on the
- * dictionary's free list.
- */
- if (JSSLOT_FREE(getClass()) <= slot) {
- MOZ_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan());
- setSlot(slot, PrivateUint32Value(last));
- last = slot;
- return;
- }
- }
- setSlot(slot, UndefinedValue());
-}
-
-static bool
-PurgeProtoChain(ExclusiveContext *cx, JSObject *objArg, HandleId id)
-{
- /* Root locally so we can re-assign. */
- RootedObject obj(cx, objArg);
-
- RootedShape shape(cx);
- while (obj) {
- /* Lookups will not be cached through non-native protos. */
- if (!obj->isNative())
- break;
-
- shape = obj->as<NativeObject>().lookup(cx, id);
- if (shape)
- return obj->as<NativeObject>().shadowingShapeChange(cx, *shape);
-
- obj = obj->getProto();
- }
-
- return true;
-}
-
-static bool
-PurgeScopeChainHelper(ExclusiveContext *cx, HandleObject objArg, HandleId id)
-{
- /* Re-root locally so we can re-assign. */
- RootedObject obj(cx, objArg);
-
- MOZ_ASSERT(obj->isNative());
- MOZ_ASSERT(obj->isDelegate());
-
- /* Lookups on integer ids cannot be cached through prototypes. */
- if (JSID_IS_INT(id))
- return true;
-
- PurgeProtoChain(cx, obj->getProto(), id);
-
- /*
- * We must purge the scope chain only for Call objects as they are the only
- * kind of cacheable non-global object that can gain properties after outer
- * properties with the same names have been cached or traced. Call objects
- * may gain such properties via eval introducing new vars; see bug 490364.
- */
- if (obj->is<CallObject>()) {
- while ((obj = obj->enclosingScope()) != nullptr) {
- if (!PurgeProtoChain(cx, obj, id))
- return false;
- }
- }
-
- return true;
-}
-
-/*
- * PurgeScopeChain does nothing if obj is not itself a prototype or parent
- * scope, else it reshapes the scope and prototype chains it links. It calls
- * PurgeScopeChainHelper, which asserts that obj is flagged as a delegate
- * (i.e., obj has ever been on a prototype or parent chain).
- */
-static inline bool
-PurgeScopeChain(ExclusiveContext *cx, JS::HandleObject obj, JS::HandleId id)
-{
- if (obj->isDelegate())
- return PurgeScopeChainHelper(cx, obj, id);
- return true;
-}
-
-bool
-baseops::DefineGeneric(ExclusiveContext *cx, HandleNativeObject obj, HandleId id, HandleValue value,
- PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
-{
- return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
-}
-
-/* static */ bool
JSObject::defineGeneric(ExclusiveContext *cx, HandleObject obj,
HandleId id, HandleValue value,
JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
{
MOZ_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
js::DefineGenericOp op = obj->getOps()->defineGeneric;
if (op) {
if (!cx->shouldBeJSContext())
@@ -4038,724 +3144,45 @@ JSObject::defineGeneric(ExclusiveContext
JSObject::defineProperty(ExclusiveContext *cx, HandleObject obj,
PropertyName *name, HandleValue value,
JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
{
RootedId id(cx, NameToId(name));
return defineGeneric(cx, obj, id, value, getter, setter, attrs);
}
-bool
-baseops::DefineElement(ExclusiveContext *cx, HandleNativeObject obj, uint32_t index, HandleValue value,
- PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
-{
- RootedId id(cx);
- if (index <= JSID_INT_MAX) {
- id = INT_TO_JSID(index);
- return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
- }
-
- AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
-
- if (!IndexToId(cx, index, &id))
- return false;
-
- return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
-}
-
/* static */ bool
JSObject::defineElement(ExclusiveContext *cx, HandleObject obj,
uint32_t index, HandleValue value,
JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
{
js::DefineElementOp op = obj->getOps()->defineElement;
if (op) {
if (!cx->shouldBeJSContext())
return false;
return op(cx->asJSContext(), obj, index, value, getter, setter, attrs);
}
return baseops::DefineElement(cx, obj.as<NativeObject>(), index, value, getter, setter, attrs);
}
-Shape *
-NativeObject::addDataProperty(ExclusiveContext *cx, jsid idArg, uint32_t slot, unsigned attrs)
-{
- MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
- RootedNativeObject self(cx, this);
- RootedId id(cx, idArg);
- return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
-}
-
-Shape *
-NativeObject::addDataProperty(ExclusiveContext *cx, HandlePropertyName name,
- uint32_t slot, unsigned attrs)
-{
- MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
- RootedNativeObject self(cx, this);
- RootedId id(cx, NameToId(name));
- return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
-}
-
-/*
- * Backward compatibility requires allowing addProperty hooks to mutate the
- * nominal initial value of a slotful property, while GC safety wants that
- * value to be stored before the call-out through the hook. Optimize to do
- * both while saving cycles for classes that stub their addProperty hook.
- */
-template <ExecutionMode mode>
-static inline bool
-CallAddPropertyHook(typename ExecutionModeTraits<mode>::ExclusiveContextType cxArg,
- const Class *clasp, HandleNativeObject obj, HandleShape shape,
- HandleValue nominal)
-{
- if (clasp->addProperty != JS_PropertyStub) {
- if (mode == ParallelExecution)
- return false;
-
- ExclusiveContext *cx = cxArg->asExclusiveContext();
- if (!cx->shouldBeJSContext())
- return false;
-
- /* Make a local copy of value so addProperty can mutate its inout parameter. */
- RootedValue value(cx, nominal);
-
- Rooted<jsid> id(cx, shape->propid());
- if (!CallJSPropertyOp(cx->asJSContext(), clasp->addProperty, obj, id, &value)) {
- obj->removeProperty(cx, shape->propid());
- return false;
- }
- if (value.get() != nominal) {
- if (shape->hasSlot())
- obj->setSlotWithType(cx, shape, value);
- }
- }
- return true;
-}
-
-template <ExecutionMode mode>
-static inline bool
-CallAddPropertyHookDense(typename ExecutionModeTraits<mode>::ExclusiveContextType cxArg,
- const Class *clasp, HandleNativeObject obj, uint32_t index,
- HandleValue nominal)
-{
- /* Inline addProperty for array objects. */
- if (obj->is<ArrayObject>()) {
- ArrayObject *arr = &obj->as<ArrayObject>();
- uint32_t length = arr->length();
- if (index >= length) {
- if (mode == ParallelExecution) {
- /* We cannot deal with overflows in parallel. */
- if (length > INT32_MAX)
- return false;
- arr->setLengthInt32(index + 1);
- } else {
- arr->setLength(cxArg->asExclusiveContext(), index + 1);
- }
- }
- return true;
- }
-
- if (clasp->addProperty != JS_PropertyStub) {
- if (mode == ParallelExecution)
- return false;
-
- ExclusiveContext *cx = cxArg->asExclusiveContext();
- if (!cx->shouldBeJSContext())
- return false;
-
- if (!obj->maybeCopyElementsForWrite(cx))
- return false;
-
- /* Make a local copy of value so addProperty can mutate its inout parameter. */
- RootedValue value(cx, nominal);
-
- Rooted<jsid> id(cx, INT_TO_JSID(index));
- if (!CallJSPropertyOp(cx->asJSContext(), clasp->addProperty, obj, id, &value)) {
- obj->setDenseElementHole(cx, index);
- return false;
- }
- if (value.get() != nominal)
- obj->setDenseElementWithType(cx, index, value);
- }
-
- return true;
-}
-
-template <ExecutionMode mode>
-static bool
-UpdateShapeTypeAndValue(typename ExecutionModeTraits<mode>::ExclusiveContextType cx,
- NativeObject *obj, Shape *shape, const Value &value)
-{
- jsid id = shape->propid();
- if (shape->hasSlot()) {
- if (mode == ParallelExecution) {
- if (!obj->setSlotIfHasType(shape, value, /* overwriting = */ false))
- return false;
- } else {
- obj->setSlotWithType(cx->asExclusiveContext(), shape, value, /* overwriting = */ false);
- }
-
- // Per the acquired properties analysis, when the shape of a partially
- // initialized object is changed to its fully initialized shape, its
- // type can be updated as well.
- if (types::TypeNewScript *newScript = obj->typeRaw()->newScript()) {
- if (newScript->initializedShape() == shape)
- obj->setType(newScript->initializedType());
- }
- }
- if (!shape->hasSlot() || !shape->hasDefaultGetter() || !shape->hasDefaultSetter()) {
- if (mode == ParallelExecution) {
- if (!IsTypePropertyIdMarkedNonData(obj, id))
- return false;
- } else {
- MarkTypePropertyNonData(cx->asExclusiveContext(), obj, id);
- }
- }
- if (!shape->writable()) {
- if (mode == ParallelExecution) {
- if (!IsTypePropertyIdMarkedNonWritable(obj, id))
- return false;
- } else {
- MarkTypePropertyNonWritable(cx->asExclusiveContext(), obj, id);
- }
- }
- return true;
-}
-
-template <ExecutionMode mode>
-static inline bool
-DefinePropertyOrElement(typename ExecutionModeTraits<mode>::ExclusiveContextType cx,
- HandleNativeObject obj, HandleId id,
- PropertyOp getter, StrictPropertyOp setter,
- unsigned attrs, HandleValue value,
- bool callSetterAfterwards, bool setterIsStrict)
-{
- /* Use dense storage for new indexed properties where possible. */
- if (JSID_IS_INT(id) &&
- getter == JS_PropertyStub &&
- setter == JS_StrictPropertyStub &&
- attrs == JSPROP_ENUMERATE &&
- (!obj->isIndexed() || !obj->containsPure(id)) &&
- !IsAnyTypedArray(obj))
- {
- uint32_t index = JSID_TO_INT(id);
- bool definesPast;
- if (!WouldDefinePastNonwritableLength(cx, obj, index, setterIsStrict, &definesPast))
- return false;
- if (definesPast)
- return true;
-
- NativeObject::EnsureDenseResult result;
- if (mode == ParallelExecution) {
- if (obj->writeToIndexWouldMarkNotPacked(index))
- return false;
- result = obj->ensureDenseElementsPreservePackedFlag(cx, index, 1);
- } else {
- result = obj->ensureDenseElements(cx->asExclusiveContext(), index, 1);
- }
-
- if (result == NativeObject::ED_FAILED)
- return false;
- if (result == NativeObject::ED_OK) {
- if (mode == ParallelExecution) {
- if (!obj->setDenseElementIfHasType(index, value))
- return false;
- } else {
- obj->setDenseElementWithType(cx->asExclusiveContext(), index, value);
- }
- return CallAddPropertyHookDense<mode>(cx, obj->getClass(), obj, index, value);
- }
- }
-
- if (obj->is<ArrayObject>()) {
- Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
- if (id == NameToId(cx->names().length)) {
- if (mode == SequentialExecution && !cx->shouldBeJSContext())
- return false;
- return ArraySetLength<mode>(ExecutionModeTraits<mode>::toContextType(cx), arr, id,
- attrs, value, setterIsStrict);
- }
-
- uint32_t index;
- if (js_IdIsIndex(id, &index)) {
- bool definesPast;
- if (!WouldDefinePastNonwritableLength(cx, arr, index, setterIsStrict, &definesPast))
- return false;
- if (definesPast)
- return true;
- }
- }
-
- // Don't define new indexed properties on typed arrays.
- if (IsAnyTypedArray(obj)) {
- uint64_t index;
- if (IsTypedArrayIndex(id, &index))
- return true;
- }
-
- AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
-
- RootedShape shape(cx, NativeObject::putProperty<mode>(cx, obj, id, getter, setter,
- SHAPE_INVALID_SLOT, attrs, 0));
- if (!shape)
- return false;
-
- if (!UpdateShapeTypeAndValue<mode>(cx, obj, shape, value))
- return false;
-
- /*
- * Clear any existing dense index after adding a sparse indexed property,
- * and investigate converting the object to dense indexes.
- */
- if (JSID_IS_INT(id)) {
- if (mode == ParallelExecution)
- return false;
-
- if (!obj->maybeCopyElementsForWrite(cx))
- return false;
-
- ExclusiveContext *ncx = cx->asExclusiveContext();
- uint32_t index = JSID_TO_INT(id);
- NativeObject::removeDenseElementForSparseIndex(ncx, obj, index);
- NativeObject::EnsureDenseResult result = NativeObject::maybeDensifySparseElements(ncx, obj);
- if (result == NativeObject::ED_FAILED)
- return false;
- if (result == NativeObject::ED_OK) {
- MOZ_ASSERT(setter == JS_StrictPropertyStub);
- return CallAddPropertyHookDense<mode>(cx, obj->getClass(), obj, index, value);
- }
- }
-
- if (!CallAddPropertyHook<mode>(cx, obj->getClass(), obj, shape, value))
- return false;
-
- if (callSetterAfterwards && setter != JS_StrictPropertyStub) {
- if (!cx->shouldBeJSContext())
- return false;
- RootedValue nvalue(cx, value);
- return NativeSet<mode>(ExecutionModeTraits<mode>::toContextType(cx),
- obj, obj, shape, setterIsStrict, &nvalue);
- }
- return true;
-}
-
-static bool
-NativeLookupOwnProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id,
- MutableHandle<Shape*> shapep);
-
-bool
-js::DefineNativeProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id, HandleValue value,
- PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
-{
- MOZ_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
-
- AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
-
- RootedShape shape(cx);
- RootedValue updateValue(cx, value);
- bool shouldDefine = true;
-
- /*
- * If defining a getter or setter, we must check for its counterpart and
- * update the attributes and property ops. A getter or setter is really
- * only half of a property.
- */
- if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
- if (!NativeLookupOwnProperty(cx, obj, id, &shape))
- return false;
- if (shape) {
- /*
- * If we are defining a getter whose setter was already defined, or
- * vice versa, finish the job via obj->changeProperty.
- */
- if (IsImplicitDenseOrTypedArrayElement(shape)) {
- if (IsAnyTypedArray(obj)) {
- /* Ignore getter/setter properties added to typed arrays. */
- return true;
- }
- if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
- return false;
- shape = obj->lookup(cx, id);
- }
- if (shape->isAccessorDescriptor()) {
- attrs = ApplyOrDefaultAttributes(attrs, shape);
- shape = NativeObject::changeProperty<SequentialExecution>(cx, obj, shape, attrs,
- JSPROP_GETTER | JSPROP_SETTER,
- (attrs & JSPROP_GETTER)
- ? getter
- : shape->getter(),
- (attrs & JSPROP_SETTER)
- ? setter
- : shape->setter());
- if (!shape)
- return false;
- shouldDefine = false;
- }
- }
- } else if (!(attrs & JSPROP_IGNORE_VALUE)) {
- /*
- * We might still want to ignore redefining some of our attributes, if the
- * request came through a proxy after Object.defineProperty(), but only if we're redefining
- * a data property.
- * FIXME: All this logic should be removed when Proxies use PropDesc, but we need to
- * remove JSPropertyOp getters and setters first.
- * FIXME: This is still wrong for various array types, and will set the wrong attributes
- * by accident, but we can't use NativeLookupOwnProperty in this case, because of resolve
- * loops.
- */
- shape = obj->lookup(cx, id);
- if (shape && shape->isDataDescriptor())
- attrs = ApplyOrDefaultAttributes(attrs, shape);
- } else {
- /*
- * We have been asked merely to update some attributes by a caller of
- * Object.defineProperty, laundered through the proxy system, and returning here. We can
- * get away with just using JSObject::changeProperty here.
- */
- if (!NativeLookupOwnProperty(cx, obj, id, &shape))
- return false;
-
- if (shape) {
- // Don't forget about arrays.
- if (IsImplicitDenseOrTypedArrayElement(shape)) {
- if (obj->is<TypedArrayObject>()) {
- /*
- * Silently ignore attempts to change individial index attributes.
- * FIXME: Uses the same broken behavior as for accessors. This should
- * probably throw.
- */
- return true;
- }
- if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
- return false;
- shape = obj->lookup(cx, id);
- }
-
- attrs = ApplyOrDefaultAttributes(attrs, shape);
-
- /* Keep everything from the shape that isn't the things we're changing */
- unsigned attrMask = ~(JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
- shape = NativeObject::changeProperty<SequentialExecution>(cx, obj, shape, attrs, attrMask,
- shape->getter(), shape->setter());
- if (!shape)
- return false;
- if (shape->hasSlot())
- updateValue = obj->getSlot(shape->slot());
- shouldDefine = false;
- }
- }
-
- /*
- * Purge the property cache of any properties named by id that are about
- * to be shadowed in obj's scope chain.
- */
- if (!PurgeScopeChain(cx, obj, id))
- return false;
-
- /* Use the object's class getter and setter by default. */
- const Class *clasp = obj->getClass();
- if (!getter && !(attrs & JSPROP_GETTER))
- getter = clasp->getProperty;
- if (!setter && !(attrs & JSPROP_SETTER))
- setter = clasp->setProperty;
-
- if (shouldDefine) {
- // Handle the default cases here. Anyone that wanted to set non-default attributes has
- // cleared the IGNORE flags by now. Since we can never get here with JSPROP_IGNORE_VALUE
- // relevant, just clear it.
- attrs = ApplyOrDefaultAttributes(attrs) & ~JSPROP_IGNORE_VALUE;
- return DefinePropertyOrElement<SequentialExecution>(cx, obj, id, getter, setter,
- attrs, value, false, false);
- }
-
- MOZ_ASSERT(shape);
-
- JS_ALWAYS_TRUE(UpdateShapeTypeAndValue<SequentialExecution>(cx, obj, shape, updateValue));
-
- return CallAddPropertyHook<SequentialExecution>(cx, clasp, obj, shape, updateValue);
-}
-
-/*
- * Call obj's resolve hook.
- *
- * cx, id, and flags are the parameters initially passed to the ongoing lookup;
- * objp and propp are its out parameters. obj is an object along the prototype
- * chain from where the lookup started.
- *
- * There are four possible outcomes:
- *
- * - On failure, report an error or exception and return false.
- *
- * - If we are already resolving a property of *curobjp, set *recursedp = true,
- * and return true.
- *
- * - If the resolve hook finds or defines the sought property, set *objp and
- * *propp appropriately, set *recursedp = false, and return true.
- *
- * - Otherwise no property was resolved. Set *propp = nullptr and
- * *recursedp = false and return true.
- */
-static MOZ_ALWAYS_INLINE bool
-CallResolveOp(JSContext *cx, HandleNativeObject obj, HandleId id, MutableHandleObject objp,
- MutableHandleShape propp, bool *recursedp)
-{
- const Class *clasp = obj->getClass();
- JSResolveOp resolve = clasp->resolve;
-
- /*
- * Avoid recursion on (obj, id) already being resolved on cx.
- *
- * Once we have successfully added an entry for (obj, key) to
- * cx->resolvingTable, control must go through cleanup: before
- * returning. But note that JS_DHASH_ADD may find an existing
- * entry, in which case we bail to suppress runaway recursion.
- */
- AutoResolving resolving(cx, obj, id);
- if (resolving.alreadyStarted()) {
- /* Already resolving id in obj -- suppress recursion. */
- *recursedp = true;
- return true;
- }
- *recursedp = false;
-
- propp.set(nullptr);
-
- if (clasp->flags & JSCLASS_NEW_RESOLVE) {
- JSNewResolveOp newresolve = reinterpret_cast<JSNewResolveOp>(resolve);
- RootedObject obj2(cx, nullptr);
- if (!newresolve(cx, obj, id, &obj2))
- return false;
-
- /*
- * We trust the new style resolve hook to set obj2 to nullptr when
- * the id cannot be resolved. But, when obj2 is not null, we do
- * not assume that id must exist and do full nativeLookup for
- * compatibility.
- */
- if (!obj2)
- return true;
-
- if (!obj2->isNative()) {
- /* Whoops, newresolve handed back a foreign obj2. */
- MOZ_ASSERT(obj2 != obj);
- return JSObject::lookupGeneric(cx, obj2, id, objp, propp);
- }
-
- objp.set(obj2);
- } else {
- if (!resolve(cx, obj, id))
- return false;
-
- objp.set(obj);
- }
-
- NativeObject *nobjp = &objp->as<NativeObject>();
-
- if (JSID_IS_INT(id) && nobjp->containsDenseElement(JSID_TO_INT(id))) {
- MarkDenseOrTypedArrayElementFound<CanGC>(propp);
- return true;
- }
-
- Shape *shape;
- if (!nobjp->empty() && (shape = nobjp->lookup(cx, id)))
- propp.set(shape);
- else
- objp.set(nullptr);
-
- return true;
-}
-
-template <AllowGC allowGC>
-static MOZ_ALWAYS_INLINE bool
-LookupOwnPropertyInline(ExclusiveContext *cx,
- typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
- typename MaybeRooted<jsid, allowGC>::HandleType id,
- typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
- typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp,
- bool *donep)
-{
- // Check for a native dense element.
- if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
- objp.set(obj);
- MarkDenseOrTypedArrayElementFound<allowGC>(propp);
- *donep = true;
- return true;
- }
-
- // Check for a typed array element. Integer lookups always finish here
- // so that integer properties on the prototype are ignored even for out
- // of bounds accesses.
- if (IsAnyTypedArray(obj)) {
- uint64_t index;
- if (IsTypedArrayIndex(id, &index)) {
- if (index < AnyTypedArrayLength(obj)) {
- objp.set(obj);
- MarkDenseOrTypedArrayElementFound<allowGC>(propp);
- } else {
- objp.set(nullptr);
- propp.set(nullptr);
- }
- *donep = true;
- return true;
- }
- }
-
- // Check for a native property.
- if (Shape *shape = obj->lookup(cx, id)) {
- objp.set(obj);
- propp.set(shape);
- *donep = true;
- return true;
- }
-
- // id was not found in obj. Try obj's resolve hook, if any.
- if (obj->getClass()->resolve != JS_ResolveStub) {
- if (!cx->shouldBeJSContext() || !allowGC)
- return false;
-
- bool recursed;
- if (!CallResolveOp(cx->asJSContext(),
- MaybeRooted<NativeObject*, allowGC>::toHandle(obj),
- MaybeRooted<jsid, allowGC>::toHandle(id),
- MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
- MaybeRooted<Shape*, allowGC>::toMutableHandle(propp),
- &recursed))
- {
- return false;
- }
-
- if (recursed) {
- objp.set(nullptr);
- propp.set(nullptr);
- *donep = true;
- return true;
- }
-
- if (propp) {
- *donep = true;
- return true;
- }
- }
-
- *donep = false;
- return true;
-}
-
-static bool
-NativeLookupOwnProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id,
- MutableHandle<Shape*> shapep)
-{
- RootedObject pobj(cx);
- bool done;
-
- if (!LookupOwnPropertyInline<CanGC>(cx, obj, id, &pobj, shapep, &done))
- return false;
- if (!done || pobj != obj)
- shapep.set(nullptr);
- return true;
-}
-
-template <AllowGC allowGC>
-static MOZ_ALWAYS_INLINE bool
-LookupPropertyInline(ExclusiveContext *cx,
- typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
- typename MaybeRooted<jsid, allowGC>::HandleType id,
- typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
- typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
-{
- /* NB: The logic of this procedure is implicitly reflected in
- * BaselineIC.cpp's |EffectlesslyLookupProperty| logic.
- * If this changes, please remember to update the logic there as well.
- */
-
- /* Search scopes starting with obj and following the prototype link. */
- typename MaybeRooted<NativeObject*, allowGC>::RootType current(cx, obj);
-
- while (true) {
- bool done;
- if (!LookupOwnPropertyInline<allowGC>(cx, current, id, objp, propp, &done))
- return false;
- if (done)
- return true;
-
- typename MaybeRooted<JSObject*, allowGC>::RootType proto(cx, current->getProto());
-
- if (!proto)
- break;
- if (!proto->isNative()) {
- if (!cx->shouldBeJSContext() || !allowGC)
- return false;
- return JSObject::lookupGeneric(cx->asJSContext(),
- MaybeRooted<JSObject*, allowGC>::toHandle(proto),
- MaybeRooted<jsid, allowGC>::toHandle(id),
- MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
- MaybeRooted<Shape*, allowGC>::toMutableHandle(propp));
- }
-
- current = &proto->template as<NativeObject>();
- }
-
- objp.set(nullptr);
- propp.set(nullptr);
- return true;
-}
-
-template <AllowGC allowGC>
-bool
-baseops::LookupProperty(ExclusiveContext *cx,
- typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
- typename MaybeRooted<jsid, allowGC>::HandleType id,
- typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
- typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
-{
- return LookupPropertyInline<allowGC>(cx, obj, id, objp, propp);
-}
-
-template bool
-baseops::LookupProperty<CanGC>(ExclusiveContext *cx, HandleNativeObject obj, HandleId id,
- MutableHandleObject objp, MutableHandleShape propp);
-
-template bool
-baseops::LookupProperty<NoGC>(ExclusiveContext *cx, NativeObject *obj, jsid id,
- FakeMutableHandle<JSObject*> objp,
- FakeMutableHandle<Shape*> propp);
-
/* static */ bool
JSObject::lookupGeneric(JSContext *cx, HandleObject obj, js::HandleId id,
MutableHandleObject objp, MutableHandleShape propp)
{
/* NB: The logic of lookupGeneric is implicitly reflected in
* BaselineIC.cpp's |EffectlesslyLookupProperty| logic.
* If this changes, please remember to update the logic there as well.
*/
LookupGenericOp op = obj->getOps()->lookupGeneric;
if (op)
return op(cx, obj, id, objp, propp);
return baseops::LookupProperty<CanGC>(cx, obj.as<NativeObject>(), id, objp, propp);
}
bool
-baseops::LookupElement(JSContext *cx, HandleNativeObject obj, uint32_t index,
- MutableHandleObject objp, MutableHandleShape propp)
-{
- RootedId id(cx);
- if (!IndexToId(cx, index, &id))
- return false;
-
- return LookupPropertyInline<CanGC>(cx, obj, id, objp, propp);
-}
-
-bool
-js::LookupNativeProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id,
- MutableHandleObject objp, MutableHandleShape propp)
-{
- return LookupPropertyInline<CanGC>(cx, obj, id, objp, propp);
-}
-
-bool
js::LookupName(JSContext *cx, HandlePropertyName name, HandleObject scopeChain,
MutableHandleObject objp, MutableHandleObject pobjp, MutableHandleShape propp)
{
RootedId id(cx, NameToId(name));
for (RootedObject scope(cx, scopeChain); scope; scope = scope->enclosingScope()) {
if (!JSObject::lookupGeneric(cx, scope, id, pobjp, propp))
return false;
@@ -4912,279 +3339,16 @@ js::HasOwnProperty(JSContext *cx, Handle
RootedObject pobj(cx);
RootedShape shape(cx);
if (!HasOwnProperty<CanGC>(cx, obj->getOps()->lookupGeneric, obj, id, &pobj, &shape))
return false;
*resultp = (shape != nullptr);
return true;
}
-template <AllowGC allowGC>
-static MOZ_ALWAYS_INLINE bool
-NativeGetInline(JSContext *cx,
- typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
- typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
- typename MaybeRooted<NativeObject*, allowGC>::HandleType pobj,
- typename MaybeRooted<Shape*, allowGC>::HandleType shape,
- typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
-{
- if (shape->hasSlot()) {
- vp.set(pobj->getSlot(shape->slot()));
- MOZ_ASSERT_IF(!vp.isMagic(JS_UNINITIALIZED_LEXICAL) &&
- !pobj->hasSingletonType() &&
- !pobj->template is<ScopeObject>() &&
- shape->hasDefaultGetter(),
- js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), vp));
- } else {
- vp.setUndefined();
- }
- if (shape->hasDefaultGetter())
- return true;
-
- {
- jsbytecode *pc;
- JSScript *script = cx->currentScript(&pc);
- if (script && script->hasBaselineScript()) {
- switch (JSOp(*pc)) {
- case JSOP_GETPROP:
- case JSOP_CALLPROP:
- case JSOP_LENGTH:
- script->baselineScript()->noteAccessedGetter(script->pcToOffset(pc));
- break;
- default:
- break;
- }
- }
- }
-
- if (!allowGC)
- return false;
-
- if (!shape->get(cx,
- MaybeRooted<JSObject*, allowGC>::toHandle(receiver),
- MaybeRooted<JSObject*, allowGC>::toHandle(obj),
- MaybeRooted<JSObject*, allowGC>::toHandle(pobj),
- MaybeRooted<Value, allowGC>::toMutableHandle(vp)))
- {
- return false;
- }
-
- /* Update slotful shapes according to the value produced by the getter. */
- if (shape->hasSlot() && pobj->contains(cx, shape))
- pobj->setSlot(shape->slot(), vp);
-
- return true;
-}
-
-bool
-js::NativeGet(JSContext *cx, HandleObject obj, HandleNativeObject pobj, HandleShape shape,
- MutableHandleValue vp)
-{
- return NativeGetInline<CanGC>(cx, obj, obj, pobj, shape, vp);
-}
-
-template <ExecutionMode mode>
-bool
-js::NativeSet(typename ExecutionModeTraits<mode>::ContextType cxArg,
- HandleNativeObject obj, Handle<JSObject*> receiver,
- HandleShape shape, bool strict, MutableHandleValue vp)
-{
- MOZ_ASSERT(cxArg->isThreadLocal(obj));
- MOZ_ASSERT(obj->isNative());
-
- if (shape->hasSlot()) {
- /* If shape has a stub setter, just store vp. */
- if (shape->hasDefaultSetter()) {
- if (mode == ParallelExecution) {
- if (!obj->setSlotIfHasType(shape, vp))
- return false;
- } else {
- // Global properties declared with 'var' will be initially
- // defined with an undefined value, so don't treat the initial
- // assignments to such properties as overwrites.
- bool overwriting = !obj->is<GlobalObject>() || !obj->getSlot(shape->slot()).isUndefined();
- obj->setSlotWithType(cxArg->asExclusiveContext(), shape, vp, overwriting);
- }
-
- return true;
- }
- }
-
- if (mode == ParallelExecution)
- return false;
- JSContext *cx = cxArg->asJSContext();
-
- if (!shape->hasSlot()) {
- /*
- * Allow API consumers to create shared properties with stub setters.
- * Such properties effectively function as data descriptors which are
- * not writable, so attempting to set such a property should do nothing
- * or throw if we're in strict mode.
- */
- if (!shape->hasGetterValue() && shape->hasDefaultSetter())
- return js_ReportGetterOnlyAssignment(cx, strict);
- }
-
- RootedValue ovp(cx, vp);
-
- uint32_t sample = cx->runtime()->propertyRemovals;
- if (!shape->set(cx, obj, receiver, strict, vp))
- return false;
-
- /*
- * Update any slot for the shape with the value produced by the setter,
- * unless the setter deleted the shape.
- */
- if (shape->hasSlot() &&
- (MOZ_LIKELY(cx->runtime()->propertyRemovals == sample) ||
- obj->contains(cx, shape)))
- {
- obj->setSlot(shape->slot(), vp);
- }
-
- return true;
-}
-
-template bool
-js::NativeSet<SequentialExecution>(JSContext *cx,
- HandleNativeObject obj, HandleObject receiver,
- HandleShape shape, bool strict, MutableHandleValue vp);
-template bool
-js::NativeSet<ParallelExecution>(ForkJoinContext *cx,
- HandleNativeObject obj, HandleObject receiver,
- HandleShape shape, bool strict, MutableHandleValue vp);
-
-template <AllowGC allowGC>
-static MOZ_ALWAYS_INLINE bool
-GetPropertyHelperInline(JSContext *cx,
- typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
- typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
- typename MaybeRooted<jsid, allowGC>::HandleType id,
- typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
-{
- /* This call site is hot -- use the always-inlined variant of LookupNativeProperty(). */
- typename MaybeRooted<JSObject*, allowGC>::RootType obj2(cx);
- typename MaybeRooted<Shape*, allowGC>::RootType shape(cx);
- if (!LookupPropertyInline<allowGC>(cx, obj, id, &obj2, &shape))
- return false;
-
- if (!shape) {
- if (!allowGC)
- return false;
-
- vp.setUndefined();
-
- if (!CallJSPropertyOp(cx, obj->getClass()->getProperty,
- MaybeRooted<JSObject*, allowGC>::toHandle(obj),
- MaybeRooted<jsid, allowGC>::toHandle(id),
- MaybeRooted<Value, allowGC>::toMutableHandle(vp)))
- {
- return false;
- }
-
- /*
- * Give a strict warning if foo.bar is evaluated by a script for an
- * object foo with no property named 'bar'.
- */
- if (vp.isUndefined()) {
- jsbytecode *pc = nullptr;
- RootedScript script(cx, cx->currentScript(&pc));
- if (!pc)
- return true;
- JSOp op = (JSOp) *pc;
-
- if (op == JSOP_GETXPROP) {
- /* Undefined property during a name lookup, report an error. */
- JSAutoByteString printable;
- if (js_ValueToPrintable(cx, IdToValue(id), &printable))
- js_ReportIsNotDefined(cx, printable.ptr());
- return false;
- }
-
- /* Don't warn if extra warnings not enabled or for random getprop operations. */
- if (!cx->compartment()->options().extraWarnings(cx) || (op != JSOP_GETPROP && op != JSOP_GETELEM))
- return true;
-
- /* Don't warn repeatedly for the same script. */
- if (!script || script->warnedAboutUndefinedProp())
- return true;
-
- /*
- * Don't warn in self-hosted code (where the further presence of
- * JS::RuntimeOptions::werror() would result in impossible-to-avoid
- * errors to entirely-innocent client code).
- */
- if (script->selfHosted())
- return true;
-
- /* We may just be checking if that object has an iterator. */
- if (JSID_IS_ATOM(id, cx->names().iteratorIntrinsic))
- return true;
-
- /* Do not warn about tests like (obj[prop] == undefined). */
- pc += js_CodeSpec[op].length;
- if (Detecting(cx, script, pc))
- return true;
-
- unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT;
- script->setWarnedAboutUndefinedProp();
-
- /* Ok, bad undefined property reference: whine about it. */
- RootedValue val(cx, IdToValue(id));
- if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP,
- JSDVG_IGNORE_STACK, val, js::NullPtr(),
- nullptr, nullptr))
- {
- return false;
- }
- }
- return true;
- }
-
- if (!obj2->isNative()) {
- if (!allowGC)
- return false;
- HandleObject obj2Handle = MaybeRooted<JSObject*, allowGC>::toHandle(obj2);
- HandleObject receiverHandle = MaybeRooted<JSObject*, allowGC>::toHandle(receiver);
- HandleId idHandle = MaybeRooted<jsid, allowGC>::toHandle(id);
- MutableHandleValue vpHandle = MaybeRooted<Value, allowGC>::toMutableHandle(vp);
- return obj2->template is<ProxyObject>()
- ? Proxy::get(cx, obj2Handle, receiverHandle, idHandle, vpHandle)
- : JSObject::getGeneric(cx, obj2Handle, obj2Handle, idHandle, vpHandle);
- }
-
- typename MaybeRooted<NativeObject*, allowGC>::HandleType nobj2 =
- MaybeRooted<JSObject*, allowGC>::template downcastHandle<NativeObject>(obj2);
-
- if (IsImplicitDenseOrTypedArrayElement(shape)) {
- vp.set(nobj2->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
- return true;
- }
-
- /* This call site is hot -- use the always-inlined variant of NativeGet(). */
- if (!NativeGetInline<allowGC>(cx, obj, receiver, nobj2, shape, vp))
- return false;
-
- return true;
-}
-
-bool
-baseops::GetProperty(JSContext *cx, HandleNativeObject obj, HandleObject receiver, HandleId id, MutableHandleValue vp)
-{
- /* This call site is hot -- use the always-inlined variant of GetPropertyHelper(). */
- return GetPropertyHelperInline<CanGC>(cx, obj, receiver, id, vp);
-}
-
-bool
-baseops::GetPropertyNoGC(JSContext *cx, NativeObject *obj, JSObject *receiver, jsid id, Value *vp)
-{
- AutoAssertNoException nogc(cx);
- return GetPropertyHelperInline<NoGC>(cx, obj, receiver, id, vp);
-}
-
static MOZ_ALWAYS_INLINE bool
LookupPropertyPureInline(JSObject *obj, jsid id, NativeObject **objp, Shape **propp)
{
if (!obj->isNative())
return false;
NativeObject *current = &obj->as<NativeObject>();
while (true) {
@@ -5337,51 +3501,16 @@ js::GetObjectElementOperationPure(Thread
JSAtom *name = &prop.toString()->asAtom();
if (name->isIndex(&index))
return GetElementPure(cx, obj, index, vp);
return GetPropertyPure(cx, obj, NameToId(name->asPropertyName()), vp);
}
bool
-baseops::GetElement(JSContext *cx, HandleNativeObject obj, HandleObject receiver, uint32_t index,
- MutableHandleValue vp)
-{
- RootedId id(cx);
- if (!IndexToId(cx, index, &id))
- return false;
-
- /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
- return GetPropertyHelperInline<CanGC>(cx, obj, receiver, id, vp);
-}
-
-static bool
-MaybeReportUndeclaredVarAssignment(JSContext *cx, JSString *propname)
-{
- {
- JSScript *script = cx->currentScript(nullptr, JSContext::ALLOW_CROSS_COMPARTMENT);
- if (!script)
- return true;
-
- // If the code is not strict and extra warnings aren't enabled, then no
- // check is needed.
- if (!script->strict() && !cx->compartment()->options().extraWarnings(cx))
- return true;
- }
-
- JSAutoByteString bytes(cx, propname);
- return !!bytes &&
- JS_ReportErrorFlagsAndNumber(cx,
- (JSREPORT_WARNING | JSREPORT_STRICT
- | JSREPORT_STRICT_MODE_ERROR),
- js_GetErrorMessage, nullptr,
- JSMSG_UNDECLARED_VAR, bytes.ptr());
-}
-
-bool
JSObject::reportReadOnly(ThreadSafeContext *cxArg, jsid id, unsigned report)
{
if (cxArg->isForkJoinContext())
return cxArg->asForkJoinContext()->reportError(report);
if (!cxArg->isJSContext())
return true;
@@ -5429,394 +3558,16 @@ JSObject::callMethod(JSContext *cx, Hand
{
RootedValue fval(cx);
RootedObject obj(cx, this);
if (!JSObject::getGeneric(cx, obj, obj, id, &fval))
return false;
return Invoke(cx, ObjectValue(*obj), fval, argc, argv, vp);
}
-template <ExecutionMode mode>
-bool
-baseops::SetPropertyHelper(typename ExecutionModeTraits<mode>::ContextType cxArg,
- HandleNativeObject obj, HandleObject receiver, HandleId id,
- QualifiedBool qualified, MutableHandleValue vp, bool strict)
-{
- MOZ_ASSERT(cxArg->isThreadLocal(obj));
-
- if (MOZ_UNLIKELY(obj->watched())) {
- if (mode == ParallelExecution)
- return false;
-
- /* Fire watchpoints, if any. */
- JSContext *cx = cxArg->asJSContext();
- WatchpointMap *wpmap = cx->compartment()->watchpointMap;
- if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
- return false;
- }
-
- RootedObject pobj(cxArg);
- RootedShape shape(cxArg);
- if (mode == ParallelExecution) {
- NativeObject *npobj;
- if (!LookupPropertyPure(obj, id, &npobj, shape.address()))
- return false;
- pobj = npobj;
- } else {
- JSContext *cx = cxArg->asJSContext();
- if (!LookupNativeProperty(cx, obj, id, &pobj, &shape))
- return false;
- }
- if (shape) {
- if (!pobj->isNative()) {
- if (pobj->is<ProxyObject>()) {
- if (mode == ParallelExecution)
- return false;
-
- JSContext *cx = cxArg->asJSContext();
- Rooted<PropertyDescriptor> pd(cx);
- if (!Proxy::getPropertyDescriptor(cx, pobj, id, &pd))
- return false;
-
- if ((pd.attributes() & (JSPROP_SHARED | JSPROP_SHADOWABLE)) == JSPROP_SHARED) {
- return !pd.setter() ||
- CallSetter(cx, receiver, id, pd.setter(), pd.attributes(), strict, vp);
- }
-
- if (pd.isReadonly()) {
- if (strict)
- return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
- if (cx->compartment()->options().extraWarnings(cx))
- return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
- return true;
- }
- }
-
- shape = nullptr;
- }
- } else {
- /* We should never add properties to lexical blocks. */
- MOZ_ASSERT(!obj->is<BlockObject>());
-
- if (obj->isUnqualifiedVarObj() && !qualified) {
- if (mode == ParallelExecution)
- return false;
-
- if (!MaybeReportUndeclaredVarAssignment(cxArg->asJSContext(), JSID_TO_STRING(id)))
- return false;
- }
- }
-
- /*
- * Now either shape is null, meaning id was not found in obj or one of its
- * prototypes; or shape is non-null, meaning id was found directly in pobj.
- */
- unsigned attrs = JSPROP_ENUMERATE;
- const Class *clasp = obj->getClass();
- PropertyOp getter = clasp->getProperty;
- StrictPropertyOp setter = clasp->setProperty;
-
- if (IsImplicitDenseOrTypedArrayElement(shape)) {
- /* ES5 8.12.4 [[Put]] step 2, for a dense data property on pobj. */
- if (pobj != obj)
- shape = nullptr;
- } else if (shape) {
- /* ES5 8.12.4 [[Put]] step 2. */
- if (shape->isAccessorDescriptor()) {
- if (shape->hasDefaultSetter()) {
- /* Bail out of parallel execution if we are strict to throw. */
- if (mode == ParallelExecution)
- return !strict;
-
- return js_ReportGetterOnlyAssignment(cxArg->asJSContext(), strict);
- }
- } else {
- MOZ_ASSERT(shape->isDataDescriptor());
-
- if (!shape->writable()) {
- /*
- * Error in strict mode code, warn with extra warnings
- * options, otherwise do nothing.
- *
- * Bail out of parallel execution if we are strict to throw.
- */
- if (mode == ParallelExecution)
- return !strict;
-
- JSContext *cx = cxArg->asJSContext();
- if (strict)
- return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
- if (cx->compartment()->options().extraWarnings(cx))
- return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
- return true;
- }
- }
-
- attrs = shape->attributes();
- if (pobj != obj) {
- /*
- * We found id in a prototype object: prepare to share or shadow.
- */
- if (!shape->shadowable()) {
- if (shape->hasDefaultSetter() && !shape->hasGetterValue())
- return true;
-
- if (mode == ParallelExecution)
- return false;
-
- return shape->set(cxArg->asJSContext(), obj, receiver, strict, vp);
- }
-
- /*
- * Preserve attrs except JSPROP_SHARED, getter, and setter when
- * shadowing any property that has no slot (is shared). We must
- * clear the shared attribute for the shadowing shape so that the
- * property in obj that it defines has a slot to retain the value
- * being set, in case the setter simply cannot operate on instances
- * of obj's class by storing the value in some class-specific
- * location.
- */
- if (!shape->hasSlot()) {
- attrs &= ~JSPROP_SHARED;
- getter = shape->getter();
- setter = shape->setter();
- } else {
- /* Restore attrs to the ECMA default for new properties. */
- attrs = JSPROP_ENUMERATE;
- }
-
- /*
- * Forget we found the proto-property now that we've copied any
- * needed member values.
- */
- shape = nullptr;
- }
- }
-
- if (IsImplicitDenseOrTypedArrayElement(shape)) {
- uint32_t index = JSID_TO_INT(id);
-
- if (IsAnyTypedArray(obj)) {
- double d;
- if (mode == ParallelExecution) {
- // Bail if converting the value might invoke user-defined
- // conversions.
- if (vp.isObject())
- return false;
- if (!NonObjectToNumber(cxArg, vp, &d))
- return false;
- } else {
- if (!ToNumber(cxArg->asJSContext(), vp, &d))
- return false;
- }
-
- // Silently do nothing for out-of-bounds sets, for consistency with
- // current behavior. (ES6 currently says to throw for this in
- // strict mode code, so we may eventually need to change.)
- uint32_t len = AnyTypedArrayLength(obj);
- if (index < len) {
- if (obj->is<TypedArrayObject>())
- TypedArrayObject::setElement(obj->as<TypedArrayObject>(), index, d);
- else
- SharedTypedArrayObject::setElement(obj->as<SharedTypedArrayObject>(), index, d);
- }
- return true;
- }
-
- bool definesPast;
- if (!WouldDefinePastNonwritableLength(cxArg, obj, index, strict, &definesPast))
- return false;
- if (definesPast) {
- /* Bail out of parallel execution if we are strict to throw. */
- if (mode == ParallelExecution)
- return !strict;
- return true;
- }
-
- if (!obj->maybeCopyElementsForWrite(cxArg))
- return false;
-
- if (mode == ParallelExecution)
- return obj->setDenseElementIfHasType(index, vp);
-
- obj->setDenseElementWithType(cxArg->asJSContext(), index, vp);
- return true;
- }
-
- if (obj->is<ArrayObject>() && id == NameToId(cxArg->names().length)) {
- Rooted<ArrayObject*> arr(cxArg, &obj->as<ArrayObject>());
- return ArraySetLength<mode>(cxArg, arr, id, attrs, vp, strict);
- }
-
- if (!shape) {
- bool extensible;
- if (mode == ParallelExecution) {
- if (obj->is<ProxyObject>())
- return false;
- extensible = obj->nonProxyIsExtensible();
- } else {
- if (!JSObject::isExtensible(cxArg->asJSContext(), obj, &extensible))
- return false;
- }
-
- if (!extensible) {
- /* Error in strict mode code, warn with extra warnings option, otherwise do nothing. */
- if (strict)
- return obj->reportNotExtensible(cxArg);
- if (mode == SequentialExecution &&
- cxArg->asJSContext()->compartment()->options().extraWarnings(cxArg->asJSContext()))
- {
- return obj->reportNotExtensible(cxArg, JSREPORT_STRICT | JSREPORT_WARNING);
- }
- return true;
- }
-
- if (mode == ParallelExecution) {
- if (obj->isDelegate())
- return false;
-
- if (getter != JS_PropertyStub || !HasTypePropertyId(obj, id, vp))
- return false;
- } else {
- JSContext *cx = cxArg->asJSContext();
-
- /* Purge the property cache of now-shadowed id in obj's scope chain. */
- if (!PurgeScopeChain(cx, obj, id))
- return false;
- }
-
- return DefinePropertyOrElement<mode>(cxArg, obj, id, getter, setter,
- attrs, vp, true, strict);
- }
-
- return NativeSet<mode>(cxArg, obj, receiver, shape, strict, vp);
-}
-
-template bool
-baseops::SetPropertyHelper<SequentialExecution>(JSContext *cx, HandleNativeObject obj,
- HandleObject receiver, HandleId id,
- QualifiedBool qualified,
- MutableHandleValue vp, bool strict);
-template bool
-baseops::SetPropertyHelper<ParallelExecution>(ForkJoinContext *cx, HandleNativeObject obj,
- HandleObject receiver, HandleId id,
- QualifiedBool qualified,
- MutableHandleValue vp, bool strict);
-
-bool
-baseops::SetElementHelper(JSContext *cx, HandleNativeObject obj, HandleObject receiver, uint32_t index,
- MutableHandleValue vp, bool strict)
-{
- RootedId id(cx);
- if (!IndexToId(cx, index, &id))
- return false;
- return baseops::SetPropertyHelper<SequentialExecution>(cx, obj, receiver, id, Qualified, vp,
- strict);
-}
-
-bool
-baseops::GetAttributes(JSContext *cx, HandleNativeObject obj, HandleId id, unsigned *attrsp)
-{
- RootedObject nobj(cx);
- RootedShape shape(cx);
- if (!baseops::LookupProperty<CanGC>(cx, obj, id, &nobj, &shape))
- return false;
- if (!shape) {
- *attrsp = 0;
- return true;
- }
- if (!nobj->isNative())
- return JSObject::getGenericAttributes(cx, nobj, id, attrsp);
-
- *attrsp = GetShapeAttributes(nobj, shape);
- return true;
-}
-
-bool
-baseops::SetAttributes(JSContext *cx, HandleNativeObject obj, HandleId id, unsigned *attrsp)
-{
- RootedObject nobj(cx);
- RootedShape shape(cx);
- if (!baseops::LookupProperty<CanGC>(cx, obj, id, &nobj, &shape))
- return false;
- if (!shape)
- return true;
- if (nobj->isNative() && IsImplicitDenseOrTypedArrayElement(shape)) {
- if (IsAnyTypedArray(nobj.get())) {
- if (*attrsp == (JSPROP_ENUMERATE | JSPROP_PERMANENT))
- return true;
- JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_ARRAY_ATTRS);
- return false;
- }
- if (!NativeObject::sparsifyDenseElement(cx, nobj.as<NativeObject>(), JSID_TO_INT(id)))
- return false;
- shape = nobj->as<NativeObject>().lookup(cx, id);
- }
- if (nobj->isNative()) {
- if (!NativeObject::changePropertyAttributes(cx, nobj.as<NativeObject>(), shape, *attrsp))
- return false;
- if (*attrsp & JSPROP_READONLY)
- MarkTypePropertyNonWritable(cx, nobj, id);
- return true;
- } else {
- return JSObject::setGenericAttributes(cx, nobj, id, attrsp);
- }
-}
-
-bool
-baseops::DeleteGeneric(JSContext *cx, HandleNativeObject obj, HandleId id, bool *succeeded)
-{
- RootedObject proto(cx);
- RootedShape shape(cx);
- if (!baseops::LookupProperty<CanGC>(cx, obj, id, &proto, &shape))
- return false;
- if (!shape || proto != obj) {
- /*
- * If no property, or the property comes from a prototype, call the
- * class's delProperty hook, passing succeeded as the result parameter.
- */
- return CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded);
- }
-
- cx->runtime()->gc.poke();
-
- if (IsImplicitDenseOrTypedArrayElement(shape)) {
- if (IsAnyTypedArray(obj)) {
- // Don't delete elements from typed arrays.
- *succeeded = false;
- return true;
- }
-
- if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded))
- return false;
- if (!succeeded)
- return true;
-
- NativeObject *nobj = &obj->as<NativeObject>();
- if (!nobj->maybeCopyElementsForWrite(cx))
- return false;
-
- nobj->setDenseElementHole(cx, JSID_TO_INT(id));
- return SuppressDeletedProperty(cx, obj, id);
- }
-
- if (!shape->configurable()) {
- *succeeded = false;
- return true;
- }
-
- RootedId propid(cx, shape->propid());
- if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, propid, succeeded))
- return false;
- if (!succeeded)
- return true;
-
- return obj->removeProperty(cx, id) && SuppressDeletedProperty(cx, obj, id);
-}
-
bool
js::WatchGuts(JSContext *cx, JS::HandleObject origObj, JS::HandleId id, JS::HandleObject callable)
{
RootedObject obj(cx, GetInnerObject(origObj));
if (obj->isNative()) {
// Use sparse indexes for watched objects, as dense elements can be
// written to without checking the watchpoint map.
if (!NativeObject::sparsifyDenseElements(cx, obj.as<NativeObject>()))
@@ -6569,8 +4320,63 @@ JSObject::addSizeOfExcludingThis(mozilla
#ifdef JS_HAS_CTYPES
} else {
// This must be the last case.
info->objectsMallocHeapMisc +=
js::SizeOfDataIfCDataObject(mallocSizeOf, const_cast<JSObject *>(this));
#endif
}
}
+
+bool
+JSObject::hasIdempotentProtoChain() const
+{
+ // Return false if obj (or an object on its proto chain) is non-native or
+ // has a resolve or lookup hook.
+ JSObject *obj = const_cast<JSObject *>(this);
+ while (true) {
+ if (!obj->isNative())
+ return false;
+
+ JSResolveOp resolve = obj->getClass()->resolve;
+ if (resolve != JS_ResolveStub && resolve != (JSResolveOp) js::fun_resolve)
+ return false;
+
+ if (obj->getOps()->lookupProperty || obj->getOps()->lookupGeneric || obj->getOps()->lookupElement)
+ return false;
+
+ obj = obj->getProto();
+ if (!obj)
+ return true;
+ }
+}
+
+void
+JSObject::markChildren(JSTracer *trc)
+{
+ MarkTypeObject(trc, &type_, "type");
+
+ MarkShape(trc, &shape_, "shape");
+
+ const Class *clasp = type_->clasp();
+ if (clasp->trace)
+ clasp->trace(trc, this);
+
+ if (shape_->isNative()) {
+ NativeObject *nobj = &as<NativeObject>();
+ MarkObjectSlots(trc, nobj, 0, nobj->slotSpan());
+
+ do {
+ if (nobj->denseElementsAreCopyOnWrite()) {
+ HeapPtrNativeObject &owner = nobj->getElementsHeader()->ownerObject();
+ if (owner != nobj) {
+ MarkObject(trc, &owner, "objectElementsOwner");
+ break;
+ }
+ }
+
+ gc::MarkArraySlots(trc,
+ nobj->getDenseInitializedLength(),
+ nobj->getDenseElementsAllowCopyOnWrite(),
+ "objectElements");
+ } while (false);
+ }
+}
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1058,27 +1058,16 @@ CreateThis(JSContext *cx, const js::Clas
extern JSObject *
CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto, HandleObject parent);
extern NativeObject *
DeepCloneObjectLiteral(JSContext *cx, HandleNativeObject obj, NewObjectKind newKind = GenericObject);
/*
- * Return successfully added or changed shape or nullptr on error.
- */
-extern bool
-DefineNativeProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id, HandleValue value,
- PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
-
-extern bool
-LookupNativeProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id,
- js::MutableHandleObject objp, js::MutableHandleShape propp);
-
-/*
* Call the [[DefineOwnProperty]] internal method of obj.
*
* If obj is an array, this follows ES5 15.4.5.1.
* If obj is any other native object, this follows ES5 8.12.9.
* If obj is a proxy, this calls the proxy handler's defineProperty method.
* Otherwise, this reports an error and returns false.
*/
extern bool
@@ -1133,26 +1122,16 @@ LookupNameUnqualified(JSContext *cx, Han
extern JSObject *
js_FindVariableScope(JSContext *cx, JSFunction **funp);
namespace js {
bool
-NativeGet(JSContext *cx, HandleObject obj, HandleNativeObject pobj,
- HandleShape shape, MutableHandle<Value> vp);
-
-template <ExecutionMode mode>
-bool
-NativeSet(typename ExecutionModeTraits<mode>::ContextType cx,
- HandleNativeObject obj, HandleObject receiver,
- HandleShape shape, bool strict, MutableHandleValue vp);
-
-bool
LookupPropertyPure(JSObject *obj, jsid id, NativeObject **objp, Shape **propp);
bool
GetPropertyPure(ThreadSafeContext *cx, JSObject *obj, jsid id, Value *vp);
inline bool
GetPropertyPure(ThreadSafeContext *cx, JSObject *obj, PropertyName *name, Value *vp)
{
@@ -1164,29 +1143,16 @@ GetOwnPropertyDescriptor(JSContext *cx,
MutableHandle<PropertyDescriptor> desc);
bool
GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp);
bool
NewPropertyDescriptorObject(JSContext *cx, Handle<PropertyDescriptor> desc, MutableHandleValue vp);
-/*
- * If obj has an already-resolved data property for id, return true and
- * store the property value in *vp.
- */
-extern bool
-HasDataProperty(JSContext *cx, NativeObject *obj, jsid id, Value *vp);
-
-inline bool
-HasDataProperty(JSContext *cx, NativeObject *obj, PropertyName *name, Value *vp)
-{
- return HasDataProperty(cx, obj, NameToId(name), vp);
-}
-
extern bool
IsDelegate(JSContext *cx, HandleObject obj, const Value &v, bool *result);
// obj is a JSObject*, but we root it immediately up front. We do it
// that way because we need a Rooted temporary in this method anyway.
extern bool
IsDelegateOfObject(JSContext *cx, HandleObject protoObj, JSObject* obj, bool *result);
@@ -1257,11 +1223,21 @@ GetFirstArgumentAsObject(JSContext *cx,
/* Helpers for throwing. These always return false. */
extern bool
Throw(JSContext *cx, jsid id, unsigned errorNumber);
extern bool
Throw(JSContext *cx, JSObject *obj, unsigned errorNumber);
+namespace baseops {
+
+extern bool
+Watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable);
+
+extern bool
+Unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id);
+
+} /* namespace baseops */
+
} /* namespace js */
#endif /* jsobj_h */
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -792,25 +792,16 @@ NewObjectMetadata(ExclusiveContext *cxAr
if (!cx->compartment()->callObjectMetadataCallback(cx, pmetadata))
return false;
}
}
return true;
}
-inline bool
-DefineNativeProperty(ExclusiveContext *cx, HandleNativeObject obj,
- PropertyName *name, HandleValue value,
- PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
-{
- Rooted<jsid> id(cx, NameToId(name));
- return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
-}
-
namespace baseops {
inline bool
LookupProperty(ExclusiveContext *cx, HandleNativeObject obj, PropertyName *name,
MutableHandleObject objp, MutableHandleShape propp)
{
Rooted<jsid> id(cx, NameToId(name));
return LookupProperty<CanGC>(cx, obj, id, objp, propp);
@@ -821,16 +812,50 @@ DefineProperty(ExclusiveContext *cx, Han
JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
{
Rooted<jsid> id(cx, NameToId(name));
return DefineGeneric(cx, obj, id, value, getter, setter, attrs);
}
} /* namespace baseops */
+static inline unsigned
+ApplyAttributes(unsigned attrs, bool enumerable, bool writable, bool configurable)
+{
+ /*
+ * Respect the fact that some callers may want to preserve existing attributes as much as
+ * possible, or install defaults otherwise.
+ */
+ if (attrs & JSPROP_IGNORE_ENUMERATE) {
+ attrs &= ~JSPROP_IGNORE_ENUMERATE;
+ if (enumerable)
+ attrs |= JSPROP_ENUMERATE;
+ else
+ attrs &= ~JSPROP_ENUMERATE;
+ }
+ if (attrs & JSPROP_IGNORE_READONLY) {
+ attrs &= ~JSPROP_IGNORE_READONLY;
+ // Only update the writability if it's relevant
+ if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
+ if (!writable)
+ attrs |= JSPROP_READONLY;
+ else
+ attrs &= ~JSPROP_READONLY;
+ }
+ }
+ if (attrs & JSPROP_IGNORE_PERMANENT) {
+ attrs &= ~JSPROP_IGNORE_PERMANENT;
+ if (!configurable)
+ attrs |= JSPROP_PERMANENT;
+ else
+ attrs &= ~JSPROP_PERMANENT;
+ }
+ return attrs;
+}
+
} /* namespace js */
extern js::NativeObject *
js_InitClass(JSContext *cx, js::HandleObject obj, JSObject *parent_proto,
const js::Class *clasp, JSNative constructor, unsigned nargs,
const JSPropertySpec *ps, const JSFunctionSpec *fs,
const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs,
js::NativeObject **ctorp = nullptr,
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -883,22 +883,22 @@ InnerViewTable::removeViews(ArrayBufferO
MOZ_ASSERT(p);
map.remove(p);
}
bool
InnerViewTable::sweepEntry(JSObject **pkey, ViewVector &views)
{
- if (IsObjectAboutToBeFinalized(pkey))
+ if (IsObjectAboutToBeFinalizedFromAnyThread(pkey))
return true;
MOZ_ASSERT(!views.empty());
for (size_t i = 0; i < views.length(); i++) {
- if (IsObjectAboutToBeFinalized(&views[i])) {
+ if (IsObjectAboutToBeFinalizedFromAnyThread(&views[i])) {
views[i--] = views.back();
views.popBack();
}
}
return views.empty();
}
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -709,16 +709,111 @@ GlobalHelperThreadState::canStartCompres
}
bool
GlobalHelperThreadState::canStartGCHelperTask()
{
return !gcHelperWorklist().empty();
}
+bool
+GlobalHelperThreadState::canStartGCParallelTask()
+{
+ return !gcParallelWorklist().empty();
+}
+
+bool
+js::GCParallelTask::startWithLockHeld()
+{
+ MOZ_ASSERT(HelperThreadState().isLocked());
+
+ // Tasks cannot be started twice.
+ MOZ_ASSERT(state == NotStarted);
+
+ // If we do the shutdown GC before running anything, we may never
+ // have initialized the helper threads. Just use the serial path
+ // since we cannot safely intialize them at this point.
+ if (!HelperThreadState().threads)
+ return false;
+
+ if (!HelperThreadState().gcParallelWorklist().append(this))
+ return false;
+ state = Dispatched;
+
+ HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER);
+
+ return true;
+}
+
+bool
+js::GCParallelTask::start()
+{
+ AutoLockHelperThreadState helperLock;
+ return startWithLockHeld();
+}
+
+void
+js::GCParallelTask::joinWithLockHeld()
+{
+ MOZ_ASSERT(HelperThreadState().isLocked());
+
+ if (state == NotStarted)
+ return;
+
+ while (state != Finished)
+ HelperThreadState().wait(GlobalHelperThreadState::CONSUMER);
+ state = NotStarted;
+}
+
+void
+js::GCParallelTask::join()
+{
+ AutoLockHelperThreadState helperLock;
+ joinWithLockHeld();
+}
+
+void
+js::GCParallelTask::runFromMainThread(JSRuntime *rt)
+{
+ MOZ_ASSERT(state == NotStarted);
+ MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(rt));
+ uint64_t timeStart = PRMJ_Now();
+ run();
+ duration_ = PRMJ_Now() - timeStart;
+}
+
+void
+js::GCParallelTask::runFromHelperThread()
+{
+ MOZ_ASSERT(HelperThreadState().isLocked());
+
+ {
+ AutoUnlockHelperThreadState parallelSection;
+ uint64_t timeStart = PRMJ_Now();
+ run();
+ duration_ = PRMJ_Now() - timeStart;
+ }
+
+ state = Finished;
+ HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER);
+}
+
+void
+HelperThread::handleGCParallelWorkload()
+{
+ MOZ_ASSERT(HelperThreadState().isLocked());
+ MOZ_ASSERT(HelperThreadState().canStartGCParallelTask());
+ MOZ_ASSERT(idle());
+
+ MOZ_ASSERT(!gcParallelTask);
+ gcParallelTask = HelperThreadState().gcParallelWorklist().popCopy();
+ gcParallelTask->runFromHelperThread();
+ gcParallelTask = nullptr;
+}
+
static void
LeaveParseTaskZone(JSRuntime *rt, ParseTask *task)
{
// Mark the zone as no longer in use by an ExclusiveContext, and available
// to be collected by the GC.
task->cx->leaveCompartment(task->cx->compartment());
rt->clearUsedByExclusiveThread(task->cx->zone());
}
@@ -1232,17 +1327,18 @@ HelperThread::threadLoop()
bool ionCompile = false;
while (true) {
if (terminate)
return;
if (HelperThreadState().canStartAsmJSCompile() ||
(ionCompile = HelperThreadState().pendingIonCompileHasSufficientPriority()) ||
HelperThreadState().canStartParseTask() ||
HelperThreadState().canStartCompressionTask() ||
- HelperThreadState().canStartGCHelperTask())
+ HelperThreadState().canStartGCHelperTask() ||
+ HelperThreadState().canStartGCParallelTask())
{
break;
}
HelperThreadState().wait(GlobalHelperThreadState::PRODUCER);
}
// Dispatch tasks, prioritizing AsmJS work.
if (HelperThreadState().canStartAsmJSCompile())
@@ -1250,12 +1346,14 @@ HelperThread::threadLoop()
else if (ionCompile)
handleIonWorkload();
else if (HelperThreadState().canStartParseTask())
handleParseWorkload();
else if (HelperThreadState().canStartCompressionTask())
handleCompressionWorkload();
else if (HelperThreadState().canStartGCHelperTask())
handleGCHelperWorkload();
+ else if (HelperThreadState().canStartGCParallelTask())
+ handleGCParallelWorkload();
else
MOZ_CRASH("No task to perform");
}
}
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -42,16 +42,17 @@ class GlobalHelperThreadState
// Number of threads to create. May be accessed without locking.
size_t threadCount;
typedef Vector<jit::IonBuilder*, 0, SystemAllocPolicy> IonBuilderVector;
typedef Vector<AsmJSParallelTask*, 0, SystemAllocPolicy> AsmJSParallelTaskVector;
typedef Vector<ParseTask*, 0, SystemAllocPolicy> ParseTaskVector;
typedef Vector<SourceCompressionTask*, 0, SystemAllocPolicy> SourceCompressionTaskVector;
typedef Vector<GCHelperState *, 0, SystemAllocPolicy> GCHelperStateVector;
+ typedef Vector<GCParallelTask *, 0, SystemAllocPolicy> GCParallelTaskVector;
typedef mozilla::LinkedList<jit::IonBuilder> IonBuilderList;
// List of available threads, or null if the thread state has not been initialized.
HelperThread *threads;
private:
// The lists below are all protected by |lock|.
@@ -82,16 +83,19 @@ class GlobalHelperThreadState
ParseTaskVector parseWaitingOnGC_;
// Source compression worklist.
SourceCompressionTaskVector compressionWorklist_;
// Runtimes which have sweeping / allocating work to do.
GCHelperStateVector gcHelperWorklist_;
+ // GC tasks needing to be done in parallel.
+ GCParallelTaskVector gcParallelWorklist_;
+
public:
size_t maxIonCompilationThreads() const {
return 1;
}
size_t maxAsmJSCompilationThreads() const {
if (cpuCount < 2)
return 2;
return cpuCount;
@@ -173,21 +177,27 @@ class GlobalHelperThreadState
return compressionWorklist_;
}
GCHelperStateVector &gcHelperWorklist() {
MOZ_ASSERT(isLocked());
return gcHelperWorklist_;
}
+ GCParallelTaskVector &gcParallelWorklist() {
+ MOZ_ASSERT(isLocked());
+ return gcParallelWorklist_;
+ }
+
bool canStartAsmJSCompile();
bool canStartIonCompile();
bool canStartParseTask();
bool canStartCompressionTask();
bool canStartGCHelperTask();
+ bool canStartGCParallelTask();
// Unlike the methods above, the value returned by this method can change
// over time, even if the helper thread state lock is held throughout.
bool pendingIonCompileHasSufficientPriority();
jit::IonBuilder *highestPriorityPendingIonCompile(bool remove = false);
HelperThread *lowestPriorityUnpausedIonCompileAtThreshold();
HelperThread *highestPriorityPausedIonCompile();
@@ -294,27 +304,36 @@ struct HelperThread
ParseTask *parseTask;
/* Any source being compressed on this thread. */
SourceCompressionTask *compressionTask;
/* Any GC state for background sweeping or allocating being performed. */
GCHelperState *gcHelperState;
+ /* State required to perform a GC parallel task. */
+ GCParallelTask *gcParallelTask;
+
bool idle() const {
- return !ionBuilder && !asmData && !parseTask && !compressionTask && !gcHelperState;
+ return !ionBuilder &&
+ !asmData &&
+ !parseTask &&
+ !compressionTask &&
+ !gcHelperState &&
+ !gcParallelTask;
}
void destroy();
void handleAsmJSWorkload();
void handleIonWorkload();
void handleParseWorkload();
void handleCompressionWorkload();
void handleGCHelperWorkload();
+ void handleGCParallelWorkload();
static void ThreadMain(void *arg);
void threadLoop();
};
/* Methods for interacting with helper threads. */
// Initialize helper threads unless already initialized.
--- a/js/src/vm/ObjectImpl-inl.h
+++ b/js/src/vm/ObjectImpl-inl.h
@@ -455,16 +455,236 @@ NewNativeObjectWithType(JSContext *cx, H
inline NativeObject *
NewNativeObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent,
NewObjectKind newKind = GenericObject)
{
return MaybeNativeObject(NewObjectWithType(cx, type, parent, newKind));
}
+/*
+ * Call obj's resolve hook.
+ *
+ * cx, id, and flags are the parameters initially passed to the ongoing lookup;
+ * objp and propp are its out parameters. obj is an object along the prototype
+ * chain from where the lookup started.
+ *
+ * There are four possible outcomes:
+ *
+ * - On failure, report an error or exception and return false.
+ *
+ * - If we are already resolving a property of *curobjp, set *recursedp = true,
+ * and return true.
+ *
+ * - If the resolve hook finds or defines the sought property, set *objp and
+ * *propp appropriately, set *recursedp = false, and return true.
+ *
+ * - Otherwise no property was resolved. Set *propp = nullptr and
+ * *recursedp = false and return true.
+ */
+static MOZ_ALWAYS_INLINE bool
+CallResolveOp(JSContext *cx, HandleNativeObject obj, HandleId id, MutableHandleObject objp,
+ MutableHandleShape propp, bool *recursedp)
+{
+ const Class *clasp = obj->getClass();
+ JSResolveOp resolve = clasp->resolve;
+
+ /*
+ * Avoid recursion on (obj, id) already being resolved on cx.
+ *
+ * Once we have successfully added an entry for (obj, key) to
+ * cx->resolvingTable, control must go through cleanup: before
+ * returning. But note that JS_DHASH_ADD may find an existing
+ * entry, in which case we bail to suppress runaway recursion.
+ */
+ AutoResolving resolving(cx, obj, id);
+ if (resolving.alreadyStarted()) {
+ /* Already resolving id in obj -- suppress recursion. */
+ *recursedp = true;
+ return true;
+ }
+ *recursedp = false;
+
+ propp.set(nullptr);
+
+ if (clasp->flags & JSCLASS_NEW_RESOLVE) {
+ JSNewResolveOp newresolve = reinterpret_cast<JSNewResolveOp>(resolve);
+ RootedObject obj2(cx, nullptr);
+ if (!newresolve(cx, obj, id, &obj2))
+ return false;
+
+ /*
+ * We trust the new style resolve hook to set obj2 to nullptr when
+ * the id cannot be resolved. But, when obj2 is not null, we do
+ * not assume that id must exist and do full nativeLookup for
+ * compatibility.
+ */
+ if (!obj2)
+ return true;
+
+ if (!obj2->isNative()) {
+ /* Whoops, newresolve handed back a foreign obj2. */
+ MOZ_ASSERT(obj2 != obj);
+ return JSObject::lookupGeneric(cx, obj2, id, objp, propp);
+ }
+
+ objp.set(obj2);
+ } else {
+ if (!resolve(cx, obj, id))
+ return false;
+
+ objp.set(obj);
+ }
+
+ NativeObject *nobjp = &objp->as<NativeObject>();
+
+ if (JSID_IS_INT(id) && nobjp->containsDenseElement(JSID_TO_INT(id))) {
+ MarkDenseOrTypedArrayElementFound<CanGC>(propp);
+ return true;
+ }
+
+ Shape *shape;
+ if (!nobjp->empty() && (shape = nobjp->lookup(cx, id)))
+ propp.set(shape);
+ else
+ objp.set(nullptr);
+
+ return true;
+}
+
+template <AllowGC allowGC>
+static MOZ_ALWAYS_INLINE bool
+LookupOwnPropertyInline(ExclusiveContext *cx,
+ typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
+ typename MaybeRooted<jsid, allowGC>::HandleType id,
+ typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
+ typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp,
+ bool *donep)
+{
+ // Check for a native dense element.
+ if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
+ objp.set(obj);
+ MarkDenseOrTypedArrayElementFound<allowGC>(propp);
+ *donep = true;
+ return true;
+ }
+
+ // Check for a typed array element. Integer lookups always finish here
+ // so that integer properties on the prototype are ignored even for out
+ // of bounds accesses.
+ if (IsAnyTypedArray(obj)) {
+ uint64_t index;
+ if (IsTypedArrayIndex(id, &index)) {
+ if (index < AnyTypedArrayLength(obj)) {
+ objp.set(obj);
+ MarkDenseOrTypedArrayElementFound<allowGC>(propp);
+ } else {
+ objp.set(nullptr);
+ propp.set(nullptr);
+ }
+ *donep = true;
+ return true;
+ }
+ }
+
+ // Check for a native property.
+ if (Shape *shape = obj->lookup(cx, id)) {
+ objp.set(obj);
+ propp.set(shape);
+ *donep = true;
+ return true;
+ }
+
+ // id was not found in obj. Try obj's resolve hook, if any.
+ if (obj->getClass()->resolve != JS_ResolveStub) {
+ if (!cx->shouldBeJSContext() || !allowGC)
+ return false;
+
+ bool recursed;
+ if (!CallResolveOp(cx->asJSContext(),
+ MaybeRooted<NativeObject*, allowGC>::toHandle(obj),
+ MaybeRooted<jsid, allowGC>::toHandle(id),
+ MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
+ MaybeRooted<Shape*, allowGC>::toMutableHandle(propp),
+ &recursed))
+ {
+ return false;
+ }
+
+ if (recursed) {
+ objp.set(nullptr);
+ propp.set(nullptr);
+ *donep = true;
+ return true;
+ }
+
+ if (propp) {
+ *donep = true;
+ return true;
+ }
+ }
+
+ *donep = false;
+ return true;
+}
+
+template <AllowGC allowGC>
+static MOZ_ALWAYS_INLINE bool
+LookupPropertyInline(ExclusiveContext *cx,
+ typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
+ typename MaybeRooted<jsid, allowGC>::HandleType id,
+ typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
+ typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
+{
+ /* NB: The logic of this procedure is implicitly reflected in
+ * BaselineIC.cpp's |EffectlesslyLookupProperty| logic.
+ * If this changes, please remember to update the logic there as well.
+ */
+
+ /* Search scopes starting with obj and following the prototype link. */
+ typename MaybeRooted<NativeObject*, allowGC>::RootType current(cx, obj);
+
+ while (true) {
+ bool done;
+ if (!LookupOwnPropertyInline<allowGC>(cx, current, id, objp, propp, &done))
+ return false;
+ if (done)
+ return true;
+
+ typename MaybeRooted<JSObject*, allowGC>::RootType proto(cx, current->getProto());
+
+ if (!proto)
+ break;
+ if (!proto->isNative()) {
+ if (!cx->shouldBeJSContext() || !allowGC)
+ return false;
+ return JSObject::lookupGeneric(cx->asJSContext(),
+ MaybeRooted<JSObject*, allowGC>::toHandle(proto),
+ MaybeRooted<jsid, allowGC>::toHandle(id),
+ MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
+ MaybeRooted<Shape*, allowGC>::toMutableHandle(propp));
+ }
+
+ current = &proto->template as<NativeObject>();
+ }
+
+ objp.set(nullptr);
+ propp.set(nullptr);
+ return true;
+}
+
+inline bool
+DefineNativeProperty(ExclusiveContext *cx, HandleNativeObject obj,
+ PropertyName *name, HandleValue value,
+ PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+{
+ RootedId id(cx, NameToId(name));
+ return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
+}
+
} // namespace js
inline uint8_t *
JSObject::fakeNativeFixedData(size_t nslots) const
{
return static_cast<const js::NativeObject*>(this)->fixedData(nslots);
}
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -1,27 +1,38 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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 "vm/ObjectImpl-inl.h"
+#include "mozilla/CheckedInt.h"
+
+#include "jswatchpoint.h"
+
#include "gc/Marking.h"
#include "js/Value.h"
#include "vm/Debugger.h"
#include "vm/TypedArrayCommon.h"
#include "jsobjinlines.h"
+
+#include "vm/ArrayObject-inl.h"
#include "vm/Shape-inl.h"
using namespace js;
using JS::GenericNaN;
+using mozilla::DebugOnly;
+using mozilla::RoundUpPow2;
+
+JS_STATIC_ASSERT(int32_t((NativeObject::NELEMENTS_LIMIT - 1) * sizeof(Value)) ==
+ int64_t((NativeObject::NELEMENTS_LIMIT - 1) * sizeof(Value)));
PropDesc::PropDesc()
{
setUndefined();
}
void
PropDesc::setUndefined()
@@ -35,17 +46,16 @@ PropDesc::setUndefined()
hasValue_ = false;
hasWritable_ = false;
hasEnumerable_ = false;
hasConfigurable_ = false;
isUndefined_ = true;
}
-
bool
PropDesc::checkGetter(JSContext *cx)
{
if (hasGet_) {
if (!IsCallable(get_) && !get_.isUndefined()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
js_getter_str);
return false;
@@ -283,46 +293,2013 @@ js::NativeObject::dynamicSlotsCount(uint
return SLOT_CAPACITY_MIN;
uint32_t slots = mozilla::RoundUpPow2(span);
MOZ_ASSERT(slots >= span);
return slots;
}
void
-JSObject::markChildren(JSTracer *trc)
-{
- MarkTypeObject(trc, &type_, "type");
-
- MarkShape(trc, &shape_, "shape");
-
- const Class *clasp = type_->clasp();
- if (clasp->trace)
- clasp->trace(trc, this);
-
- if (shape_->isNative()) {
- NativeObject *nobj = &as<NativeObject>();
- MarkObjectSlots(trc, nobj, 0, nobj->slotSpan());
-
- do {
- if (nobj->denseElementsAreCopyOnWrite()) {
- HeapPtrNativeObject &owner = nobj->getElementsHeader()->ownerObject();
- if (owner != nobj) {
- MarkObject(trc, &owner, "objectElementsOwner");
- break;
- }
- }
-
- gc::MarkArraySlots(trc,
- nobj->getDenseInitializedLength(),
- nobj->getDenseElementsAllowCopyOnWrite(),
- "objectElements");
- } while (false);
- }
-}
-
-void
PropDesc::trace(JSTracer *trc)
{
gc::MarkValueRoot(trc, &value_, "PropDesc value");
gc::MarkValueRoot(trc, &get_, "PropDesc get");
gc::MarkValueRoot(trc, &set_, "PropDesc set");
}
+
+/* static */ inline bool
+NativeObject::updateSlotsForSpan(ThreadSafeContext *cx,
+ HandleNativeObject obj, size_t oldSpan, size_t newSpan)
+{
+ MOZ_ASSERT(cx->isThreadLocal(obj));
+ MOZ_ASSERT(oldSpan != newSpan);
+
+ size_t oldCount = dynamicSlotsCount(obj->numFixedSlots(), oldSpan, obj->getClass());
+ size_t newCount = dynamicSlotsCount(obj->numFixedSlots(), newSpan, obj->getClass());
+
+ if (oldSpan < newSpan) {
+ if (oldCount < newCount && !growSlots(cx, obj, oldCount, newCount))
+ return false;
+
+ if (newSpan == oldSpan + 1)
+ obj->initSlotUnchecked(oldSpan, UndefinedValue());
+ else
+ obj->initializeSlotRange(oldSpan, newSpan - oldSpan);
+ } else {
+ /* Trigger write barriers on the old slots before reallocating. */
+ obj->prepareSlotRangeForOverwrite(newSpan, oldSpan);
+ obj->invalidateSlotRange(newSpan, oldSpan - newSpan);
+
+ if (oldCount > newCount)
+ shrinkSlots(cx, obj, oldCount, newCount);
+ }
+
+ return true;
+}
+
+/* static */ bool
+NativeObject::setLastProperty(ThreadSafeContext *cx, HandleNativeObject obj, HandleShape shape)
+{
+ MOZ_ASSERT(cx->isThreadLocal(obj));
+ MOZ_ASSERT(!obj->inDictionaryMode());
+ MOZ_ASSERT(!shape->inDictionary());
+ MOZ_ASSERT(shape->compartment() == obj->compartment());
+ MOZ_ASSERT(shape->numFixedSlots() == obj->numFixedSlots());
+
+ size_t oldSpan = obj->lastProperty()->slotSpan();
+ size_t newSpan = shape->slotSpan();
+
+ if (oldSpan == newSpan) {
+ obj->shape_ = shape;
+ return true;
+ }
+
+ if (!updateSlotsForSpan(cx, obj, oldSpan, newSpan))
+ return false;
+
+ obj->shape_ = shape;
+ return true;
+}
+
+void
+NativeObject::setLastPropertyShrinkFixedSlots(Shape *shape)
+{
+ MOZ_ASSERT(!inDictionaryMode());
+ MOZ_ASSERT(!shape->inDictionary());
+ MOZ_ASSERT(shape->compartment() == compartment());
+ MOZ_ASSERT(lastProperty()->slotSpan() == shape->slotSpan());
+
+ DebugOnly<size_t> oldFixed = numFixedSlots();
+ DebugOnly<size_t> newFixed = shape->numFixedSlots();
+ MOZ_ASSERT(newFixed < oldFixed);
+ MOZ_ASSERT(shape->slotSpan() <= oldFixed);
+ MOZ_ASSERT(shape->slotSpan() <= newFixed);
+ MOZ_ASSERT(dynamicSlotsCount(oldFixed, shape->slotSpan(), getClass()) == 0);
+ MOZ_ASSERT(dynamicSlotsCount(newFixed, shape->slotSpan(), getClass()) == 0);
+
+ shape_ = shape;
+}
+
+/* static */ bool
+NativeObject::setSlotSpan(ThreadSafeContext *cx, HandleNativeObject obj, uint32_t span)
+{
+ MOZ_ASSERT(cx->isThreadLocal(obj));
+ MOZ_ASSERT(obj->inDictionaryMode());
+
+ size_t oldSpan = obj->lastProperty()->base()->slotSpan();
+ if (oldSpan == span)
+ return true;
+
+ if (!updateSlotsForSpan(cx, obj, oldSpan, span))
+ return false;
+
+ obj->lastProperty()->base()->setSlotSpan(span);
+ return true;
+}
+
+// This will not run the garbage collector. If a nursery cannot accomodate the slot array
+// an attempt will be made to place the array in the tenured area.
+static HeapSlot *
+AllocateSlots(ThreadSafeContext *cx, JSObject *obj, uint32_t nslots)
+{
+#ifdef JSGC_GENERATIONAL
+ if (cx->isJSContext())
+ return cx->asJSContext()->runtime()->gc.nursery.allocateSlots(obj, nslots);
+#endif
+#ifdef JSGC_FJGENERATIONAL
+ if (cx->isForkJoinContext())
+ return cx->asForkJoinContext()->nursery().allocateSlots(obj, nslots);
+#endif
+ return obj->zone()->pod_malloc<HeapSlot>(nslots);
+}
+
+// This will not run the garbage collector. If a nursery cannot accomodate the slot array
+// an attempt will be made to place the array in the tenured area.
+//
+// If this returns null then the old slots will be left alone.
+static HeapSlot *
+ReallocateSlots(ThreadSafeContext *cx, JSObject *obj, HeapSlot *oldSlots,
+ uint32_t oldCount, uint32_t newCount)
+{
+#ifdef JSGC_GENERATIONAL
+ if (cx->isJSContext()) {
+ return cx->asJSContext()->runtime()->gc.nursery.reallocateSlots(obj, oldSlots,
+ oldCount, newCount);
+ }
+#endif
+#ifdef JSGC_FJGENERATIONAL
+ if (cx->isForkJoinContext()) {
+ return cx->asForkJoinContext()->nursery().reallocateSlots(obj, oldSlots,
+ oldCount, newCount);
+ }
+#endif
+ return obj->zone()->pod_realloc<HeapSlot>(oldSlots, oldCount, newCount);
+}
+
+/* static */ bool
+NativeObject::growSlots(ThreadSafeContext *cx, HandleNativeObject obj, uint32_t oldCount, uint32_t newCount)
+{
+ MOZ_ASSERT(cx->isThreadLocal(obj));
+ MOZ_ASSERT(newCount > oldCount);
+ MOZ_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
+
+ /*
+ * Slot capacities are determined by the span of allocated objects. Due to
+ * the limited number of bits to store shape slots, object growth is
+ * throttled well before the slot capacity can overflow.
+ */
+ MOZ_ASSERT(newCount < NELEMENTS_LIMIT);
+
+ if (!oldCount) {
+ obj->slots = AllocateSlots(cx, obj, newCount);
+ if (!obj->slots)
+ return false;
+ Debug_SetSlotRangeToCrashOnTouch(obj->slots, newCount);
+ return true;
+ }
+
+ HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount);
+ if (!newslots)
+ return false; /* Leave slots at its old size. */
+
+ obj->slots = newslots;
+
+ Debug_SetSlotRangeToCrashOnTouch(obj->slots + oldCount, newCount - oldCount);
+
+ return true;
+}
+
+static void
+FreeSlots(ThreadSafeContext *cx, HeapSlot *slots)
+{
+#ifdef JSGC_GENERATIONAL
+ // Note: threads without a JSContext do not have access to GGC nursery allocated things.
+ if (cx->isJSContext())
+ return cx->asJSContext()->runtime()->gc.nursery.freeSlots(slots);
+#endif
+#ifdef JSGC_FJGENERATIONAL
+ if (cx->isForkJoinContext())
+ return cx->asForkJoinContext()->nursery().freeSlots(slots);
+#endif
+ js_free(slots);
+}
+
+/* static */ void
+NativeObject::shrinkSlots(ThreadSafeContext *cx, HandleNativeObject obj,
+ uint32_t oldCount, uint32_t newCount)
+{
+ MOZ_ASSERT(cx->isThreadLocal(obj));
+ MOZ_ASSERT(newCount < oldCount);
+
+ if (newCount == 0) {
+ FreeSlots(cx, obj->slots);
+ obj->slots = nullptr;
+ return;
+ }
+
+ MOZ_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
+
+ HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount);
+ if (!newslots)
+ return; /* Leave slots at its old size. */
+
+ obj->slots = newslots;
+}
+
+/* static */ bool
+NativeObject::sparsifyDenseElement(ExclusiveContext *cx, HandleNativeObject obj, uint32_t index)
+{
+ if (!obj->maybeCopyElementsForWrite(cx))
+ return false;
+
+ RootedValue value(cx, obj->getDenseElement(index));
+ MOZ_ASSERT(!value.isMagic(JS_ELEMENTS_HOLE));
+
+ removeDenseElementForSparseIndex(cx, obj, index);
+
+ uint32_t slot = obj->slotSpan();
+ if (!obj->addDataProperty(cx, INT_TO_JSID(index), slot, JSPROP_ENUMERATE)) {
+ obj->setDenseElement(index, value);
+ return false;
+ }
+
+ MOZ_ASSERT(slot == obj->slotSpan() - 1);
+ obj->initSlot(slot, value);
+
+ return true;
+}
+
+/* static */ bool
+NativeObject::sparsifyDenseElements(js::ExclusiveContext *cx, HandleNativeObject obj)
+{
+ if (!obj->maybeCopyElementsForWrite(cx))
+ return false;
+
+ uint32_t initialized = obj->getDenseInitializedLength();
+
+ /* Create new properties with the value of non-hole dense elements. */
+ for (uint32_t i = 0; i < initialized; i++) {
+ if (obj->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE))
+ continue;
+
+ if (!sparsifyDenseElement(cx, obj, i))
+ return false;
+ }
+
+ if (initialized)
+ obj->setDenseInitializedLength(0);
+
+ /*
+ * Reduce storage for dense elements which are now holes. Explicitly mark
+ * the elements capacity as zero, so that any attempts to add dense
+ * elements will be caught in ensureDenseElements.
+ */
+ if (obj->getDenseCapacity()) {
+ obj->shrinkElements(cx, 0);
+ obj->getElementsHeader()->capacity = 0;
+ }
+
+ return true;
+}
+
+bool
+NativeObject::willBeSparseElements(uint32_t requiredCapacity, uint32_t newElementsHint)
+{
+ MOZ_ASSERT(isNative());
+ MOZ_ASSERT(requiredCapacity > MIN_SPARSE_INDEX);
+
+ uint32_t cap = getDenseCapacity();
+ MOZ_ASSERT(requiredCapacity >= cap);
+
+ if (requiredCapacity >= NELEMENTS_LIMIT)
+ return true;
+
+ uint32_t minimalDenseCount = requiredCapacity / SPARSE_DENSITY_RATIO;
+ if (newElementsHint >= minimalDenseCount)
+ return false;
+ minimalDenseCount -= newElementsHint;
+
+ if (minimalDenseCount > cap)
+ return true;
+
+ uint32_t len = getDenseInitializedLength();
+ const Value *elems = getDenseElements();
+ for (uint32_t i = 0; i < len; i++) {
+ if (!elems[i].isMagic(JS_ELEMENTS_HOLE) && !--minimalDenseCount)
+ return false;
+ }
+ return true;
+}
+
+/* static */ NativeObject::EnsureDenseResult
+NativeObject::maybeDensifySparseElements(js::ExclusiveContext *cx, HandleNativeObject obj)
+{
+ /*
+ * Wait until after the object goes into dictionary mode, which must happen
+ * when sparsely packing any array with more than MIN_SPARSE_INDEX elements
+ * (see PropertyTree::MAX_HEIGHT).
+ */
+ if (!obj->inDictionaryMode())
+ return ED_SPARSE;
+
+ /*
+ * Only measure the number of indexed properties every log(n) times when
+ * populating the object.
+ */
+ uint32_t slotSpan = obj->slotSpan();
+ if (slotSpan != RoundUpPow2(slotSpan))
+ return ED_SPARSE;
+
+ /* Watch for conditions under which an object's elements cannot be dense. */
+ if (!obj->nonProxyIsExtensible() || obj->watched())
+ return ED_SPARSE;
+
+ /*
+ * The indexes in the object need to be sufficiently dense before they can
+ * be converted to dense mode.
+ */
+ uint32_t numDenseElements = 0;
+ uint32_t newInitializedLength = 0;
+
+ RootedShape shape(cx, obj->lastProperty());
+ while (!shape->i