Merge m-c to inbound a=merge

This commit is contained in:
Wes Kocher 2014-08-22 17:05:17 -07:00
commit aa4a8265fb
194 changed files with 2344 additions and 1107 deletions

0
.lock
View File

View File

@ -12,10 +12,10 @@
<!--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="1865c6639c51f0290d5778adef146147d5d6a5f0">
<project name="platform_build" path="build" remote="b2g" revision="53a59364ce4f14068034c8d6fe01f4f6b9f78f23">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="e424c85eda87a40c0fa64d6a779c3fa368bf770b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -23,7 +23,7 @@
<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="76a2d31804463dcb49e2fd852c8c2e27a89e9e46"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d59843c1d66bd8d93c6012efda47fa447b99813c"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
@ -127,9 +127,9 @@
<!-- Stock Android things -->
<project name="platform/external/icu4c" path="external/icu4c" revision="2bb01561780583cc37bc667f0ea79f48a122d8a2"/>
<!-- dolphin specific things -->
<project name="device/sprd" path="device/sprd" revision="66f858de575b95e334f32f6c7ac9d1cd85e9f0d8"/>
<project name="device/sprd" path="device/sprd" revision="9a1f8e59b0cbf91d99b02f836b5197a822eadf1a"/>
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="4e58336019b5cbcfd134caf55b142236cf986618"/>
<project name="platform/frameworks/av" path="frameworks/av" revision="cbd80d8c03fc639dd810b17c4b682c67abc06ee8"/>
<project name="platform/frameworks/av" path="frameworks/av" revision="facca8d3e35431b66f85a4eb42bc6c5b24bd04da"/>
<project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>
<project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/>
<project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>

View File

@ -19,13 +19,13 @@
<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="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e424c85eda87a40c0fa64d6a779c3fa368bf770b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<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="a567a787b5ac3e0cb663aa6464b18a24ec764409"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="76a2d31804463dcb49e2fd852c8c2e27a89e9e46"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d59843c1d66bd8d93c6012efda47fa447b99813c"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

View File

@ -17,10 +17,10 @@
</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="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="e424c85eda87a40c0fa64d6a779c3fa368bf770b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="76a2d31804463dcb49e2fd852c8c2e27a89e9e46"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d59843c1d66bd8d93c6012efda47fa447b99813c"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

View File

@ -12,10 +12,10 @@
<!--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="1865c6639c51f0290d5778adef146147d5d6a5f0">
<project name="platform_build" path="build" remote="b2g" revision="53a59364ce4f14068034c8d6fe01f4f6b9f78f23">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="e424c85eda87a40c0fa64d6a779c3fa368bf770b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -23,7 +23,7 @@
<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="76a2d31804463dcb49e2fd852c8c2e27a89e9e46"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d59843c1d66bd8d93c6012efda47fa447b99813c"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>

View File

@ -19,13 +19,13 @@
<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="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e424c85eda87a40c0fa64d6a779c3fa368bf770b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<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="a567a787b5ac3e0cb663aa6464b18a24ec764409"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="76a2d31804463dcb49e2fd852c8c2e27a89e9e46"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d59843c1d66bd8d93c6012efda47fa447b99813c"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

View File

@ -17,10 +17,10 @@
</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="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="e424c85eda87a40c0fa64d6a779c3fa368bf770b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="76a2d31804463dcb49e2fd852c8c2e27a89e9e46"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d59843c1d66bd8d93c6012efda47fa447b99813c"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "670b42b547f817727fc98f2983c606e8cc8766af",
"revision": "2b76279a1d0770938d1ed660f40faba07b2a5e19",
"repo_path": "/integration/gaia-central"
}

View File

@ -17,12 +17,12 @@
<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="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e424c85eda87a40c0fa64d6a779c3fa368bf770b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<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="76a2d31804463dcb49e2fd852c8c2e27a89e9e46"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d59843c1d66bd8d93c6012efda47fa447b99813c"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
<project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>

View File

@ -15,7 +15,7 @@
<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="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e424c85eda87a40c0fa64d6a779c3fa368bf770b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,10 +17,10 @@
</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="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="e424c85eda87a40c0fa64d6a779c3fa368bf770b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="76a2d31804463dcb49e2fd852c8c2e27a89e9e46"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d59843c1d66bd8d93c6012efda47fa447b99813c"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

View File

@ -17,12 +17,12 @@
<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="afcdd36f13e75adcdebe57d842a277fd587faf28"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="e424c85eda87a40c0fa64d6a779c3fa368bf770b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3bb61a27cd2941b2ba9b616a11aaa44269210396"/>
<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="76a2d31804463dcb49e2fd852c8c2e27a89e9e46"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d59843c1d66bd8d93c6012efda47fa447b99813c"/>
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>

View File

@ -248,6 +248,8 @@ pref("xpinstall.whitelist.add", "addons.mozilla.org");
pref("xpinstall.whitelist.add.180", "marketplace.firefox.com");
pref("lightweightThemes.update.enabled", true);
pref("lightweightThemes.getMoreURL", "https://addons.mozilla.org/%LOCALE%/firefox/themes");
pref("lightweightThemes.recommendedThemes", "[{\"id\":\"recommended-1\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/a-web-browser-renaissance/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.header.jpg\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.footer.jpg\",\"textcolor\":\"#000000\",\"accentcolor\":\"#f2d9b1\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.icon.jpg\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/1.preview.jpg\",\"author\":\"Sean.Martell\",\"version\":\"0\"},{\"id\":\"recommended-2\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/space-fantasy/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.header.jpg\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.footer.jpg\",\"textcolor\":\"#ffffff\",\"accentcolor\":\"#d9d9d9\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.icon.jpg\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/2.preview.jpg\",\"author\":\"fx5800p\",\"version\":\"1.0\"},{\"id\":\"recommended-3\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/linen-light/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/3.header.png\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/3.footer.png\",\"textcolor\":\"#None\",\"accentcolor\":\"#ada8a8\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/3.icon.png\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/3.preview.png\",\"author\":\"DVemer\",\"version\":\"1.0\"},{\"id\":\"recommended-4\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/pastel-gradient/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.header.png\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.footer.png\",\"textcolor\":\"#000000\",\"accentcolor\":\"#000000\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.icon.png\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/4.preview.png\",\"author\":\"darrinhenein\",\"version\":\"1.0\"},{\"id\":\"recommended-5\",\"homepageURL\":\"https://addons.mozilla.org/firefox/addon/carbon-light/\",\"headerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/5.header.png\",\"footerURL\":\"resource:///chrome/browser/content/browser/defaultthemes/5.footer.png\",\"textcolor\":\"#3b3b3b\",\"accentcolor\":\"#2e2e2e\",\"iconURL\":\"resource:///chrome/browser/content/browser/defaultthemes/5.icon.jpg\",\"previewURL\":\"resource:///chrome/browser/content/browser/defaultthemes/5.preview.jpg\",\"author\":\"Jaxivo\",\"version\":\"1.0\"}]");
// UI tour experience.
pref("browser.uitour.enabled", true);

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 731 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -168,8 +168,7 @@ let gGrid = {
' class="newtab-control newtab-control-pin"/>' +
'<input type="button" title="' + newTabString("block") + '"' +
' class="newtab-control newtab-control-block"/>' +
'<input type="button" title="' + newTabString("sponsored") + '"' +
' class="newtab-control newtab-control-sponsored"/>';
'<span class="newtab-sponsored">' + newTabString("sponsored.button") + '</span>';
this._siteFragment = document.createDocumentFragment();
this._siteFragment.appendChild(site);

View File

@ -149,19 +149,77 @@ input[type=button] {
}
/* TITLES */
.newtab-sponsored,
.newtab-title {
bottom: -26px;
font-size: 13px;
left: 0;
overflow: hidden;
padding-top: 14px;
position: absolute;
right: 0;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
}
.newtab-title {
font-size: 13px;
left: 0;
padding-top: 14px;
text-overflow: ellipsis;
}
.newtab-sponsored {
background-color: #f9f9f9;
border: 1px solid #dcdcdc;
border-radius: 2px;
color: #9b9b9b;
cursor: pointer;
display: none;
font-family: Arial;
font-size: 10px;
height: 17px;
line-height: 17px;
margin-bottom: -1px;
padding: 0 4px;
}
.newtab-sponsored:-moz-any(:hover, [active]) {
background-color: #dcdcdc;
color: #666666;
}
.newtab-sponsored:-moz-locale-dir(rtl) {
left: 0;
right: auto;
}
.newtab-site:-moz-any([type=enhanced], [type=sponsored]) .newtab-sponsored {
display: block;
}
.sponsored-explain,
.sponsored-explain a {
color: white;
}
.sponsored-explain {
background-color: rgba(51, 51, 51, 0.95);
border-bottom-left-radius: 6px;
border-bottom-right-radius: 6px;
bottom: 0px;
line-height: 20px;
padding: 15px 10px;
position: absolute;
text-align: start;
}
.sponsored-explain input {
background-size: 20px;
height: 20px;
opacity: 1;
pointer-events: none;
position: static;
width: 20px;
}
/* CONTROLS */
.newtab-control {
position: absolute;
@ -185,31 +243,16 @@ input[type=button] {
}
}
.newtab-control-sponsored:-moz-locale-dir(rtl),
.newtab-control-pin:-moz-locale-dir(ltr),
.newtab-control-block:-moz-locale-dir(rtl) {
left: 4px;
}
.newtab-control-sponsored:-moz-locale-dir(ltr),
.newtab-control-block:-moz-locale-dir(ltr),
.newtab-control-pin:-moz-locale-dir(rtl) {
right: 4px;
}
.newtab-control.newtab-control-sponsored {
bottom: -20px;
height: 14px;
-moz-margin-end: -5px;
opacity: 1;
top: auto;
width: 14px;
}
.newtab-site:not([type=sponsored]) .newtab-control-sponsored {
display: none;
}
/* DRAG & DROP */
/*
@ -224,19 +267,6 @@ input[type=button] {
opacity: 0.01;
}
/* SPONSORED PANEL */
#sponsored-panel {
width: 330px;
}
#sponsored-panel description {
margin: 0;
}
#sponsored-panel .text-link {
margin: 12px 0 0;
}
/* SEARCH */
#newtab-search-container {
display: -moz-box;

View File

@ -35,7 +35,13 @@ XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() {
createBundle("chrome://browser/locale/newTab.properties");
});
function newTabString(name) gStringBundle.GetStringFromName('newtab.' + name);
function newTabString(name, args) {
let stringName = "newtab." + name;
if (!args) {
return gStringBundle.GetStringFromName(stringName);
}
return gStringBundle.formatStringFromName(stringName, args, args.length);
}
function inPrivateBrowsingMode() {
return PrivateBrowsingUtils.isWindowPrivate(window);
@ -44,6 +50,8 @@ function inPrivateBrowsingMode() {
const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
const XUL_NAMESPACE = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const TILES_EXPLAIN_LINK = "https://support.mozilla.org/kb/how-do-sponsored-tiles-work";
#include transformations.js
#include page.js
#include grid.js

View File

@ -20,14 +20,6 @@
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&newtab.pageTitle;">
<xul:panel id="sponsored-panel" orient="vertical" type="arrow">
<xul:description id="sponsored-panel-release-descr">&newtab.sponsored.release.message;</xul:description>
<xul:description id="sponsored-panel-trial-descr">&newtab.sponsored.trial.message2;</xul:description>
<xul:label class="text-link"
href="https://support.mozilla.org/kb/how-do-sponsored-tiles-work"
value="&newtab.panel.link.text;" />
</xul:panel>
<xul:panel id="newtab-search-panel" orient="vertical" type="arrow"
noautohide="true">
<xul:hbox id="newtab-search-manage" class="newtab-search-panel-engine">

View File

@ -24,17 +24,6 @@ let gPage = {
// listen from the xul window and filter then delegate
addEventListener("click", this, false);
// Initialize sponsored panel
this._sponsoredPanel = document.getElementById("sponsored-panel");
let link = this._sponsoredPanel.querySelector(".text-link");
link.addEventListener("click", () => this._sponsoredPanel.hidePopup());
if (UpdateChannel.get().startsWith("release")) {
document.getElementById("sponsored-panel-trial-descr").style.display = "none";
}
else {
document.getElementById("sponsored-panel-release-descr").style.display = "none";
}
// Check if the new tab feature is enabled.
let enabled = gAllPages.enabled;
if (enabled)
@ -89,21 +78,6 @@ let gPage = {
}
},
/**
* Shows sponsored panel
*/
showSponsoredPanel: function Page_showSponsoredPanel(aTarget) {
if (this._sponsoredPanel.state == "closed") {
let self = this;
this._sponsoredPanel.addEventListener("popuphidden", function onPopupHidden(aEvent) {
self._sponsoredPanel.removeEventListener("popuphidden", onPopupHidden, false);
aTarget.removeAttribute("panelShown");
});
}
aTarget.setAttribute("panelShown", "true");
this._sponsoredPanel.openPopup(aTarget);
},
/**
* Internally initializes the page. This runs only when/if the feature
* is/gets enabled.

View File

@ -120,8 +120,9 @@ Site.prototype = {
* Renders the site's data (fills the HTML fragment).
*/
_render: function Site_render() {
let enhanced = gAllPages.enhanced && DirectoryLinksProvider.getEnhancedLink(this.link);
let url = this.url;
let title = this.title || url;
let title = enhanced && enhanced.title || this.title || url;
let tooltip = (title == url ? title : title + "\n" + url);
let link = this._querySelector(".newtab-link");
@ -185,7 +186,7 @@ Site.prototype = {
this._node.addEventListener("mouseover", this, false);
// Specially treat the sponsored icon to prevent regular hover effects
let sponsored = this._querySelector(".newtab-control-sponsored");
let sponsored = this._querySelector(".newtab-sponsored");
sponsored.addEventListener("mouseover", () => {
this.cell.node.setAttribute("ignorehover", "true");
});
@ -218,6 +219,30 @@ Site.prototype = {
.add(aIndex);
},
_toggleSponsored: function() {
let button = this._querySelector(".newtab-sponsored");
if (button.hasAttribute("active")) {
let explain = this._querySelector(".sponsored-explain");
explain.parentNode.removeChild(explain);
button.removeAttribute("active");
}
else {
let explain = document.createElementNS(HTML_NAMESPACE, "div");
explain.className = "sponsored-explain";
this.node.appendChild(explain);
let link = '<a href="' + TILES_EXPLAIN_LINK + '">' +
newTabString("learn.link") + "</a>";
let type = this.node.getAttribute("type");
let icon = '<input type="button" class="newtab-control newtab-' +
(type == "sponsored" ? "control-block" : "customize") + '"/>';
explain.innerHTML = newTabString(type + ".explain", [icon, link]);
button.setAttribute("active", "true");
}
},
/**
* Handles site click events.
*/
@ -226,6 +251,8 @@ Site.prototype = {
let pinned = this.isPinned();
let tileIndex = this.cell.index;
let {button, target} = aEvent;
// Handle tile/thumbnail link click
if (target.classList.contains("newtab-link") ||
target.parentElement.classList.contains("newtab-link")) {
// Record for primary and middle clicks
@ -234,6 +261,10 @@ Site.prototype = {
action = "click";
}
}
// Handle sponsored explanation link click
else if (target.parentElement.classList.contains("sponsored-explain")) {
action = "sponsored_link";
}
// Only handle primary clicks for the remaining targets
else if (button == 0) {
aEvent.preventDefault();
@ -241,8 +272,9 @@ Site.prototype = {
this.block();
action = "block";
}
else if (target.classList.contains("newtab-control-sponsored")) {
gPage.showSponsoredPanel(target);
else if (target.classList.contains("sponsored-explain") ||
target.classList.contains("newtab-sponsored")) {
this._toggleSponsored();
action = "sponsored";
}
else if (pinned) {

View File

@ -46,29 +46,21 @@
<getter><![CDATA[
if (this._frame)
return this._frame;
let notificationFrameId = "social-mark-frame-" + this.getAttribute("origin");
let provider = Social._getProviderFromOrigin(this.getAttribute("origin"));
let size = provider.getPageSize("marks");
let {width, height} = size ? size : {width: 330, height: 100};
this._frame = SharedFrame.createFrame(
notificationFrameId, /* frame name */
this.panel, /* parent */
{
"type": "content",
"mozbrowser": "true",
"class": "social-panel-frame",
"id": notificationFrameId,
"tooltip": "aHTMLTooltip",
"flex": "1",
"context": "contentAreaContextMenu",
"origin": this.getAttribute("origin"),
"src": "about:blank",
"style": "width: " + width + "px; height: " + height + "px;",
}
);
let iframe = this._frame = document.createElement("iframe");
iframe.setAttribute("type", "content");
iframe.setAttribute("class", "social-panel-frame");
iframe.setAttribute("flex", "1");
iframe.setAttribute("tooltip", "aHTMLTooltip");
iframe.setAttribute("context", "contentAreaContextMenu");
iframe.setAttribute("origin", provider.origin);
iframe.setAttribute("style", "width: " + width + "px; height: " + height + "px;");
this.panel.appendChild(iframe);
this._frame.addEventListener("DOMLinkAdded", this);
this.setAttribute("notificationFrameId", notificationFrameId);
return this._frame;
]]></getter>
</property>
@ -117,6 +109,9 @@
this._dynamicResizer = null;
}
this.content.setAttribute("src", "about:blank");
// called during onhidden, make sure the docshell is updated
if (this._frame.docShell)
this._frame.docShell.createAboutBlankContentViewer(null);
// do we have a savable page loaded?
let aURI = gBrowser.currentURI;
@ -216,18 +211,6 @@
<parameter name="aResetOnClose"/>
<body><![CDATA[
let panel = this.panel;
let frameId = this.getAttribute("notificationFrameId");
SharedFrame.setOwner(frameId, this.content);
// Clear dimensions on all browsers so the panel size will
// only use the selected browser.
let frameIter = panel.firstElementChild;
while (frameIter) {
frameIter.collapsed = (frameIter != this.content);
frameIter = frameIter.nextElementSibling;
}
let anchor = document.getAnonymousElementByAttribute(this._anchor, "class", "toolbarbutton-icon");
// Bug 849216 - open the popup in a setTimeout so we avoid the auto-rollup
// handling from preventing it being opened in some cases.

View File

@ -64,6 +64,7 @@ support-files =
offlineQuotaNotification.cacheManifest
offlineQuotaNotification.html
page_style_sample.html
parsingTestHelpers.jsm
print_postdata.sjs
redirect_bug623155.sjs
searchSuggestionEngine.sjs
@ -342,6 +343,8 @@ skip-if = buildapp == 'mulet' || e10s # Bug 866413 - PageInfo doesn't work in e1
skip-if = e10s # Bug ?????? - test directly manipulates content
[browser_parsable_css.js]
[browser_parsable_script.js]
skip-if = debug || asan # Times out on debug/asan, and we are less picky about our JS there
[browser_pinnedTabs.js]
skip-if = e10s # Bug 921905 - pinTab/unpinTab fail in e10s

View File

@ -17,6 +17,9 @@ const kWhitelist = [
{sourceName: /loop\/.*sdk-content\/.*\.css$/i /* TokBox SDK assets, see bug 1032469 */}
];
let moduleLocation = gTestPath.replace(/\/[^\/]*$/i, "/parsingTestHelpers.jsm");
let {generateURIsFromDirTree} = Cu.import(moduleLocation, {});
/**
* Check if an error should be ignored due to matching one of the whitelist
* objects defined in kWhitelist
@ -40,113 +43,12 @@ function ignoredError(aErrorObject) {
return false;
}
/**
* Returns a promise that is resolved with a list of CSS files to check,
* represented by their nsIURI objects.
*
* @param appDir the application directory to scan for CSS files (nsIFile)
*/
function generateURIsFromDirTree(appDir) {
let rv = [];
let dirQueue = [appDir.path];
return Task.spawn(function*() {
while (dirQueue.length) {
let nextDir = dirQueue.shift();
let {subdirs, cssfiles} = yield iterateOverPath(nextDir);
dirQueue = dirQueue.concat(subdirs);
rv = rv.concat(cssfiles);
}
return rv;
});
}
/* Shorthand constructor to construct an nsI(Local)File */
let LocalFile = Components.Constructor("@mozilla.org/file/local;1", Ci.nsIFile, "initWithPath");
/**
* Uses OS.File.DirectoryIterator to asynchronously iterate over a directory.
* It returns a promise that is resolved with an object with two properties:
* - cssfiles: an array of nsIURIs corresponding to CSS that needs checking
* - subdirs: an array of paths for subdirectories we need to recurse into
* (handled by generateURIsFromDirTree above)
*
* @param path the path to check (string)
*/
function iterateOverPath(path) {
let iterator = new OS.File.DirectoryIterator(path);
let parentDir = new LocalFile(path);
let subdirs = [];
let cssfiles = [];
// Iterate through the directory
let promise = iterator.forEach(
function onEntry(entry) {
if (entry.isDir) {
let subdir = parentDir.clone();
subdir.append(entry.name);
subdirs.push(subdir.path);
} else if (entry.name.endsWith(".css")) {
let file = parentDir.clone();
file.append(entry.name);
let uriSpec = getURLForFile(file);
cssfiles.push(Services.io.newURI(uriSpec, null, null));
} else if (entry.name.endsWith(".ja")) {
let file = parentDir.clone();
file.append(entry.name);
let subentries = [uri for (uri of generateEntriesFromJarFile(file))];
cssfiles = cssfiles.concat(subentries);
}
}
);
let outerPromise = Promise.defer();
promise.then(function() {
outerPromise.resolve({cssfiles: cssfiles, subdirs: subdirs});
iterator.close();
}, function(e) {
outerPromise.reject(e);
iterator.close();
});
return outerPromise.promise;
}
/* Helper function to generate a URI spec (NB: not an nsIURI yet!)
* given an nsIFile object */
function getURLForFile(file) {
let fileHandler = Services.io.getProtocolHandler("file");
fileHandler = fileHandler.QueryInterface(Ci.nsIFileProtocolHandler);
return fileHandler.getURLSpecFromFile(file);
}
/**
* A generator that generates nsIURIs for CSS files found in jar files
* like omni.ja.
*
* @param jarFile an nsIFile object for the jar file that needs checking.
*/
function* generateEntriesFromJarFile(jarFile) {
const ZipReader = new Components.Constructor("@mozilla.org/libjar/zip-reader;1", "nsIZipReader", "open");
let zr = new ZipReader(jarFile);
let entryEnumerator = zr.findEntries("*.css$");
const kURIStart = getURLForFile(jarFile);
while (entryEnumerator.hasMore()) {
let entry = entryEnumerator.getNext();
let entryURISpec = "jar:" + kURIStart + "!/" + entry;
yield Services.io.newURI(entryURISpec, null, null);
}
zr.close();
}
/**
* The actual test.
*/
add_task(function checkAllTheCSS() {
let appDir = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
// This asynchronously produces a list of URLs (sadly, mostly sync on our
// test infrastructure because it runs against jarfiles there, and
// our zipreader APIs are all sync)
let uris = yield generateURIsFromDirTree(appDir);
let uris = yield generateURIsFromDirTree(appDir, ".css");
// Create a clean iframe to load all the files into:
let hiddenWin = Services.appShell.hiddenDOMWindow;

View File

@ -0,0 +1,83 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* This list allows pre-existing or 'unfixable' JS issues to remain, while we
* detect newly occurring issues in shipping JS. It is a list of regexes
* matching files which have errors:
*/
const kWhitelist = new Set([
/defaults\/profile\/prefs.js$/,
]);
let moduleLocation = gTestPath.replace(/\/[^\/]*$/i, "/parsingTestHelpers.jsm");
let {generateURIsFromDirTree} = Cu.import(moduleLocation, {});
let {Reflect} = Cu.import("resource://gre/modules/reflect.jsm", {});
/**
* Check if an error should be ignored due to matching one of the whitelist
* objects defined in kWhitelist
*
* @param uri the uri to check against the whitelist
* @return true if the uri should be skipped, false otherwise.
*/
function uriIsWhiteListed(uri) {
for (let whitelistItem of kWhitelist) {
if (whitelistItem.test(uri.spec)) {
return true;
}
}
return false;
}
function parsePromise(uri) {
let promise = new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("GET", uri, true);
xhr.onreadystatechange = function() {
if (this.readyState == this.DONE) {
let scriptText = this.responseText;
let ast;
try {
info("Checking " + uri);
ast = Reflect.parse(scriptText);
resolve(true);
} catch (ex) {
let errorMsg = "Script error reading " + uri + ": " + ex;
ok(false, errorMsg);
resolve(false);
}
}
};
xhr.onerror = (error) => {
ok(false, "XHR error reading " + uri + ": " + error);
resolve(false);
};
xhr.overrideMimeType("application/javascript");
xhr.send(null);
});
return promise;
}
add_task(function* checkAllTheJS() {
let appDir = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
// This asynchronously produces a list of URLs (sadly, mostly sync on our
// test infrastructure because it runs against jarfiles there, and
// our zipreader APIs are all sync)
let uris = yield generateURIsFromDirTree(appDir, [".js", ".jsm"]);
// We create an array of promises so we can parallelize all our parsing
// and file loading activity:
let allPromises = [];
for (let uri of uris) {
if (uriIsWhiteListed(uri)) {
info("Not checking " + uri.spec);
continue;
}
allPromises.push(parsePromise(uri.spec));
}
let promiseResults = yield Promise.all(allPromises);
is(promiseResults.filter((x) => !x).length, 0, "There should be 0 parsing errors");
});

View File

@ -0,0 +1,126 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
this.EXPORTED_SYMBOLS = ["generateURIsFromDirTree"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
/* Shorthand constructors to construct an nsI(Local)File and zip reader: */
const LocalFile = new Components.Constructor("@mozilla.org/file/local;1", Ci.nsIFile, "initWithPath");
const ZipReader = new Components.Constructor("@mozilla.org/libjar/zip-reader;1", "nsIZipReader", "open");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/Task.jsm");
/**
* Returns a promise that is resolved with a list of files that have one of the
* extensions passed, represented by their nsIURI objects, which exist inside
* the directory passed.
*
* @param dir the directory which to scan for files (nsIFile)
* @param extensions the extensions of files we're interested in (Array).
*/
function generateURIsFromDirTree(dir, extensions) {
if (!Array.isArray(extensions)) {
extensions = [extensions];
}
let dirQueue = [dir.path];
return Task.spawn(function*() {
let rv = [];
while (dirQueue.length) {
let nextDir = dirQueue.shift();
let {subdirs, files} = yield iterateOverPath(nextDir, extensions);
dirQueue.push(...subdirs);
rv.push(...files);
}
return rv;
});
}
/**
* Uses OS.File.DirectoryIterator to asynchronously iterate over a directory.
* It returns a promise that is resolved with an object with two properties:
* - files: an array of nsIURIs corresponding to files that match the extensions passed
* - subdirs: an array of paths for subdirectories we need to recurse into
* (handled by generateURIsFromDirTree above)
*
* @param path the path to check (string)
* @param extensions the file extensions we're interested in.
*/
function iterateOverPath(path, extensions) {
let iterator = new OS.File.DirectoryIterator(path);
let parentDir = new LocalFile(path);
let subdirs = [];
let files = [];
let pathEntryIterator = (entry) => {
if (entry.isDir) {
subdirs.push(entry.path);
} else if (extensions.some((extension) => entry.name.endsWith(extension))) {
let file = parentDir.clone();
file.append(entry.name);
let uriSpec = getURLForFile(file);
files.push(Services.io.newURI(uriSpec, null, null));
} else if (entry.name.endsWith(".ja") || entry.name.endsWith(".jar")) {
let file = parentDir.clone();
file.append(entry.name);
for (let extension of extensions) {
let jarEntryIterator = generateEntriesFromJarFile(file, extension);
files.push(...jarEntryIterator);
}
}
};
return new Promise((resolve, reject) => {
Task.spawn(function* () {
try {
// Iterate through the directory
yield iterator.forEach(pathEntryIterator);
resolve({files: files, subdirs: subdirs});
} catch (ex) {
reject(ex);
} finally {
iterator.close();
}
});
});
}
/* Helper function to generate a URI spec (NB: not an nsIURI yet!)
* given an nsIFile object */
function getURLForFile(file) {
let fileHandler = Services.io.getProtocolHandler("file");
fileHandler = fileHandler.QueryInterface(Ci.nsIFileProtocolHandler);
return fileHandler.getURLSpecFromActualFile(file);
}
/**
* A generator that generates nsIURIs for particular files found in jar files
* like omni.ja.
*
* @param jarFile an nsIFile object for the jar file that needs checking.
* @param extension the extension we're interested in.
*/
function* generateEntriesFromJarFile(jarFile, extension) {
let zr = new ZipReader(jarFile);
let entryEnumerator = zr.findEntries("*" + extension + "$");
const kURIStart = getURLForFile(jarFile);
while (entryEnumerator.hasMore()) {
let entry = entryEnumerator.getNext();
// Ignore the JS cache which is stored in omni.ja
if (entry.startsWith("jsloader") || entry.startsWith("jssubloader")) {
continue;
}
let entryURISpec = "jar:" + kURIStart + "!/" + entry;
yield Services.io.newURI(entryURISpec, null, null);
}
zr.close();
}

View File

@ -7,6 +7,7 @@ gDirectorySource = "data:application/json," + JSON.stringify({
"en-US": [{
url: "http://example.com/",
enhancedImageURI: "data:image/png;base64,helloWORLD",
title: "title",
type: "organic"
}]
});
@ -28,6 +29,7 @@ function runTests() {
return {
type: siteNode.getAttribute("type"),
enhanced: siteNode.querySelector(".enhanced-content").style.backgroundImage,
title: siteNode.querySelector(".newtab-title").textContent,
};
}
@ -37,27 +39,40 @@ function runTests() {
// Test with enhanced = false
NewTabUtils.allPages.enhanced = false;
yield addNewTabPageTab();
let {type, enhanced} = getData(0);
let {type, enhanced, title} = getData(0);
is(type, "organic", "directory link is organic");
isnot(enhanced, "", "directory link has enhanced image");
is(title, "title");
is(getData(1), null, "history link pushed out by directory link");
// Test with enhanced = true
NewTabUtils.allPages.enhanced = true;
yield addNewTabPageTab();
let {type, enhanced} = getData(0);
let {type, enhanced, title} = getData(0);
is(type, "organic", "directory link is still organic");
isnot(enhanced, "", "directory link still has enhanced image");
is(title, "title");
is(getData(1), null, "history link still pushed out by directory link");
// Test with a pinned link
setPinnedLinks("-1");
yield addNewTabPageTab();
let {type, enhanced} = getData(0);
let {type, enhanced, title} = getData(0);
is(type, "enhanced", "pinned history link is enhanced");
isnot(enhanced, "", "pinned history link has enhanced image");
is(title, "title");
is(getData(1), null, "directory link pushed out by pinned history link");
// Test pinned link with enhanced = false
NewTabUtils.allPages.enhanced = false;
yield addNewTabPageTab();
let {type, enhanced, title} = getData(0);
isnot(type, "enhanced", "history link is not enhanced");
is(enhanced, "", "history link has no enhanced image");
is(title, "site#-1");
is(getData(1), null, "directory link still pushed out by pinned history link");
}

View File

@ -8,26 +8,29 @@ function runTests() {
let site = getCell(0).node.querySelector(".newtab-site");
site.setAttribute("type", "sponsored");
let sponsoredPanel = getContentDocument().getElementById("sponsored-panel");
is(sponsoredPanel.state, "closed", "Sponsored panel must be closed");
// test explain text appearing upon a click
let sponsoredButton = site.querySelector(".newtab-sponsored");
EventUtils.synthesizeMouseAtCenter(sponsoredButton, {}, getContentWindow());
let explain = site.querySelector(".sponsored-explain");
isnot(explain, null, "Sponsored explanation shown");
ok(explain.querySelector("input").classList.contains("newtab-control-block"), "sponsored tiles show blocked image");
ok(sponsoredButton.hasAttribute("active"), "Sponsored button has active attribute");
function continueOnceOn(event) {
sponsoredPanel.addEventListener(event, function listener() {
sponsoredPanel.removeEventListener(event, listener);
executeSoon(TestRunner.next);
});
}
// test dismissing sponsored explain
EventUtils.synthesizeMouseAtCenter(sponsoredButton, {}, getContentWindow());
is(site.querySelector(".sponsored-explain"), null, "Sponsored explanation no longer shown");
ok(!sponsoredButton.hasAttribute("active"), "Sponsored button does not have active attribute");
// test sponsoredPanel appearing upon a click
continueOnceOn("popupshown");
let sponsoredButton = site.querySelector(".newtab-control-sponsored");
yield EventUtils.synthesizeMouseAtCenter(sponsoredButton, {}, getContentWindow());
is(sponsoredPanel.state, "open", "Sponsored panel opens on click");
ok(sponsoredButton.hasAttribute("panelShown"), "Sponsored button has panelShown attribute");
// test with enhanced tile
site.setAttribute("type", "enhanced");
EventUtils.synthesizeMouseAtCenter(sponsoredButton, {}, getContentWindow());
explain = site.querySelector(".sponsored-explain");
isnot(explain, null, "Sponsored explanation shown");
ok(explain.querySelector("input").classList.contains("newtab-customize"), "enhanced tiles show customize image");
ok(sponsoredButton.hasAttribute("active"), "Sponsored button has active attribute");
// test sponsoredPanel hiding
continueOnceOn("popuphidden");
yield sponsoredPanel.hidePopup();
is(sponsoredPanel.state, "closed", "Sponsored panel correctly closed/hidden");
ok(!sponsoredButton.hasAttribute("panelShown"), "Sponsored button does not have panelShown attribute");
// test dismissing enhanced explain
EventUtils.synthesizeMouseAtCenter(sponsoredButton, {}, getContentWindow());
is(site.querySelector(".sponsored-explain"), null, "Sponsored explanation no longer shown");
ok(!sponsoredButton.hasAttribute("active"), "Sponsored button does not have active attribute");
}

View File

@ -78,6 +78,26 @@ browser.jar:
* content/browser/browser-tabPreviews.xml (content/browser-tabPreviews.xml)
* content/browser/chatWindow.xul (content/chatWindow.xul)
content/browser/content.js (content/content.js)
content/browser/defaultthemes/1.footer.jpg (content/defaultthemes/1.footer.jpg)
content/browser/defaultthemes/1.header.jpg (content/defaultthemes/1.header.jpg)
content/browser/defaultthemes/1.icon.jpg (content/defaultthemes/1.icon.jpg)
content/browser/defaultthemes/1.preview.jpg (content/defaultthemes/1.preview.jpg)
content/browser/defaultthemes/2.footer.jpg (content/defaultthemes/2.footer.jpg)
content/browser/defaultthemes/2.header.jpg (content/defaultthemes/2.header.jpg)
content/browser/defaultthemes/2.icon.jpg (content/defaultthemes/2.icon.jpg)
content/browser/defaultthemes/2.preview.jpg (content/defaultthemes/2.preview.jpg)
content/browser/defaultthemes/3.footer.png (content/defaultthemes/3.footer.png)
content/browser/defaultthemes/3.header.png (content/defaultthemes/3.header.png)
content/browser/defaultthemes/3.icon.png (content/defaultthemes/3.icon.png)
content/browser/defaultthemes/3.preview.png (content/defaultthemes/3.preview.png)
content/browser/defaultthemes/4.footer.png (content/defaultthemes/4.footer.png)
content/browser/defaultthemes/4.header.png (content/defaultthemes/4.header.png)
content/browser/defaultthemes/4.icon.png (content/defaultthemes/4.icon.png)
content/browser/defaultthemes/4.preview.png (content/defaultthemes/4.preview.png)
content/browser/defaultthemes/5.footer.png (content/defaultthemes/5.footer.png)
content/browser/defaultthemes/5.header.png (content/defaultthemes/5.header.png)
content/browser/defaultthemes/5.icon.jpg (content/defaultthemes/5.icon.jpg)
content/browser/defaultthemes/5.preview.jpg (content/defaultthemes/5.preview.jpg)
content/browser/newtab/newTab.xul (content/newtab/newTab.xul)
* content/browser/newtab/newTab.js (content/newtab/newTab.js)
content/browser/newtab/newTab.css (content/newtab/newTab.css)

View File

@ -27,11 +27,15 @@ Cu.import("resource:///modules/CustomizableUI.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DragPositionManager",
"resource:///modules/DragPositionManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
"resource:///modules/BrowserUITelemetry.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
"resource://gre/modules/LightweightThemeManager.jsm");
let gModuleName = "[CustomizeMode]";
#include logging.js
@ -1218,6 +1222,119 @@ CustomizeMode.prototype = {
}
},
openAddonsManagerThemes: function(aEvent) {
aEvent.target.parentNode.parentNode.hidePopup();
this.window.BrowserOpenAddonsMgr('addons://list/theme');
},
getMoreThemes: function(aEvent) {
aEvent.target.parentNode.parentNode.hidePopup();
let getMoreURL = Services.urlFormatter.formatURLPref("lightweightThemes.getMoreURL");
this.window.openUILinkIn(getMoreURL, "tab");
},
onLWThemesMenuShowing: function(aEvent) {
AddonManager.getAddonsByTypes(["theme"], function(aThemes) {
function buildToolbarButton(doc, aTheme) {
function previewTheme(aEvent) {
LightweightThemeManager.previewTheme(aEvent.target.theme);
}
function resetPreview() {
LightweightThemeManager.resetPreview();
}
let tbb = doc.createElement("toolbarbutton");
tbb.theme = aTheme;
tbb.setAttribute("label", aTheme.name);
tbb.setAttribute("image", aTheme.iconURL);
tbb.setAttribute("tooltiptext", aTheme.description);
tbb.setAttribute("tabindex", "0");
tbb.classList.add("customization-lwtheme-menu-theme");
tbb.setAttribute("aria-checked", aTheme.isActive);
tbb.setAttribute("role", "menuitemradio");
if (aTheme.isActive) {
tbb.setAttribute("active", "true");
}
tbb.addEventListener("focus", previewTheme);
tbb.addEventListener("mouseover", previewTheme);
tbb.addEventListener("blur", resetPreview);
tbb.addEventListener("mouseout", resetPreview);
return tbb;
}
const DEFAULT_THEME_ID = "{972ce4c6-7e08-4474-a285-3208198ce6fd}";
// Order the themes so the Default theme is always at the beginning.
aThemes.sort((a,b) => {a.id != DEFAULT_THEME_ID});
let doc = this.window.document;
let footer = doc.getElementById("customization-lwtheme-menu-footer");
let panel = footer.parentNode;
let themesInMyThemesSection = 0;
let recommendedLabel = doc.getElementById("customization-lwtheme-menu-recommended");
for (let theme of aThemes) {
// Only allow the Default full theme to be shown in this list.
if ("skinnable" in theme &&
theme.id != DEFAULT_THEME_ID) {
continue;
}
let tbb = buildToolbarButton(doc, theme);
tbb.addEventListener("command", function() {
this.theme.userDisabled = false;
this.parentNode.hidePopup();
});
panel.insertBefore(tbb, recommendedLabel);
themesInMyThemesSection++;
}
let lwthemePrefs = Services.prefs.getBranch("lightweightThemes.");
let recommendedThemes = lwthemePrefs.getComplexValue("recommendedThemes",
Ci.nsISupportsString).data;
recommendedThemes = JSON.parse(recommendedThemes);
let sb = Services.strings.createBundle("chrome://browser/locale/lightweightThemes.properties");
for (let theme of recommendedThemes) {
theme.name = sb.GetStringFromName("lightweightThemes." + theme.id + ".name");
theme.description = sb.GetStringFromName("lightweightThemes." + theme.id + ".description");
let tbb = buildToolbarButton(doc, theme);
tbb.addEventListener("command", function() {
LightweightThemeManager.setLocalTheme(this.theme);
recommendedThemes = recommendedThemes.filter((aTheme) => { return aTheme.id != this.theme.id; });
let string = Cc["@mozilla.org/supports-string;1"]
.createInstance(Ci.nsISupportsString);
string.data = JSON.stringify(recommendedThemes);
lwthemePrefs.setComplexValue("recommendedThemes",
Ci.nsISupportsString, string);
this.parentNode.hidePopup();
});
panel.insertBefore(tbb, footer);
}
let hideRecommendedLabel = (footer.previousSibling == recommendedLabel);
recommendedLabel.hidden = hideRecommendedLabel;
let hideMyThemesSection = themesInMyThemesSection < 2 && hideRecommendedLabel;
let headerLabel = doc.getElementById("customization-lwtheme-menu-header");
if (hideMyThemesSection) {
let element = recommendedLabel.previousSibling;
while (element && element != headerLabel) {
element.hidden = true;
element = element.previousSibling;
}
}
headerLabel.hidden = hideMyThemesSection;
}.bind(this));
},
onLWThemesMenuHidden: function(aEvent) {
let doc = aEvent.target.ownerDocument;
let footer = doc.getElementById("customization-lwtheme-menu-footer");
let recommendedLabel = doc.getElementById("customization-lwtheme-menu-recommended");
for (let element of [footer, recommendedLabel]) {
while (element.previousSibling &&
element.previousSibling.localName == "toolbarbutton") {
element.previousSibling.remove();
}
}
},
_onUIChange: function() {
this._changed = true;
if (!this.resetting) {

View File

@ -29,6 +29,27 @@
<button id="customization-toolbar-visibility-button" label="&customizeMode.toolbars;" class="customizationmode-button" type="menu">
<menupopup id="customization-toolbar-menu" onpopupshowing="onViewToolbarsPopupShowing(event)"/>
</button>
<button id="customization-lwtheme-button" label="&customizeMode.lwthemes;" class="customizationmode-button" type="menu">
<panel type="arrow" id="customization-lwtheme-menu"
onpopupshowing="gCustomizeMode.onLWThemesMenuShowing(event);"
onpopuphidden="gCustomizeMode.onLWThemesMenuHidden(event);"
role="menu">
<label id="customization-lwtheme-menu-header" value="&customizeMode.lwthemes.myThemes;"/>
<label id="customization-lwtheme-menu-recommended" value="&customizeMode.lwthemes.recommended;"/>
<hbox id="customization-lwtheme-menu-footer">
<toolbarbutton class="customization-lwtheme-menu-footeritem"
label="&customizeMode.lwthemes.menuManage;"
accesskey="&customizeMode.lwthemes.menuManage.accessKey;"
tabindex="0"
oncommand="gCustomizeMode.openAddonsManagerThemes(event);"/>
<toolbarbutton class="customization-lwtheme-menu-footeritem"
label="&customizeMode.lwthemes.menuGetMore;"
accesskey="&customizeMode.lwthemes.menuGetMore.accessKey;"
tabindex="0"
oncommand="gCustomizeMode.getMoreThemes(event);"/>
</hbox>
</panel>
</button>
<spacer id="customization-footer-spacer"/>
<button id="customization-undo-reset-button"
class="customizationmode-button"

View File

@ -115,6 +115,7 @@ skip-if = os == "linux"
[browser_996364_registerArea_different_properties.js]
[browser_996635_remove_non_widgets.js]
[browser_1003588_no_specials_in_panel.js]
[browser_1007336_lwthemes_in_customize_mode.js]
[browser_1008559_anchor_undo_restore.js]
[browser_1042100_default_placements_update.js]
[browser_bootstrapped_custom_toolbar.js]

View File

@ -0,0 +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/. */
"use strict";
const DEFAULT_THEME_ID = "{972ce4c6-7e08-4474-a285-3208198ce6fd}";
add_task(function () {
Services.prefs.clearUserPref("lightweightThemes.usedThemes");
Services.prefs.clearUserPref("lightweightThemes.recommendedThemes");
yield startCustomizing();
let themesButton = document.getElementById("customization-lwtheme-button");
let popup = document.getElementById("customization-lwtheme-menu");
let popupShownPromise = popupShown(popup);
EventUtils.synthesizeMouseAtCenter(themesButton, {});
info("Clicked on themes button");
yield popupShownPromise;
let header = document.getElementById("customization-lwtheme-menu-header");
let recommendedHeader = document.getElementById("customization-lwtheme-menu-recommended");
is(header.nextSibling.nextSibling, recommendedHeader,
"There should only be one theme (default) in the 'My Themes' section by default");
is(header.nextSibling.theme.id, DEFAULT_THEME_ID, "That theme should be the default theme");
let firstLWTheme = recommendedHeader.nextSibling;
let firstLWThemeId = firstLWTheme.theme.id;
let themeChangedPromise = promiseObserverNotified("lightweight-theme-changed");
firstLWTheme.doCommand();
info("Clicked on first theme");
yield themeChangedPromise;
popupShownPromise = popupShown(popup);
EventUtils.synthesizeMouseAtCenter(themesButton, {});
info("Clicked on themes button");
yield popupShownPromise;
is(header.nextSibling.theme.id, DEFAULT_THEME_ID, "The first theme should be the Default theme");
let installedThemeId = header.nextSibling.nextSibling.theme.id;
ok(installedThemeId.startsWith(firstLWThemeId),
"The second theme in the 'My Themes' section should be the newly installed theme: " +
"Installed theme id: " + installedThemeId + "; First theme ID: " + firstLWThemeId);
is(header.nextSibling.nextSibling.nextSibling, recommendedHeader,
"There should be two themes in the 'My Themes' section");
let defaultTheme = header.nextSibling;
defaultTheme.doCommand();
is(Services.prefs.getBoolPref("lightweightThemes.isThemeSelected"), false, "No lwtheme should be selected");
});
add_task(function asyncCleanup() {
yield endCustomizing();
Services.prefs.clearUserPref("lightweightThemes.usedThemes");
Services.prefs.clearUserPref("lightweightThemes.recommendedThemes");
})

View File

@ -227,6 +227,15 @@ function startCustomizing(aWindow=window) {
return deferred.promise;
}
function promiseObserverNotified(aTopic) {
let deferred = Promise.defer();
Services.obs.addObserver(function onNotification(aSubject, aTopic, aData) {
Services.obs.removeObserver(onNotification, aTopic);
deferred.resolve({subject: aSubject, data: aData});
}, aTopic, false);
return deferred.promise;
}
function openAndLoadWindow(aOptions, aWaitForDelayedStartup=false) {
let deferred = Promise.defer();
let win = OpenBrowserWindow(aOptions);

View File

@ -26,7 +26,7 @@ box,
min-height: 3em;
max-height: 14em;
max-width: 400px;
min-width: 180px;
min-width: 200px;
}
.splitview-nav {

View File

@ -697,6 +697,13 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY customizeMode.restoreDefaults "Restore Defaults">
<!ENTITY customizeMode.toolbars "Show / Hide Toolbars">
<!ENTITY customizeMode.titlebar "Title Bar">
<!ENTITY customizeMode.lwthemes "Themes">
<!ENTITY customizeMode.lwthemes.myThemes "My Themes">
<!ENTITY customizeMode.lwthemes.recommended "Recommended">
<!ENTITY customizeMode.lwthemes.menuManage "Manage">
<!ENTITY customizeMode.lwthemes.menuManage.accessKey "M">
<!ENTITY customizeMode.lwthemes.menuGetMore "Get More Themes">
<!ENTITY customizeMode.lwthemes.menuGetMore.accessKey "G">
<!ENTITY social.chatBar.commandkey "c">
<!ENTITY social.chatBar.label "Focus chats">

View File

@ -0,0 +1,18 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
lightweightThemes.recommended-1.name=A Web Browser Renaissance
lightweightThemes.recommended-1.description=A Web Browser Renaissance is (C) Sean.Martell. Available under CC-BY-SA. No warranty.
lightweightThemes.recommended-2.name=Space Fantasy
lightweightThemes.recommended-2.description=Space Fantasy is (C) fx5800p. Available under CC-BY-SA. No warranty.
lightweightThemes.recommended-3.name=Linen Light
lightweightThemes.recommended-3.description=Linen Light is (C) DVemer. Available under CC-BY-SA. No warranty.
lightweightThemes.recommended-4.name=Pastel Gradient
lightweightThemes.recommended-4.description=Pastel Gradient is (C) darrinhenein. Available under CC-BY. No warranty.
lightweightThemes.recommended-5.name=Carbon Light
lightweightThemes.recommended-5.description=Carbon Light is (C) Jaxivo. Available under CC-BY-SA. No warranty.

View File

@ -12,6 +12,3 @@
<!ENTITY newtab.undo.undoButton "Undo.">
<!ENTITY newtab.undo.restoreButton "Restore All.">
<!ENTITY newtab.undo.closeTooltip "Hide">
<!ENTITY newtab.sponsored.release.message "This Sponsor site was suggested because we hoped youd find it interesting and because it supports Mozillas mission.">
<!ENTITY newtab.sponsored.trial.message2 "This site was suggested because we hoped youd find it interesting and because it supports Mozillas mission.">
<!ENTITY newtab.panel.link.text "Learn more…">

View File

@ -5,4 +5,16 @@
newtab.pin=Pin this site at its current position
newtab.unpin=Unpin this site
newtab.block=Remove this site
newtab.sponsored=Show information on sponsored tiles
# LOCALIZATION NOTE(newtab.sponsored.button): This text appears for sponsored
# and enhanced tiles on the same line as the tile's title, so prefer short
# strings to avoid overlap. This string should be uppercase.
newtab.sponsored.button=SPONSORED
# LOCALIZATION NOTE(newtab.sponsored.explain): %1$S will be replaced inline by
# the (X) block icon. %2$S will be replaced by an active link using string
# newtab.learn.link as text.
newtab.sponsored.explain=This tile is being shown to you on behalf of a Mozilla partner. You can remove it at any time by clicking the %1$S button. %2$S
# LOCALIZATION NOTE(newtab.enhanced.explain): %1$S will be replaced inline by
# the gear icon used to customize the new tab window. %2$S will be replaced by
# an active link using string newtab.learn.link as text.
newtab.enhanced.explain=A Mozilla partner has visually enhanced this tile, replacing the screenshot. You can turn off enhanced tiles by clicking the %1$S button for your preferences. %2$S
newtab.learn.link=Learn more…

View File

@ -66,6 +66,7 @@
locale/browser/devtools/app-manager.properties (%chrome/browser/devtools/app-manager.properties)
locale/browser/devtools/webide.dtd (%chrome/browser/devtools/webide.dtd)
locale/browser/devtools/webide.properties (%chrome/browser/devtools/webide.properties)
locale/browser/lightweightThemes.properties (%chrome/browser/lightweightThemes.properties)
locale/browser/loop/loop.properties (%chrome/browser/loop/loop.properties)
locale/browser/newTab.dtd (%chrome/browser/newTab.dtd)
locale/browser/newTab.properties (%chrome/browser/newTab.properties)

View File

@ -81,7 +81,7 @@ this.ContentSearch = {
// them immediately, which would result in non-FIFO responses due to the
// asynchrononicity added by converting image data URIs to ArrayBuffers.
_eventQueue: [],
_currentEvent: null,
_currentEventPromise: null,
// This is used to handle search suggestions. It maps xul:browsers to objects
// { controller, previousFormHistoryResult }. See _onMessageGetSuggestions.
@ -94,6 +94,16 @@ this.ContentSearch = {
Services.obs.addObserver(this, "browser-search-engine-modified", false);
},
destroy: function () {
Cc["@mozilla.org/globalmessagemanager;1"].
getService(Ci.nsIMessageListenerManager).
removeMessageListener(INBOUND_MESSAGE, this);
Services.obs.removeObserver(this, "browser-search-engine-modified");
this._eventQueue.length = 0;
return Promise.resolve(this._currentEventPromise);
},
/**
* Focuses the search input in the page with the given message manager.
* @param messageManager
@ -141,22 +151,24 @@ this.ContentSearch = {
}
},
_processEventQueue: Task.async(function* () {
if (this._currentEvent || !this._eventQueue.length) {
_processEventQueue: function () {
if (this._currentEventPromise || !this._eventQueue.length) {
return;
}
this._currentEvent = this._eventQueue.shift();
try {
yield this["_on" + this._currentEvent.type](this._currentEvent.data);
}
catch (err) {
Cu.reportError(err);
}
finally {
this._currentEvent = null;
this._processEventQueue();
}
}),
let event = this._eventQueue.shift();
return this._currentEventPromise = Task.spawn(function* () {
try {
yield this["_on" + event.type](event.data);
} catch (err) {
Cu.reportError(err);
} finally {
this._currentEventPromise = null;
this._processEventQueue();
}
}.bind(this));
},
_onMessage: Task.async(function* (msg) {
let methodName = "_onMessage" + msg.data.type;

View File

@ -45,10 +45,6 @@
-moz-padding-start: 0;
}
.subviewbutton.bookmark-item > .toolbarbutton-icon {
-moz-margin-start: 3px;
}
/* subviewbutton entries for social sidebars have images that come from external
/* sources, and are not guaranteed to be the size we want, so force the size on
/* those icons. */

View File

@ -118,8 +118,6 @@ browser.jar:
skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css)
skin/classic/browser/feeds/subscribe-ui.css (feeds/subscribe-ui.css)
* skin/classic/browser/newtab/newTab.css (newtab/newTab.css)
skin/classic/browser/newtab/controls.png (../shared/newtab/controls.png)
skin/classic/browser/newtab/controls@2x.png (../shared/newtab/controls@2x.png)
skin/classic/browser/newtab/controls.svg (../shared/newtab/controls.svg)
skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png)
skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.png)

View File

@ -192,8 +192,6 @@ browser.jar:
skin/classic/browser/feeds/audioFeedIcon.png (feeds/feedIcon.png)
skin/classic/browser/feeds/audioFeedIcon16.png (feeds/feedIcon16.png)
* skin/classic/browser/newtab/newTab.css (newtab/newTab.css)
skin/classic/browser/newtab/controls.png (../shared/newtab/controls.png)
skin/classic/browser/newtab/controls@2x.png (../shared/newtab/controls@2x.png)
skin/classic/browser/newtab/controls.svg (../shared/newtab/controls.svg)
skin/classic/browser/setDesktopBackground.css
skin/classic/browser/monitor.png

View File

@ -276,4 +276,97 @@ toolbarpaletteitem[place="toolbar"] {
margin-right: 0;
}
#customization-lwtheme-menu > .panel-arrowcontainer > .panel-arrowcontent {
-moz-box-orient: vertical;
/* Make the panel padding uniform across all platforms due to the
styling of the section headers and footer. */
padding: 10px;
}
.customization-lwtheme-menu-theme > .toolbarbutton-icon {
width: 32px;
height: 32px;
}
.customization-lwtheme-menu-theme {
-moz-appearance: none;
border: 1px solid transparent;
margin: 0 -5px 5px;
padding-top: 0;
-moz-padding-end: 5px;
padding-bottom: 0;
-moz-padding-start: 0;
}
.customization-lwtheme-menu-theme[active="true"],
.customization-lwtheme-menu-theme:hover {
background-color: hsla(210,4%,10%,.08);
border-color: hsla(210,4%,10%,.11);
}
.customization-lwtheme-menu-theme[active="true"],
.customization-lwtheme-menu-theme:hover:active {
background-color: hsla(210,4%,10%,.15);
}
.customization-lwtheme-menu-theme > .toolbarbutton-icon {
margin: 5px;
}
.customization-lwtheme-menu-theme > .toolbarbutton-text {
text-align: start;
}
#customization-lwtheme-menu-header,
#customization-lwtheme-menu-recommended {
padding: 10px;
margin-bottom: 5px;
}
#customization-lwtheme-menu-header,
#customization-lwtheme-menu-recommended,
#customization-lwtheme-menu-footer {
background-color: hsla(210,4%,10%,.05);
color: hsl(0,0%,50%);
margin-right: -10px;
margin-left: -10px;
}
#customization-lwtheme-menu-header {
margin-top: -10px;
border-bottom: 1px solid hsla(210,4%,10%,.05);
}
#customization-lwtheme-menu-recommended {
border-top: 1px solid hsla(210,4%,10%,.05);
border-bottom: 1px solid hsla(210,4%,10%,.05);
}
#customization-lwtheme-menu-footer {
border-top: 1px solid hsla(210,4%,10%,.05);
margin-bottom: -10px;
}
.customization-lwtheme-menu-footeritem {
-moz-appearance: none;
-moz-box-flex: 1;
color: hsl(0,0%,50%);
border: 1px solid transparent;
padding: 10px;
margin-left: 0;
margin-right: 0;
}
.customization-lwtheme-menu-footeritem:hover {
background-color: hsla(210,4%,10%,.15);
}
#customization-lwtheme-menu-footer:not(:hover) > .customization-lwtheme-menu-footeritem:first-child {
-moz-border-end: 1px solid hsla(210,4%,10%,.15);
}
#customization-lwtheme-menu-footer:hover > .customization-lwtheme-menu-footeritem:first-child {
-moz-border-end-color: transparent;
}
%include customizeTip.inc.css

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@ -62,7 +62,8 @@
}
/* CUSTOMIZE */
#newtab-customize-button {
#newtab-customize-button,
.newtab-customize {
background-color: transparent;
background-image: -moz-image-rect(url(chrome://browser/skin/newtab/controls.svg), 0, 32, 32, 0);
background-size: 28px;
@ -160,10 +161,6 @@
font-weight: bold;
}
.newtab-site[type=sponsored] .newtab-title {
-moz-padding-end: 24px;
}
/* CONTROLS */
.newtab-control {
background-color: transparent;
@ -199,24 +196,3 @@
.newtab-control-block:hover:active {
background-image: -moz-image-rect(url(chrome://browser/skin/newtab/controls.svg), 0, 256, 32, 224);
}
.newtab-control-sponsored {
background-image: url(chrome://browser/skin/newtab/controls.png);
background-position: -249px -1px;
background-size: auto;
}
@media (min-resolution: 2dppx) {
.newtab-control-sponsored {
background-image: url(chrome://browser/skin/newtab/controls@2x.png);
background-size: 296px;
}
}
.newtab-control-sponsored:hover {
background-position: -265px -1px;
}
.newtab-control-sponsored[panelShown] {
background-position: -281px -1px;
}

View File

@ -100,10 +100,6 @@ menuitem[type="checkbox"].subviewbutton {
-moz-padding-start: 0;
}
.subviewbutton.bookmark-item > .toolbarbutton-icon {
-moz-margin-start: 3px;
}
/* subviewbutton entries for social sidebars have images that come from external
/* sources, and are not guaranteed to be the size we want, so force the size on
/* those icons. */

View File

@ -141,8 +141,6 @@ browser.jar:
skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css)
skin/classic/browser/feeds/subscribe-ui.css (feeds/subscribe-ui.css)
* skin/classic/browser/newtab/newTab.css (newtab/newTab.css)
skin/classic/browser/newtab/controls.png (../shared/newtab/controls.png)
skin/classic/browser/newtab/controls@2x.png (../shared/newtab/controls@2x.png)
skin/classic/browser/newtab/controls.svg (../shared/newtab/controls.svg)
skin/classic/browser/places/places.css (places/places.css)
* skin/classic/browser/places/organizer.css (places/organizer.css)
@ -556,8 +554,6 @@ browser.jar:
skin/classic/aero/browser/feeds/subscribe.css (feeds/subscribe.css)
skin/classic/aero/browser/feeds/subscribe-ui.css (feeds/subscribe-ui.css)
* skin/classic/aero/browser/newtab/newTab.css (newtab/newTab.css)
skin/classic/aero/browser/newtab/controls.png (../shared/newtab/controls.png)
skin/classic/aero/browser/newtab/controls@2x.png (../shared/newtab/controls@2x.png)
skin/classic/aero/browser/newtab/controls.svg (../shared/newtab/controls.svg)
* skin/classic/aero/browser/places/places.css (places/places-aero.css)
* skin/classic/aero/browser/places/organizer.css (places/organizer-aero.css)

View File

@ -26,8 +26,10 @@ class MOZ_STACK_CLASS nsViewportInfo
{
public:
nsViewportInfo(const mozilla::ScreenIntSize& aDisplaySize,
bool aAllowZoom = true, bool aAllowDoubleTapZoom = true) :
mDefaultZoom(1.0),
const mozilla::CSSToScreenScale& aDefaultZoom,
bool aAllowZoom,
bool aAllowDoubleTapZoom) :
mDefaultZoom(aDefaultZoom),
mAutoSize(true),
mAllowZoom(aAllowZoom),
mAllowDoubleTapZoom(aAllowDoubleTapZoom)

View File

@ -7501,15 +7501,27 @@ nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv)
nsViewportInfo
nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
{
nsPresContext* context = mPresShell->GetPresContext();
float fullZoom = context ? context->GetFullZoom() : 1.0;
fullZoom = (fullZoom == 0.0) ? 1.0 : fullZoom;
CSSToScreenScale defaultScale = CSSToLayoutDeviceScale(fullZoom) *
LayoutDeviceToScreenScale(1.0);
// In cases where the width of the CSS viewport is less than or equal to the width
// of the display (i.e. width <= device-width) then we disable double-tap-to-zoom
// behaviour. See bug 941995 for details.
switch (mViewportType) {
case DisplayWidthHeight:
return nsViewportInfo(aDisplaySize);
return nsViewportInfo(aDisplaySize,
defaultScale,
/*allowZoom*/ true,
/*allowDoubleTapZoom*/ true);
case DisplayWidthHeightNoZoom:
return nsViewportInfo(aDisplaySize, /*allowZoom*/ false, /*allowDoubleTapZoom*/ false);
return nsViewportInfo(aDisplaySize,
defaultScale,
/*allowZoom*/ false,
/*allowDoubleTapZoom*/ false);
case Unknown:
{
nsAutoString viewport;
@ -7529,7 +7541,10 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
{
// We're making an assumption that the docType can't change here
mViewportType = DisplayWidthHeight;
return nsViewportInfo(aDisplaySize, /*allowZoom*/true, /*allowDoubleTapZoom*/false);
return nsViewportInfo(aDisplaySize,
defaultScale,
/*allowZoom*/true,
/*allowDoubleTapZoom*/false);
}
}
}
@ -7538,7 +7553,10 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly);
if (handheldFriendly.EqualsLiteral("true")) {
mViewportType = DisplayWidthHeight;
return nsViewportInfo(aDisplaySize, /*allowZoom*/true, /*allowDoubleTapZoom*/false);
return nsViewportInfo(aDisplaySize,
defaultScale,
/*allowZoom*/true,
/*allowDoubleTapZoom*/false);
}
// Bug 940036. This is bad. When FirefoxOS was built, apps installed
@ -7561,7 +7579,10 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
"ImplicitMetaViewportTagFallback");
}
mViewportType = DisplayWidthHeightNoZoom;
return nsViewportInfo(aDisplaySize, /*allowZoom*/false, /*allowDoubleTapZoom*/false);
return nsViewportInfo(aDisplaySize,
defaultScale,
/*allowZoom*/false,
/*allowDoubleTapZoom*/false);
}
}
@ -7652,8 +7673,11 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
if (mValidHeight && !aDisplaySize.IsEmpty()) {
size.width = size.height * aDisplaySize.width / aDisplaySize.height;
} else {
// Stretch CSS pixel size of viewport to keep device pixel size
// unchanged after full zoom applied.
// See bug 974242.
size.width = Preferences::GetInt("browser.viewport.desktopWidth",
kViewportDefaultScreenWidth);
kViewportDefaultScreenWidth) / fullZoom;
}
}
@ -7664,10 +7688,13 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
size.height = size.width;
}
}
// Now convert the scale into device pixels per CSS pixel.
// Now convert the scale into device pixels per CSS pixel base on this formula
// CSSPixel x widget scale x full zoom = LayoutDevicePixel
nsIWidget *widget = nsContentUtils::WidgetForDocument(this);
CSSToLayoutDeviceScale pixelRatio = widget ? widget->GetDefaultScale()
: CSSToLayoutDeviceScale(1.0f);
CSSToLayoutDeviceScale pixelRatio = CSSToLayoutDeviceScale(
(widget ? widget->GetDefaultScale().scale : 1.0f) * fullZoom);
CSSToScreenScale scaleFloat = mScaleFloat * pixelRatio;
CSSToScreenScale scaleMinFloat = mScaleMinFloat * pixelRatio;
CSSToScreenScale scaleMaxFloat = mScaleMaxFloat * pixelRatio;

View File

@ -329,6 +329,13 @@ TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize)
metrics.SetScrollId(viewId);
}
if (nsIPresShell* shell = document->GetShell()) {
if (nsPresContext* context = shell->GetPresContext()) {
metrics.mDevPixelsPerCSSPixel = CSSToLayoutDeviceScale(
(float)nsPresContext::AppUnitsPerCSSPixel() / context->AppUnitsPerDevPixel());
}
}
metrics.mCumulativeResolution = metrics.GetZoom() / metrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
// This is the root layer, so the cumulative resolution is the same
// as the resolution.
@ -758,6 +765,8 @@ TabChild::HandleEvent(nsIDOMEvent* aEvent)
// This meta data may or may not have been a meta viewport tag. If it was,
// we should handle it immediately.
HandlePossibleViewportChange(mInnerSize);
} else if (eventType.EqualsLiteral("FullZoomChange")) {
HandlePossibleViewportChange(mInnerSize);
}
return NS_OK;
@ -1463,6 +1472,7 @@ TabChild::ActorDestroy(ActorDestroyReason why)
(mTabChildGlobal->mMessageManager.get())->Disconnect();
mTabChildGlobal->mMessageManager = nullptr;
}
if (Id() != 0) {
NestedTabChildMap().erase(Id());
}
@ -2546,6 +2556,7 @@ TabChild::InitTabChildGlobal(FrameScriptLoading aScriptLoading)
root->SetParentTarget(scope);
chromeHandler->AddEventListener(NS_LITERAL_STRING("DOMMetaAdded"), this, false);
chromeHandler->AddEventListener(NS_LITERAL_STRING("FullZoomChange"), this, false);
}
if (aScriptLoading != DONT_LOAD_SCRIPTS && !mTriedBrowserInit) {

View File

@ -56,6 +56,9 @@ this.SystemMessagePermissionsTable = {
"bluetooth-opp-transfer-start": {
"bluetooth": []
},
"cellbroadcast-received": {
"cellbroadcast": []
},
"connection": { },
"captive-portal": {
"wifi-manage": []

View File

@ -10,9 +10,8 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
const RIL_MMSSERVICE_CONTRACTID = "@mozilla.org/mms/rilmmsservice;1";
const RIL_MMSSERVICE_CID = Components.ID("{217ddd76-75db-4210-955d-8806cd8d87f9}");
@ -54,6 +53,7 @@ const _HTTP_STATUS_USER_CANCELLED = -1;
const _HTTP_STATUS_RADIO_DISABLED = -2;
const _HTTP_STATUS_NO_SIM_CARD = -3;
const _HTTP_STATUS_ACQUIRE_TIMEOUT = -4;
const _HTTP_STATUS_FAILED_TO_ROUTE = -5;
// Non-standard MMS status for internal use.
const _MMS_ERROR_MESSAGE_DELETED = -1;
@ -63,6 +63,7 @@ const _MMS_ERROR_SIM_CARD_CHANGED = -4;
const _MMS_ERROR_SHUTDOWN = -5;
const _MMS_ERROR_USER_CANCELLED_NO_REASON = -6;
const _MMS_ERROR_SIM_NOT_MATCHED = -7;
const _MMS_ERROR_FAILED_TO_ROUTE = -8;
const CONFIG_SEND_REPORT_NEVER = 0;
const CONFIG_SEND_REPORT_DEFAULT_NO = 1;
@ -158,6 +159,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gRil",
"@mozilla.org/ril;1",
"nsIRadioInterfaceLayer");
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
"@mozilla.org/network/manager;1",
"nsINetworkManager");
XPCOMUtils.defineLazyGetter(this, "MMS", function() {
let MMS = {};
Cu.import("resource://gre/modules/MmsPduHelper.jsm", MMS);
@ -202,6 +207,7 @@ function MmsConnection(aServiceId) {
this.serviceId = aServiceId;
this.radioInterface = gRil.getRadioInterface(aServiceId);
this.pendingCallbacks = [];
this.hostsToRoute = [];
this.connectTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this.disconnectTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
};
@ -216,7 +222,8 @@ MmsConnection.prototype = {
setApnSetting: function(network) {
this.mmsc = network.mmsc;
this.mmsProxy = network.mmsProxy;
// Workaround an xpconnect issue with undefined string objects. See bug 808220.
this.mmsProxy = (network === "undefined") ? undefined : network.mmsProxy;
this.mmsPort = network.mmsPort;
},
@ -252,6 +259,12 @@ MmsConnection.prototype = {
/** MMS network connection reference count. */
refCount: 0,
// cache of hosts to be accessed when this connection is alive.
hostsToRoute: null,
// cache of the networkInterface acquired during this connection.
networkInterface: null,
connectTimer: null,
disconnectTimer: null,
@ -274,9 +287,29 @@ MmsConnection.prototype = {
*/
onDisconnectTimerTimeout: function() {
if (DEBUG) debug("onDisconnectTimerTimeout: deactivate the MMS data call.");
if (this.connected) {
this.radioInterface.deactivateDataCallByType("mms");
if (!this.connected) {
return;
}
let deactivateMmsDataCall = (aError) => {
if (aError) debug("Failed to removeHostRoute: " + aError);
// Clear cache.
this.hostsToRoute = [];
this.networkInterface = null;
this.radioInterface.deactivateDataCallByType("mms");
};
let promises =
this.hostsToRoute.map(function(aHost) {
return gNetworkManager.removeHostRoute(this.networkInterface, aHost);
}, this);
return Promise.all(promises)
.then(() => deactivateMmsDataCall(),
(aError) => deactivateMmsDataCall(aError));
},
init: function() {
@ -393,6 +426,10 @@ MmsConnection.prototype = {
if (DEBUG) debug("acquire: buffer the MMS request and setup the MMS data call.");
this.radioInterface.setupDataCallByType("mms");
// Clear cache when setup new connection.
this.hostsToRoute = [];
this.networkInterface = null;
// Set a timer to clear the buffered MMS requests if the
// MMS network fails to be connected within a time period.
this.connectTimer.
@ -429,6 +466,34 @@ MmsConnection.prototype = {
}
},
/**
* Helper to ensure the routing of each transaction.
*
* @param url
* Optional url for retrieving mms.
*
* @return a Promise resolved if added or rejected, otherwise.
*/
ensureRouting: function(url) {
let host = this.mmsProxy;
if (!this.mmsProxy) {
host = url;
}
try {
let uri = Services.io.newURI(host, null, null);
host = uri.host;
} catch (e) {}
return gNetworkManager.addHostRoute(this.networkInterface, host)
.then(() => {
if (this.hostsToRoute.indexOf(host) < 0) {
this.hostsToRoute.push(host);
}
});
},
shutdown: function() {
Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
Services.obs.removeObserver(this, kNetworkConnStateChangedTopic);
@ -467,6 +532,8 @@ MmsConnection.prototype = {
this.connected = connected;
if (!this.connected) {
this.hostsToRoute = [];
this.networkInterface = null;
return;
}
@ -474,10 +541,13 @@ MmsConnection.prototype = {
// which is going to be used for the HTTP requests later.
this.setApnSetting(network);
// Cache connected network.
this.networkInterface = network;
if (DEBUG) debug("Got the MMS network connected! Resend the buffered " +
"MMS requests: number: " + this.pendingCallbacks.length);
this.connectTimer.cancel();
this.flushPendingCallbacks(_HTTP_STATUS_ACQUIRE_CONNECTION_SUCCESS)
this.flushPendingCallbacks(_HTTP_STATUS_ACQUIRE_CONNECTION_SUCCESS);
break;
}
case NS_XPCOM_SHUTDOWN_OBSERVER_ID: {
@ -646,13 +716,23 @@ XPCOMUtils.defineLazyGetter(this, "gMmsTransactionHelper", function() {
url = mmsConnection.mmsc;
}
if (DEBUG) debug("sendRequest: register proxy filter to " + url);
let proxyFilter = new MmsProxyFilter(mmsConnection, url);
gpps.registerFilter(proxyFilter, 0);
let startTransaction = function () {
if (DEBUG) debug("sendRequest: register proxy filter to " + url);
let proxyFilter = new MmsProxyFilter(mmsConnection, url);
gpps.registerFilter(proxyFilter, 0);
cancellable.xhr = this.sendHttpRequest(mmsConnection, method,
url, istream, proxyFilter,
cancellable.done.bind(cancellable));
cancellable.xhr = this.sendHttpRequest(mmsConnection, method,
url, istream, proxyFilter,
cancellable.done.bind(cancellable));
}.bind(this);
mmsConnection.ensureRouting(url)
.then(() => startTransaction(),
(aError) => {
debug("Failed to ensureRouting: " + aError);
cancellable.done(_HTTP_STATUS_FAILED_TO_ROUTE);
});
}).bind(this));
return cancellable;
@ -817,6 +897,8 @@ XPCOMUtils.defineLazyGetter(this, "gMmsTransactionHelper", function() {
return _MMS_ERROR_RADIO_DISABLED;
case _HTTP_STATUS_NO_SIM_CARD:
return _MMS_ERROR_NO_SIM_CARD;
case _HTTP_STATUS_FAILED_TO_ROUTE:
return _MMS_ERROR_FAILED_TO_ROUTE;
case HTTP_STATUS_OK:
return MMS.MMS_PDU_ERROR_OK;
default:

View File

@ -263,8 +263,8 @@ NfcMessageHandler::InitializeNotification(const Parcel& aParcel, EventOptions& a
aOptions.mMajorVersion = aParcel.readInt32();
aOptions.mMinorVersion = aParcel.readInt32();
if (aOptions.mMajorVersion != NFCD_MAJOR_VERSION &&
aOptions.mMinorVersion != NFCD_MAJOR_VERSION) {
if (aOptions.mMajorVersion != NFCD_MAJOR_VERSION ||
aOptions.mMinorVersion != NFCD_MINOR_VERSION) {
CHROMIUM_LOG("NFCD version mismatched. majorVersion: %d, minorVersion: %d",
aOptions.mMajorVersion, aOptions.mMinorVersion);
}

View File

@ -22,39 +22,6 @@ this.DEBUG_ALL = false;
this.DEBUG_CONTENT_HELPER = false || DEBUG_ALL;
this.DEBUG_NFC = false || DEBUG_ALL;
// Current version
this.NFC_MAJOR_VERSION = 1;
this.NFC_MINOR_VERSION = 7;
this.NFC_REQUEST_CONFIG = 0;
this.NFC_REQUEST_CONNECT = 1;
this.NFC_REQUEST_CLOSE = 2;
this.NFC_REQUEST_GET_DETAILS = 3;
this.NFC_REQUEST_READ_NDEF = 4;
this.NFC_REQUEST_WRITE_NDEF = 5;
this.NFC_REQUEST_MAKE_NDEF_READ_ONLY = 6;
this.NFC_RESPONSE_GENERAL = 1000;
this.NFC_RESPONSE_CONFIG = 1001;
this.NFC_RESPONSE_READ_NDEF_DETAILS = 1002;
this.NFC_RESPONSE_READ_NDEF = 1003;
this.NFC_NOTIFICATION_INITIALIZED = 2000;
this.NFC_NOTIFICATION_TECH_DISCOVERED = 2001;
this.NFC_NOTIFICATION_TECH_LOST = 2002;
this.NFC_TECHS = {
0:"NDEF",
1:"NDEF_WRITEABLE",
2:"NDEF_FORMATABLE",
3:"P2P",
4:"NFC_A",
5:"NFC_B",
6:"NFC_F",
7:"NFC_V",
8:"NFC_ISO_DEP"
};
// nfcd error codes
this.NFC_SUCCESS = 0;
this.NFC_ERROR_IO = -1;

View File

@ -340,12 +340,6 @@ NetworkManager.prototype = {
gNetworkService.removeDefaultRoute(network);
this.setAndConfigureActive();
#ifdef MOZ_B2G_RIL
// Resolve and add extra host route. For example, mms proxy or mmsc.
// IMPORTANT: The offline state of DNSService will be set implicitly in
// setAndConfigureActive() by modifying Services.io.offline.
// Always setExtraHostRoute() after setAndConfigureActive().
this.setExtraHostRoute(network);
// Update data connection when Wifi connected/disconnected
if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
for (let i = 0; i < this.mRil.numRadioInterfaces; i++) {
@ -366,8 +360,7 @@ NetworkManager.prototype = {
if (this.isNetworkTypeMobile(network.type)) {
this.removeHostRoutes(network);
}
// Remove extra host route. For example, mms proxy or mmsc.
this.removeExtraHostRoute(network);
// Remove secondary default route for dun.
if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
this.removeSecondaryDefaultRoute(network);
@ -455,6 +448,56 @@ NetworkManager.prototype = {
this.setAndConfigureActive();
},
_updateRoutes: function(doAdd, ipAddresses, networkName, gateways) {
let promises = [];
ipAddresses.forEach((aIpAddress) => {
let gateway = this.selectGateway(gateways, aIpAddress);
if (gateway) {
promises.push((doAdd)
? gNetworkService.addHostRoute(networkName, gateway, aIpAddress)
: gNetworkService.removeHostRoute(networkName, gateway, aIpAddress));
}
});
return Promise.all(promises);
},
isValidatedNetwork: function(network) {
let isValid = false;
try {
isValid = (this.getNetworkId(network) in this.networkInterfaces);
} catch (e) {
debug("Invalid network interface: " + e);
}
return isValid;
},
addHostRoute: function(network, host) {
if (!this.isValidatedNetwork(network)) {
return Promise.reject("Invalid network interface.");
}
return this.resolveHostname(host)
.then((ipAddresses) => this._updateRoutes(true,
ipAddresses,
network.name,
network.getGateways()));
},
removeHostRoute: function(network, host) {
if (!this.isValidatedNetwork(network)) {
return Promise.reject("Invalid network interface.");
}
return this.resolveHostname(host)
.then((ipAddresses) => this._updateRoutes(false,
ipAddresses,
network.name,
network.getGateways()));
},
#ifdef MOZ_B2G_RIL
isNetworkTypeSecondaryMobile: function(type) {
return (type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS ||
@ -470,35 +513,16 @@ NetworkManager.prototype = {
setHostRoutes: function(network) {
let hosts = network.getDnses().concat(network.httpProxyHost);
let gateways = network.getGateways();
let promises = [];
for (let i = 0; i < hosts.length; i++) {
let host = hosts[i];
let gateway = this.selectGateway(gateways, host);
if (gateway && host) {
promises.push(gNetworkService.addHostRoute(network.name, gateway, host));
}
}
return Promise.all(promises);
return this._updateRoutes(true, hosts, network.name, network.getGateways());
},
removeHostRoutes: function(network) {
let hosts = network.getDnses().concat(network.httpProxyHost);
let gateways = network.getGateways();
let promises = [];
for (let i = 0; i < hosts.length; i++) {
let host = hosts[i];
let gateway = this.selectGateway(gateways, host);
if (gateway && host) {
promises.push(gNetworkService.removeHostRoute(network.name, gateway, host));
}
}
return Promise.all(promises);
return this._updateRoutes(false, hosts, network.name, network.getGateways());
},
#endif
selectGateway: function(gateways, host) {
for (let i = 0; i < gateways.length; i++) {
@ -511,88 +535,7 @@ NetworkManager.prototype = {
return null;
},
setExtraHostRoute: function(network) {
if (network.type != Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS) {
return Promise.resolve();
}
if (!(network instanceof Ci.nsIRilNetworkInterface)) {
let errorMsg = "Network for MMS must be an instance of " +
"nsIRilNetworkInterface";
debug(errorMsg);
return Promise.reject(errorMsg);
}
network = network.QueryInterface(Ci.nsIRilNetworkInterface);
debug("Adding mmsproxy and/or mmsc route for " + network.name);
let hostToResolve = network.mmsProxy;
// Workaround an xpconnect issue with undefined string objects.
// See bug 808220
if (!hostToResolve || hostToResolve === "undefined") {
hostToResolve = network.mmsc;
}
let mmsHosts = this.resolveHostname([hostToResolve]);
if (mmsHosts.length == 0) {
let errorMsg = "No valid hostnames can be added. Stop adding host route.";
debug(errorMsg);
return Promise.reject(errorMsg);
}
let gateways = network.getGateways();
let promises = [];
for (let i = 0; i < mmsHosts.length; i++) {
let gateway = this.selectGateway(gateways, mmsHosts[i]);
if (gateway) {
promises.push(gNetworkService.addHostRoute(network.name, gateway,
mmsHosts[i]));
}
}
return Promise.all(promises);
},
removeExtraHostRoute: function(network) {
if (network.type != Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS) {
return Promise.resolve();
}
if (!(network instanceof Ci.nsIRilNetworkInterface)) {
let errorMsg = "Network for MMS must be an instance of " +
"nsIRilNetworkInterface";
debug(errorMsg);
return Promise.reject(errorMsg);
}
network = network.QueryInterface(Ci.nsIRilNetworkInterface);
debug("Removing mmsproxy and/or mmsc route for " + network.name);
let hostToResolve = network.mmsProxy;
// Workaround an xpconnect issue with undefined string objects.
// See bug 808220
if (!hostToResolve || hostToResolve === "undefined") {
hostToResolve = network.mmsc;
}
let mmsHosts = this.resolveHostname([hostToResolve]);
if (mmsHosts.length == 0) {
let errorMsg = "No valid hostnames can be removed. Stop removing host route.";
debug(errorMsg);
return Promise.reject(errorMsg);
}
let gateways = network.getGateways();
let promises = [];
for (let i = 0; i < mmsHosts.length; i++) {
let gateway = this.selectGateway(gateways, mmsHosts[i]);
if (gateway) {
promises.push(gNetworkService.removeHostRoute(network.name, gateway,
mmsHosts[i]));
}
}
return Promise.all(promises);
},
#ifdef MOZ_B2G_RIL
setSecondaryDefaultRoute: function(network) {
let gateways = network.getGateways();
for (let i = 0; i < gateways.length; i++) {
@ -713,43 +656,46 @@ NetworkManager.prototype = {
}
},
#ifdef MOZ_B2G_RIL
resolveHostname: function(hosts) {
let retval = [];
for (let hostname of hosts) {
// Sanity check for null, undefined and empty string... etc.
if (!hostname) {
continue;
}
try {
let uri = Services.io.newURI(hostname, null, null);
hostname = uri.host;
} catch (e) {}
// An extra check for hostnames that cannot be made by newURI(...).
// For example, an IP address like "10.1.1.1".
if (hostname.match(this.REGEXP_IPV4) ||
hostname.match(this.REGEXP_IPV6)) {
retval.push(hostname);
continue;
}
try {
let hostnameIps = gDNSService.resolve(hostname, 0);
while (hostnameIps.hasMore()) {
retval.push(hostnameIps.getNextAddrAsString());
debug("Found IP at: " + JSON.stringify(retval));
}
} catch (e) {
debug("Failed to resolve '" + hostname + "', exception: " + e);
}
resolveHostname: function(hostname) {
// Sanity check for null, undefined and empty string... etc.
if (!hostname) {
return Promise.reject(new Error("hostname is empty: " + hostname));
}
return retval;
if (hostname.match(this.REGEXP_IPV4) ||
hostname.match(this.REGEXP_IPV6)) {
return Promise.resolve([hostname]);
}
let deferred = Promise.defer();
let onLookupComplete = (aRequest, aRecord, aStatus) => {
if (!Components.isSuccessCode(aStatus)) {
deferred.reject(new Error(
"Failed to resolve '" + hostname + "', with status: " + aStatus));
return;
}
let retval = [];
while (aRecord.hasMore()) {
retval.push(aRecord.getNextAddrAsString());
}
if (!retval.length) {
deferred.reject(new Error("No valid address after DNS lookup!"));
return;
}
if (DEBUG) debug("hostname is resolved: " + hostname);
if (DEBUG) debug("Addresses: " + JSON.stringify(retval));
deferred.resolve(retval);
};
// TODO: Bug 992772 - Resolve the hostname with specified networkInterface.
gDNSService.asyncResolve(hostname, 0, onLookupComplete, Services.tm.mainThread);
return deferred.promise;
},
#endif
convertConnectionType: function(network) {
// If there is internal interface change (e.g., MOBILE_MMS, MOBILE_SUPL),

View File

@ -2284,6 +2284,7 @@ RadioInterface.prototype = {
break;
case "cellbroadcast-received":
message.timestamp = Date.now();
this.broadcastCbsSystemMessage(message);
gMessageManager.sendCellBroadcastMessage("RIL:CellBroadcastReceived",
this.clientId, message);
break;
@ -3310,6 +3311,48 @@ RadioInterface.prototype = {
},
/**
* A helper to broadcast the system message to launch registered apps
* like CMAS app and etc.
*
* @param aName
* The system message name.
* @param aMessage
* The Cellbroadcast message received from ril_worker.
*/
broadcastCbsSystemMessage: function(aMessage) {
// Create system message with the same structure of nsIDOMMozCellBroadcastMessage
// and nsIDOMMozCellBroadcastEtwsInfo.
let etws = (aMessage.etws != null)
? {
warningType: (aMessage.etws.warningType != null)
? RIL.CB_ETWS_WARNING_TYPE_NAMES[aMessage.etws.warningType]
: null,
emergencyUserAlert: aMessage.etws.emergencyUserAlert,
popup: aMessage.etws.popup
}
: null;
let systemMessage = {
serviceId: this.clientId,
gsmGeographicalScope: RIL.CB_GSM_GEOGRAPHICAL_SCOPE_NAMES[aMessage.geographicalScope],
messageCode: aMessage.messageCode,
messageId: aMessage.messageId,
language: aMessage.language,
body: aMessage.fullBody,
messageClass: aMessage.messageClass,
timestamp: aMessage.timestamp,
etws: etws,
cdmaServiceCategory: aMessage.serviceCategory
};
if (DEBUG) {
this.debug("CBS system message to be broadcasted: " + JSON.stringify(systemMessage));
}
gSystemMessenger.broadcastMessage("cellbroadcast-received", systemMessage);
},
/**
* Set the setting value of "time.clock.automatic-update.available".
*/

View File

@ -97,7 +97,7 @@ interface nsINetworkInterface : nsISupports
/**
* Manage network interfaces.
*/
[scriptable, uuid(f3193805-c070-4d23-bd5c-a439eb8610c3)]
[scriptable, uuid(19822018-2454-11e4-baa7-2b5894f0af6f)]
interface nsINetworkManager : nsISupports
{
/**
@ -196,4 +196,34 @@ interface nsINetworkManager : nsISupports
in nsINetworkInterface networkInterface,
in jsval config,
in nsIWifiTetheringCallback callback);
/**
* Add host route to the specified network into routing table.
*
* @param network
* The network interface where the host to be routed to.
* @param host
* The host to be added.
* The host will be resolved in advance if it's not an ip-address.
*
* @return a Promise
* resolved if added; rejected, otherwise.
*/
jsval addHostRoute(in nsINetworkInterface network,
in DOMString host);
/**
* Remove host route to the specified network from routing table.
*
* @param network
* The network interface where the routing to be removed from.
* @param host
* The host routed to the network.
* The host will be resolved in advance if it's not an ip-address.
*
* @return a Promise
* resolved if removed; rejected, otherwise.
*/
jsval removeHostRoute(in nsINetworkInterface network,
in DOMString host);
};

View File

@ -2529,78 +2529,106 @@ RilObject.prototype = {
return true;
},
_serviceCodeToKeyString: function(serviceCode) {
switch (serviceCode) {
case MMI_SC_CFU:
case MMI_SC_CF_BUSY:
case MMI_SC_CF_NO_REPLY:
case MMI_SC_CF_NOT_REACHABLE:
case MMI_SC_CF_ALL:
case MMI_SC_CF_ALL_CONDITIONAL:
return MMI_KS_SC_CALL_FORWARDING;
case MMI_SC_PIN:
return MMI_KS_SC_PIN;
case MMI_SC_PIN2:
return MMI_KS_SC_PIN2;
case MMI_SC_PUK:
return MMI_KS_SC_PUK;
case MMI_SC_PUK2:
return MMI_KS_SC_PUK2;
case MMI_SC_IMEI:
return MMI_KS_SC_IMEI;
case MMI_SC_CLIP:
return MMI_KS_SC_CLIP;
case MMI_SC_CLIR:
return MMI_KS_SC_CLIR;
case MMI_SC_BAOC:
case MMI_SC_BAOIC:
case MMI_SC_BAOICxH:
case MMI_SC_BAIC:
case MMI_SC_BAICr:
case MMI_SC_BA_ALL:
case MMI_SC_BA_MO:
case MMI_SC_BA_MT:
return MMI_KS_SC_CALL_BARRING;
case MMI_SC_CALL_WAITING:
return MMI_SC_CALL_WAITING;
default:
return MMI_KS_SC_USSD;
}
},
sendMMI: function(options) {
if (DEBUG) {
this.context.debug("SendMMI " + JSON.stringify(options));
}
let mmiString = options.mmi;
let mmi = this._parseMMI(mmiString);
let _sendMMIError = (function(errorMsg, mmiServiceCode) {
let mmi = this._parseMMI(options.mmi);
if (DEBUG) {
this.context.debug("MMI " + JSON.stringify(mmi));
}
let _sendMMIError = (function(errorMsg) {
options.success = false;
options.errorMsg = errorMsg;
if (mmiServiceCode) {
options.mmiServiceCode = mmiServiceCode;
}
this.sendChromeMessage(options);
}).bind(this);
function _isValidPINPUKRequest(mmiServiceCode) {
// It's neither a valid mmi code nor an ongoing ussd.
if (!mmi && !this._ussdSession) {
_sendMMIError(MMI_ERROR_KS_ERROR);
return;
}
options.mmiServiceCode = mmi ?
this._serviceCodeToKeyString(mmi.serviceCode) : MMI_KS_SC_USSD;
function _isValidPINPUKRequest() {
// The only allowed MMI procedure for ICC PIN, PIN2, PUK and PUK2 handling
// is "Registration" (**).
if (!mmi.procedure || mmi.procedure != MMI_PROCEDURE_REGISTRATION ) {
_sendMMIError(MMI_ERROR_KS_INVALID_ACTION, mmiServiceCode);
if (mmi.procedure != MMI_PROCEDURE_REGISTRATION ) {
_sendMMIError(MMI_ERROR_KS_INVALID_ACTION);
return false;
}
if (!mmi.sia || !mmi.sia.length || !mmi.sib || !mmi.sib.length ||
!mmi.sic || !mmi.sic.length) {
_sendMMIError(MMI_ERROR_KS_ERROR, mmiServiceCode);
return false;
}
if (mmi.sib != mmi.sic) {
_sendMMIError(MMI_ERROR_KS_MISMATCH_PIN, mmiServiceCode);
if (!mmi.sia || !mmi.sib || !mmi.sic) {
_sendMMIError(MMI_ERROR_KS_ERROR);
return false;
}
if (mmi.sia.length < 4 || mmi.sia.length > 8 ||
mmi.sib.length < 4 || mmi.sib.length > 8 ||
mmi.sic.length < 4 || mmi.sic.length > 8) {
_sendMMIError(MMI_ERROR_KS_INVALID_PIN, mmiServiceCode);
_sendMMIError(MMI_ERROR_KS_INVALID_PIN);
return false;
}
if (mmi.sib != mmi.sic) {
_sendMMIError(MMI_ERROR_KS_MISMATCH_PIN);
return false;
}
return true;
}
let _isRadioAvailable = (function(mmiServiceCode) {
let _isRadioAvailable = (function() {
if (this.radioState !== GECKO_RADIOSTATE_READY) {
_sendMMIError(GECKO_ERROR_RADIO_NOT_AVAILABLE, mmiServiceCode);
_sendMMIError(GECKO_ERROR_RADIO_NOT_AVAILABLE);
return false;
}
return true;
}).bind(this);
// If we couldn't parse the MMI code, we'll send it as an USSD request.
if (mmi === null) {
if (this._ussdSession) {
if (!_isRadioAvailable(MMI_KS_SC_USSD)) {
return;
}
options.ussd = mmiString;
this.sendUSSD(options);
return;
}
_sendMMIError(MMI_ERROR_KS_ERROR);
return;
}
if (DEBUG) {
this.context.debug("MMI " + JSON.stringify(mmi));
}
// We check if the MMI service code is supported and in that case we
// trigger the appropriate RIL request if possible.
let sc = mmi.serviceCode;
@ -2612,14 +2640,13 @@ RilObject.prototype = {
case MMI_SC_CF_NOT_REACHABLE:
case MMI_SC_CF_ALL:
case MMI_SC_CF_ALL_CONDITIONAL:
if (!_isRadioAvailable(MMI_KS_SC_CALL_FORWARDING)) {
if (!_isRadioAvailable()) {
return;
}
// Call forwarding requires at least an action, given by the MMI
// procedure, and a reason, given by the MMI service code, but there
// is no way that we get this far without a valid procedure or service
// code.
options.mmiServiceCode = MMI_KS_SC_CALL_FORWARDING;
options.action = MMI_PROC_TO_CF_ACTION[mmi.procedure];
options.reason = MMI_SC_TO_CF_REASON[sc];
options.number = mmi.sia;
@ -2640,12 +2667,10 @@ RilObject.prototype = {
// an MMI code of the form **04*OLD_PIN*NEW_PIN*NEW_PIN#, where old PIN
// should be entered as the SIA parameter and the new PIN as SIB and
// SIC.
if (!_isRadioAvailable(MMI_KS_SC_PIN) ||
!_isValidPINPUKRequest(MMI_KS_SC_PIN)) {
if (!_isRadioAvailable() || !_isValidPINPUKRequest()) {
return;
}
options.mmiServiceCode = MMI_KS_SC_PIN;
options.pin = mmi.sia;
options.newPin = mmi.sib;
this.changeICCPIN(options);
@ -2657,12 +2682,10 @@ RilObject.prototype = {
// enter and MMI code of the form **042*OLD_PIN2*NEW_PIN2*NEW_PIN2#,
// where the old PIN2 should be entered as the SIA parameter and the
// new PIN2 as SIB and SIC.
if (!_isRadioAvailable(MMI_KS_SC_PIN2) ||
!_isValidPINPUKRequest(MMI_KS_SC_PIN2)) {
if (!_isRadioAvailable() || !_isValidPINPUKRequest()) {
return;
}
options.mmiServiceCode = MMI_KS_SC_PIN2;
options.pin = mmi.sia;
options.newPin = mmi.sib;
this.changeICCPIN2(options);
@ -2674,12 +2697,10 @@ RilObject.prototype = {
// enter an MMI code of the form **05*PUK*NEW_PIN*NEW_PIN#, where PUK
// should be entered as the SIA parameter and the new PIN as SIB and
// SIC.
if (!_isRadioAvailable(MMI_KS_SC_PUK) ||
!_isValidPINPUKRequest(MMI_KS_SC_PUK)) {
if (!_isRadioAvailable() || !_isValidPINPUKRequest()) {
return;
}
options.mmiServiceCode = MMI_KS_SC_PUK;
options.puk = mmi.sia;
options.newPin = mmi.sib;
this.enterICCPUK(options);
@ -2691,12 +2712,10 @@ RilObject.prototype = {
// enter an MMI code of the form **052*PUK2*NEW_PIN2*NEW_PIN2#, where
// PUK2 should be entered as the SIA parameter and the new PIN2 as SIB
// and SIC.
if (!_isRadioAvailable(MMI_KS_SC_PUK2) ||
!_isValidPINPUKRequest(MMI_KS_SC_PUK2)) {
if (!_isRadioAvailable() || !_isValidPINPUKRequest()) {
return;
}
options.mmiServiceCode = MMI_KS_SC_PUK2;
options.puk = mmi.sia;
options.newPin = mmi.sib;
this.enterICCPUK2(options);
@ -2710,7 +2729,6 @@ RilObject.prototype = {
return;
}
// If we already had the device's IMEI, we just send it to chrome.
options.mmiServiceCode = MMI_KS_SC_IMEI;
options.success = true;
options.statusMessage = this.IMEI;
this.sendChromeMessage(options);
@ -2718,12 +2736,11 @@ RilObject.prototype = {
// CLIP
case MMI_SC_CLIP:
options.mmiServiceCode = MMI_KS_SC_CLIP;
options.procedure = mmi.procedure;
if (options.procedure === MMI_PROCEDURE_INTERROGATION) {
this.queryCLIP(options);
} else {
_sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED, MMI_KS_SC_CLIP);
_sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED);
}
return;
@ -2732,7 +2749,6 @@ RilObject.prototype = {
// point in the future. In the mean time we handle temporary CLIR MMI
// commands through the dial() function. Please see bug 889737.
case MMI_SC_CLIR:
options.mmiServiceCode = MMI_KS_SC_CLIR;
options.procedure = mmi.procedure;
switch (options.procedure) {
case MMI_PROCEDURE_INTERROGATION:
@ -2745,7 +2761,7 @@ RilObject.prototype = {
options.clirMode = CLIR_SUPPRESSION;
break;
default:
_sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED, MMI_KS_SC_CLIR);
_sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED);
return;
}
options.isSetCLIR = true;
@ -2761,7 +2777,6 @@ RilObject.prototype = {
case MMI_SC_BA_ALL:
case MMI_SC_BA_MO:
case MMI_SC_BA_MT:
options.mmiServiceCode = MMI_KS_SC_CALL_BARRING;
options.password = mmi.sia || "";
options.serviceClass = this._siToServiceClass(mmi.sib);
options.facility = MMI_SC_TO_CB_FACILITY[sc];
@ -2775,7 +2790,7 @@ RilObject.prototype = {
} else if (mmi.procedure === MMI_PROCEDURE_DEACTIVATION) {
options.enabled = 0;
} else {
_sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED, MMI_KS_SC_CALL_BARRING);
_sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED);
return;
}
this.setICCFacilityLock(options);
@ -2783,11 +2798,10 @@ RilObject.prototype = {
// Call waiting
case MMI_SC_CALL_WAITING:
if (!_isRadioAvailable(MMI_KS_SC_CALL_WAITING)) {
if (!_isRadioAvailable()) {
return;
}
options.mmiServiceCode = MMI_KS_SC_CALL_WAITING;
if (mmi.procedure === MMI_PROCEDURE_INTERROGATION) {
this._handleQueryMMICallWaiting(options);
@ -2799,7 +2813,7 @@ RilObject.prototype = {
} else if (mmi.procedure === MMI_PROCEDURE_DEACTIVATION) {
options.enabled = false;
} else {
_sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED, MMI_KS_SC_CALL_WAITING);
_sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED);
return;
}
@ -2808,22 +2822,13 @@ RilObject.prototype = {
return;
}
// If the MMI code is not a known code and is a recognized USSD request,
// it shall still be sent as a USSD request.
if (mmi.fullMMI) {
if (!_isRadioAvailable(MMI_KS_SC_USSD)) {
return;
}
options.ussd = mmi.fullMMI;
options.mmiServiceCode = MMI_KS_SC_USSD;
this.sendUSSD(options);
// If the MMI code is not a known code, it is treated as an ussd.
if (!_isRadioAvailable()) {
return;
}
// At this point, the MMI string is considered as not valid MMI code and
// not valid USSD code.
_sendMMIError(MMI_ERROR_KS_ERROR);
options.ussd = mmi.fullMMI;
this.sendUSSD(options);
},
/**
@ -2844,7 +2849,6 @@ RilObject.prototype = {
* Cancel pending USSD.
*/
cancelUSSD: function(options) {
options.mmiServiceCode = MMI_KS_SC_USSD;
this.context.Buf.simpleRequest(REQUEST_CANCEL_USSD, options);
},
@ -6056,7 +6060,6 @@ RilObject.prototype[REQUEST_GET_IMEI] = function REQUEST_GET_IMEI(length, option
return;
}
options.mmiServiceCode = MMI_KS_SC_IMEI;
options.success = (options.rilRequestError === 0);
options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
if ((!options.success || this.IMEI == null) && !options.errorMsg) {
@ -14174,9 +14177,8 @@ SimRecordHelperObject.prototype = {
ICCIOHelper.loadNextRecord(options);
} else {
RIL.iccInfoPrivate.OPL = opl;
RIL.overrideICCNetworkName();
}
RIL.overrideICCNetworkName();
}
ICCIOHelper.loadLinearFixedEF({fileId: ICC_EF_OPL,
@ -14245,9 +14247,8 @@ SimRecordHelperObject.prototype = {
}
}
RIL.iccInfoPrivate.PNN = pnn;
RIL.overrideICCNetworkName();
}
RIL.overrideICCNetworkName();
}
let pnn = [];

View File

@ -459,6 +459,19 @@ TelephonyService.prototype = {
return hasConference ? numCalls + 1 : numCalls;
},
/**
* Get arbitrary one of active call.
*/
_getOneActiveCall: function(aClientId) {
for (let index in this._currentCalls[aClientId]) {
let call = this._currentCalls[aClientId][index];
if (call.state === nsITelephonyService.CALL_STATE_CONNECTED) {
return call;
}
}
return null;
},
_addCdmaChildCall: function(aClientId, aNumber, aParentId) {
let childCall = {
callIndex: CDMA_SECOND_CALL_INDEX,
@ -509,6 +522,7 @@ TelephonyService.prototype = {
});
},
cachedDialRequest: null,
isDialing: false,
dial: function(aClientId, aNumber, aIsDialEmergency, aCallback) {
@ -563,7 +577,25 @@ TelephonyService.prototype = {
}
}
this._dialInternal(aClientId, options, aCallback);
// Before we dial, we have to hold the active call first.
let activeCall = this._getOneActiveCall(aClientId);
if (!activeCall) {
this._dialInternal(aClientId, options, aCallback);
} else {
if (DEBUG) debug("There is an active call. Hold it first before dial.");
this.cachedDialRequest = {
clientId: aClientId,
options: options,
callback: aCallback
};
if (activeCall.isConference) {
this.holdConference(aClientId);
} else {
this.holdCall(aClientId, activeCall.callIndex);
}
}
}, cause => {
aCallback.notifyDialError(DIAL_ERROR_BAD_NUMBER);
});
@ -588,7 +620,7 @@ TelephonyService.prototype = {
} else {
// RIL doesn't hold the 2nd call. We create one by ourselves.
aCallback.notifyDialSuccess(CDMA_SECOND_CALL_INDEX, response.number);
this._addCdmaChildCall(aClientId, aNumber, currentCdmaCallIndex);
this._addCdmaChildCall(aClientId, response.number, currentCdmaCallIndex);
}
});
},
@ -899,6 +931,15 @@ TelephonyService.prototype = {
this._currentCalls[aClientId][aCall.callIndex] = call;
}
// Handle cached dial request.
if (this.cachedDialRequest && !this._getOneActiveCall()) {
if (DEBUG) debug("All calls held. Perform the cached dial request.");
let request = this.cachedDialRequest;
this._dialInternal(request.clientId, request.options, request.callback);
this.cachedDialRequest = null;
}
this._notifyAllListeners("callStateChanged", [aClientId,
call.callIndex,
call.state,

View File

@ -64,3 +64,4 @@ disabled = Bug 821958
[test_temporary_clir.js]
[test_outgoing_error_state.js]
[test_mmi_code.js]
[test_outgoing_auto_hold.js]

View File

@ -0,0 +1,52 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
function testAutoHoldCall() {
let outCall1;
let outCall2;
return gDial("0900000001")
.then(call => { outCall1 = call; })
.then(() => gRemoteAnswer(outCall1))
.then(() => {
is(outCall1.state, "connected");
})
.then(() => gDial("0900000002"))
.then(call => { outCall2 = call; })
.then(() => {
is(outCall1.state, "held");
})
.then(() => gRemoteHangUpCalls([outCall1, outCall2]));
}
function testAutoHoldConferenceCall() {
let subCall1;
let subCall2;
let outCall;
return gSetupConference(["0900000001", "0900000002"])
.then(calls => {
[subCall1, subCall2] = calls;
is(conference.state, "connected");
})
.then(() => gDial("0900000003"))
.then(call => { outCall = call; })
.then(() => {
is(subCall1.state, "held");
is(subCall2.state, "held");
is(conference.state, "held");
})
.then(() => gRemoteHangUpCalls([subCall1, subCall2, outCall]));
}
startTest(function() {
testAutoHoldCall()
.then(() => testAutoHoldConferenceCall())
.then(null, () => {
ok(false, 'promise rejects during test.');
})
.then(finish);
});

View File

@ -14,7 +14,7 @@ function runAJAXTest() {
function onManifestLoad(manifest) {
if (manifest.testcases) {
AJAXtests = manifest.testcases;
runAJAXTest();
runAJAXTest();
} else {
ok(false, "manifest check", "no manifest!?!");
SimpleTest.finish();
@ -26,5 +26,10 @@ function fetchManifest() {
d.addBoth(onManifestLoad);
}
// Double timeout duration. Since this test case takes longer than 300 seconds
// on B2G emulator.
// See bug 968783.
SimpleTest.requestLongerTimeout(2);
SimpleTest.waitForExplicitFinish();
addLoadEvent(fetchManifest);

View File

@ -15,6 +15,8 @@
#include "nsISupportsImpl.h"
#include "nsContentUtils.h"
#include "prprf.h"
// Undo the damage done by mozzconf.h
#undef compress
@ -1124,7 +1126,7 @@ MessageChannel::DispatchSyncMessage(const Message& aMsg)
Result rv = mListener->OnMessageReceived(aMsg, reply);
mDispatchingSyncMessage = false;
if (!MaybeHandleError(rv, "DispatchSyncMessage")) {
if (!MaybeHandleError(rv, aMsg, "DispatchSyncMessage")) {
delete reply;
reply = new Message();
reply->set_sync();
@ -1182,7 +1184,7 @@ MessageChannel::DispatchUrgentMessage(const Message& aMsg)
mDispatchingUrgentMessageCount--;
gDispatchingUrgentMessageCount--;
if (!MaybeHandleError(rv, "DispatchUrgentMessage")) {
if (!MaybeHandleError(rv, aMsg, "DispatchUrgentMessage")) {
delete reply;
reply = new Message();
reply->set_urgent();
@ -1204,7 +1206,7 @@ MessageChannel::DispatchRPCMessage(const Message& aMsg)
Message *reply = nullptr;
if (!MaybeHandleError(mListener->OnCallReceived(aMsg, reply), "DispatchRPCMessage")) {
if (!MaybeHandleError(mListener->OnCallReceived(aMsg, reply), aMsg, "DispatchRPCMessage")) {
delete reply;
reply = new Message();
reply->set_rpc();
@ -1228,7 +1230,7 @@ MessageChannel::DispatchAsyncMessage(const Message& aMsg)
NS_RUNTIMEABORT("unhandled special message!");
}
MaybeHandleError(mListener->OnMessageReceived(aMsg), "DispatchAsyncMessage");
MaybeHandleError(mListener->OnMessageReceived(aMsg), aMsg, "DispatchAsyncMessage");
}
void
@ -1296,7 +1298,7 @@ MessageChannel::DispatchInterruptMessage(const Message& aMsg, size_t stackDepth)
Result rv = mListener->OnCallReceived(aMsg, reply);
--mRemoteStackDepthGuess;
if (!MaybeHandleError(rv, "DispatchInterruptMessage")) {
if (!MaybeHandleError(rv, aMsg, "DispatchInterruptMessage")) {
delete reply;
reply = new Message();
reply->set_interrupt();
@ -1556,7 +1558,7 @@ MessageChannel::ReportConnectionError(const char* aChannelName) const
}
bool
MessageChannel::MaybeHandleError(Result code, const char* channelName)
MessageChannel::MaybeHandleError(Result code, const Message& aMsg, const char* channelName)
{
if (MsgProcessed == code)
return true;
@ -1587,7 +1589,12 @@ MessageChannel::MaybeHandleError(Result code, const char* channelName)
return false;
}
PrintErrorMessage(mSide, channelName, errorMsg);
char printedMsg[512];
PR_snprintf(printedMsg, sizeof(printedMsg),
"(msgtype=0x%lX,name=%s) %s",
aMsg.type(), aMsg.name(), errorMsg);
PrintErrorMessage(mSide, channelName, printedMsg);
mListener->OnProcessingError(code);

View File

@ -205,7 +205,7 @@ class MessageChannel : HasResultCodes
void OnNotifyMaybeChannelError();
void ReportConnectionError(const char* aChannelName) const;
void ReportMessageRouteError(const char* channelName) const;
bool MaybeHandleError(Result code, const char* channelName);
bool MaybeHandleError(Result code, const Message& aMsg, const char* channelName);
void Clear();

View File

@ -3039,6 +3039,7 @@ nsDocumentViewer::SetFullZoom(float aFullZoom)
return NS_ERROR_FAILURE;
}
bool fullZoomChange = (mPageZoom != aFullZoom);
mPageZoom = aFullZoom;
struct ZoomInfo ZoomInfo = { aFullZoom };
@ -3052,9 +3053,12 @@ nsDocumentViewer::SetFullZoom(float aFullZoom)
// And do the external resources
mDocument->EnumerateExternalResources(SetExtResourceFullZoom, &ZoomInfo);
nsContentUtils::DispatchChromeEvent(mDocument, static_cast<nsIDocument*>(mDocument),
NS_LITERAL_STRING("FullZoomChange"),
true, true);
// Dispatch FullZoomChange event only if fullzoom value really was been changed
if (fullZoomChange) {
nsContentUtils::DispatchChromeEvent(mDocument, static_cast<nsIDocument*>(mDocument),
NS_LITERAL_STRING("FullZoomChange"),
true, true);
}
return NS_OK;
}

View File

@ -1161,10 +1161,10 @@ random == 445004-1.html 445004-1-ref.html # bug 472268
skip-if(B2G) fails-if(Android) == 446100-1b.html about:blank
skip-if(B2G) fails-if(Android) == 446100-1c.html about:blank
== 446100-1d.html about:blank
skip-if(B2G&&browserIsRemote) == 446100-1e.html about:blank
skip-if(B2G&&browserIsRemote) == 446100-1f.html about:blank
== 446100-1e.html about:blank
== 446100-1f.html about:blank
skip-if(B2G) fails-if(Android) == 446100-1g.html about:blank
skip-if(B2G&&browserIsRemote) == 446100-1h.html about:blank
== 446100-1h.html about:blank
skip-if(B2G) == 447749-1.html 447749-1-ref.html
fuzzy(127,2) == 448193.html 448193-ref.html
!= 449149-1a.html about:blank
@ -1633,8 +1633,8 @@ fails-if(Android&&AndroidVersion!=10&&AndroidVersion!=17) == 617242-1.html 61724
!= 618071.html 618071-notref.html
== 619117-1.html 619117-1-ref.html
HTTP(..) == 619511-1.html 619511-1-ref.html
skip-if(Android||(B2G&&browserIsRemote)) HTTP(..) == 621253-1-externalFilter.html 621253-1-ref.html
skip-if(Android||(B2G&&browserIsRemote)) == 621253-1-internalFilter.html 621253-1-ref.html
skip-if(Android) HTTP(..) == 621253-1-externalFilter.html 621253-1-ref.html
skip-if(Android) == 621253-1-internalFilter.html 621253-1-ref.html
HTTP(..) == 621253-2-externalFilter.html 621253-2-ref.html
== 621253-2-internalFilter.html 621253-2-ref.html
skip-if(B2G) random-if(winWidget) fuzzy-if(OSX==10.8,19,17) == 621918-1.svg 621918-1-ref.svg # 1-pixel diacritic positioning discrepancy in rotated text (may depend on platform fonts)

View File

@ -15,10 +15,10 @@ fuzzy-if(Android,4,34) == image-opacity-02.svg image-opacity-02-ref.svg # Bug 77
== image-svg-inline-zoom-in-01b.html ../pass.svg
== image-svg-inline-zoom-in-01c.html ../pass.svg
== image-svg-inline-zoom-in-01d.html ../pass.svg
skip-if(B2G&&browserIsRemote) == image-svg-inline-zoom-out-01a.html ../pass.svg
skip-if(B2G&&browserIsRemote) == image-svg-inline-zoom-out-01b.html ../pass.svg
skip-if(B2G&&browserIsRemote) == image-svg-inline-zoom-out-01c.html ../pass.svg
skip-if(B2G&&browserIsRemote) == image-svg-inline-zoom-out-01d.html ../pass.svg
== image-svg-inline-zoom-out-01a.html ../pass.svg
== image-svg-inline-zoom-out-01b.html ../pass.svg
== image-svg-inline-zoom-out-01c.html ../pass.svg
== image-svg-inline-zoom-out-01d.html ../pass.svg
== image-svg-inline-sprite-zoom-in-01a.html image-svg-inline-sprite-zoom-in-01-ref.html
== image-svg-inline-sprite-zoom-in-01b.html image-svg-inline-sprite-zoom-in-01-ref.html
== image-svg-inline-sprite-zoom-out-01a.html image-svg-inline-sprite-zoom-out-01-ref.html

View File

@ -230,6 +230,11 @@
</intent-filter>
</activity-alias>
<activity android:name="org.mozilla.gecko.StartPane"
android:theme="@style/GeckoStartPane"
android:excludeFromRecents="true"
android:noHistory="true" />
<activity android:name="org.mozilla.gecko.webapp.Dispatcher"
android:noHistory="true" >
<intent-filter>

View File

@ -77,10 +77,12 @@ import org.mozilla.gecko.widget.GeckoActionProvider;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@ -99,6 +101,7 @@ import android.nfc.NfcEvent;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.InputDevice;
@ -146,6 +149,7 @@ public class BrowserApp extends GeckoApp
// Request ID for startActivityForResult.
private static final int ACTIVITY_REQUEST_PREFERENCES = 1001;
public static final String ACTION_NEW_PROFILE = "org.mozilla.gecko.NEW_PROFILE";
private BrowserSearch mBrowserSearch;
private View mBrowserSearchContainer;
@ -209,6 +213,8 @@ public class BrowserApp extends GeckoApp
private OrderedBroadcastHelper mOrderedBroadcastHelper;
private BroadcastReceiver mOnboardingReceiver;
private BrowserHealthReporter mBrowserHealthReporter;
// The tab to be selected on editing mode exit.
@ -551,6 +557,8 @@ public class BrowserApp extends GeckoApp
"Updater:Launch",
"BrowserToolbar:Visibility");
registerOnboardingReceiver(this);
Distribution distribution = Distribution.init(this);
// Init suggested sites engine in BrowserDB.
@ -604,6 +612,25 @@ public class BrowserApp extends GeckoApp
}
}
private void registerOnboardingReceiver(Context context) {
final LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
// Receiver for launching first run start pane on new profile creation.
mOnboardingReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
launchStartPane(BrowserApp.this);
}
};
lbm.registerReceiver(mOnboardingReceiver, new IntentFilter(ACTION_NEW_PROFILE));
}
private void launchStartPane(Context context) {
final Intent startIntent = new Intent(context, StartPane.class);
context.startActivity(startIntent);
}
private Class<?> getMediaPlayerManager() {
if (AppConstants.MOZ_MEDIA_PLAYER) {
try {
@ -650,6 +677,9 @@ public class BrowserApp extends GeckoApp
// Register for Prompt:ShowTop so we can foreground this activity even if it's hidden.
EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener)this,
"Prompt:ShowTop");
final LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
lbm.unregisterReceiver(mOnboardingReceiver);
}
private void setBrowserToolbarListeners() {

View File

@ -45,6 +45,9 @@ class ChromeCast implements GeckoMediaPlayer {
private GoogleApiClient apiClient;
private RemoteMediaPlayer remoteMediaPlayer;
private boolean canMirror;
private String mSessionId;
private MirrorChannel mMirrorChannel;
private boolean mApplicationStarted = false;
// Callback to start playback of a url on a remote device
private class VideoPlayCallback implements ResultCallback<ApplicationConnectionResult>,
@ -90,6 +93,7 @@ class ChromeCast implements GeckoMediaPlayer {
remoteMediaPlayer = new RemoteMediaPlayer();
remoteMediaPlayer.setOnStatusUpdatedListener(this);
remoteMediaPlayer.setOnMetadataUpdatedListener(this);
mSessionId = result.getSessionId();
try {
Cast.CastApi.setMessageReceivedCallbacks(apiClient, remoteMediaPlayer.getNamespace(), remoteMediaPlayer);
@ -99,7 +103,7 @@ class ChromeCast implements GeckoMediaPlayer {
startPlayback();
} else {
callback.sendError(null);
callback.sendError(status.toString());
}
}
@ -122,7 +126,7 @@ class ChromeCast implements GeckoMediaPlayer {
}
debug("Media load failed " + result.getStatus());
callback.sendError(null);
callback.sendError(result.getStatus().toString());
}
});
@ -133,7 +137,7 @@ class ChromeCast implements GeckoMediaPlayer {
debug("Problem opening media during loading", e);
}
callback.sendError(null);
callback.sendError("");
}
}
@ -188,6 +192,8 @@ class ChromeCast implements GeckoMediaPlayer {
@Override
public void onConnected(Bundle connectionHint) {
if (!apiClient.isConnected()) {
debug("Connection failed");
callback.sendError("Not connected");
return;
}
@ -219,14 +225,34 @@ class ChromeCast implements GeckoMediaPlayer {
callback.sendSuccess(null);
}
public boolean verifySession(final EventCallback callback) {
if (apiClient == null || !apiClient.isConnected()) {
debug("Can't play. No connection");
callback.sendError("Not connected");
return false;
}
if (mSessionId == null) {
debug("Can't play. No session");
callback.sendError("No session");
return false;
}
return true;
}
public void play(final EventCallback callback) {
if (!verifySession(callback)) {
return;
}
remoteMediaPlayer.play(apiClient).setResultCallback(new ResultCallback<MediaChannelResult>() {
@Override
public void onResult(MediaChannelResult result) {
Status status = result.getStatus();
if (!status.isSuccess()) {
debug("Unable to toggle pause: " + status.getStatusCode());
callback.sendError(null);
debug("Unable to play: " + status.getStatusCode());
callback.sendError(status.toString());
} else {
callback.sendSuccess(null);
}
@ -235,13 +261,17 @@ class ChromeCast implements GeckoMediaPlayer {
}
public void pause(final EventCallback callback) {
if (!verifySession(callback)) {
return;
}
remoteMediaPlayer.pause(apiClient).setResultCallback(new ResultCallback<MediaChannelResult>() {
@Override
public void onResult(MediaChannelResult result) {
Status status = result.getStatus();
if (!status.isSuccess()) {
debug("Unable to toggle pause: " + status.getStatusCode());
callback.sendError(null);
debug("Unable to pause: " + status.getStatusCode());
callback.sendError(status.toString());
} else {
callback.sendSuccess(null);
}
@ -250,6 +280,10 @@ class ChromeCast implements GeckoMediaPlayer {
}
public void end(final EventCallback callback) {
if (!verifySession(callback)) {
return;
}
Cast.CastApi.stopApplication(apiClient).setResultCallback(new ResultCallback<Status>() {
@Override
public void onResult(Status result) {
@ -257,6 +291,7 @@ class ChromeCast implements GeckoMediaPlayer {
try {
Cast.CastApi.removeMessageReceivedCallbacks(apiClient, remoteMediaPlayer.getNamespace());
remoteMediaPlayer = null;
mSessionId = null;
apiClient.disconnect();
apiClient = null;
@ -271,18 +306,13 @@ class ChromeCast implements GeckoMediaPlayer {
}
if (callback != null) {
callback.sendError(null);
callback.sendError(result.getStatus().toString());
}
}
});
}
private String mSessionId;
MirrorChannel mMirrorChannel;
boolean mApplicationStarted = false;
class MirrorChannel implements MessageReceivedCallback {
/**
* @return custom namespace
*/
@ -347,7 +377,7 @@ class ChromeCast implements GeckoMediaPlayer {
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Casting:Mirror", route.getId()));
} else {
callback.sendError(null);
callback.sendError(status.toString());
}
}
}

View File

@ -6,9 +6,6 @@
package org.mozilla.gecko;
import org.mozilla.gecko.mozglue.JNITarget;
import org.mozilla.gecko.util.NativeEventListener;
import org.mozilla.gecko.util.NativeJSObject;
import org.mozilla.gecko.util.EventCallback;
import android.content.BroadcastReceiver;
import android.content.Context;
@ -34,7 +31,7 @@ import android.util.Log;
* connection type defined in Network Information API version 3.
*/
public class GeckoNetworkManager extends BroadcastReceiver implements NativeEventListener {
public class GeckoNetworkManager extends BroadcastReceiver {
private static final String LOGTAG = "GeckoNetworkManager";
static private final GeckoNetworkManager sInstance = new GeckoNetworkManager();
@ -96,8 +93,6 @@ public class GeckoNetworkManager extends BroadcastReceiver implements NativeEven
if (mShouldNotify) {
startListening();
}
EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener)this, "Wifi:Enable");
}
private void startListening() {
@ -117,25 +112,6 @@ public class GeckoNetworkManager extends BroadcastReceiver implements NativeEven
if (mShouldNotify) {
stopListening();
}
EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener)this, "Wifi:Enable");
}
@Override
public void handleMessage(final String event, final NativeJSObject message,
final EventCallback callback) {
if (event.equals("Wifi:Enable")) {
final WifiManager mgr = (WifiManager) mApplicationContext.getSystemService(Context.WIFI_SERVICE);
if (!mgr.isWifiEnabled()) {
mgr.setWifiEnabled(true);
} else {
// If Wifi is enabled, maybe you need to select a network
Intent intent = new Intent(android.provider.Settings.ACTION_WIFI_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mApplicationContext.startActivity(intent);
}
}
}
private void stopListening() {

View File

@ -25,6 +25,8 @@ import org.mozilla.gecko.util.INISection;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
@ -656,6 +658,10 @@ public final class GeckoProfile {
Log.w(LOGTAG, "Couldn't write times.json.", e);
}
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(mApplicationContext);
final Intent intent = new Intent(BrowserApp.ACTION_NEW_PROFILE);
lbm.sendBroadcast(intent);
return profileDir;
}

View File

@ -0,0 +1,49 @@
package org.mozilla.gecko;
import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class StartPane extends Activity {
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.onboard_start_pane);
final Button accountButton = (Button) findViewById(R.id.button_account);
accountButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
showAccountSetup();
}
});
final Button browserButton = (Button) findViewById(R.id.button_browser);
browserButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
showBrowser();
}
});
}
private void showBrowser() {
// StartPane is on the stack above the browser, so just kill this activity.
finish();
}
private void showAccountSetup() {
final Intent intent = new Intent(this, FxAccountGetStartedActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}
}

View File

@ -543,6 +543,7 @@ sync_java_files = [
'browserid/verifier/BrowserIDVerifierDelegate.java',
'browserid/verifier/BrowserIDVerifierException.java',
'browserid/VerifyingPublicKey.java',
'fxa/AccountLoader.java',
'fxa/activities/FxAccountAbstractActivity.java',
'fxa/activities/FxAccountAbstractSetupActivity.java',
'fxa/activities/FxAccountConfirmAccountActivity.java',

View File

@ -0,0 +1,184 @@
/* 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/. */
package org.mozilla.gecko.fxa;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.sync.setup.SyncAccounts;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.support.v4.content.AsyncTaskLoader;
/**
* A Loader that queries and updates based on the existence of Firefox and
* legacy Sync Android Accounts.
*
* The loader returns an Android Account (of either Account type) if an account
* exists, and null to indicate no Account is present.
*
* The loader listens for Accounts added and deleted, and also Accounts being
* updated by Sync or another Activity, via the use of
* {@link AndroidFxAccount#setState(org.mozilla.gecko.fxa.login.State)}.
* Be careful of message loops if you update the account state from an activity
* that uses this loader.
*
* This implementation is based on
* <a href="http://www.androiddesignpatterns.com/2012/08/implementing-loaders.html">http://www.androiddesignpatterns.com/2012/08/implementing-loaders.html</a>.
*/
public class AccountLoader extends AsyncTaskLoader<Account> {
protected Account account = null;
protected BroadcastReceiver broadcastReceiver = null;
public AccountLoader(Context context) {
super(context);
}
// Task that performs the asynchronous load **/
@Override
public Account loadInBackground() {
final Context context = getContext();
Account foundAccount = FirefoxAccounts.getFirefoxAccount(context);
if (foundAccount == null) {
final Account[] syncAccounts = SyncAccounts.syncAccounts(context);
if (syncAccounts != null && syncAccounts.length > 0) {
foundAccount = syncAccounts[0];
}
}
return foundAccount;
}
// Deliver the results to the registered listener.
@Override
public void deliverResult(Account data) {
if (isReset()) {
// The Loader has been reset; ignore the result and invalidate the data.
releaseResources(data);
return;
}
// Hold a reference to the old data so it doesn't get garbage collected.
// We must protect it until the new data has been delivered.
Account oldData = account;
account = data;
if (isStarted()) {
// If the Loader is in a started state, deliver the results to the
// client. The superclass method does this for us.
super.deliverResult(data);
}
// Invalidate the old data as we don't need it any more.
if (oldData != null && oldData != data) {
releaseResources(oldData);
}
}
// The Loaders state-dependent behavior.
@Override
protected void onStartLoading() {
if (account != null) {
// Deliver any previously loaded data immediately.
deliverResult(account);
}
// Begin monitoring the underlying data source.
if (broadcastReceiver == null) {
broadcastReceiver = makeNewObserver();
registerObserver(broadcastReceiver);
}
if (takeContentChanged() || account == null) {
// When the observer detects a change, it should call onContentChanged()
// on the Loader, which will cause the next call to takeContentChanged()
// to return true. If this is ever the case (or if the current data is
// null), we force a new load.
forceLoad();
}
}
@Override
protected void onStopLoading() {
// The Loader is in a stopped state, so we should attempt to cancel the
// current load (if there is one).
cancelLoad();
// Note that we leave the observer as is. Loaders in a stopped state
// should still monitor the data source for changes so that the Loader
// will know to force a new load if it is ever started again.
}
@Override
protected void onReset() {
// Ensure the loader has been stopped. In CursorLoader and the template
// this code follows (see the class comment), this is onStopLoading, which
// appears to not set the started flag (see Loader itself).
stopLoading();
// At this point we can release the resources associated with 'mData'.
if (account != null) {
releaseResources(account);
account = null;
}
// The Loader is being reset, so we should stop monitoring for changes.
if (broadcastReceiver != null) {
final BroadcastReceiver observer = broadcastReceiver;
broadcastReceiver = null;
unregisterObserver(observer);
}
}
@Override
public void onCanceled(Account data) {
// Attempt to cancel the current asynchronous load.
super.onCanceled(data);
// The load has been canceled, so we should release the resources
// associated with 'data'.
releaseResources(data);
}
private void releaseResources(Account data) {
// For a simple List, there is nothing to do. For something like a Cursor, we
// would close it in this method. All resources associated with the Loader
// should be released here.
}
// Observer which receives notifications when the data changes.
protected BroadcastReceiver makeNewObserver() {
final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Must be called on the main thread of the process. We register the
// broadcast receiver with a null Handler (see registerObserver), which
// ensures we're on the main thread when we receive this intent.
onContentChanged();
}
};
return broadcastReceiver;
}
protected void registerObserver(BroadcastReceiver observer) {
final IntentFilter intentFilter = new IntentFilter();
// Android Account added or removed.
intentFilter.addAction(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
// Firefox Account internal state changed.
intentFilter.addAction(FxAccountConstants.ACCOUNT_STATE_CHANGED_ACTION);
// null means: "the main thread of the process will be used." We must call
// onContentChanged on the main thread of the process; this ensures we do.
final Handler handler = null;
getContext().registerReceiver(observer, intentFilter, FxAccountConstants.PER_ACCOUNT_TYPE_PERMISSION, handler);
}
protected void unregisterObserver(BroadcastReceiver observer) {
getContext().unregisterReceiver(observer);
}
}

View File

@ -67,4 +67,14 @@ public class FxAccountConstants {
* received only by Firefox channels sharing the same Android Firefox Account type.
*/
public static final String PER_ACCOUNT_TYPE_PERMISSION = "@MOZ_ANDROID_SHARED_FXACCOUNT_TYPE@.permission.PER_ACCOUNT_TYPE";
/**
* This action is broadcast when an Android Firefox Account's internal state
* is changed.
* <p>
* It is protected by signing-level permission PER_ACCOUNT_TYPE_PERMISSION and
* can be received only by Firefox versions sharing the same Android Firefox
* Account type.
*/
public static final String ACCOUNT_STATE_CHANGED_ACTION = "@MOZ_ANDROID_SHARED_FXACCOUNT_TYPE@.accounts.ACCOUNT_STATE_CHANGED_ACTION";
}

View File

@ -160,7 +160,8 @@ public class FxAccountUpdateCredentialsActivity extends FxAccountAbstractSetupAc
fxAccount.dump();
}
redirectToActivity(FxAccountStatusActivity.class);
setResult(RESULT_OK);
finish();
}
}

View File

@ -24,6 +24,7 @@ import org.mozilla.gecko.fxa.login.State.StateLabel;
import org.mozilla.gecko.fxa.login.StateFactory;
import org.mozilla.gecko.sync.ExtendedJSONObject;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.setup.Constants;
import android.accounts.Account;
import android.accounts.AccountManager;
@ -483,6 +484,13 @@ public class AndroidFxAccount {
" to state " + state.getStateLabel().toString());
updateBundleValue(BUNDLE_KEY_STATE_LABEL, state.getStateLabel().name());
updateBundleValue(BUNDLE_KEY_STATE, state.toJSONObject().toJSONString());
broadcastAccountStateChangedIntent();
}
protected void broadcastAccountStateChangedIntent() {
final Intent intent = new Intent(FxAccountConstants.ACCOUNT_STATE_CHANGED_ACTION);
intent.putExtra(Constants.JSON_KEY_ACCOUNT, account.name);
context.sendBroadcast(intent, FxAccountConstants.PER_ACCOUNT_TYPE_PERMISSION);
}
public synchronized State getState() {

View File

@ -140,23 +140,10 @@ public class BookmarksPanel extends HomeFragment {
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Reattach the fragment, forcing a reinflation of its view.
// We use commitAllowingStateLoss() instead of commit() here to avoid
// an IllegalStateException. If the phone is rotated while Fennec
// is in the background, onConfigurationChanged() is fired.
// onConfigurationChanged() is called before onResume(), so
// using commit() would throw an IllegalStateException since it can't
// be used between the Activity's onSaveInstanceState() and
// onResume().
if (isVisible()) {
// The parent stack is saved just so that the folder state can be
// restored on rotation.
mSavedParentStack = mListAdapter.getParentStack();
getFragmentManager().beginTransaction()
.detach(this)
.attach(this)
.commitAllowingStateLoss();
}
}

Some files were not shown because too many files have changed in this diff Show More