mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 14:22:01 +00:00
Merge m-c to inbound. a=merge
CLOSED TREE
This commit is contained in:
commit
43bcefb0f7
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="249b8c08c1d57961ef6c905f3498fa62b032bf24"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
@ -23,7 +23,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<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="5a63e2b9f3ef85e82a33440cb73c55dff4e9bf78"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
|
||||
<!-- 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"/>
|
||||
@ -115,10 +115,10 @@
|
||||
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="f7d9bf71cf6693474f3f2a81a4ba62c0fc5646aa"/>
|
||||
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="cfcef469537869947abb9aa1d656774cc2678d4c"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="e0fc03e0a3062063c3c85996dcc881c0a49ed98d"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="48d2332e6d8400cdc0de273ceff2abe8aaababf8"/>
|
||||
<project name="platform/system/extras" path="system/extras" revision="10e78a05252b3de785f88c2d0b9ea8a428009c50"/>
|
||||
<project name="platform/system/media" path="system/media" revision="7ff72c2ea2496fa50b5e8a915e56e901c3ccd240"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8f7d94ac711af4678169805137c6c42def39b3ed"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
|
||||
<project name="platform/system/netd" path="system/netd" revision="3ae56364946d4a5bf5a5f83f12f9a45a30398e33"/>
|
||||
<project name="platform/system/security" path="system/security" revision="ee8068b9e7bfb2770635062fc9c2035be2142bd8"/>
|
||||
<project name="platform/system/vold" path="system/vold" revision="bb33b1ce8ad9cd3fc4311801b4d56db1d5c8175b"/>
|
||||
|
@ -19,7 +19,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="249b8c08c1d57961ef6c905f3498fa62b032bf24"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
|
||||
|
@ -17,10 +17,10 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="249b8c08c1d57961ef6c905f3498fa62b032bf24"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="5a63e2b9f3ef85e82a33440cb73c55dff4e9bf78"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
@ -117,10 +117,10 @@
|
||||
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="842e33e43a55ea44833b9e23e4d180fa17c843af"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5db24726f0f42124304195a6bdea129039eeeaeb"/>
|
||||
<project name="platform/system/bluetooth" path="system/bluetooth" revision="930ae098543881f47eac054677726ee4b998b2f8"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="e0fc03e0a3062063c3c85996dcc881c0a49ed98d"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="48d2332e6d8400cdc0de273ceff2abe8aaababf8"/>
|
||||
<project name="platform_system_core" path="system/core" remote="b2g" revision="542d1f59dc331b472307e5bd043101d14d5a3a3e"/>
|
||||
<project name="platform/system/extras" path="system/extras" revision="18c1180e848e7ab8691940481f5c1c8d22c37b3e"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8f7d94ac711af4678169805137c6c42def39b3ed"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
|
||||
<project name="platform/system/media" path="system/media" revision="d90b836f66bf1d9627886c96f3a2d9c3007fbb80"/>
|
||||
<project name="platform/system/netd" path="system/netd" revision="56112dd7b811301b718d0643a82fd5cac9522073"/>
|
||||
<project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="249b8c08c1d57961ef6c905f3498fa62b032bf24"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
@ -23,7 +23,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<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="5a63e2b9f3ef85e82a33440cb73c55dff4e9bf78"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
|
||||
<!-- 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"/>
|
||||
@ -115,10 +115,10 @@
|
||||
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="f7d9bf71cf6693474f3f2a81a4ba62c0fc5646aa"/>
|
||||
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="b562b01c93de9578d5db537b6a602a38e1aaa0ce"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="387f03e815f57d536dd922706db1622bddba8d81"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="e0fc03e0a3062063c3c85996dcc881c0a49ed98d"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="48d2332e6d8400cdc0de273ceff2abe8aaababf8"/>
|
||||
<project name="platform/system/extras" path="system/extras" revision="5356165f67f4a81c2ef28671c13697f1657590df"/>
|
||||
<project name="platform/system/media" path="system/media" revision="be0e2fe59a8043fa5200f75697df9220a99abe9d"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8f7d94ac711af4678169805137c6c42def39b3ed"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
|
||||
<project name="platform/system/netd" path="system/netd" revision="36704b0da24debcab8090156568ac236315036bb"/>
|
||||
<project name="platform/system/security" path="system/security" revision="583374f69f531ba68fc3dcbff1f74893d2a96406"/>
|
||||
<project name="platform/system/vold" path="system/vold" revision="d4455b8cf361f8353e8aebac15ffd64b4aedd2b9"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="249b8c08c1d57961ef6c905f3498fa62b032bf24"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
@ -23,7 +23,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<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="5a63e2b9f3ef85e82a33440cb73c55dff4e9bf78"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="50d1ca4ab8add54523b7bc692860d57e8ee4c0d1"/>
|
||||
<project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="fb3845864573857677f9b500040a8f011eaf5078"/>
|
||||
@ -127,9 +127,9 @@
|
||||
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="2c0d193349c55337e37196a7f2d5cef37753ed3e"/>
|
||||
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="a982f43b7f2d5916dc3a859667a8ba78e50b6202"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="6e18b61ee446bdd9880c07ae84197a087490c2e5"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="e0fc03e0a3062063c3c85996dcc881c0a49ed98d"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="48d2332e6d8400cdc0de273ceff2abe8aaababf8"/>
|
||||
<project name="platform/system/extras" path="system/extras" revision="18f7c51415917eb0e21b30f220db7bd0be4130a7"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8f7d94ac711af4678169805137c6c42def39b3ed"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
|
||||
<project name="platform/system/media" path="system/media" revision="adf8fbacf7395858884690df5e3ce46bc75fa683"/>
|
||||
<project name="platform/system/netd" path="system/netd" revision="655392625db084a7122d65a15acf74db7f1da7f7"/>
|
||||
<project name="platform/system/security" path="system/security" revision="e6b3fdd892ad994ec3fd0b8959d630e31881801b"/>
|
||||
|
32
b2g/config/emulator-x86-kk/config.json
Normal file
32
b2g/config/emulator-x86-kk/config.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"config_version": 2,
|
||||
"tooltool_manifest": "releng-emulator-kk.tt",
|
||||
"mock_target": "mozilla-centos6-x86_64",
|
||||
"mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel", "glibc-devel.i686", "libstdc++.i686", "zlib-devel.i686", "ncurses-devel.i686", "libX11-devel.i686", "mesa-libGL-devel.i686", "mesa-libGL-devel", "libX11-devel", "git", "libxml2"],
|
||||
"mock_files": [["/home/cltbld/.ssh", "/home/mock_mozilla/.ssh"]],
|
||||
"build_targets": ["droid", "package-emulator", "package-tests"],
|
||||
"upload_files": [
|
||||
"{workdir}/out/target/product/generic/*.tar.bz2",
|
||||
"{workdir}/out/target/product/generic/tests/*.zip",
|
||||
"{workdir}/out/emulator.tar.gz",
|
||||
"{objdir}/dist/b2g-*.crashreporter-symbols.zip",
|
||||
"{workdir}/sources.xml"
|
||||
],
|
||||
"public_upload_files": [
|
||||
"{workdir}/out/target/product/generic/*.tar.bz2",
|
||||
"{workdir}/out/target/product/generic/tests/*.zip",
|
||||
"{objdir}/dist/b2g-*.crashreporter-symbols.zip",
|
||||
"{objdir}/dist/b2g-*.tar.gz",
|
||||
"{workdir}/sources.xml"
|
||||
],
|
||||
"upload_platform": "emulator-kk",
|
||||
"gecko_l10n_root": "https://hg.mozilla.org/l10n-central",
|
||||
"gaia": {
|
||||
"l10n": {
|
||||
"vcs": "hgtool",
|
||||
"root": "https://hg.mozilla.org/gaia-l10n"
|
||||
}
|
||||
},
|
||||
"b2g_manifest": "emulator-kk.xml",
|
||||
"b2g_manifest_intree": true
|
||||
}
|
9
b2g/config/emulator-x86-kk/releng-emulator-kk.tt
Normal file
9
b2g/config/emulator-x86-kk/releng-emulator-kk.tt
Normal file
@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"size": 80458572,
|
||||
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gcc.tar.xz",
|
||||
"unpack": "True"
|
||||
}
|
||||
]
|
1
b2g/config/emulator-x86-kk/sources.xml
Symbolic link
1
b2g/config/emulator-x86-kk/sources.xml
Symbolic link
@ -0,0 +1 @@
|
||||
../emulator-kk/sources.xml
|
32
b2g/config/emulator-x86-l/config.json
Normal file
32
b2g/config/emulator-x86-l/config.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"config_version": 2,
|
||||
"tooltool_manifest": "releng-emulator-l.tt",
|
||||
"mock_target": "mozilla-centos6-x86_64",
|
||||
"mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel", "glibc-devel.i686", "libstdc++.i686", "zlib-devel.i686", "ncurses-devel.i686", "libX11-devel.i686", "mesa-libGL-devel.i686", "mesa-libGL-devel", "libX11-devel", "git", "libxml2"],
|
||||
"mock_files": [["/home/cltbld/.ssh", "/home/mock_mozilla/.ssh"]],
|
||||
"build_targets": ["droid", "package-emulator", "package-tests"],
|
||||
"upload_files": [
|
||||
"{workdir}/out/target/product/generic/*.tar.bz2",
|
||||
"{workdir}/out/target/product/generic/tests/*.zip",
|
||||
"{workdir}/out/emulator.tar.gz",
|
||||
"{objdir}/dist/b2g-*.crashreporter-symbols.zip",
|
||||
"{workdir}/sources.xml"
|
||||
],
|
||||
"public_upload_files": [
|
||||
"{workdir}/out/target/product/generic/*.tar.bz2",
|
||||
"{workdir}/out/target/product/generic/tests/*.zip",
|
||||
"{objdir}/dist/b2g-*.crashreporter-symbols.zip",
|
||||
"{objdir}/dist/b2g-*.tar.gz",
|
||||
"{workdir}/sources.xml"
|
||||
],
|
||||
"upload_platform": "emulator-l",
|
||||
"gecko_l10n_root": "https://hg.mozilla.org/l10n-central",
|
||||
"gaia": {
|
||||
"l10n": {
|
||||
"vcs": "hgtool",
|
||||
"root": "https://hg.mozilla.org/gaia-l10n"
|
||||
}
|
||||
},
|
||||
"b2g_manifest": "emulator-l.xml",
|
||||
"b2g_manifest_intree": true
|
||||
}
|
9
b2g/config/emulator-x86-l/releng-emulator-l.tt
Normal file
9
b2g/config/emulator-x86-l/releng-emulator-l.tt
Normal file
@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"size": 80458572,
|
||||
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
|
||||
"algorithm": "sha512",
|
||||
"filename": "gcc.tar.xz",
|
||||
"unpack": "True"
|
||||
}
|
||||
]
|
1
b2g/config/emulator-x86-l/sources.xml
Symbolic link
1
b2g/config/emulator-x86-l/sources.xml
Symbolic link
@ -0,0 +1 @@
|
||||
../emulator-l/sources.xml
|
@ -19,7 +19,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="249b8c08c1d57961ef6c905f3498fa62b032bf24"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="249b8c08c1d57961ef6c905f3498fa62b032bf24"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
@ -23,7 +23,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<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="5a63e2b9f3ef85e82a33440cb73c55dff4e9bf78"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
|
||||
<!-- 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"/>
|
||||
@ -109,9 +109,9 @@
|
||||
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="f7d9bf71cf6693474f3f2a81a4ba62c0fc5646aa"/>
|
||||
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="69d524e80cdf3981006627c65ac85f3a871238a3"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="e0fc03e0a3062063c3c85996dcc881c0a49ed98d"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="48d2332e6d8400cdc0de273ceff2abe8aaababf8"/>
|
||||
<project name="platform/system/extras" path="system/extras" revision="576f57b6510de59c08568b53c0fb60588be8689e"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8f7d94ac711af4678169805137c6c42def39b3ed"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
|
||||
<project name="platform/system/netd" path="system/netd" revision="a6531f7befb49b1c81bc0de7e51c5482b308e1c5"/>
|
||||
<project name="platform/system/security" path="system/security" revision="ee8068b9e7bfb2770635062fc9c2035be2142bd8"/>
|
||||
<project name="platform/system/vold" path="system/vold" revision="42fa2a0f14f965970a4b629a176bbd2666edf017"/>
|
||||
|
@ -17,10 +17,10 @@
|
||||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="249b8c08c1d57961ef6c905f3498fa62b032bf24"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="5a63e2b9f3ef85e82a33440cb73c55dff4e9bf78"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"git": {
|
||||
"git_revision": "249b8c08c1d57961ef6c905f3498fa62b032bf24",
|
||||
"git_revision": "9cc496cecc37d7a29f9279827cdf6e4891211f67",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "87ffbce1342f44bdeee96c7d08d41ac630254eb3",
|
||||
"revision": "9e79307fd6bcade07847b92d42948a6a6a334f79",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
@ -17,10 +17,10 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="249b8c08c1d57961ef6c905f3498fa62b032bf24"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="5a63e2b9f3ef85e82a33440cb73c55dff4e9bf78"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
@ -117,10 +117,10 @@
|
||||
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="842e33e43a55ea44833b9e23e4d180fa17c843af"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5db24726f0f42124304195a6bdea129039eeeaeb"/>
|
||||
<project name="platform/system/bluetooth" path="system/bluetooth" revision="930ae098543881f47eac054677726ee4b998b2f8"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="e0fc03e0a3062063c3c85996dcc881c0a49ed98d"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="48d2332e6d8400cdc0de273ceff2abe8aaababf8"/>
|
||||
<project name="platform_system_core" path="system/core" remote="b2g" revision="542d1f59dc331b472307e5bd043101d14d5a3a3e"/>
|
||||
<project name="platform/system/extras" path="system/extras" revision="18c1180e848e7ab8691940481f5c1c8d22c37b3e"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8f7d94ac711af4678169805137c6c42def39b3ed"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
|
||||
<project name="platform/system/media" path="system/media" revision="d90b836f66bf1d9627886c96f3a2d9c3007fbb80"/>
|
||||
<project name="platform/system/netd" path="system/netd" revision="56112dd7b811301b718d0643a82fd5cac9522073"/>
|
||||
<project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="249b8c08c1d57961ef6c905f3498fa62b032bf24"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9cc496cecc37d7a29f9279827cdf6e4891211f67"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2aa4a75c63cd6e93870a8bddbba45f863cbfd9a3"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
@ -23,7 +23,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<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="5a63e2b9f3ef85e82a33440cb73c55dff4e9bf78"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f5de61a5d8fdaa2db3d4e17e0c4212ec4d54a365"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="50d1ca4ab8add54523b7bc692860d57e8ee4c0d1"/>
|
||||
<project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="fb3845864573857677f9b500040a8f011eaf5078"/>
|
||||
@ -127,9 +127,9 @@
|
||||
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="2c0d193349c55337e37196a7f2d5cef37753ed3e"/>
|
||||
<project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="a982f43b7f2d5916dc3a859667a8ba78e50b6202"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="6e18b61ee446bdd9880c07ae84197a087490c2e5"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="e0fc03e0a3062063c3c85996dcc881c0a49ed98d"/>
|
||||
<project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="48d2332e6d8400cdc0de273ceff2abe8aaababf8"/>
|
||||
<project name="platform/system/extras" path="system/extras" revision="18f7c51415917eb0e21b30f220db7bd0be4130a7"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8f7d94ac711af4678169805137c6c42def39b3ed"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
|
||||
<project name="platform/system/media" path="system/media" revision="adf8fbacf7395858884690df5e3ce46bc75fa683"/>
|
||||
<project name="platform/system/netd" path="system/netd" revision="655392625db084a7122d65a15acf74db7f1da7f7"/>
|
||||
<project name="platform/system/security" path="system/security" revision="e6b3fdd892ad994ec3fd0b8959d630e31881801b"/>
|
||||
|
@ -1871,11 +1871,6 @@ pref("dom.ipc.reportProcessHangs", false);
|
||||
pref("dom.ipc.reportProcessHangs", true);
|
||||
#endif
|
||||
|
||||
#ifndef NIGHTLY_BUILD
|
||||
// Disable reader mode by default.
|
||||
pref("reader.parse-on-load.enabled", false);
|
||||
#endif
|
||||
|
||||
// Enable ReadingList browser UI by default.
|
||||
pref("browser.readinglist.enabled", true);
|
||||
pref("browser.readinglist.sidebarEverOpened", false);
|
||||
|
@ -2100,53 +2100,81 @@ function loadURI(uri, referrer, postData, allowThirdPartyFixup, referrerPolicy)
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function getShortcutOrURIAndPostData(aURL, aCallback) {
|
||||
let mayInheritPrincipal = false;
|
||||
let postData = null;
|
||||
let shortcutURL = null;
|
||||
let keyword = aURL;
|
||||
let param = "";
|
||||
|
||||
// XXX Bug 1100294 will remove this little hack by using an async version of
|
||||
// PlacesUtils.getURLAndPostDataForKeyword(). For now we simulate an async
|
||||
// execution with at least a setTimeout(fn, 0).
|
||||
let originalCallback = aCallback;
|
||||
aCallback = data => setTimeout(() => originalCallback(data));
|
||||
|
||||
let offset = aURL.indexOf(" ");
|
||||
if (offset > 0) {
|
||||
keyword = aURL.substr(0, offset);
|
||||
param = aURL.substr(offset + 1);
|
||||
/**
|
||||
* Given a urlbar value, discerns between URIs, keywords and aliases.
|
||||
*
|
||||
* @param url
|
||||
* The urlbar value.
|
||||
* @param callback (optional, deprecated)
|
||||
* The callback function invoked when done. This parameter is
|
||||
* deprecated, please use the Promise that is returned.
|
||||
*
|
||||
* @return Promise<{ postData, url, mayInheritPrincipal }>
|
||||
*/
|
||||
function getShortcutOrURIAndPostData(url, callback = null) {
|
||||
if (callback) {
|
||||
Deprecated.warning("Please use the Promise returned by " +
|
||||
"getShortcutOrURIAndPostData() instead of passing a " +
|
||||
"callback",
|
||||
"https://bugzilla.mozilla.org/show_bug.cgi?id=1100294");
|
||||
}
|
||||
|
||||
let engine = Services.search.getEngineByAlias(keyword);
|
||||
if (engine) {
|
||||
let submission = engine.getSubmission(param, null, "keyword");
|
||||
postData = submission.postData;
|
||||
aCallback({ postData: submission.postData, url: submission.uri.spec,
|
||||
mayInheritPrincipal: mayInheritPrincipal });
|
||||
return;
|
||||
}
|
||||
return Task.spawn(function* () {
|
||||
let mayInheritPrincipal = false;
|
||||
let postData = null;
|
||||
let shortcutURL = null;
|
||||
let keyword = url;
|
||||
let param = "";
|
||||
|
||||
[shortcutURL, postData] =
|
||||
PlacesUtils.getURLAndPostDataForKeyword(keyword);
|
||||
let offset = url.indexOf(" ");
|
||||
if (offset > 0) {
|
||||
keyword = url.substr(0, offset);
|
||||
param = url.substr(offset + 1);
|
||||
}
|
||||
|
||||
if (!shortcutURL) {
|
||||
aCallback({ postData: postData, url: aURL,
|
||||
mayInheritPrincipal: mayInheritPrincipal });
|
||||
return;
|
||||
}
|
||||
let engine = Services.search.getEngineByAlias(keyword);
|
||||
if (engine) {
|
||||
let submission = engine.getSubmission(param, null, "keyword");
|
||||
postData = submission.postData;
|
||||
return { postData: submission.postData, url: submission.uri.spec,
|
||||
mayInheritPrincipal };
|
||||
}
|
||||
|
||||
let escapedPostData = "";
|
||||
if (postData)
|
||||
escapedPostData = unescape(postData);
|
||||
let entry = yield PlacesUtils.keywords.fetch(keyword);
|
||||
if (entry) {
|
||||
shortcutURL = entry.url.href;
|
||||
postData = entry.postData;
|
||||
}
|
||||
|
||||
if (/%s/i.test(shortcutURL) || /%s/i.test(escapedPostData)) {
|
||||
let charset = "";
|
||||
const re = /^(.*)\&mozcharset=([a-zA-Z][_\-a-zA-Z0-9]+)\s*$/;
|
||||
let matches = shortcutURL.match(re);
|
||||
if (!shortcutURL) {
|
||||
return { postData, url, mayInheritPrincipal };
|
||||
}
|
||||
|
||||
let escapedPostData = "";
|
||||
if (postData)
|
||||
escapedPostData = unescape(postData);
|
||||
|
||||
if (/%s/i.test(shortcutURL) || /%s/i.test(escapedPostData)) {
|
||||
let charset = "";
|
||||
const re = /^(.*)\&mozcharset=([a-zA-Z][_\-a-zA-Z0-9]+)\s*$/;
|
||||
let matches = shortcutURL.match(re);
|
||||
|
||||
if (matches) {
|
||||
[, shortcutURL, charset] = matches;
|
||||
} else {
|
||||
let uri;
|
||||
try {
|
||||
// makeURI() throws if URI is invalid.
|
||||
uri = makeURI(shortcutURL);
|
||||
} catch (ex) {}
|
||||
|
||||
if (uri) {
|
||||
// Try to get the saved character-set.
|
||||
// Will return an empty string if character-set is not found.
|
||||
charset = yield PlacesUtils.getCharsetForURI(uri);
|
||||
}
|
||||
}
|
||||
|
||||
let continueOperation = function () {
|
||||
// encodeURIComponent produces UTF-8, and cannot be used for other charsets.
|
||||
// escape() works in those cases, but it doesn't uri-encode +, @, and /.
|
||||
// Therefore we need to manually replace these ASCII characters by their
|
||||
@ -2169,40 +2197,29 @@ function getShortcutOrURIAndPostData(aURL, aCallback) {
|
||||
// document's principal.
|
||||
mayInheritPrincipal = true;
|
||||
|
||||
aCallback({ postData: postData, url: shortcutURL,
|
||||
mayInheritPrincipal: mayInheritPrincipal });
|
||||
return { postData, url: shortcutURL, mayInheritPrincipal };
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
[, shortcutURL, charset] = matches;
|
||||
continueOperation();
|
||||
} else {
|
||||
// Try to get the saved character-set.
|
||||
// makeURI throws if URI is invalid.
|
||||
// Will return an empty string if character-set is not found.
|
||||
try {
|
||||
PlacesUtils.getCharsetForURI(makeURI(shortcutURL))
|
||||
.then(c => { charset = c; continueOperation(); });
|
||||
} catch (ex) {
|
||||
continueOperation();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (param) {
|
||||
// This keyword doesn't take a parameter, but one was provided. Just return
|
||||
// the original URL.
|
||||
postData = null;
|
||||
if (param) {
|
||||
// This keyword doesn't take a parameter, but one was provided. Just return
|
||||
// the original URL.
|
||||
postData = null;
|
||||
|
||||
return { postData, url, mayInheritPrincipal };
|
||||
}
|
||||
|
||||
aCallback({ postData: postData, url: aURL,
|
||||
mayInheritPrincipal: mayInheritPrincipal });
|
||||
} else {
|
||||
// This URL came from a bookmark, so it's safe to let it inherit the current
|
||||
// document's principal.
|
||||
mayInheritPrincipal = true;
|
||||
|
||||
aCallback({ postData: postData, url: shortcutURL,
|
||||
mayInheritPrincipal: mayInheritPrincipal });
|
||||
}
|
||||
return { postData, url: shortcutURL, mayInheritPrincipal };
|
||||
}).then(data => {
|
||||
if (callback) {
|
||||
callback(data);
|
||||
}
|
||||
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
function getPostDataStream(aStringData, aKeyword, aEncKeyword, aType) {
|
||||
@ -3252,7 +3269,7 @@ var newTabButtonObserver = {
|
||||
onDrop: function (aEvent)
|
||||
{
|
||||
let url = browserDragAndDrop.drop(aEvent, { });
|
||||
getShortcutOrURIAndPostData(url, data => {
|
||||
getShortcutOrURIAndPostData(url).then(data => {
|
||||
if (data.url) {
|
||||
// allow third-party services to fixup this URL
|
||||
openNewTabWith(data.url, null, data.postData, aEvent, true);
|
||||
@ -3272,7 +3289,7 @@ var newWindowButtonObserver = {
|
||||
onDrop: function (aEvent)
|
||||
{
|
||||
let url = browserDragAndDrop.drop(aEvent, { });
|
||||
getShortcutOrURIAndPostData(url, data => {
|
||||
getShortcutOrURIAndPostData(url).then(data => {
|
||||
if (data.url) {
|
||||
// allow third-party services to fixup this URL
|
||||
openNewWindowWith(data.url, null, data.postData, true);
|
||||
@ -5608,7 +5625,7 @@ function middleMousePaste(event) {
|
||||
lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
|
||||
}
|
||||
|
||||
getShortcutOrURIAndPostData(clipboard, data => {
|
||||
getShortcutOrURIAndPostData(clipboard).then(data => {
|
||||
try {
|
||||
makeURI(data.url);
|
||||
} catch (ex) {
|
||||
@ -5645,7 +5662,7 @@ function handleDroppedLink(event, url, name)
|
||||
{
|
||||
let lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
|
||||
|
||||
getShortcutOrURIAndPostData(url, data => {
|
||||
getShortcutOrURIAndPostData(url).then(data => {
|
||||
if (data.url &&
|
||||
lastLocationChange == gBrowser.selectedBrowser.lastLocationChange)
|
||||
loadURI(data.url, null, data.postData, false);
|
||||
|
@ -190,9 +190,9 @@ let gGrid = {
|
||||
// Save the cell's computed height/width including margin and border
|
||||
if (this._cellMargin === undefined) {
|
||||
let refCell = document.querySelector(".newtab-cell");
|
||||
this._cellMargin = parseFloat(getComputedStyle(refCell).marginTop) +
|
||||
this._cellMargin = parseFloat(getComputedStyle(refCell).marginTop);
|
||||
this._cellHeight = refCell.offsetHeight + this._cellMargin +
|
||||
parseFloat(getComputedStyle(refCell).marginBottom);
|
||||
this._cellHeight = refCell.offsetHeight + this._cellMargin;
|
||||
this._cellWidth = refCell.offsetWidth + this._cellMargin;
|
||||
}
|
||||
|
||||
|
@ -13,34 +13,34 @@ function* promise_first_result(inputText) {
|
||||
|
||||
add_task(function*() {
|
||||
// This test is only relevant if UnifiedComplete is enabled.
|
||||
if (!Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete")) {
|
||||
todo(false, "Stop supporting old autocomplete components.");
|
||||
return;
|
||||
}
|
||||
let ucpref = Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", true);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", ucpref);
|
||||
});
|
||||
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab("about:mozilla");
|
||||
let tabs = [tab];
|
||||
registerCleanupFunction(() => {
|
||||
registerCleanupFunction(function* () {
|
||||
for (let tab of tabs)
|
||||
gBrowser.removeTab(tab);
|
||||
PlacesUtils.bookmarks.removeItem(itemId);
|
||||
yield PlacesUtils.bookmarks.remove(bm);
|
||||
});
|
||||
|
||||
yield promiseTabLoadEvent(tab);
|
||||
|
||||
let itemId =
|
||||
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
|
||||
NetUtil.newURI("http://example.com/?q=%s"),
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX,
|
||||
"test");
|
||||
PlacesUtils.bookmarks.setKeywordForBookmark(itemId, "keyword");
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: "http://example.com/?q=%s",
|
||||
title: "test" });
|
||||
yield PlacesUtils.keywords.insert({ keyword: "keyword",
|
||||
url: "http://example.com/?q=%s" });
|
||||
|
||||
let result = yield promise_first_result("keyword something");
|
||||
isnot(result, null, "Expect a keyword result");
|
||||
|
||||
is(result.getAttribute("type"), "action keyword", "Expect correct `type` attribute");
|
||||
is(result.getAttribute("actiontype"), "keyword", "Expect correct `actiontype` attribute");
|
||||
is(result.getAttribute("title"), "test", "Expect correct title");
|
||||
is(result.getAttribute("title"), "example.com", "Expect correct title");
|
||||
|
||||
// We need to make a real URI out of this to ensure it's normalised for
|
||||
// comparison.
|
||||
@ -50,9 +50,9 @@ add_task(function*() {
|
||||
is_element_visible(result._title, "Title element should be visible");
|
||||
is(result._title.childNodes.length, 1, "Title element should have 1 child");
|
||||
is(result._title.childNodes[0].nodeName, "#text", "That child should be a text node");
|
||||
is(result._title.childNodes[0].data, "test", "Node should contain the name of the bookmark");
|
||||
is(result._title.childNodes[0].data, "example.com", "Node should contain the name of the bookmark");
|
||||
|
||||
is_element_visible(result._extra, "Extra element should be visible");
|
||||
is_element_visible(result._extraBox, "Extra element should be visible");
|
||||
is(result._extra.childNodes.length, 1, "Title element should have 1 child");
|
||||
is(result._extra.childNodes[0].nodeName, "span", "That child should be a span node");
|
||||
let span = result._extra.childNodes[0];
|
||||
|
@ -1,19 +1,19 @@
|
||||
add_task(function*() {
|
||||
// This test is only relevant if UnifiedComplete is enabled.
|
||||
if (!Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete")) {
|
||||
todo(false, "Stop supporting old autocomplete components.");
|
||||
return;
|
||||
}
|
||||
|
||||
let itemId =
|
||||
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
|
||||
NetUtil.newURI("http://example.com/?q=%s"),
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX,
|
||||
"test");
|
||||
PlacesUtils.bookmarks.setKeywordForBookmark(itemId, "keyword");
|
||||
|
||||
let ucpref = Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", true);
|
||||
registerCleanupFunction(() => {
|
||||
PlacesUtils.bookmarks.removeItem(itemId);
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", ucpref);
|
||||
});
|
||||
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: "http://example.com/?q=%s",
|
||||
title: "test" });
|
||||
yield PlacesUtils.keywords.insert({ keyword: "keyword",
|
||||
url: "http://example.com/?q=%s" })
|
||||
|
||||
registerCleanupFunction(function* () {
|
||||
yield PlacesUtils.bookmarks.remove(bm);
|
||||
});
|
||||
|
||||
yield promiseAutocompleteResultPopup("keyword search");
|
||||
@ -21,12 +21,12 @@ add_task(function*() {
|
||||
|
||||
info("Before override");
|
||||
is_element_hidden(result._url, "URL element should be hidden");
|
||||
is_element_visible(result._extra, "Extra element should be visible");
|
||||
is_element_visible(result._extraBox, "Extra element should be visible");
|
||||
|
||||
info("During override");
|
||||
EventUtils.synthesizeKey("VK_SHIFT" , { type: "keydown" });
|
||||
is_element_hidden(result._url, "URL element should be hidden");
|
||||
is_element_visible(result._extra, "Extra element should be visible");
|
||||
is_element_visible(result._extraBox, "Extra element should be visible");
|
||||
|
||||
EventUtils.synthesizeKey("VK_SHIFT" , { type: "keyup" });
|
||||
|
||||
|
@ -3,10 +3,11 @@
|
||||
|
||||
add_task(function* () {
|
||||
// This test is only relevant if UnifiedComplete is enabled.
|
||||
if (!Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete")) {
|
||||
todo(false, "Stop supporting old autocomplete components.");
|
||||
return;
|
||||
}
|
||||
let ucpref = Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", true);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", ucpref);
|
||||
});
|
||||
|
||||
Services.search.addEngineWithDetails("MozSearch", "", "", "", "GET",
|
||||
"http://example.com/?q={searchTerms}");
|
||||
|
@ -4,7 +4,12 @@
|
||||
**/
|
||||
|
||||
add_task(function* () {
|
||||
// This test is only relevant if UnifiedComplete is enabled.
|
||||
let ucpref = Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", true);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", ucpref);
|
||||
});
|
||||
|
||||
let iconURI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC";
|
||||
Services.search.addEngineWithDetails("MozSearch", iconURI, "moz", "", "GET",
|
||||
@ -20,7 +25,6 @@ add_task(function* () {
|
||||
Services.search.currentEngine = originalEngine;
|
||||
let engine = Services.search.getEngineByName("MozSearch");
|
||||
Services.search.removeEngine(engine);
|
||||
Services.prefs.clearUserPref("browser.urlbar.unifiedcomplete");
|
||||
|
||||
try {
|
||||
gBrowser.removeTab(tab);
|
||||
|
@ -3,10 +3,11 @@
|
||||
|
||||
add_task(function*() {
|
||||
// This test is only relevant if UnifiedComplete is enabled.
|
||||
if (!Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete")) {
|
||||
todo(false, "Stop supporting old autocomplete components.");
|
||||
return;
|
||||
}
|
||||
let ucpref = Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", true);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", ucpref);
|
||||
});
|
||||
|
||||
let tab = gBrowser.addTab("about:about");
|
||||
yield promiseTabLoaded(tab);
|
||||
|
@ -10,12 +10,15 @@ function is_selected(index) {
|
||||
|
||||
add_task(function*() {
|
||||
// This test is only relevant if UnifiedComplete is enabled.
|
||||
if (!Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete")) {
|
||||
todo(false, "Stop supporting old autocomplete components.");
|
||||
return;
|
||||
}
|
||||
let ucpref = Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", true);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", ucpref);
|
||||
});
|
||||
|
||||
registerCleanupFunction(() => PlacesTestUtils.clearHistory());
|
||||
registerCleanupFunction(function* () {
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
});
|
||||
|
||||
let visits = [];
|
||||
repeat(10, i => {
|
||||
|
@ -2,17 +2,16 @@ add_task(function*() {
|
||||
// This test is only relevant if UnifiedComplete is enabled.
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", true);
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
|
||||
registerCleanupFunction(function* () {
|
||||
Services.prefs.clearUserPref("browser.urlbar.unifiedcomplete");
|
||||
yield PlacesUtils.bookmarks.remove(bm);
|
||||
});
|
||||
|
||||
let itemId =
|
||||
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
|
||||
NetUtil.newURI("http://example.com/?q=%s"),
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX,
|
||||
"test");
|
||||
PlacesUtils.bookmarks.setKeywordForBookmark(itemId, "keyword");
|
||||
let bm = yield PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: "http://example.com/?q=%s",
|
||||
title: "test" });
|
||||
yield PlacesUtils.keywords.insert({ keyword: "keyword",
|
||||
url: "http://example.com/?q=%s" });
|
||||
|
||||
yield new Promise(resolve => waitForFocus(resolve, window));
|
||||
|
||||
|
@ -3,10 +3,11 @@
|
||||
|
||||
add_task(function*() {
|
||||
// This test is only relevant if UnifiedComplete is enabled.
|
||||
if (!Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete")) {
|
||||
todo(false, "Stop supporting old autocomplete components.");
|
||||
return;
|
||||
}
|
||||
let ucpref = Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", true);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", ucpref);
|
||||
});
|
||||
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab("about:mozilla", {animate: false});
|
||||
yield promiseTabLoaded(tab);
|
||||
|
@ -15,8 +15,7 @@ add_task(function*() {
|
||||
return;
|
||||
}
|
||||
|
||||
registerCleanupFunction(() => PlacesTestUtils.clearHistory());
|
||||
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
let visits = [];
|
||||
repeat(10, i => {
|
||||
visits.push({
|
||||
@ -25,13 +24,18 @@ add_task(function*() {
|
||||
});
|
||||
yield PlacesTestUtils.addVisits(visits);
|
||||
|
||||
registerCleanupFunction(function* () {
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
});
|
||||
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab("about:mozilla", {animate: false});
|
||||
yield promiseTabLoaded(tab);
|
||||
yield promiseAutocompleteResultPopup("example.com/autocomplete");
|
||||
|
||||
let popup = gURLBar.popup;
|
||||
let results = popup.richlistbox.children;
|
||||
is(results.length, 10, "Should get 11 results");
|
||||
let results = popup.richlistbox.children.filter(is_visible);
|
||||
|
||||
is(results.length, 10, "Should get 10 results");
|
||||
is_selected(-1);
|
||||
|
||||
info("Key Down to select the next item");
|
||||
|
@ -4,10 +4,11 @@
|
||||
|
||||
add_task(function* test_switchtab_override() {
|
||||
// This test is only relevant if UnifiedComplete is enabled.
|
||||
if (!Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete")) {
|
||||
todo(false, "Stop supporting old autocomplete components.");
|
||||
return;
|
||||
}
|
||||
let ucpref = Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", true);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", ucpref);
|
||||
});
|
||||
|
||||
let testURL = "http://example.org/browser/browser/base/content/test/general/dummy_page.html";
|
||||
|
||||
|
@ -7,29 +7,32 @@ function is_selected(index) {
|
||||
|
||||
add_task(function*() {
|
||||
// This test is only relevant if UnifiedComplete is enabled.
|
||||
if (!Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete")) {
|
||||
todo(false, "Stop supporting old autocomplete components.");
|
||||
return;
|
||||
}
|
||||
|
||||
let ucpref = Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", true);
|
||||
registerCleanupFunction(() => {
|
||||
PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
|
||||
Services.prefs.setBoolPref("browser.urlbar.unifiedcomplete", ucpref);
|
||||
});
|
||||
|
||||
let itemId =
|
||||
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
|
||||
NetUtil.newURI("http://example.com/?q=%s"),
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX,
|
||||
"test");
|
||||
PlacesUtils.bookmarks.setKeywordForBookmark(itemId, "keyword");
|
||||
let bookmarks = [];
|
||||
bookmarks.push((yield PlacesUtils.bookmarks
|
||||
.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: "http://example.com/?q=%s",
|
||||
title: "test" })));
|
||||
yield PlacesUtils.keywords.insert({ keyword: "keyword",
|
||||
url: "http://example.com/?q=%s" });
|
||||
|
||||
// This item only needed so we can select the keyword item, select something
|
||||
// else, then select the keyword item again.
|
||||
itemId =
|
||||
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
|
||||
NetUtil.newURI("http://example.com/keyword"),
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX,
|
||||
"keyword abc");
|
||||
bookmarks.push((yield PlacesUtils.bookmarks
|
||||
.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||
url: "http://example.com/keyword",
|
||||
title: "keyword abc" })));
|
||||
|
||||
registerCleanupFunction(function* () {
|
||||
for (let bm of bookmarks) {
|
||||
yield PlacesUtils.bookmarks.remove(bm);
|
||||
}
|
||||
});
|
||||
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab("about:mozilla", {animate: false});
|
||||
yield promiseTabLoaded(tab);
|
||||
|
@ -100,8 +100,7 @@ add_task(function* test_getshortcutoruri() {
|
||||
let query = data.keyword;
|
||||
if (data.searchWord)
|
||||
query += " " + data.searchWord;
|
||||
let returnedData = yield new Promise(
|
||||
resolve => getShortcutOrURIAndPostData(query, resolve));
|
||||
let returnedData = yield getShortcutOrURIAndPostData(query);
|
||||
// null result.url means we should expect the same query we sent in
|
||||
let expected = result.url || query;
|
||||
is(returnedData.url, expected, "got correct URL for " + data.keyword);
|
||||
|
@ -16,7 +16,9 @@ function is_selected(index) {
|
||||
}
|
||||
|
||||
add_task(function*() {
|
||||
registerCleanupFunction(() => PlacesTestUtils.clearHistory());
|
||||
registerCleanupFunction(function* () {
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
});
|
||||
|
||||
yield PlacesTestUtils.clearHistory();
|
||||
let tabCount = gBrowser.tabs.length;
|
||||
|
@ -450,7 +450,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
getShortcutOrURIAndPostData(url, data => {
|
||||
getShortcutOrURIAndPostData(url).then(data => {
|
||||
aCallback([data.url, data.postData, data.mayInheritPrincipal]);
|
||||
});
|
||||
]]></body>
|
||||
|
@ -1661,7 +1661,7 @@ BrowserGlue.prototype = {
|
||||
},
|
||||
|
||||
_migrateUI: function BG__migrateUI() {
|
||||
const UI_VERSION = 28;
|
||||
const UI_VERSION = 29;
|
||||
const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
|
||||
let currentUIVersion = 0;
|
||||
try {
|
||||
@ -2016,6 +2016,21 @@ BrowserGlue.prototype = {
|
||||
Services.prefs.clearUserPref("browser.devedition.showCustomizeButton");
|
||||
}
|
||||
|
||||
if (currentUIVersion < 29) {
|
||||
let group = null;
|
||||
try {
|
||||
group = Services.prefs.getComplexValue("font.language.group",
|
||||
Ci.nsIPrefLocalizedString);
|
||||
} catch (ex) {}
|
||||
if (group &&
|
||||
["tr", "x-baltic", "x-central-euro"].some(g => g == group.data)) {
|
||||
// Latin groups were consolidated.
|
||||
group.data = "x-western";
|
||||
Services.prefs.setComplexValue("font.language.group",
|
||||
Ci.nsIPrefLocalizedString, group);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the migration version.
|
||||
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
|
||||
},
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
|
||||
/* 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/. */
|
||||
@ -232,6 +231,20 @@ var gPermissionManager = {
|
||||
var urlLabel = document.getElementById("urlLabel");
|
||||
urlLabel.hidden = !urlFieldVisible;
|
||||
|
||||
let treecols = document.getElementsByTagName("treecols")[0];
|
||||
treecols.addEventListener("click", event => {
|
||||
if (event.target.nodeName != "treecol" || event.button != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sortField = event.target.getAttribute("data-field-name");
|
||||
if (!sortField) {
|
||||
return;
|
||||
}
|
||||
|
||||
gPermissionManager.onPermissionSort(sortField);
|
||||
});
|
||||
|
||||
Services.obs.notifyObservers(null, NOTIFICATION_FLUSH_PERMISSIONS, this._type);
|
||||
Services.obs.addObserver(this, "perm-changed", false);
|
||||
|
||||
|
@ -53,10 +53,10 @@
|
||||
onselect="gPermissionManager.onPermissionSelected();">
|
||||
<treecols>
|
||||
<treecol id="siteCol" label="&treehead.sitename.label;" flex="3"
|
||||
onclick="gPermissionManager.onPermissionSort('rawHost');" persist="width"/>
|
||||
data-field-name="rawHost" persist="width"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="statusCol" label="&treehead.status.label;" flex="1"
|
||||
onclick="gPermissionManager.onPermissionSort('capability');" persist="width"/>
|
||||
data-field-name="capability" persist="width"/>
|
||||
</treecols>
|
||||
<treechildren/>
|
||||
</tree>
|
||||
|
@ -33,6 +33,12 @@ XPCOMUtils.defineLazyModuleGetter(this, 'ReadingList',
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'Sync',
|
||||
'resource:///modules/readinglist/Sync.jsm');
|
||||
|
||||
// FxAccountsCommon.js doesn't use a "namespace", so create one here.
|
||||
XPCOMUtils.defineLazyGetter(this, "fxAccountsCommon", function() {
|
||||
let namespace = {};
|
||||
Cu.import("resource://gre/modules/FxAccountsCommon.js", namespace);
|
||||
return namespace;
|
||||
});
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["ReadingListScheduler"];
|
||||
|
||||
@ -75,6 +81,7 @@ function InternalScheduler(readingList = null) {
|
||||
"browserwindow.syncui",
|
||||
"FirefoxAccounts",
|
||||
"readinglist.api",
|
||||
"readinglist.scheduler",
|
||||
"readinglist.serverclient",
|
||||
"readinglist.sync",
|
||||
];
|
||||
@ -175,6 +182,8 @@ InternalScheduler.prototype = {
|
||||
if (this.state == this.STATE_ERROR_AUTHENTICATION) {
|
||||
this.state = this.STATE_OK;
|
||||
}
|
||||
// and sync now.
|
||||
this._syncNow();
|
||||
break;
|
||||
|
||||
// The rest just indicate that now is probably a good time to check if
|
||||
@ -285,10 +294,24 @@ InternalScheduler.prototype = {
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
|
||||
return intervals.schedule;
|
||||
}).catch(err => {
|
||||
this.log.error("Sync failed", err);
|
||||
// XXX - how to detect an auth error?
|
||||
this.state = err == this._engine.ERROR_AUTHENTICATION ?
|
||||
// This isn't ideal - we really should have _canSync() check this - but
|
||||
// that requires a refactor to turn _canSync() into a promise-based
|
||||
// function.
|
||||
if (err.message == fxAccountsCommon.ERROR_NO_ACCOUNT ||
|
||||
err.message == fxAccountsCommon.ERROR_UNVERIFIED_ACCOUNT) {
|
||||
// make everything look like success.
|
||||
this.log.info("Can't sync due to FxA account state " + err.message);
|
||||
this.state = this.STATE_OK;
|
||||
this._logManager.resetFileLog(this._logManager.REASON_SUCCESS);
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:finish", null);
|
||||
// it's unfortunate that we are probably going to hit this every
|
||||
// 2 hours, but it should be invisible to the user.
|
||||
return intervals.schedule;
|
||||
}
|
||||
this.state = err.message == fxAccountsCommon.ERROR_AUTH_ERROR ?
|
||||
this.STATE_ERROR_AUTHENTICATION : this.STATE_ERROR_OTHER;
|
||||
this.log.error("Sync failed, now in state '${state}': ${err}",
|
||||
{state: this.state, err});
|
||||
this._logManager.resetFileLog(this._logManager.REASON_ERROR);
|
||||
Services.obs.notifyObservers(null, "readinglist:sync:error", null);
|
||||
return intervals.retry;
|
||||
|
@ -278,8 +278,13 @@ SyncImpl.prototype = {
|
||||
}
|
||||
// Note that the server seems to return a 200 if an identical item already
|
||||
// exists, but we shouldn't be uploading identical items in this phase in
|
||||
// normal usage, so treat 200 as an unexpected response.
|
||||
if (response.status != 201) {
|
||||
// normal usage. But if something goes wrong locally (eg, we upload but
|
||||
// get some error even though the upload worked) we will see this.
|
||||
// So allow 200 but log a warning.
|
||||
if (response.status == 200) {
|
||||
log.debug("Attempting to upload a new item found the server already had it", response);
|
||||
// but we still process it.
|
||||
} else if (response.status != 201) {
|
||||
this._handleUnexpectedResponse("uploading a new item", response);
|
||||
continue;
|
||||
}
|
||||
@ -404,7 +409,13 @@ SyncImpl.prototype = {
|
||||
continue;
|
||||
}
|
||||
// new item
|
||||
yield this.list.addItem(localRecordFromServerRecord(serverRecord));
|
||||
let localRecord = localRecordFromServerRecord(serverRecord);
|
||||
try {
|
||||
yield this.list.addItem(localRecord);
|
||||
} catch (ex) {
|
||||
log.warn("Failed to add a new item from server record ${serverRecord}: ${ex}",
|
||||
{serverRecord, ex});
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
@ -442,7 +453,12 @@ SyncImpl.prototype = {
|
||||
throw new Error("Item should exist");
|
||||
}
|
||||
localItem._record = localRecordFromServerRecord(serverRecord);
|
||||
yield this.list.updateItem(localItem);
|
||||
try {
|
||||
yield this.list.updateItem(localItem);
|
||||
} catch (ex) {
|
||||
log.warn("Failed to update an item from server record ${serverRecord}: ${ex}",
|
||||
{serverRecord, ex});
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
@ -458,7 +474,12 @@ SyncImpl.prototype = {
|
||||
// consumers are notified properly. Set the syncStatus to NEW so that the
|
||||
// list truly deletes the item.
|
||||
item._record.syncStatus = ReadingList.SyncStatus.NEW;
|
||||
yield this.list.deleteItem(item);
|
||||
try {
|
||||
yield this.list.deleteItem(item);
|
||||
} catch (ex) {
|
||||
log.warn("Failed delete local item with id ${guid}: ${ex}",
|
||||
{guid, ex});
|
||||
}
|
||||
return;
|
||||
}
|
||||
// If item is null, then it may not actually exist locally, or it may have
|
||||
@ -466,7 +487,12 @@ SyncImpl.prototype = {
|
||||
// that case, try to delete it directly from the store. As far as the list
|
||||
// is concerned, the item has already been deleted.
|
||||
log.debug("Item not present in list, deleting it by GUID instead");
|
||||
this.list._store.deleteItemByGUID(guid);
|
||||
try {
|
||||
this.list._store.deleteItemByGUID(guid);
|
||||
} catch (ex) {
|
||||
log.warn("Failed to delete local item with id ${guid}: ${ex}",
|
||||
{guid, ex});
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
|
@ -57,8 +57,11 @@ const ALLOWED_IMAGE_SCHEMES = new Set(["https", "data"]);
|
||||
// The frecency of a directory link
|
||||
const DIRECTORY_FRECENCY = 1000;
|
||||
|
||||
// The frecency of a related link
|
||||
const RELATED_FRECENCY = Infinity;
|
||||
// The frecency of a suggested link
|
||||
const SUGGESTED_FRECENCY = Infinity;
|
||||
|
||||
// Default number of times to show a link
|
||||
const DEFAULT_FREQUENCY_CAP = 5;
|
||||
|
||||
// Divide frecency by this amount for pings
|
||||
const PING_SCORE_DIVISOR = 10000;
|
||||
@ -89,14 +92,19 @@ let DirectoryLinksProvider = {
|
||||
_enhancedLinks: new Map(),
|
||||
|
||||
/**
|
||||
* A mapping from site to a list of related link objects
|
||||
* A mapping from site to remaining number of views
|
||||
*/
|
||||
_relatedLinks: new Map(),
|
||||
_frequencyCaps: new Map(),
|
||||
|
||||
/**
|
||||
* A set of top sites that we can provide related links for
|
||||
* A mapping from site to a list of suggested link objects
|
||||
*/
|
||||
_topSitesWithRelatedLinks: new Set(),
|
||||
_suggestedLinks: new Map(),
|
||||
|
||||
/**
|
||||
* A set of top sites that we can provide suggested links for
|
||||
*/
|
||||
_topSitesWithSuggestedLinks: new Set(),
|
||||
|
||||
get _observedPrefs() Object.freeze({
|
||||
enhanced: PREF_NEWTAB_ENHANCED,
|
||||
@ -201,11 +209,11 @@ let DirectoryLinksProvider = {
|
||||
}
|
||||
},
|
||||
|
||||
_cacheRelatedLinks: function(link) {
|
||||
for (let relatedSite of link.frecent_sites) {
|
||||
let relatedMap = this._relatedLinks.get(relatedSite) || new Map();
|
||||
relatedMap.set(link.url, link);
|
||||
this._relatedLinks.set(relatedSite, relatedMap);
|
||||
_cacheSuggestedLinks: function(link) {
|
||||
for (let suggestedSite of link.frecent_sites) {
|
||||
let suggestedMap = this._suggestedLinks.get(suggestedSite) || new Map();
|
||||
suggestedMap.set(link.url, link);
|
||||
this._suggestedLinks.set(suggestedSite, suggestedMap);
|
||||
}
|
||||
},
|
||||
|
||||
@ -325,6 +333,23 @@ let DirectoryLinksProvider = {
|
||||
* @return download promise
|
||||
*/
|
||||
reportSitesAction: function DirectoryLinksProvider_reportSitesAction(sites, action, triggeringSiteIndex) {
|
||||
// Check if the suggested tile was shown
|
||||
if (action == "view") {
|
||||
sites.slice(0, triggeringSiteIndex + 1).forEach(site => {
|
||||
let {targetedSite, url} = site.link;
|
||||
if (targetedSite) {
|
||||
this._decreaseFrequencyCap(url, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Use up all views if the user clicked on a frequency capped tile
|
||||
else if (action == "click") {
|
||||
let {targetedSite, url} = sites[triggeringSiteIndex].link;
|
||||
if (targetedSite) {
|
||||
this._decreaseFrequencyCap(url, DEFAULT_FREQUENCY_CAP);
|
||||
}
|
||||
}
|
||||
|
||||
let newtabEnhanced = false;
|
||||
let pingEndPoint = "";
|
||||
try {
|
||||
@ -413,9 +438,10 @@ let DirectoryLinksProvider = {
|
||||
*/
|
||||
getLinks: function DirectoryLinksProvider_getLinks(aCallback) {
|
||||
this._readDirectoryLinksFile().then(rawLinks => {
|
||||
// Reset the cache of related tiles and enhanced images for this new set of links
|
||||
// Reset the cache of suggested tiles and enhanced images for this new set of links
|
||||
this._enhancedLinks.clear();
|
||||
this._relatedLinks.clear();
|
||||
this._frequencyCaps.clear();
|
||||
this._suggestedLinks.clear();
|
||||
|
||||
let validityFilter = function(link) {
|
||||
// Make sure the link url is allowed and images too if they exist
|
||||
@ -435,16 +461,22 @@ let DirectoryLinksProvider = {
|
||||
rawLinks.suggested.filter(validityFilter).forEach((link, position) => {
|
||||
setCommonProperties(link, rawLinks.suggested.length, position);
|
||||
|
||||
// We cache related tiles here but do not push any of them in the links list yet.
|
||||
// The decision for which related tile to include will be made separately.
|
||||
this._cacheRelatedLinks(link);
|
||||
// We cache suggested tiles here but do not push any of them in the links list yet.
|
||||
// The decision for which suggested tile to include will be made separately.
|
||||
this._cacheSuggestedLinks(link);
|
||||
this._frequencyCaps.set(link.url, DEFAULT_FREQUENCY_CAP);
|
||||
});
|
||||
|
||||
return rawLinks.directory.filter(validityFilter).map((link, position) => {
|
||||
let links = rawLinks.directory.filter(validityFilter).map((link, position) => {
|
||||
setCommonProperties(link, rawLinks.directory.length, position);
|
||||
link.frecency = DIRECTORY_FRECENCY;
|
||||
return link;
|
||||
});
|
||||
|
||||
// Allow for one link suggestion on top of the default directory links
|
||||
this.maxNumLinks = links.length + 1;
|
||||
|
||||
return links;
|
||||
}).catch(ex => {
|
||||
Cu.reportError(ex);
|
||||
return [];
|
||||
@ -476,32 +508,32 @@ let DirectoryLinksProvider = {
|
||||
},
|
||||
|
||||
_handleManyLinksChanged: function() {
|
||||
this._topSitesWithRelatedLinks.clear();
|
||||
this._relatedLinks.forEach((relatedLinks, site) => {
|
||||
this._topSitesWithSuggestedLinks.clear();
|
||||
this._suggestedLinks.forEach((suggestedLinks, site) => {
|
||||
if (NewTabUtils.isTopPlacesSite(site)) {
|
||||
this._topSitesWithRelatedLinks.add(site);
|
||||
this._topSitesWithSuggestedLinks.add(site);
|
||||
}
|
||||
});
|
||||
this._updateRelatedTile();
|
||||
this._updateSuggestedTile();
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates _topSitesWithRelatedLinks based on the link that was changed.
|
||||
* Updates _topSitesWithSuggestedLinks based on the link that was changed.
|
||||
*
|
||||
* @return true if _topSitesWithRelatedLinks was modified, false otherwise.
|
||||
* @return true if _topSitesWithSuggestedLinks was modified, false otherwise.
|
||||
*/
|
||||
_handleLinkChanged: function(aLink) {
|
||||
let changedLinkSite = NewTabUtils.extractSite(aLink.url);
|
||||
let linkStored = this._topSitesWithRelatedLinks.has(changedLinkSite);
|
||||
let linkStored = this._topSitesWithSuggestedLinks.has(changedLinkSite);
|
||||
|
||||
if (!NewTabUtils.isTopPlacesSite(changedLinkSite) && linkStored) {
|
||||
this._topSitesWithRelatedLinks.delete(changedLinkSite);
|
||||
this._topSitesWithSuggestedLinks.delete(changedLinkSite);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this._relatedLinks.has(changedLinkSite) &&
|
||||
if (this._suggestedLinks.has(changedLinkSite) &&
|
||||
NewTabUtils.isTopPlacesSite(changedLinkSite) && !linkStored) {
|
||||
this._topSitesWithRelatedLinks.add(changedLinkSite);
|
||||
this._topSitesWithSuggestedLinks.add(changedLinkSite);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -517,7 +549,7 @@ let DirectoryLinksProvider = {
|
||||
// Make sure NewTabUtils.links handles the notification first.
|
||||
setTimeout(() => {
|
||||
if (this._handleLinkChanged(aLink)) {
|
||||
this._updateRelatedTile();
|
||||
this._updateSuggestedTile();
|
||||
}
|
||||
}, 0);
|
||||
},
|
||||
@ -530,12 +562,27 @@ let DirectoryLinksProvider = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Chooses and returns a related tile based on a user's top sites
|
||||
* that we have an available related tile for.
|
||||
*
|
||||
* @return the chosen related tile, or undefined if there isn't one
|
||||
* Record for a url that some number of views have been used
|
||||
* @param url String url of the suggested link
|
||||
* @param amount Number of equivalent views to decrease
|
||||
*/
|
||||
_updateRelatedTile: function() {
|
||||
_decreaseFrequencyCap(url, amount) {
|
||||
let remainingViews = this._frequencyCaps.get(url) - amount;
|
||||
this._frequencyCaps.set(url, remainingViews);
|
||||
|
||||
// Reached the number of views, so pick a new one.
|
||||
if (remainingViews <= 0) {
|
||||
this._updateSuggestedTile();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Chooses and returns a suggested tile based on a user's top sites
|
||||
* that we have an available suggested tile for.
|
||||
*
|
||||
* @return the chosen suggested tile, or undefined if there isn't one
|
||||
*/
|
||||
_updateSuggestedTile: function() {
|
||||
let sortedLinks = NewTabUtils.getProviderLinks(this);
|
||||
|
||||
if (!sortedLinks) {
|
||||
@ -544,67 +591,78 @@ let DirectoryLinksProvider = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete the current related tile, if one exists.
|
||||
// Delete the current suggested tile, if one exists.
|
||||
let initialLength = sortedLinks.length;
|
||||
this.maxNumLinks = initialLength;
|
||||
if (initialLength) {
|
||||
let mostFrecentLink = sortedLinks[0];
|
||||
if (mostFrecentLink.targetedSite) {
|
||||
this._callObservers("onLinkChanged", {
|
||||
url: mostFrecentLink.url,
|
||||
frecency: 0,
|
||||
frecency: SUGGESTED_FRECENCY,
|
||||
lastVisitDate: mostFrecentLink.lastVisitDate,
|
||||
type: mostFrecentLink.type,
|
||||
}, 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._topSitesWithRelatedLinks.size == 0) {
|
||||
// There are no potential related links we can show.
|
||||
if (this._topSitesWithSuggestedLinks.size == 0) {
|
||||
// There are no potential suggested links we can show.
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a flat list of all possible links we can show as related.
|
||||
// Note that many top sites may map to the same related links, but we only
|
||||
// want to count each related link once (based on url), thus possibleLinks is a map
|
||||
// from url to relatedLink. Thus, each link has an equal chance of being chosen at
|
||||
// Create a flat list of all possible links we can show as suggested.
|
||||
// Note that many top sites may map to the same suggested links, but we only
|
||||
// want to count each suggested link once (based on url), thus possibleLinks is a map
|
||||
// from url to suggestedLink. Thus, each link has an equal chance of being chosen at
|
||||
// random from flattenedLinks if it appears only once.
|
||||
let possibleLinks = new Map();
|
||||
let targetedSites = new Map();
|
||||
this._topSitesWithRelatedLinks.forEach(topSiteWithRelatedLink => {
|
||||
let relatedLinksMap = this._relatedLinks.get(topSiteWithRelatedLink);
|
||||
relatedLinksMap.forEach((relatedLink, url) => {
|
||||
possibleLinks.set(url, relatedLink);
|
||||
this._topSitesWithSuggestedLinks.forEach(topSiteWithSuggestedLink => {
|
||||
let suggestedLinksMap = this._suggestedLinks.get(topSiteWithSuggestedLink);
|
||||
suggestedLinksMap.forEach((suggestedLink, url) => {
|
||||
// Skip this link if we've shown it too many times already
|
||||
if (this._frequencyCaps.get(url) <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
possibleLinks.set(url, suggestedLink);
|
||||
|
||||
// Keep a map of URL to targeted sites. We later use this to show the user
|
||||
// what site they visited to trigger this suggestion.
|
||||
if (!targetedSites.get(url)) {
|
||||
targetedSites.set(url, []);
|
||||
}
|
||||
targetedSites.get(url).push(topSiteWithRelatedLink);
|
||||
targetedSites.get(url).push(topSiteWithSuggestedLink);
|
||||
})
|
||||
});
|
||||
|
||||
// We might have run out of possible links to show
|
||||
let numLinks = possibleLinks.size;
|
||||
if (numLinks == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let flattenedLinks = [...possibleLinks.values()];
|
||||
|
||||
// Choose our related link at random
|
||||
let relatedIndex = Math.floor(Math.random() * flattenedLinks.length);
|
||||
let chosenRelatedLink = flattenedLinks[relatedIndex];
|
||||
// Choose our suggested link at random
|
||||
let suggestedIndex = Math.floor(Math.random() * numLinks);
|
||||
let chosenSuggestedLink = flattenedLinks[suggestedIndex];
|
||||
|
||||
// Show the new directory tile.
|
||||
this._callObservers("onLinkChanged", {
|
||||
url: chosenRelatedLink.url,
|
||||
title: chosenRelatedLink.title,
|
||||
frecency: RELATED_FRECENCY,
|
||||
lastVisitDate: chosenRelatedLink.lastVisitDate,
|
||||
type: chosenRelatedLink.type,
|
||||
url: chosenSuggestedLink.url,
|
||||
title: chosenSuggestedLink.title,
|
||||
frecency: SUGGESTED_FRECENCY,
|
||||
lastVisitDate: chosenSuggestedLink.lastVisitDate,
|
||||
type: chosenSuggestedLink.type,
|
||||
|
||||
// Choose the first site a user has visited as the target. In the future,
|
||||
// this should be the site with the highest frecency. However, we currently
|
||||
// store frecency by URL not by site.
|
||||
targetedSite: targetedSites.get(chosenRelatedLink.url).length ?
|
||||
targetedSites.get(chosenRelatedLink.url)[0] : null
|
||||
targetedSite: targetedSites.get(chosenSuggestedLink.url).length ?
|
||||
targetedSites.get(chosenSuggestedLink.url)[0] : null
|
||||
});
|
||||
return chosenRelatedLink;
|
||||
return chosenSuggestedLink;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -624,11 +682,11 @@ let DirectoryLinksProvider = {
|
||||
this._observers.delete(aObserver);
|
||||
},
|
||||
|
||||
_callObservers: function DirectoryLinksProvider__callObservers(aMethodName, aArg) {
|
||||
_callObservers(methodName, ...args) {
|
||||
for (let obs of this._observers) {
|
||||
if (typeof(obs[aMethodName]) == "function") {
|
||||
if (typeof(obs[methodName]) == "function") {
|
||||
try {
|
||||
obs[aMethodName](this, aArg);
|
||||
obs[methodName](this, ...args);
|
||||
} catch (err) {
|
||||
Cu.reportError(err);
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ do_get_profile();
|
||||
|
||||
const DIRECTORY_LINKS_FILE = "directoryLinks.json";
|
||||
const DIRECTORY_FRECENCY = 1000;
|
||||
const SUGGESTED_FRECENCY = Infinity;
|
||||
const kURLData = {"directory": [{"url":"http://example.com","title":"LocalSource"}]};
|
||||
const kTestURL = 'data:application/json,' + JSON.stringify(kURLData);
|
||||
|
||||
@ -61,7 +62,7 @@ const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
|
||||
|
||||
let gLastRequestPath;
|
||||
|
||||
let relatedTile1 = {
|
||||
let suggestedTile1 = {
|
||||
url: "http://turbotax.com",
|
||||
type: "affiliate",
|
||||
lastVisitDate: 3,
|
||||
@ -72,7 +73,7 @@ let relatedTile1 = {
|
||||
"taxslayer.com"
|
||||
]
|
||||
};
|
||||
let relatedTile2 = {
|
||||
let suggestedTile2 = {
|
||||
url: "http://irs.gov",
|
||||
type: "affiliate",
|
||||
lastVisitDate: 2,
|
||||
@ -83,7 +84,7 @@ let relatedTile2 = {
|
||||
"taxslayer.com"
|
||||
]
|
||||
};
|
||||
let relatedTile3 = {
|
||||
let suggestedTile3 = {
|
||||
url: "http://hrblock.com",
|
||||
type: "affiliate",
|
||||
lastVisitDate: 1,
|
||||
@ -94,7 +95,7 @@ let relatedTile3 = {
|
||||
"taxslayer.com"
|
||||
]
|
||||
};
|
||||
let someOtherSite = {url: "http://someothersite.com", title: "Not_A_Related_Site"};
|
||||
let someOtherSite = {url: "http://someothersite.com", title: "Not_A_Suggested_Site"};
|
||||
|
||||
function getHttpHandler(path) {
|
||||
let code = 200;
|
||||
@ -214,11 +215,11 @@ function run_test() {
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function test_updateRelatedTile() {
|
||||
add_task(function test_updateSuggestedTile() {
|
||||
let topSites = ["site0.com", "1040.com", "site2.com", "hrblock.com", "site4.com", "freetaxusa.com", "site6.com"];
|
||||
|
||||
// Initial setup
|
||||
let data = {"suggested": [relatedTile1, relatedTile2, relatedTile3], "directory": [someOtherSite]};
|
||||
let data = {"suggested": [suggestedTile1, suggestedTile2, suggestedTile3], "directory": [someOtherSite]};
|
||||
let dataURI = 'data:application/json,' + JSON.stringify(data);
|
||||
|
||||
let testObserver = new TestFirstRun();
|
||||
@ -237,49 +238,49 @@ add_task(function test_updateRelatedTile() {
|
||||
return links;
|
||||
}
|
||||
|
||||
do_check_eq(DirectoryLinksProvider._updateRelatedTile(), undefined);
|
||||
do_check_eq(DirectoryLinksProvider._updateSuggestedTile(), undefined);
|
||||
|
||||
function TestFirstRun() {
|
||||
this.promise = new Promise(resolve => {
|
||||
this.onLinkChanged = (directoryLinksProvider, link) => {
|
||||
links.unshift(link);
|
||||
let possibleLinks = [relatedTile1.url, relatedTile2.url, relatedTile3.url];
|
||||
let possibleLinks = [suggestedTile1.url, suggestedTile2.url, suggestedTile3.url];
|
||||
|
||||
isIdentical([...DirectoryLinksProvider._topSitesWithRelatedLinks], ["hrblock.com", "1040.com", "freetaxusa.com"]);
|
||||
isIdentical([...DirectoryLinksProvider._topSitesWithSuggestedLinks], ["hrblock.com", "1040.com", "freetaxusa.com"]);
|
||||
do_check_true(possibleLinks.indexOf(link.url) > -1);
|
||||
do_check_eq(link.frecency, Infinity);
|
||||
do_check_eq(link.frecency, SUGGESTED_FRECENCY);
|
||||
do_check_eq(link.type, "affiliate");
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function TestChangingRelatedTile() {
|
||||
function TestChangingSuggestedTile() {
|
||||
this.count = 0;
|
||||
this.promise = new Promise(resolve => {
|
||||
this.onLinkChanged = (directoryLinksProvider, link) => {
|
||||
this.count++;
|
||||
let possibleLinks = [relatedTile1.url, relatedTile2.url, relatedTile3.url];
|
||||
let possibleLinks = [suggestedTile1.url, suggestedTile2.url, suggestedTile3.url];
|
||||
|
||||
do_check_true(possibleLinks.indexOf(link.url) > -1);
|
||||
do_check_eq(link.type, "affiliate");
|
||||
do_check_true(this.count <= 2);
|
||||
|
||||
if (this.count == 1) {
|
||||
// The removed related link is the one we added initially.
|
||||
// The removed suggested link is the one we added initially.
|
||||
do_check_eq(link.url, links.shift().url);
|
||||
do_check_eq(link.frecency, 0);
|
||||
do_check_eq(link.frecency, SUGGESTED_FRECENCY);
|
||||
} else {
|
||||
links.unshift(link);
|
||||
do_check_eq(link.frecency, Infinity);
|
||||
do_check_eq(link.frecency, SUGGESTED_FRECENCY);
|
||||
}
|
||||
isIdentical([...DirectoryLinksProvider._topSitesWithRelatedLinks], ["hrblock.com", "freetaxusa.com"]);
|
||||
isIdentical([...DirectoryLinksProvider._topSitesWithSuggestedLinks], ["hrblock.com", "freetaxusa.com"]);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function TestRemovingRelatedTile() {
|
||||
function TestRemovingSuggestedTile() {
|
||||
this.count = 0;
|
||||
this.promise = new Promise(resolve => {
|
||||
this.onLinkChanged = (directoryLinksProvider, link) => {
|
||||
@ -287,32 +288,32 @@ add_task(function test_updateRelatedTile() {
|
||||
|
||||
do_check_eq(link.type, "affiliate");
|
||||
do_check_eq(this.count, 1);
|
||||
do_check_eq(link.frecency, 0);
|
||||
do_check_eq(link.frecency, SUGGESTED_FRECENCY);
|
||||
do_check_eq(link.url, links.shift().url);
|
||||
isIdentical([...DirectoryLinksProvider._topSitesWithRelatedLinks], []);
|
||||
isIdentical([...DirectoryLinksProvider._topSitesWithSuggestedLinks], []);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Test first call to '_updateRelatedTile()', called when fetching directory links.
|
||||
// Test first call to '_updateSuggestedTile()', called when fetching directory links.
|
||||
yield testObserver.promise;
|
||||
DirectoryLinksProvider.removeObserver(testObserver);
|
||||
|
||||
// Removing a top site that doesn't have a related link should
|
||||
// not change the current related tile.
|
||||
// Removing a top site that doesn't have a suggested link should
|
||||
// not change the current suggested tile.
|
||||
let removedTopsite = topSites.shift();
|
||||
do_check_eq(removedTopsite, "site0.com");
|
||||
do_check_false(NewTabUtils.isTopPlacesSite(removedTopsite));
|
||||
let updateRelatedTile = DirectoryLinksProvider._handleLinkChanged({
|
||||
let updateSuggestedTile = DirectoryLinksProvider._handleLinkChanged({
|
||||
url: "http://" + removedTopsite,
|
||||
type: "history",
|
||||
});
|
||||
do_check_false(updateRelatedTile);
|
||||
do_check_false(updateSuggestedTile);
|
||||
|
||||
// Removing a top site that has a related link should
|
||||
// remove any current related tile and add a new one.
|
||||
testObserver = new TestChangingRelatedTile();
|
||||
// Removing a top site that has a suggested link should
|
||||
// remove any current suggested tile and add a new one.
|
||||
testObserver = new TestChangingSuggestedTile();
|
||||
DirectoryLinksProvider.addObserver(testObserver);
|
||||
removedTopsite = topSites.shift();
|
||||
do_check_eq(removedTopsite, "1040.com");
|
||||
@ -325,10 +326,10 @@ add_task(function test_updateRelatedTile() {
|
||||
do_check_eq(testObserver.count, 2);
|
||||
DirectoryLinksProvider.removeObserver(testObserver);
|
||||
|
||||
// Removing all top sites with related links should remove
|
||||
// the current related link and not replace it.
|
||||
// Removing all top sites with suggested links should remove
|
||||
// the current suggested link and not replace it.
|
||||
topSites = [];
|
||||
testObserver = new TestRemovingRelatedTile();
|
||||
testObserver = new TestRemovingSuggestedTile();
|
||||
DirectoryLinksProvider.addObserver(testObserver);
|
||||
DirectoryLinksProvider.onManyLinksChanged();
|
||||
yield testObserver.promise;
|
||||
@ -339,38 +340,38 @@ add_task(function test_updateRelatedTile() {
|
||||
NewTabUtils.getProviderLinks = origGetProviderLinks;
|
||||
});
|
||||
|
||||
add_task(function test_relatedLinksMap() {
|
||||
let data = {"suggested": [relatedTile1, relatedTile2, relatedTile3], "directory": [someOtherSite]};
|
||||
add_task(function test_suggestedLinksMap() {
|
||||
let data = {"suggested": [suggestedTile1, suggestedTile2, suggestedTile3], "directory": [someOtherSite]};
|
||||
let dataURI = 'data:application/json,' + JSON.stringify(data);
|
||||
|
||||
yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
|
||||
let links = yield fetchData();
|
||||
|
||||
// Ensure the related tiles were not considered directory tiles.
|
||||
// Ensure the suggested tiles were not considered directory tiles.
|
||||
do_check_eq(links.length, 1);
|
||||
let expected_data = [{url: "http://someothersite.com", title: "Not_A_Related_Site", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}];
|
||||
let expected_data = [{url: "http://someothersite.com", title: "Not_A_Suggested_Site", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}];
|
||||
isIdentical(links, expected_data);
|
||||
|
||||
// Check for correctly saved related tiles data.
|
||||
// Check for correctly saved suggested tiles data.
|
||||
expected_data = {
|
||||
"taxact.com": [relatedTile1, relatedTile2, relatedTile3],
|
||||
"hrblock.com": [relatedTile1, relatedTile2],
|
||||
"1040.com": [relatedTile1, relatedTile3],
|
||||
"taxslayer.com": [relatedTile1, relatedTile2, relatedTile3],
|
||||
"freetaxusa.com": [relatedTile2, relatedTile3],
|
||||
"taxact.com": [suggestedTile1, suggestedTile2, suggestedTile3],
|
||||
"hrblock.com": [suggestedTile1, suggestedTile2],
|
||||
"1040.com": [suggestedTile1, suggestedTile3],
|
||||
"taxslayer.com": [suggestedTile1, suggestedTile2, suggestedTile3],
|
||||
"freetaxusa.com": [suggestedTile2, suggestedTile3],
|
||||
};
|
||||
|
||||
DirectoryLinksProvider._relatedLinks.forEach((relatedLinks, site) => {
|
||||
let relatedLinksItr = relatedLinks.values();
|
||||
DirectoryLinksProvider._suggestedLinks.forEach((suggestedLinks, site) => {
|
||||
let suggestedLinksItr = suggestedLinks.values();
|
||||
for (let link of expected_data[site]) {
|
||||
isIdentical(relatedLinksItr.next().value, link);
|
||||
isIdentical(suggestedLinksItr.next().value, link);
|
||||
}
|
||||
})
|
||||
|
||||
yield promiseCleanDirectoryLinksProvider();
|
||||
});
|
||||
|
||||
add_task(function test_topSitesWithRelatedLinks() {
|
||||
add_task(function test_topSitesWithSuggestedLinks() {
|
||||
let topSites = ["site0.com", "1040.com", "site2.com", "hrblock.com", "site4.com", "freetaxusa.com", "site6.com"];
|
||||
let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
|
||||
NewTabUtils.isTopPlacesSite = function(site) {
|
||||
@ -383,45 +384,181 @@ add_task(function test_topSitesWithRelatedLinks() {
|
||||
return [];
|
||||
}
|
||||
|
||||
// We start off with no top sites with related links.
|
||||
do_check_eq(DirectoryLinksProvider._topSitesWithRelatedLinks.size, 0);
|
||||
// We start off with no top sites with suggested links.
|
||||
do_check_eq(DirectoryLinksProvider._topSitesWithSuggestedLinks.size, 0);
|
||||
|
||||
let data = {"suggested": [relatedTile1, relatedTile2, relatedTile3], "directory": [someOtherSite]};
|
||||
let data = {"suggested": [suggestedTile1, suggestedTile2, suggestedTile3], "directory": [someOtherSite]};
|
||||
let dataURI = 'data:application/json,' + JSON.stringify(data);
|
||||
|
||||
yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
|
||||
let links = yield fetchData();
|
||||
|
||||
// Check we've populated related links as expected.
|
||||
do_check_eq(DirectoryLinksProvider._relatedLinks.size, 5);
|
||||
// Check we've populated suggested links as expected.
|
||||
do_check_eq(DirectoryLinksProvider._suggestedLinks.size, 5);
|
||||
|
||||
// When many sites change, we update _topSitesWithRelatedLinks as expected.
|
||||
let expectedTopSitesWithRelatedLinks = ["hrblock.com", "1040.com", "freetaxusa.com"];
|
||||
// When many sites change, we update _topSitesWithSuggestedLinks as expected.
|
||||
let expectedTopSitesWithSuggestedLinks = ["hrblock.com", "1040.com", "freetaxusa.com"];
|
||||
DirectoryLinksProvider._handleManyLinksChanged();
|
||||
isIdentical([...DirectoryLinksProvider._topSitesWithRelatedLinks], expectedTopSitesWithRelatedLinks);
|
||||
isIdentical([...DirectoryLinksProvider._topSitesWithSuggestedLinks], expectedTopSitesWithSuggestedLinks);
|
||||
|
||||
// Removing site6.com as a topsite has no impact on _topSitesWithRelatedLinks.
|
||||
// Removing site6.com as a topsite has no impact on _topSitesWithSuggestedLinks.
|
||||
let popped = topSites.pop();
|
||||
DirectoryLinksProvider._handleLinkChanged({url: "http://" + popped});
|
||||
isIdentical([...DirectoryLinksProvider._topSitesWithRelatedLinks], expectedTopSitesWithRelatedLinks);
|
||||
isIdentical([...DirectoryLinksProvider._topSitesWithSuggestedLinks], expectedTopSitesWithSuggestedLinks);
|
||||
|
||||
// Removing freetaxusa.com as a topsite will remove it from _topSitesWithRelatedLinks.
|
||||
// Removing freetaxusa.com as a topsite will remove it from _topSitesWithSuggestedLinks.
|
||||
popped = topSites.pop();
|
||||
expectedTopSitesWithRelatedLinks.pop();
|
||||
expectedTopSitesWithSuggestedLinks.pop();
|
||||
DirectoryLinksProvider._handleLinkChanged({url: "http://" + popped});
|
||||
isIdentical([...DirectoryLinksProvider._topSitesWithRelatedLinks], expectedTopSitesWithRelatedLinks);
|
||||
isIdentical([...DirectoryLinksProvider._topSitesWithSuggestedLinks], expectedTopSitesWithSuggestedLinks);
|
||||
|
||||
// Re-adding freetaxusa.com as a topsite will add it to _topSitesWithRelatedLinks.
|
||||
// Re-adding freetaxusa.com as a topsite will add it to _topSitesWithSuggestedLinks.
|
||||
topSites.push(popped);
|
||||
expectedTopSitesWithRelatedLinks.push(popped);
|
||||
expectedTopSitesWithSuggestedLinks.push(popped);
|
||||
DirectoryLinksProvider._handleLinkChanged({url: "http://" + popped});
|
||||
isIdentical([...DirectoryLinksProvider._topSitesWithRelatedLinks], expectedTopSitesWithRelatedLinks);
|
||||
isIdentical([...DirectoryLinksProvider._topSitesWithSuggestedLinks], expectedTopSitesWithSuggestedLinks);
|
||||
|
||||
// Cleanup.
|
||||
NewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
|
||||
NewTabUtils.getProviderLinks = origGetProviderLinks;
|
||||
});
|
||||
|
||||
add_task(function test_frequencyCappedSites_views() {
|
||||
Services.prefs.setCharPref(kPingUrlPref, "");
|
||||
let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
|
||||
NewTabUtils.isTopPlacesSite = () => true;
|
||||
|
||||
let testUrl = "http://frequency.capped/link";
|
||||
let targets = ["top.site.com"];
|
||||
let data = {
|
||||
suggested: [{
|
||||
type: "sponsored",
|
||||
frecent_sites: targets,
|
||||
url: testUrl
|
||||
}],
|
||||
directory: [{
|
||||
type: "organic",
|
||||
url: "http://directory.site/"
|
||||
}]
|
||||
};
|
||||
let dataURI = "data:application/json," + JSON.stringify(data);
|
||||
|
||||
yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
|
||||
|
||||
// Wait for links to get loaded
|
||||
let gLinks = NewTabUtils.links;
|
||||
gLinks.addProvider(DirectoryLinksProvider);
|
||||
gLinks.populateCache();
|
||||
yield new Promise(resolve => {
|
||||
NewTabUtils.allPages.register({
|
||||
observe: _ => _,
|
||||
update() {
|
||||
NewTabUtils.allPages.unregister(this);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function synthesizeAction(action) {
|
||||
DirectoryLinksProvider.reportSitesAction([{
|
||||
link: {
|
||||
targetedSite: targets[0],
|
||||
url: testUrl
|
||||
}
|
||||
}], action, 0);
|
||||
}
|
||||
|
||||
function checkFirstTypeAndLength(type, length) {
|
||||
let links = gLinks.getLinks();
|
||||
do_check_eq(links[0].type, type);
|
||||
do_check_eq(links.length, length);
|
||||
}
|
||||
|
||||
// Make sure we get 5 views of the link before it is removed
|
||||
checkFirstTypeAndLength("sponsored", 2);
|
||||
synthesizeAction("view");
|
||||
checkFirstTypeAndLength("sponsored", 2);
|
||||
synthesizeAction("view");
|
||||
checkFirstTypeAndLength("sponsored", 2);
|
||||
synthesizeAction("view");
|
||||
checkFirstTypeAndLength("sponsored", 2);
|
||||
synthesizeAction("view");
|
||||
checkFirstTypeAndLength("sponsored", 2);
|
||||
synthesizeAction("view");
|
||||
checkFirstTypeAndLength("organic", 1);
|
||||
|
||||
// Cleanup.
|
||||
NewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
|
||||
gLinks.removeProvider(DirectoryLinksProvider);
|
||||
DirectoryLinksProvider.removeObserver(gLinks);
|
||||
Services.prefs.setCharPref(kPingUrlPref, kPingUrl);
|
||||
});
|
||||
|
||||
add_task(function test_frequencyCappedSites_click() {
|
||||
Services.prefs.setCharPref(kPingUrlPref, "");
|
||||
let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
|
||||
NewTabUtils.isTopPlacesSite = () => true;
|
||||
|
||||
let testUrl = "http://frequency.capped/link";
|
||||
let targets = ["top.site.com"];
|
||||
let data = {
|
||||
suggested: [{
|
||||
type: "sponsored",
|
||||
frecent_sites: targets,
|
||||
url: testUrl
|
||||
}],
|
||||
directory: [{
|
||||
type: "organic",
|
||||
url: "http://directory.site/"
|
||||
}]
|
||||
};
|
||||
let dataURI = "data:application/json," + JSON.stringify(data);
|
||||
|
||||
yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
|
||||
|
||||
// Wait for links to get loaded
|
||||
let gLinks = NewTabUtils.links;
|
||||
gLinks.addProvider(DirectoryLinksProvider);
|
||||
gLinks.populateCache();
|
||||
yield new Promise(resolve => {
|
||||
NewTabUtils.allPages.register({
|
||||
observe: _ => _,
|
||||
update() {
|
||||
NewTabUtils.allPages.unregister(this);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function synthesizeAction(action) {
|
||||
DirectoryLinksProvider.reportSitesAction([{
|
||||
link: {
|
||||
targetedSite: targets[0],
|
||||
url: testUrl
|
||||
}
|
||||
}], action, 0);
|
||||
}
|
||||
|
||||
function checkFirstTypeAndLength(type, length) {
|
||||
let links = gLinks.getLinks();
|
||||
do_check_eq(links[0].type, type);
|
||||
do_check_eq(links.length, length);
|
||||
}
|
||||
|
||||
// Make sure the link disappears after the first click
|
||||
checkFirstTypeAndLength("sponsored", 2);
|
||||
synthesizeAction("view");
|
||||
checkFirstTypeAndLength("sponsored", 2);
|
||||
synthesizeAction("click");
|
||||
checkFirstTypeAndLength("organic", 1);
|
||||
|
||||
// Cleanup.
|
||||
NewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
|
||||
gLinks.removeProvider(DirectoryLinksProvider);
|
||||
DirectoryLinksProvider.removeObserver(gLinks);
|
||||
Services.prefs.setCharPref(kPingUrlPref, kPingUrl);
|
||||
});
|
||||
|
||||
add_task(function test_reportSitesAction() {
|
||||
yield DirectoryLinksProvider.init();
|
||||
let deferred, expectedPath, expectedPost;
|
||||
|
@ -228,6 +228,7 @@ BluetoothHfpManager::Cleanup()
|
||||
void
|
||||
BluetoothHfpManager::Reset()
|
||||
{
|
||||
mFirstCKPD = false;
|
||||
// Phone & Device CIND
|
||||
ResetCallArray();
|
||||
// Clear Sco state
|
||||
@ -1286,6 +1287,8 @@ BluetoothHfpManager::OnConnect(const nsAString& aErrorStr)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mFirstCKPD = true;
|
||||
|
||||
/**
|
||||
* On the one hand, notify the controller that we've done for outbound
|
||||
* connections. On the other hand, we do nothing for inbound connections.
|
||||
@ -1613,7 +1616,7 @@ BluetoothHfpManager::KeyPressedNotification(const nsAString& aBdAddress)
|
||||
// Refer to AOSP HeadsetStateMachine.processKeyPressed
|
||||
if (FindFirstCall(nsITelephonyService::CALL_STATE_INCOMING)
|
||||
&& !hasActiveCall) {
|
||||
/**
|
||||
/*
|
||||
* Bluetooth HSP spec 4.2.2
|
||||
* There is an incoming call, notify Dialer to pick up the phone call
|
||||
* and SCO will be established after we get the CallStateChanged event
|
||||
@ -1622,13 +1625,22 @@ BluetoothHfpManager::KeyPressedNotification(const nsAString& aBdAddress)
|
||||
NotifyDialer(NS_LITERAL_STRING("ATA"));
|
||||
} else if (hasActiveCall) {
|
||||
if (!IsScoConnected()) {
|
||||
/**
|
||||
/*
|
||||
* Bluetooth HSP spec 4.3
|
||||
* If there's no SCO, set up a SCO link.
|
||||
*/
|
||||
ConnectSco();
|
||||
} else {
|
||||
/**
|
||||
} else if (mFirstCKPD) {
|
||||
/*
|
||||
* Bluetooth HSP spec 4.2 & 4.3
|
||||
* The SCO link connection may be set up prior to receiving the AT+CKPD=200
|
||||
* command from the HS.
|
||||
*
|
||||
* Once FxOS initiates a SCO connection before receiving the
|
||||
* AT+CKPD=200, we should ignore this key press.
|
||||
*/
|
||||
} else {
|
||||
/*
|
||||
* Bluetooth HSP spec 4.5
|
||||
* There are two ways to release SCO: sending CHUP to dialer or closing
|
||||
* SCO socket directly. We notify dialer only if there is at least one
|
||||
@ -1636,6 +1648,7 @@ BluetoothHfpManager::KeyPressedNotification(const nsAString& aBdAddress)
|
||||
*/
|
||||
NotifyDialer(NS_LITERAL_STRING("CHUP"));
|
||||
}
|
||||
mFirstCKPD = false;
|
||||
} else {
|
||||
// BLDN
|
||||
mDialingRequestProcessed = false;
|
||||
|
@ -209,6 +209,7 @@ private:
|
||||
int mCurrentVgm;
|
||||
bool mReceiveVgsFlag;
|
||||
bool mDialingRequestProcessed;
|
||||
bool mFirstCKPD;
|
||||
PhoneType mPhoneType;
|
||||
nsString mDeviceAddress;
|
||||
nsString mMsisdn;
|
||||
|
@ -5,20 +5,6 @@
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Vector;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.AppConstants.Versions;
|
||||
import org.mozilla.gecko.DynamicToolbar.PinReason;
|
||||
import org.mozilla.gecko.DynamicToolbar.VisibilityTransition;
|
||||
@ -35,6 +21,7 @@ import org.mozilla.gecko.favicons.Favicons;
|
||||
import org.mozilla.gecko.favicons.LoadFaviconTask;
|
||||
import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
|
||||
import org.mozilla.gecko.favicons.decoders.IconDirectoryEntry;
|
||||
import org.mozilla.gecko.firstrun.FirstrunPane;
|
||||
import org.mozilla.gecko.fxa.FirefoxAccounts;
|
||||
import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
@ -57,18 +44,18 @@ import org.mozilla.gecko.menu.GeckoMenuItem;
|
||||
import org.mozilla.gecko.mozglue.ContextUtils;
|
||||
import org.mozilla.gecko.mozglue.ContextUtils.SafeIntent;
|
||||
import org.mozilla.gecko.mozglue.RobocopTarget;
|
||||
import org.mozilla.gecko.firstrun.FirstrunPane;
|
||||
import org.mozilla.gecko.overlays.ui.ShareDialog;
|
||||
import org.mozilla.gecko.preferences.ClearOnShutdownPref;
|
||||
import org.mozilla.gecko.preferences.GeckoPreferences;
|
||||
import org.mozilla.gecko.prompts.Prompt;
|
||||
import org.mozilla.gecko.prompts.PromptListItem;
|
||||
import org.mozilla.gecko.sync.setup.SyncAccounts;
|
||||
import org.mozilla.gecko.tabqueue.TabQueueHelper;
|
||||
import org.mozilla.gecko.tabs.TabHistoryController;
|
||||
import org.mozilla.gecko.tabs.TabHistoryController.OnShowTabHistory;
|
||||
import org.mozilla.gecko.tabs.TabHistoryFragment;
|
||||
import org.mozilla.gecko.tabs.TabHistoryPage;
|
||||
import org.mozilla.gecko.tabs.TabsPanel;
|
||||
import org.mozilla.gecko.tabs.TabHistoryController.OnShowTabHistory;
|
||||
import org.mozilla.gecko.toolbar.AutocompleteHandler;
|
||||
import org.mozilla.gecko.toolbar.BrowserToolbar;
|
||||
import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState;
|
||||
@ -139,6 +126,20 @@ import android.widget.ListView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.Toast;
|
||||
import android.widget.ViewFlipper;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Vector;
|
||||
|
||||
public class BrowserApp extends GeckoApp
|
||||
implements TabsPanel.TabsLayoutChangeListener,
|
||||
@ -905,6 +906,19 @@ public class BrowserApp extends GeckoApp
|
||||
checkFirstrun(this, new SafeIntent(getIntent()));
|
||||
}
|
||||
|
||||
private void processTabQueue() {
|
||||
if (AppConstants.NIGHTLY_BUILD && AppConstants.MOZ_ANDROID_TAB_QUEUE) {
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (TabQueueHelper.shouldOpenTabQueueUrls(BrowserApp.this)) {
|
||||
TabQueueHelper.openQueuedUrls(BrowserApp.this, mProfile, TabQueueHelper.FILE_NAME);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@ -923,6 +937,8 @@ public class BrowserApp extends GeckoApp
|
||||
|
||||
EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener)this,
|
||||
"Prompt:ShowTop");
|
||||
|
||||
processTabQueue();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -3380,6 +3396,7 @@ public class BrowserApp extends GeckoApp
|
||||
|
||||
final boolean isViewAction = Intent.ACTION_VIEW.equals(action);
|
||||
final boolean isBookmarkAction = GeckoApp.ACTION_HOMESCREEN_SHORTCUT.equals(action);
|
||||
final boolean isTabQueueAction = TabQueueHelper.LOAD_URLS_ACTION.equals(action);
|
||||
|
||||
if (mInitialized && (isViewAction || isBookmarkAction)) {
|
||||
// Dismiss editing mode if the user is loading a URL from an external app.
|
||||
@ -3408,6 +3425,17 @@ public class BrowserApp extends GeckoApp
|
||||
GuestSession.handleIntent(this, intent);
|
||||
}
|
||||
|
||||
// If the user has clicked the tab queue notification then load the tabs.
|
||||
if(AppConstants.NIGHTLY_BUILD && AppConstants.MOZ_ANDROID_TAB_QUEUE && mInitialized && isTabQueueAction) {
|
||||
int queuedTabCount = TabQueueHelper.getTabQueueLength(this);
|
||||
TabQueueHelper.openQueuedUrls(this, mProfile, TabQueueHelper.FILE_NAME);
|
||||
|
||||
// If there's more than one tab then also show the tabs panel.
|
||||
if (queuedTabCount > 1) {
|
||||
showNormalTabs();
|
||||
}
|
||||
}
|
||||
|
||||
if (!mInitialized || !Intent.ACTION_MAIN.equals(action)) {
|
||||
return;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import org.mozilla.gecko.widget.DoorhangerConfig;
|
||||
|
||||
public class DoorHangerPopup extends AnchoredPopup
|
||||
implements GeckoEventListener,
|
||||
@ -76,16 +77,12 @@ public class DoorHangerPopup extends AnchoredPopup
|
||||
public void handleMessage(String event, JSONObject geckoObject) {
|
||||
try {
|
||||
if (event.equals("Doorhanger:Add")) {
|
||||
final int tabId = geckoObject.getInt("tabID");
|
||||
final String value = geckoObject.getString("value");
|
||||
final String message = geckoObject.getString("message");
|
||||
final JSONArray buttons = geckoObject.getJSONArray("buttons");
|
||||
final JSONObject options = geckoObject.getJSONObject("options");
|
||||
final DoorhangerConfig config = makeConfigFromJSON(geckoObject);
|
||||
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
addDoorHanger(tabId, value, message, buttons, options);
|
||||
addDoorHanger(config);
|
||||
}
|
||||
});
|
||||
} else if (event.equals("Doorhanger:Remove")) {
|
||||
@ -109,6 +106,22 @@ public class DoorHangerPopup extends AnchoredPopup
|
||||
}
|
||||
}
|
||||
|
||||
private DoorhangerConfig makeConfigFromJSON(JSONObject json) throws JSONException {
|
||||
final int tabId = json.getInt("tabID");
|
||||
final String id = json.getString("value");
|
||||
final DoorhangerConfig config = new DoorhangerConfig(tabId, id);
|
||||
|
||||
config.setMessage(json.getString("message"));
|
||||
config.setButtons(json.getJSONArray("buttons"));
|
||||
config.setOptions(json.getJSONObject("options"));
|
||||
final String typeString = json.optString("category");
|
||||
if (DoorHanger.Type.LOGIN.toString().equals(typeString)) {
|
||||
config.setType(DoorHanger.Type.LOGIN);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// This callback is automatically executed on the UI thread.
|
||||
@Override
|
||||
public void onTabChanged(final Tab tab, final Tabs.TabEvents msg, final Object data) {
|
||||
@ -149,15 +162,15 @@ public class DoorHangerPopup extends AnchoredPopup
|
||||
*
|
||||
* This method must be called on the UI thread.
|
||||
*/
|
||||
void addDoorHanger(final int tabId, final String value, final String message,
|
||||
final JSONArray buttons, final JSONObject options) {
|
||||
void addDoorHanger(DoorhangerConfig config) {
|
||||
final int tabId = config.getTabId();
|
||||
// Don't add a doorhanger for a tab that doesn't exist
|
||||
if (Tabs.getInstance().getTab(tabId) == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace the doorhanger if it already exists
|
||||
DoorHanger oldDoorHanger = getDoorHanger(tabId, value);
|
||||
DoorHanger oldDoorHanger = getDoorHanger(tabId, config.getId());
|
||||
if (oldDoorHanger != null) {
|
||||
removeDoorHanger(oldDoorHanger);
|
||||
}
|
||||
@ -166,10 +179,9 @@ public class DoorHangerPopup extends AnchoredPopup
|
||||
init();
|
||||
}
|
||||
|
||||
final DoorHanger newDoorHanger = new DoorHanger(mContext, tabId, value);
|
||||
newDoorHanger.setMessage(message);
|
||||
newDoorHanger.setOptions(options);
|
||||
final DoorHanger newDoorHanger = DoorHanger.Get(mContext, config);
|
||||
|
||||
final JSONArray buttons = config.getButtons();
|
||||
for (int i = 0; i < buttons.length(); i++) {
|
||||
try {
|
||||
JSONObject buttonObject = buttons.getJSONObject(i);
|
||||
@ -230,7 +242,7 @@ public class DoorHangerPopup extends AnchoredPopup
|
||||
*/
|
||||
DoorHanger getDoorHanger(int tabId, String value) {
|
||||
for (DoorHanger dh : mDoorHangers) {
|
||||
if (dh.getTabId() == tabId && dh.getValue().equals(value))
|
||||
if (dh.getTabId() == tabId && dh.getIdentifier().equals(value))
|
||||
return dh;
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.db.LocalBrowserDB;
|
||||
import org.mozilla.gecko.db.StubBrowserDB;
|
||||
import org.mozilla.gecko.distribution.Distribution;
|
||||
import org.mozilla.gecko.mozglue.ContextUtils;
|
||||
import org.mozilla.gecko.mozglue.RobocopTarget;
|
||||
import org.mozilla.gecko.firstrun.FirstrunPane;
|
||||
import org.mozilla.gecko.util.INIParser;
|
||||
@ -153,7 +154,7 @@ public final class GeckoProfile {
|
||||
|
||||
final String args;
|
||||
if (context instanceof Activity) {
|
||||
args = ((Activity) context).getIntent().getStringExtra("args");
|
||||
args = ContextUtils.getStringExtra(((Activity) context).getIntent(), "args");
|
||||
} else {
|
||||
args = null;
|
||||
}
|
||||
@ -679,6 +680,14 @@ public final class GeckoProfile {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean deleteFileFromProfileDir(String fileName) throws IllegalArgumentException {
|
||||
if (TextUtils.isEmpty(fileName)) {
|
||||
throw new IllegalArgumentException("Filename cannot be empty.");
|
||||
}
|
||||
File file = new File(getDir(), fileName);
|
||||
return file.delete();
|
||||
}
|
||||
|
||||
private boolean remove() {
|
||||
try {
|
||||
synchronized (this) {
|
||||
|
@ -489,8 +489,10 @@ gbjar.sources += [
|
||||
'widget/CheckableLinearLayout.java',
|
||||
'widget/ClickableWhenDisabledEditText.java',
|
||||
'widget/DateTimePicker.java',
|
||||
'widget/DefaultDoorHanger.java',
|
||||
'widget/Divider.java',
|
||||
'widget/DoorHanger.java',
|
||||
'widget/DoorhangerConfig.java',
|
||||
'widget/EllipsisTextView.java',
|
||||
'widget/FadedMultiColorTextView.java',
|
||||
'widget/FadedSingleColorTextView.java',
|
||||
@ -503,6 +505,7 @@ gbjar.sources += [
|
||||
'widget/GeckoSwipeRefreshLayout.java',
|
||||
'widget/GeckoViewFlipper.java',
|
||||
'widget/IconTabWidget.java',
|
||||
'widget/LoginDoorHanger.java',
|
||||
'widget/ResizablePathDrawable.java',
|
||||
'widget/SquaredImageView.java',
|
||||
'widget/SwipeDismissListViewTouchListener.java',
|
||||
|
@ -266,15 +266,17 @@ public class LocalReadingListStorage implements ReadingListStorage {
|
||||
ReadingListItems.RESOLVED_TITLE,
|
||||
ReadingListItems.RESOLVED_URL,
|
||||
ReadingListItems.EXCERPT,
|
||||
// TODO: ReadingListItems.IS_ARTICLE,
|
||||
// TODO: ReadingListItems.WORD_COUNT,
|
||||
};
|
||||
|
||||
|
||||
try {
|
||||
return client.query(URI_WITHOUT_DELETED, projection, selection, null, null);
|
||||
} catch (RemoteException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getModified() {
|
||||
final String selection = ReadingListItems.SYNC_STATUS + " = " + ReadingListItems.SYNC_STATUS_MODIFIED;
|
||||
|
@ -482,7 +482,7 @@ public class ReadingListClient {
|
||||
if (ReadingListConstants.DEBUG) {
|
||||
Logger.info(LOG_TAG, "Patching record " + guid + ": " + body.toJSONString());
|
||||
}
|
||||
r.post(body);
|
||||
r.patch(body);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -10,7 +10,7 @@ public class ReadingListConstants {
|
||||
public static final String GLOBAL_LOG_TAG = "FxReadingList";
|
||||
public static final String USER_AGENT = "Firefox-Android-FxReader/" + AppConstants.MOZ_APP_VERSION + " (" + AppConstants.MOZ_APP_DISPLAYNAME + ")";
|
||||
public static final String DEFAULT_DEV_ENDPOINT = "https://readinglist.dev.mozaws.net/v1/";
|
||||
public static final String DEFAULT_PROD_ENDPOINT = null; // TODO
|
||||
public static final String DEFAULT_PROD_ENDPOINT = "https://readinglist.services.mozilla.com/v1/";
|
||||
|
||||
public static final String OAUTH_ENDPOINT_PROD = "https://oauth.accounts.firefox.com/v1";
|
||||
|
||||
|
@ -248,11 +248,10 @@ public class ReadingListSynchronizer {
|
||||
}
|
||||
|
||||
if (failures == 0) {
|
||||
try {
|
||||
next.next();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
next.next();
|
||||
return;
|
||||
}
|
||||
|
||||
next.fail();
|
||||
}
|
||||
}
|
||||
@ -305,6 +304,149 @@ public class ReadingListSynchronizer {
|
||||
}
|
||||
}
|
||||
|
||||
private static class ModifiedUploadDelegate implements ReadingListRecordUploadDelegate {
|
||||
private final ReadingListChangeAccumulator acc;
|
||||
|
||||
public volatile int failures = 0;
|
||||
private final StageDelegate next;
|
||||
|
||||
ModifiedUploadDelegate(ReadingListChangeAccumulator acc, StageDelegate next) {
|
||||
this.acc = acc;
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvalidUpload(ClientReadingListRecord up,
|
||||
ReadingListResponse response) {
|
||||
recordFailed(up);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConflict(ClientReadingListRecord up,
|
||||
ReadingListResponse response) {
|
||||
// This can happen for a material change.
|
||||
failures++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(ClientReadingListRecord up,
|
||||
ReadingListRecordResponse response,
|
||||
ServerReadingListRecord down) {
|
||||
if (!TextUtils.equals(up.getGUID(), down.getGUID())) {
|
||||
// Uh oh!
|
||||
// This should never occur. We should get an onConflict instead,
|
||||
// so this would imply a server bug, or something like a truncated
|
||||
// over-long GUID string.
|
||||
//
|
||||
// Should we wish to recover from this case, probably the right approach
|
||||
// is to ensure that the GUID is overwritten locally (given that we know
|
||||
// the numeric ID).
|
||||
}
|
||||
|
||||
// We could upload our material changes but get back additional status
|
||||
// changes from the server. Apply them.
|
||||
acc.addChangedRecord(up.givenServerRecord(down));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBadRequest(ClientReadingListRecord up, MozResponse response) {
|
||||
recordFailed(up);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(ClientReadingListRecord up, Exception ex) {
|
||||
recordFailed(up);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(ClientReadingListRecord up, MozResponse response) {
|
||||
// Since we download and apply remote changes before uploading local changes, the conflict
|
||||
// window is very small. We should essentially never see true conflicts here.
|
||||
if (response.getStatusCode() == 404) {
|
||||
// We shouldn't see a 404; we should see a record with deleted=true when
|
||||
// we fetch remote changes.
|
||||
Logger.warn(LOG_TAG, "Ignoring 404 response patching record with guid: " + up.getGUID());
|
||||
} else if (response.getStatusCode() == 409) {
|
||||
// A 409 indicates that resolved_url has collided with an existing
|
||||
// record. Not much to be done here.
|
||||
Logger.info(LOG_TAG, "409 response seen; deleting record with guid: " + up.getGUID());
|
||||
acc.addDeletion(up);
|
||||
} else {
|
||||
// We should never see a 412 since we race to upload our changes (and
|
||||
// accept whatever the server gives us back).
|
||||
recordFailed(up);
|
||||
}
|
||||
}
|
||||
|
||||
private void recordFailed(ClientReadingListRecord up) {
|
||||
++failures;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBatchDone() {
|
||||
try {
|
||||
acc.finish();
|
||||
} catch (Exception e) {
|
||||
next.fail(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (failures == 0) {
|
||||
next.next();
|
||||
return;
|
||||
}
|
||||
|
||||
next.fail();
|
||||
}
|
||||
}
|
||||
|
||||
private Queue<ClientReadingListRecord> collectModifiedFromCursor(final Cursor cursor) {
|
||||
try {
|
||||
final Queue<ClientReadingListRecord> toUpload = new LinkedList<>();
|
||||
|
||||
final int columnGUID = cursor.getColumnIndexOrThrow(ReadingListItems.GUID);
|
||||
final int columnExcerpt = cursor.getColumnIndexOrThrow(ReadingListItems.EXCERPT);
|
||||
final int columnResolvedURL = cursor.getColumnIndexOrThrow(ReadingListItems.RESOLVED_URL);
|
||||
final int columnResolvedTitle = cursor.getColumnIndexOrThrow(ReadingListItems.RESOLVED_TITLE);
|
||||
// TODO: final int columnIsArticle = cursor.getColumnIndexOrThrow(ReadingListItems.IS_ARTICLE);
|
||||
// TODO: final int columnWordCount = cursor.getColumnIndexOrThrow(ReadingListItems.WORD_COUNT);
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
final String guid = cursor.getString(columnGUID);
|
||||
if (guid == null) {
|
||||
// Nothing we can do here, but this should never happen: we should
|
||||
// have uploaded this record as new before trying to upload a
|
||||
// material modification!
|
||||
continue;
|
||||
}
|
||||
|
||||
final ExtendedJSONObject o = new ExtendedJSONObject();
|
||||
o.put("id", guid);
|
||||
final String excerpt = cursor.getString(columnExcerpt); // Can be NULL.
|
||||
final String resolvedURL = cursor.getString(columnResolvedURL); // Can be NULL.
|
||||
final String resolvedTitle = cursor.getString(columnResolvedTitle); // Can be NULL.
|
||||
if (excerpt == null && resolvedURL == null && resolvedTitle == null) {
|
||||
// Nothing material to upload, so skip this record.
|
||||
continue;
|
||||
}
|
||||
o.put("excerpt", excerpt);
|
||||
o.put("resolved_url", resolvedURL);
|
||||
o.put("resolved_title", resolvedTitle);
|
||||
// TODO: o.put("is_article", cursor.getInt(columnIsArticle) == 1);
|
||||
// TODO: o.put("word_count", cursor.getInt(columnWordCount));
|
||||
|
||||
final ClientMetadata cm = null;
|
||||
final ServerMetadata sm = new ServerMetadata(guid, -1L);
|
||||
final ClientReadingListRecord record = new ClientReadingListRecord(sm, cm, o);
|
||||
toUpload.add(record);
|
||||
}
|
||||
|
||||
return toUpload;
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
private Queue<ClientReadingListRecord> accumulateNewItems(Cursor cursor) {
|
||||
try {
|
||||
final Queue<ClientReadingListRecord> toUpload = new LinkedList<>();
|
||||
@ -335,8 +477,11 @@ public class ReadingListSynchronizer {
|
||||
|
||||
// Nothing to do.
|
||||
if (toUpload.isEmpty()) {
|
||||
Logger.debug(LOG_TAG, "No new unread changes to upload. Skipping.");
|
||||
delegate.next();
|
||||
return;
|
||||
} else {
|
||||
Logger.debug(LOG_TAG, "Uploading " + toUpload.size() + " new unread changes.");
|
||||
}
|
||||
|
||||
// Upload each record. This looks like batching, but it's really chained serial requests.
|
||||
@ -368,6 +513,8 @@ public class ReadingListSynchronizer {
|
||||
Logger.debug(LOG_TAG, "No new items to upload. Skipping.");
|
||||
delegate.next();
|
||||
return;
|
||||
} else {
|
||||
Logger.debug(LOG_TAG, "Uploading " + toUpload.size() + " new items.");
|
||||
}
|
||||
|
||||
final ReadingListChangeAccumulator acc = this.local.getChangeAccumulator();
|
||||
@ -420,9 +567,79 @@ public class ReadingListSynchronizer {
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadModified(final StageDelegate delegate) {
|
||||
// TODO
|
||||
delegate.next();
|
||||
protected void uploadModified(final StageDelegate delegate) {
|
||||
try {
|
||||
// This looks strange because modified includes material changes and
|
||||
// status changes, but this is called after status changes have been
|
||||
// uploaded and removed from local storage. So what's left should be
|
||||
// material changes. Even so, it should be safe to upload status changes
|
||||
// here.
|
||||
final Cursor cursor = this.local.getModified();
|
||||
|
||||
if (cursor == null) {
|
||||
delegate.fail(new RuntimeException("Unable to get modified item cursor."));
|
||||
return;
|
||||
}
|
||||
|
||||
final Queue<ClientReadingListRecord> toUpload = collectModifiedFromCursor(cursor);
|
||||
|
||||
// Nothing to do.
|
||||
if (toUpload.isEmpty()) {
|
||||
Logger.debug(LOG_TAG, "No modified items to upload. Skipping.");
|
||||
delegate.next();
|
||||
return;
|
||||
} else {
|
||||
Logger.debug(LOG_TAG, "Uploading " + toUpload.size() + " modified items.");
|
||||
}
|
||||
|
||||
final ReadingListChangeAccumulator acc = this.local.getChangeAccumulator();
|
||||
final ModifiedUploadDelegate uploadDelegate = new ModifiedUploadDelegate(acc, new StageDelegate() {
|
||||
private boolean tryFlushChanges() {
|
||||
Logger.debug(LOG_TAG, "Flushing post-upload changes.");
|
||||
try {
|
||||
acc.finish();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Logger.warn(LOG_TAG, "Flushing changes failed! This sync went wrong.", e);
|
||||
delegate.fail(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void next() {
|
||||
Logger.debug(LOG_TAG, "Modified items uploaded successfully.");
|
||||
|
||||
if (tryFlushChanges()) {
|
||||
delegate.next();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fail() {
|
||||
Logger.warn(LOG_TAG, "Couldn't upload modified items.");
|
||||
if (tryFlushChanges()) {
|
||||
delegate.fail();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fail(Exception e) {
|
||||
Logger.warn(LOG_TAG, "Couldn't upload modified items.", e);
|
||||
if (tryFlushChanges()) {
|
||||
delegate.fail(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Handle 201 for success, 400 for invalid, 303 for redirect.
|
||||
// TODO: 200 == "was already on the server, we didn't touch it, here it is."
|
||||
// ... we need to apply it locally.
|
||||
this.remote.patch(toUpload, executor, uploadDelegate);
|
||||
} catch (Exception e) {
|
||||
delegate.fail(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void downloadIncoming(final long since, final StageDelegate delegate) {
|
||||
|
@ -28,4 +28,8 @@ public class ServerReadingListRecord extends ReadingListRecord {
|
||||
public String getAddedBy() {
|
||||
return this.fields.getString("added_by");
|
||||
}
|
||||
}
|
||||
|
||||
public String getExcerpt() {
|
||||
return this.fields.getString("excerpt");
|
||||
}
|
||||
}
|
||||
|
BIN
mobile/android/base/resources/drawable-hdpi/icon_key.png
Normal file
BIN
mobile/android/base/resources/drawable-hdpi/icon_key.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
mobile/android/base/resources/drawable-mdpi/icon_key.png
Normal file
BIN
mobile/android/base/resources/drawable-mdpi/icon_key.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
mobile/android/base/resources/drawable-xhdpi/icon_key.png
Normal file
BIN
mobile/android/base/resources/drawable-xhdpi/icon_key.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
BIN
mobile/android/base/resources/drawable-xxhdpi/icon_key.png
Normal file
BIN
mobile/android/base/resources/drawable-xxhdpi/icon_key.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
@ -16,11 +16,11 @@
|
||||
android:paddingRight="@dimen/doorhanger_padding"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView android:id="@+id/doorhanger_title"
|
||||
<TextView android:id="@+id/doorhanger_message"
|
||||
android:focusable="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.Widget.DoorHanger.Medium"/>
|
||||
android:textAppearance="@style/TextAppearance.DoorHanger.Medium"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="right"
|
||||
android:paddingLeft="@dimen/doorhanger_padding"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<CheckBox android:id="@+id/doorhanger_checkbox"
|
||||
@ -38,21 +39,22 @@
|
||||
android:textColor="@color/placeholder_active_grey"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<View android:id="@+id/divider_choices"
|
||||
<View android:id="@+id/divider_buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/divider_light"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<LinearLayout android:id="@+id/doorhanger_choices"
|
||||
<LinearLayout android:id="@+id/doorhanger_buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<View android:id="@+id/divider_doorhanger"
|
||||
<View android:id="@+id/divider_doorhanger"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/fennec_ui_orange"
|
||||
android:background="@color/divider_light"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</merge>
|
||||
|
@ -4,7 +4,7 @@
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<Button xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="48dip"
|
||||
android:textColor="@color/placeholder_active_grey"
|
||||
|
67
mobile/android/base/resources/layout/login_doorhanger.xml
Normal file
67
mobile/android/base/resources/layout/login_doorhanger.xml
Normal file
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<LinearLayout android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="@dimen/doorhanger_section_padding_small"
|
||||
android:paddingLeft="@dimen/doorhanger_section_padding_small">
|
||||
|
||||
<ImageView android:id="@+id/doorhanger_icon"
|
||||
android:layout_width="@dimen/doorhanger_icon_size"
|
||||
android:layout_height="@dimen/doorhanger_icon_size"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:paddingRight="@dimen/doorhanger_section_padding_small"
|
||||
android:src="@drawable/icon_key"/>
|
||||
|
||||
<LinearLayout android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView android:id="@+id/doorhanger_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="@dimen/doorhanger_section_padding_small"
|
||||
android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Light"/>
|
||||
|
||||
<TextView android:id="@+id/doorhanger_message"
|
||||
android:focusable="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="@dimen/doorhanger_section_padding_large"
|
||||
android:textAppearance="@style/TextAppearance.DoorHanger.Medium"/>
|
||||
|
||||
<TextView android:id="@+id/doorhanger_login"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.DoorHanger.Medium"
|
||||
android:paddingBottom="@dimen/doorhanger_section_padding_large"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View android:id="@+id/divider_buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/divider_light"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<LinearLayout android:id="@+id/doorhanger_buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<View android:id="@+id/divider_doorhanger"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/divider_light"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</merge>
|
@ -9,8 +9,6 @@
|
||||
android:id="@+id/sharedialog"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false">
|
||||
|
||||
@ -18,6 +16,8 @@
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|center"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:paddingTop="8dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
@ -6,63 +6,74 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/doorhanger_padding">
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView android:id="@+id/larry"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/larry"
|
||||
android:paddingRight="@dimen/doorhanger_padding"/>
|
||||
<LinearLayout android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/doorhanger_padding">
|
||||
|
||||
<FrameLayout android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0">
|
||||
<ImageView android:id="@+id/larry"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/larry"
|
||||
android:paddingRight="@dimen/doorhanger_padding"/>
|
||||
|
||||
<include layout="@layout/site_identity_unknown" />
|
||||
<FrameLayout android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.0">
|
||||
|
||||
<LinearLayout android:id="@+id/site_identity_known_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<include layout="@layout/site_identity_unknown" />
|
||||
|
||||
<TextView android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/placeholder_active_grey"
|
||||
android:text="@string/identity_connected_to"/>
|
||||
<LinearLayout android:id="@+id/site_identity_known_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView android:id="@+id/host"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="20sp"
|
||||
android:textColor="@color/placeholder_active_grey"
|
||||
android:textStyle="bold"/>
|
||||
<TextView android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/placeholder_active_grey"
|
||||
android:text="@string/identity_connected_to"/>
|
||||
|
||||
<TextView android:id="@+id/owner_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/placeholder_active_grey"
|
||||
android:text="@string/identity_run_by"
|
||||
android:paddingTop="12dip"/>
|
||||
<TextView android:id="@+id/host"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="20sp"
|
||||
android:textColor="@color/placeholder_active_grey"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
<TextView android:id="@+id/owner"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/placeholder_active_grey"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"/>
|
||||
<TextView android:id="@+id/owner_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/placeholder_active_grey"
|
||||
android:text="@string/identity_run_by"
|
||||
android:paddingTop="12dip"/>
|
||||
|
||||
<TextView android:id="@+id/verifier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/placeholder_active_grey"
|
||||
android:paddingTop="12dip"/>
|
||||
<TextView android:id="@+id/owner"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/placeholder_active_grey"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
</LinearLayout>
|
||||
<TextView android:id="@+id/verifier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/placeholder_active_grey"
|
||||
android:paddingTop="12dip"/>
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<View android:id="@+id/divider_doorhanger"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/divider_light"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -100,6 +100,10 @@
|
||||
<dimen name="doorhanger_padding">15dp</dimen>
|
||||
<dimen name="doorhanger_offsetX">10dp</dimen>
|
||||
<dimen name="doorhanger_offsetY">7dp</dimen>
|
||||
<dimen name="doorhanger_drawable_padding">5dp</dimen>
|
||||
<dimen name="doorhanger_section_padding_small">20dp</dimen>
|
||||
<dimen name="doorhanger_section_padding_large">30dp</dimen>
|
||||
<dimen name="doorhanger_icon_size">60dp</dimen>
|
||||
|
||||
<dimen name="flow_layout_spacing">6dp</dimen>
|
||||
<dimen name="menu_item_icon">21dp</dimen>
|
||||
|
@ -410,14 +410,21 @@
|
||||
<item name="android:textColor">?android:attr/textColorHint</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Widget.DoorHanger.Medium" parent="TextAppearance.Medium">
|
||||
<style name="TextAppearance.DoorHanger">
|
||||
<item name="android:textColor">@color/placeholder_active_grey</item>
|
||||
<item name="android:textColorLink">@color/doorhanger_link</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Widget.DoorHanger.Small" parent="TextAppearance.Small">
|
||||
<item name="android:textColor">@color/placeholder_active_grey</item>
|
||||
<item name="android:textColorLink">@color/doorhanger_link</item>
|
||||
<style name="TextAppearance.DoorHanger.Medium">
|
||||
<item name="android:textSize">16dp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.DoorHanger.Medium.Light">
|
||||
<item name="android:fontFamily">sans-serif-light</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.DoorHanger.Small">
|
||||
<item name="android:textSize">14sp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.UrlBar.Title" parent="TextAppearance.Small">
|
||||
@ -817,7 +824,7 @@
|
||||
</style>
|
||||
|
||||
<!-- Make the share overlay activity appear like an overlay. -->
|
||||
<style name="ShareOverlayActivity">
|
||||
<style name="ShareOverlayActivity" parent="Gecko">
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
|
@ -30,6 +30,7 @@ import ch.boye.httpclientandroidlib.client.AuthCache;
|
||||
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpDelete;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpGet;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpPatch;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpPost;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpPut;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpRequestBase;
|
||||
@ -341,6 +342,14 @@ public class BaseResource implements Resource {
|
||||
this.go(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void patch(HttpEntity body) {
|
||||
Logger.debug(LOG_TAG, "HTTP PATCH " + this.uri.toASCIIString());
|
||||
HttpPatch request = new HttpPatch(this.uri);
|
||||
request.setEntity(body);
|
||||
this.go(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(HttpEntity body) {
|
||||
Logger.debug(LOG_TAG, "HTTP PUT " + this.uri.toASCIIString());
|
||||
@ -463,4 +472,16 @@ public class BaseResource implements Resource {
|
||||
public void post(JSONObject jsonObject) throws UnsupportedEncodingException {
|
||||
post(jsonEntity(jsonObject));
|
||||
}
|
||||
|
||||
public void patch(JSONArray jsonArray) throws UnsupportedEncodingException {
|
||||
patch(jsonEntity(jsonArray));
|
||||
}
|
||||
|
||||
public void patch(ExtendedJSONObject o) {
|
||||
patch(jsonEntity(o));
|
||||
}
|
||||
|
||||
public void patch(JSONObject jsonObject) throws UnsupportedEncodingException {
|
||||
patch(jsonEntity(jsonObject));
|
||||
}
|
||||
}
|
||||
|
@ -15,5 +15,6 @@ public interface Resource {
|
||||
public abstract void get();
|
||||
public abstract void delete();
|
||||
public abstract void post(HttpEntity body);
|
||||
public abstract void patch(HttpEntity body);
|
||||
public abstract void put(HttpEntity body);
|
||||
}
|
||||
|
@ -190,6 +190,11 @@ public class SyncStorageRequest implements Resource {
|
||||
this.resource.post(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void patch(HttpEntity body) {
|
||||
this.resource.patch(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(HttpEntity body) {
|
||||
this.resource.put(body);
|
||||
|
@ -5,17 +5,26 @@
|
||||
|
||||
package org.mozilla.gecko.tabqueue;
|
||||
|
||||
import org.mozilla.gecko.BrowserApp;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
import org.mozilla.gecko.GeckoSharedPrefs;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.preferences.GeckoPreferences;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
import org.json.JSONArray;
|
||||
import org.mozilla.gecko.BrowserApp;
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class TabQueueHelper {
|
||||
private static final String LOGTAG = "Gecko" + TabQueueHelper.class.getSimpleName();
|
||||
@ -24,6 +33,8 @@ public class TabQueueHelper {
|
||||
public static final String LOAD_URLS_ACTION = "TAB_QUEUE_LOAD_URLS_ACTION";
|
||||
public static final int TAB_QUEUE_NOTIFICATION_ID = R.id.tabQueueNotification;
|
||||
|
||||
public static final String PREF_TAB_QUEUE_COUNT = "tab_queue_count";
|
||||
|
||||
/**
|
||||
* Reads file and converts any content to JSON, adds passed in URL to the data and writes back to the file,
|
||||
* creating the file if it doesn't already exist. This should not be run on the UI thread.
|
||||
@ -52,7 +63,9 @@ public class TabQueueHelper {
|
||||
* @param context
|
||||
* @param tabsQueued
|
||||
*/
|
||||
static public void showNotification(Context context, int tabsQueued) {
|
||||
public static void showNotification(final Context context, final int tabsQueued) {
|
||||
ThreadUtils.assertNotOnUiThread();
|
||||
|
||||
Intent resultIntent = new Intent(context, BrowserApp.class);
|
||||
resultIntent.setAction(TabQueueHelper.LOAD_URLS_ACTION);
|
||||
|
||||
@ -60,7 +73,7 @@ public class TabQueueHelper {
|
||||
|
||||
String title, text;
|
||||
final Resources resources = context.getResources();
|
||||
if(tabsQueued == 1) {
|
||||
if (tabsQueued == 1) {
|
||||
title = resources.getString(R.string.tab_queue_notification_title_singular);
|
||||
text = resources.getString(R.string.tab_queue_notification_text_singular);
|
||||
} else {
|
||||
@ -69,12 +82,68 @@ public class TabQueueHelper {
|
||||
}
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
|
||||
.setSmallIcon(R.drawable.ic_status_logo)
|
||||
.setContentTitle(title)
|
||||
.setContentText(text)
|
||||
.setContentIntent(pendingIntent);
|
||||
.setSmallIcon(R.drawable.ic_status_logo)
|
||||
.setContentTitle(title)
|
||||
.setContentText(text)
|
||||
.setContentIntent(pendingIntent);
|
||||
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.notify(TabQueueHelper.TAB_QUEUE_NOTIFICATION_ID, builder.build());
|
||||
}
|
||||
|
||||
public static boolean shouldOpenTabQueueUrls(final Context context) {
|
||||
ThreadUtils.assertNotOnUiThread();
|
||||
|
||||
// TODO: Use profile shared prefs when bug 1147925 gets fixed.
|
||||
final SharedPreferences prefs = GeckoSharedPrefs.forApp(context);
|
||||
|
||||
boolean tabQueueEnabled = prefs.getBoolean(GeckoPreferences.PREFS_TAB_QUEUE, false);
|
||||
int tabsQueued = prefs.getInt(PREF_TAB_QUEUE_COUNT, 0);
|
||||
|
||||
return tabQueueEnabled && tabsQueued > 0;
|
||||
}
|
||||
|
||||
public static int getTabQueueLength(final Context context) {
|
||||
ThreadUtils.assertNotOnUiThread();
|
||||
|
||||
// TODO: Use profile shared prefs when bug 1147925 gets fixed.
|
||||
final SharedPreferences prefs = GeckoSharedPrefs.forApp(context);
|
||||
return prefs.getInt(PREF_TAB_QUEUE_COUNT, 0);
|
||||
}
|
||||
|
||||
public static void openQueuedUrls(final Context context, final GeckoProfile profile, final String filename) {
|
||||
ThreadUtils.assertNotOnUiThread();
|
||||
|
||||
// Remove the notification.
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.cancel(TAB_QUEUE_NOTIFICATION_ID);
|
||||
|
||||
// exit early if we don't have any tabs queued
|
||||
if (getTabQueueLength(context) < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSONArray jsonArray = profile.readJSONArrayFromFile(filename);
|
||||
|
||||
if (jsonArray.length() > 0) {
|
||||
JSONObject data = new JSONObject();
|
||||
try {
|
||||
data.put("urls", jsonArray);
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tabs:OpenMultiple", data.toString()));
|
||||
} catch (JSONException e) {
|
||||
// Don't exit early as we perform cleanup at the end of this function.
|
||||
Log.e(LOGTAG, "Error sending tab queue data", e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
profile.deleteFileFromProfileDir(filename);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(LOGTAG, "Error deleting Tab Queue data file.", e);
|
||||
}
|
||||
|
||||
// TODO: Use profile shared prefs when bug 1147925 gets fixed.
|
||||
final SharedPreferences prefs = GeckoSharedPrefs.forApp(context);
|
||||
prefs.edit().remove(PREF_TAB_QUEUE_COUNT).apply();
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ package org.mozilla.gecko.tabqueue;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.os.Handler;
|
||||
@ -22,6 +23,7 @@ import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import org.mozilla.gecko.BrowserApp;
|
||||
import org.mozilla.gecko.GeckoProfile;
|
||||
import org.mozilla.gecko.GeckoSharedPrefs;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.mozglue.ContextUtils;
|
||||
|
||||
@ -165,6 +167,13 @@ public class TabQueueService extends Service {
|
||||
final GeckoProfile profile = GeckoProfile.get(applicationContext);
|
||||
int tabsQueued = TabQueueHelper.queueURL(profile, intentData, filename);
|
||||
TabQueueHelper.showNotification(applicationContext, tabsQueued);
|
||||
|
||||
// Store the number of URLs queued so that we don't have to read and process the file to see if we have
|
||||
// any urls to open.
|
||||
// TODO: Use profile shared prefs when bug 1147925 gets fixed.
|
||||
final SharedPreferences prefs = GeckoSharedPrefs.forApp(applicationContext);
|
||||
|
||||
prefs.edit().putInt(TabQueueHelper.PREF_TAB_QUEUE_COUNT, tabsQueued).apply();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
|
||||
public class StringHelper {
|
||||
private StringHelper() {}
|
||||
|
||||
@ -55,6 +54,7 @@ public class StringHelper {
|
||||
public static final String CONTEXT_MENU_REMOVE = "Remove";
|
||||
public static final String CONTEXT_MENU_COPY_ADDRESS = "Copy Address";
|
||||
public static final String CONTEXT_MENU_EDIT_SITE_SETTINGS = "Edit Site Settings";
|
||||
public static final String CONTEXT_MENU_SITE_SETTINGS_SAVE_PASSWORD = "Save Password";
|
||||
public static final String CONTEXT_MENU_ADD_TO_HOME_SCREEN = "Add to Home Screen";
|
||||
public static final String CONTEXT_MENU_PIN_SITE = "Pin Site";
|
||||
public static final String CONTEXT_MENU_UNPIN_SITE = "Unpin Site";
|
||||
@ -105,7 +105,8 @@ public class StringHelper {
|
||||
public static final String ROBOCOP_BLANK_PAGE_05_URL = "/robocop/robocop_blank_05.html";
|
||||
public static final String ROBOCOP_BOXES_URL = "/robocop/robocop_boxes.html";
|
||||
public static final String ROBOCOP_GEOLOCATION_URL = "/robocop/robocop_geolocation.html";
|
||||
public static final String ROBOCOP_LOGIN_URL = "/robocop/robocop_login.html";
|
||||
public static final String ROBOCOP_LOGIN_01_URL= "/robocop/robocop_login_01.html";
|
||||
public static final String ROBOCOP_LOGIN_02_URL= "/robocop/robocop_login_02.html";
|
||||
public static final String ROBOCOP_POPUP_URL = "/robocop/robocop_popup.html";
|
||||
public static final String ROBOCOP_OFFLINE_STORAGE_URL = "/robocop/robocop_offline_storage.html";
|
||||
public static final String ROBOCOP_PICTURE_LINK_URL = "/robocop/robocop_picture_link.html";
|
||||
@ -263,9 +264,9 @@ public class StringHelper {
|
||||
public static final String OFFLINE_ALLOW = "Allow";
|
||||
public static final String OFFLINE_DENY = "Don't allow";
|
||||
|
||||
public static final String LOGIN_MESSAGE = "Save password";
|
||||
public static final String LOGIN_ALLOW = "Save";
|
||||
public static final String LOGIN_DENY = "Don't save";
|
||||
public static final String LOGIN_MESSAGE = "Would you like " + BRAND_NAME + " to remember this login?";
|
||||
public static final String LOGIN_ALLOW = "Remember";
|
||||
public static final String LOGIN_DENY = "Never";
|
||||
|
||||
public static final String POPUP_MESSAGE = "prevented this site from opening";
|
||||
public static final String POPUP_ALLOW = "Show";
|
||||
|
@ -1,8 +1,8 @@
|
||||
<html>
|
||||
<script>
|
||||
function login(){
|
||||
document.login.username.value="Test";
|
||||
document.login.password.value="Test";
|
||||
document.login.username.value="Test1";
|
||||
document.login.password.value="Test2";
|
||||
document.getElementById('submit').click();
|
||||
}
|
||||
</script>
|
21
mobile/android/base/tests/robocop_login_02.html
Normal file
21
mobile/android/base/tests/robocop_login_02.html
Normal file
@ -0,0 +1,21 @@
|
||||
<html>
|
||||
<script>
|
||||
function login(){
|
||||
document.login.username.value="Test2";
|
||||
document.login.password.value="Test2";
|
||||
document.getElementById('submit').click();
|
||||
}
|
||||
</script>
|
||||
<head>
|
||||
<title>Robocop Login</title>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body onload="login()">
|
||||
<h2>User Login </h2>
|
||||
<form name="login" method="post" action="robocop_blank_02.html">
|
||||
Username: <input type="text" name="username" id="username"><br>
|
||||
Password: <input type="password" name="password" id="password"><br>
|
||||
<input type="submit" id="submit" name="submit" value="Login!">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
@ -78,12 +78,12 @@ public class testClearPrivateData extends PixelTest {
|
||||
}
|
||||
|
||||
public void clearPassword(){
|
||||
String passwordStrings[] = {"Save password", "Save", "Don't save"};
|
||||
String passwordStrings[] = { StringHelper.LOGIN_MESSAGE, StringHelper.LOGIN_ALLOW, StringHelper.LOGIN_DENY };
|
||||
String title = StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE;
|
||||
String loginUrl = getAbsoluteUrl(StringHelper.ROBOCOP_LOGIN_URL);
|
||||
String loginUrl = getAbsoluteUrl(StringHelper.ROBOCOP_LOGIN_01_URL);
|
||||
|
||||
loadCheckDismiss(passwordStrings[1], loginUrl, passwordStrings[0]);
|
||||
checkOption(passwordStrings[1], "Clear");
|
||||
checkOption(StringHelper.CONTEXT_MENU_SITE_SETTINGS_SAVE_PASSWORD, "Clear");
|
||||
loadCheckDismiss(passwordStrings[2], loginUrl, passwordStrings[0]);
|
||||
checkDevice(title, getAbsoluteUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL));
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ public class testDoorHanger extends BaseTest {
|
||||
String GEO_URL = getAbsoluteUrl(StringHelper.ROBOCOP_GEOLOCATION_URL);
|
||||
String BLANK_URL = getAbsoluteUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
|
||||
String OFFLINE_STORAGE_URL = getAbsoluteUrl(StringHelper.ROBOCOP_OFFLINE_STORAGE_URL);
|
||||
String LOGIN_URL = getAbsoluteUrl(StringHelper.ROBOCOP_LOGIN_URL);
|
||||
|
||||
blockForGeckoReady();
|
||||
|
||||
@ -63,7 +62,6 @@ public class testDoorHanger extends BaseTest {
|
||||
mAsserter.is(mSolo.searchText(GEO_MESSAGE), false, "Geolocation doorhanger notification is hidden when opening a new tab");
|
||||
*/
|
||||
|
||||
|
||||
boolean offlineAllowedByDefault = true;
|
||||
// Save offline-allow-by-default preferences first
|
||||
final String[] prefNames = { "offline-apps.allow_by_default" };
|
||||
@ -130,25 +128,24 @@ public class testDoorHanger extends BaseTest {
|
||||
mAsserter.ok(false, "exception setting preference", e.toString());
|
||||
}
|
||||
|
||||
|
||||
// Load login page
|
||||
loadUrlAndWait(LOGIN_URL);
|
||||
// Load new login page
|
||||
loadUrlAndWait(getAbsoluteUrl(StringHelper.ROBOCOP_LOGIN_01_URL));
|
||||
waitForText(StringHelper.LOGIN_MESSAGE);
|
||||
|
||||
// Test doorhanger is dismissed when tapping "Don't save"
|
||||
mSolo.clickOnButton(StringHelper.LOGIN_DENY);
|
||||
waitForTextDismissed(StringHelper.LOGIN_MESSAGE);
|
||||
mAsserter.is(mSolo.searchText(StringHelper.LOGIN_MESSAGE), false, "Login doorhanger notification is hidden when denying saving password");
|
||||
|
||||
// Load login page
|
||||
loadUrlAndWait(LOGIN_URL);
|
||||
waitForText(StringHelper.LOGIN_MESSAGE);
|
||||
|
||||
// Test doorhanger is dismissed when tapping "Save" and is no longer triggered
|
||||
// Test doorhanger is dismissed when tapping "Remember".
|
||||
mSolo.clickOnButton(StringHelper.LOGIN_ALLOW);
|
||||
waitForTextDismissed(StringHelper.LOGIN_MESSAGE);
|
||||
mAsserter.is(mSolo.searchText(StringHelper.LOGIN_MESSAGE), false, "Login doorhanger notification is hidden when allowing saving password");
|
||||
|
||||
// Load login page
|
||||
loadUrlAndWait(getAbsoluteUrl(StringHelper.ROBOCOP_LOGIN_02_URL));
|
||||
waitForText(StringHelper.LOGIN_MESSAGE);
|
||||
|
||||
// Test doorhanger is dismissed when tapping "Never".
|
||||
mSolo.clickOnButton(StringHelper.LOGIN_DENY);
|
||||
waitForTextDismissed(StringHelper.LOGIN_MESSAGE);
|
||||
mAsserter.is(mSolo.searchText(StringHelper.LOGIN_MESSAGE), false, "Login doorhanger notification is hidden when denying saving password");
|
||||
|
||||
testPopupBlocking();
|
||||
}
|
||||
|
||||
|
@ -180,7 +180,7 @@ public class testMasterPassword extends PixelTest {
|
||||
}
|
||||
|
||||
public void verifyLoginPage(String password, String badPassword) {
|
||||
String LOGIN_URL = getAbsoluteUrl("/robocop/robocop_login.html");
|
||||
String LOGIN_URL = getAbsoluteUrl(StringHelper.ROBOCOP_LOGIN_01_URL);
|
||||
String option [] = {"Save", "Don't save"};
|
||||
|
||||
doorhangerDisplayed(LOGIN_URL);// Check that the doorhanger is displayed
|
||||
|
@ -27,6 +27,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import org.mozilla.gecko.widget.DoorhangerConfig;
|
||||
|
||||
/**
|
||||
* SiteIdentityPopup is a singleton class that displays site identity data in
|
||||
@ -53,6 +54,8 @@ public class SiteIdentityPopup extends AnchoredPopup {
|
||||
private TextView mOwner;
|
||||
private TextView mVerifier;
|
||||
|
||||
private View mDivider;
|
||||
|
||||
private DoorHanger mMixedContentNotification;
|
||||
private DoorHanger mTrackingContentNotification;
|
||||
|
||||
@ -85,6 +88,7 @@ public class SiteIdentityPopup extends AnchoredPopup {
|
||||
mOwnerLabel = (TextView) mIdentityKnownContainer.findViewById(R.id.owner_label);
|
||||
mOwner = (TextView) mIdentityKnownContainer.findViewById(R.id.owner);
|
||||
mVerifier = (TextView) mIdentityKnownContainer.findViewById(R.id.verifier);
|
||||
mDivider = mIdentity.findViewById(R.id.divider_doorhanger);
|
||||
}
|
||||
|
||||
private void updateIdentity(final SiteIdentity siteIdentity) {
|
||||
@ -137,26 +141,27 @@ public class SiteIdentityPopup extends AnchoredPopup {
|
||||
private void addMixedContentNotification(boolean blocked) {
|
||||
// Remove any existing mixed content notification.
|
||||
removeMixedContentNotification();
|
||||
mMixedContentNotification = new DoorHanger(mContext, DoorHanger.Theme.DARK);
|
||||
|
||||
final DoorhangerConfig config = new DoorhangerConfig();
|
||||
int icon;
|
||||
String message;
|
||||
if (blocked) {
|
||||
icon = R.drawable.shield_enabled_doorhanger;
|
||||
message = mContext.getString(R.string.blocked_mixed_content_message_top) + "\n\n" +
|
||||
mContext.getString(R.string.blocked_mixed_content_message_bottom);
|
||||
config.setMessage(mContext.getString(R.string.blocked_mixed_content_message_top) + "\n\n" +
|
||||
mContext.getString(R.string.blocked_mixed_content_message_bottom));
|
||||
} else {
|
||||
icon = R.drawable.shield_disabled_doorhanger;
|
||||
message = mContext.getString(R.string.loaded_mixed_content_message);
|
||||
config.setMessage(mContext.getString(R.string.loaded_mixed_content_message));
|
||||
}
|
||||
|
||||
config.setLink(mContext.getString(R.string.learn_more), MIXED_CONTENT_SUPPORT_URL, "\n\n");
|
||||
config.setType(DoorHanger.Type.SITE);
|
||||
mMixedContentNotification = DoorHanger.Get(mContext, config);
|
||||
mMixedContentNotification.setIcon(icon);
|
||||
mMixedContentNotification.setMessage(message);
|
||||
mMixedContentNotification.addLink(mContext.getString(R.string.learn_more), MIXED_CONTENT_SUPPORT_URL, "\n\n");
|
||||
|
||||
addNotificationButtons(mMixedContentNotification, blocked);
|
||||
|
||||
mContent.addView(mMixedContentNotification);
|
||||
mDivider.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void removeMixedContentNotification() {
|
||||
@ -169,27 +174,30 @@ public class SiteIdentityPopup extends AnchoredPopup {
|
||||
private void addTrackingContentNotification(boolean blocked) {
|
||||
// Remove any existing tracking content notification.
|
||||
removeTrackingContentNotification();
|
||||
mTrackingContentNotification = new DoorHanger(mContext, DoorHanger.Theme.DARK);
|
||||
|
||||
final DoorhangerConfig config = new DoorhangerConfig();
|
||||
|
||||
int icon;
|
||||
String message;
|
||||
if (blocked) {
|
||||
icon = R.drawable.shield_enabled_doorhanger;
|
||||
message = mContext.getString(R.string.blocked_tracking_content_message_top) + "\n\n" +
|
||||
mContext.getString(R.string.blocked_tracking_content_message_bottom);
|
||||
config.setMessage(mContext.getString(R.string.blocked_tracking_content_message_top) + "\n\n" +
|
||||
mContext.getString(R.string.blocked_tracking_content_message_bottom));
|
||||
} else {
|
||||
icon = R.drawable.shield_disabled_doorhanger;
|
||||
message = mContext.getString(R.string.loaded_tracking_content_message_top) + "\n\n" +
|
||||
mContext.getString(R.string.loaded_tracking_content_message_bottom);
|
||||
config.setMessage(mContext.getString(R.string.loaded_tracking_content_message_top) + "\n\n" +
|
||||
mContext.getString(R.string.loaded_tracking_content_message_bottom));
|
||||
}
|
||||
|
||||
config.setLink(mContext.getString(R.string.learn_more), TRACKING_CONTENT_SUPPORT_URL, "\n\n");
|
||||
config.setType(DoorHanger.Type.SITE);
|
||||
mTrackingContentNotification = DoorHanger.Get(mContext, config);
|
||||
|
||||
mTrackingContentNotification.setIcon(icon);
|
||||
mTrackingContentNotification.setMessage(message);
|
||||
mTrackingContentNotification.addLink(mContext.getString(R.string.learn_more), TRACKING_CONTENT_SUPPORT_URL, "\n\n");
|
||||
|
||||
addNotificationButtons(mTrackingContentNotification, blocked);
|
||||
|
||||
mContent.addView(mTrackingContentNotification);
|
||||
mDivider.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void removeTrackingContentNotification() {
|
||||
@ -200,6 +208,7 @@ public class SiteIdentityPopup extends AnchoredPopup {
|
||||
}
|
||||
|
||||
private void addNotificationButtons(DoorHanger dh, boolean blocked) {
|
||||
// TODO: Add support for buttons in DoorHangerConfig.
|
||||
if (blocked) {
|
||||
dh.addButton(mContext.getString(R.string.disable_protection), "disable", mButtonClickListener);
|
||||
dh.addButton(mContext.getString(R.string.keep_blocking), "keepBlocking", mButtonClickListener);
|
||||
@ -276,6 +285,7 @@ public class SiteIdentityPopup extends AnchoredPopup {
|
||||
super.dismiss();
|
||||
removeMixedContentNotification();
|
||||
removeTrackingContentNotification();
|
||||
mDivider.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private class PopupButtonListener implements OnButtonClickListener {
|
||||
|
132
mobile/android/base/widget/DefaultDoorHanger.java
Normal file
132
mobile/android/base/widget/DefaultDoorHanger.java
Normal file
@ -0,0 +1,132 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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.widget;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.prompts.PromptInput;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DefaultDoorHanger extends DoorHanger {
|
||||
private static final String LOGTAG = "GeckoDefaultDoorHanger";
|
||||
|
||||
private final Resources mResources;
|
||||
private static int sSpinnerTextColor = -1;
|
||||
|
||||
private List<PromptInput> mInputs;
|
||||
private CheckBox mCheckBox;
|
||||
|
||||
public DefaultDoorHanger(Context context, DoorhangerConfig config) {
|
||||
this(context, config, Type.DEFAULT);
|
||||
}
|
||||
|
||||
public DefaultDoorHanger(Context context, DoorhangerConfig config, Type type) {
|
||||
super(context, config, type);
|
||||
|
||||
mResources = getResources();
|
||||
|
||||
if (sSpinnerTextColor == -1) {
|
||||
sSpinnerTextColor = getResources().getColor(R.color.text_color_primary_disable_only);
|
||||
}
|
||||
loadConfig(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadConfig(DoorhangerConfig config) {
|
||||
final String message = config.getMessage();
|
||||
if (message != null) {
|
||||
setMessage(message);
|
||||
}
|
||||
|
||||
final JSONObject options = config.getOptions();
|
||||
if (options != null) {
|
||||
setOptions(options);
|
||||
}
|
||||
|
||||
final DoorhangerConfig.Link link = config.getLink();
|
||||
if (link != null) {
|
||||
addLink(link.label, link.url, link.delimiter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PromptInput> getInputs() {
|
||||
return mInputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CheckBox getCheckBox() {
|
||||
return mCheckBox;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOptions(final JSONObject options) {
|
||||
super.setOptions(options);
|
||||
final JSONObject link = options.optJSONObject("link");
|
||||
if (link != null) {
|
||||
try {
|
||||
final String linkLabel = link.getString("label");
|
||||
final String linkUrl = link.getString("url");
|
||||
addLink(linkLabel, linkUrl, " ");
|
||||
} catch (JSONException e) { }
|
||||
}
|
||||
|
||||
final JSONArray inputs = options.optJSONArray("inputs");
|
||||
if (inputs != null) {
|
||||
mInputs = new ArrayList<PromptInput>();
|
||||
|
||||
final ViewGroup group = (ViewGroup) findViewById(R.id.doorhanger_inputs);
|
||||
group.setVisibility(VISIBLE);
|
||||
|
||||
for (int i = 0; i < inputs.length(); i++) {
|
||||
try {
|
||||
PromptInput input = PromptInput.getInput(inputs.getJSONObject(i));
|
||||
mInputs.add(input);
|
||||
|
||||
final int padding = mResources.getDimensionPixelSize(R.dimen.doorhanger_padding);
|
||||
View v = input.getView(getContext());
|
||||
styleInput(input, v);
|
||||
v.setPadding(0, 0, 0, padding);
|
||||
group.addView(v);
|
||||
} catch(JSONException ex) { }
|
||||
}
|
||||
}
|
||||
|
||||
final String checkBoxText = options.optString("checkbox");
|
||||
if (!TextUtils.isEmpty(checkBoxText)) {
|
||||
mCheckBox = (CheckBox) findViewById(R.id.doorhanger_checkbox);
|
||||
mCheckBox.setText(checkBoxText);
|
||||
mCheckBox.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void styleInput(PromptInput input, View view) {
|
||||
if (input instanceof PromptInput.MenulistInput) {
|
||||
styleDropdownInputs(input, view);
|
||||
}
|
||||
view.setPadding(0, 0, 0, mResources.getDimensionPixelSize(R.dimen.doorhanger_padding));
|
||||
}
|
||||
|
||||
private void styleDropdownInputs(PromptInput input, View view) {
|
||||
PromptInput.MenulistInput spinInput = (PromptInput.MenulistInput) input;
|
||||
|
||||
if (spinInput.textView != null) {
|
||||
spinInput.textView.setTextColor(sSpinnerTextColor);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,152 +5,128 @@
|
||||
|
||||
package org.mozilla.gecko.widget;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.prompts.PromptInput;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Rect;
|
||||
import android.text.Html;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.Html;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.URLSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.SpinnerAdapter;
|
||||
import android.widget.TextView;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.prompts.PromptInput;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DoorHanger extends LinearLayout {
|
||||
private static final String LOGTAG = "GeckoDoorHanger";
|
||||
public abstract class DoorHanger extends LinearLayout {
|
||||
|
||||
private static int sInputPadding = -1;
|
||||
private static int sSpinnerTextColor = -1;
|
||||
private static int sSpinnerTextSize = -1;
|
||||
public static DoorHanger Get(Context context, DoorhangerConfig config) {
|
||||
final Type type = config.getType();
|
||||
if (type != null) {
|
||||
switch (type) {
|
||||
case LOGIN:
|
||||
return new LoginDoorHanger(context, config);
|
||||
case SITE:
|
||||
return new DefaultDoorHanger(context, config, type);
|
||||
}
|
||||
}
|
||||
|
||||
return new DefaultDoorHanger(context, config);
|
||||
}
|
||||
|
||||
public static enum Type { DEFAULT, LOGIN, SITE }
|
||||
|
||||
public interface OnButtonClickListener {
|
||||
public void onButtonClick(DoorHanger dh, String tag);
|
||||
}
|
||||
|
||||
private static final LayoutParams sButtonParams;
|
||||
static {
|
||||
sButtonParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1.0f);
|
||||
}
|
||||
|
||||
private final TextView mTextView;
|
||||
private final ImageView mIcon;
|
||||
private final LinearLayout mChoicesLayout;
|
||||
private static final String LOGTAG = "GeckoDoorHanger";
|
||||
|
||||
// Divider between doorhangers.
|
||||
private final View mDivider;
|
||||
|
||||
// The tab associated with this notification.
|
||||
private final LinearLayout mButtonsContainer;
|
||||
|
||||
// The tab this doorhanger is associated with.
|
||||
private final int mTabId;
|
||||
|
||||
// Value used to identify the notification.
|
||||
private final String mValue;
|
||||
// DoorHanger identifier.
|
||||
private final String mIdentifier;
|
||||
|
||||
private final Resources mResources;
|
||||
private final ImageView mIcon;
|
||||
private final TextView mMessage;
|
||||
|
||||
private List<PromptInput> mInputs;
|
||||
private CheckBox mCheckBox;
|
||||
protected Context mContext;
|
||||
|
||||
private int mPersistence;
|
||||
private boolean mPersistWhileVisible;
|
||||
private long mTimeout;
|
||||
protected int mDividerColor;
|
||||
|
||||
// Color used for dividers above and between buttons.
|
||||
private int mDividerColor;
|
||||
protected boolean mPersistWhileVisible;
|
||||
protected int mPersistenceCount;
|
||||
protected long mTimeout;
|
||||
|
||||
public static enum Theme {
|
||||
LIGHT,
|
||||
DARK
|
||||
}
|
||||
|
||||
public interface OnButtonClickListener {
|
||||
public void onButtonClick(DoorHanger dh, String tag);
|
||||
}
|
||||
|
||||
public DoorHanger(Context context, Theme theme) {
|
||||
this(context, 0, null, theme);
|
||||
}
|
||||
|
||||
public DoorHanger(Context context, int tabId, String value) {
|
||||
this(context, tabId, value, Theme.LIGHT);
|
||||
}
|
||||
|
||||
private DoorHanger(Context context, int tabId, String value, Theme theme) {
|
||||
protected DoorHanger(Context context, DoorhangerConfig config, Type type) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
mTabId = config.getTabId();
|
||||
mIdentifier = config.getId();
|
||||
|
||||
mTabId = tabId;
|
||||
mValue = value;
|
||||
mResources = getResources();
|
||||
|
||||
if (sInputPadding == -1) {
|
||||
sInputPadding = mResources.getDimensionPixelSize(R.dimen.doorhanger_padding);
|
||||
}
|
||||
if (sSpinnerTextColor == -1) {
|
||||
sSpinnerTextColor = mResources.getColor(R.color.text_color_primary_disable_only);
|
||||
}
|
||||
if (sSpinnerTextSize == -1) {
|
||||
sSpinnerTextSize = mResources.getDimensionPixelSize(R.dimen.doorhanger_spinner_textsize);
|
||||
int resource;
|
||||
switch (type) {
|
||||
case LOGIN:
|
||||
resource = R.layout.login_doorhanger;
|
||||
break;
|
||||
default:
|
||||
resource = R.layout.doorhanger;
|
||||
}
|
||||
|
||||
setOrientation(VERTICAL);
|
||||
|
||||
LayoutInflater.from(context).inflate(R.layout.doorhanger, this);
|
||||
mTextView = (TextView) findViewById(R.id.doorhanger_title);
|
||||
mIcon = (ImageView) findViewById(R.id.doorhanger_icon);
|
||||
mChoicesLayout = (LinearLayout) findViewById(R.id.doorhanger_choices);
|
||||
LayoutInflater.from(context).inflate(resource, this);
|
||||
mDivider = findViewById(R.id.divider_doorhanger);
|
||||
|
||||
setTheme(theme);
|
||||
}
|
||||
|
||||
private void setTheme(Theme theme) {
|
||||
if (theme == Theme.LIGHT) {
|
||||
// The default styles declared in doorhanger.xml are light-themed, so we just
|
||||
// need to set the divider color that we'll use in addButton.
|
||||
mDividerColor = mResources.getColor(R.color.divider_light);
|
||||
|
||||
} else if (theme == Theme.DARK) {
|
||||
mDividerColor = mResources.getColor(R.color.divider_dark);
|
||||
|
||||
// Set a dark background, and use a smaller text size for dark-themed DoorHangers.
|
||||
setBackgroundColor(mResources.getColor(R.color.doorhanger_background_dark));
|
||||
mTextView.setTextAppearance(getContext(), R.style.TextAppearance_Widget_DoorHanger_Small);
|
||||
|
||||
// Set the inter-doorhanger divider color
|
||||
mDivider.setBackgroundColor(mDividerColor);
|
||||
mIcon = (ImageView) findViewById(R.id.doorhanger_icon);
|
||||
mMessage = (TextView) findViewById(R.id.doorhanger_message);
|
||||
if (type == Type.SITE) {
|
||||
mMessage.setTextAppearance(getContext(), R.style.TextAppearance_DoorHanger_Small);
|
||||
}
|
||||
mButtonsContainer = (LinearLayout) findViewById(R.id.doorhanger_buttons);
|
||||
|
||||
mDividerColor = getResources().getColor(R.color.divider_light);
|
||||
setOrientation(VERTICAL);
|
||||
}
|
||||
|
||||
abstract protected void loadConfig(DoorhangerConfig config);
|
||||
|
||||
protected void setOptions(final JSONObject options) {
|
||||
final int persistence = options.optInt("persistence");
|
||||
if (persistence > 0) {
|
||||
mPersistenceCount = persistence;
|
||||
}
|
||||
|
||||
mPersistWhileVisible = options.optBoolean("persistWhileVisible");
|
||||
|
||||
final long timeout = options.optLong("timeout");
|
||||
if (timeout > 0) {
|
||||
mTimeout = timeout;
|
||||
}
|
||||
}
|
||||
|
||||
public int getTabId() {
|
||||
return mTabId;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public List<PromptInput> getInputs() {
|
||||
return mInputs;
|
||||
}
|
||||
|
||||
public CheckBox getCheckBox() {
|
||||
return mCheckBox;
|
||||
public String getIdentifier() {
|
||||
return mIdentifier;
|
||||
}
|
||||
|
||||
public void showDivider() {
|
||||
@ -161,19 +137,18 @@ public class DoorHanger extends LinearLayout {
|
||||
mDivider.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
Spanned markupMessage = Html.fromHtml(message);
|
||||
mTextView.setMovementMethod(LinkMovementMethod.getInstance()); // Necessary for clickable links
|
||||
mTextView.setText(markupMessage);
|
||||
}
|
||||
|
||||
public void setIcon(int resId) {
|
||||
mIcon.setImageResource(resId);
|
||||
mIcon.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void addLink(String label, String url, String delimiter) {
|
||||
String title = mTextView.getText().toString();
|
||||
protected void setMessage(String message) {
|
||||
Spanned markupMessage = Html.fromHtml(message);
|
||||
mMessage.setText(markupMessage);
|
||||
}
|
||||
|
||||
protected void addLink(String label, String url, String delimiter) {
|
||||
String title = mMessage.getText().toString();
|
||||
SpannableString titleWithLink = new SpannableString(title + delimiter + label);
|
||||
URLSpan linkSpan = new URLSpan(url) {
|
||||
@Override
|
||||
@ -183,12 +158,12 @@ public class DoorHanger extends LinearLayout {
|
||||
};
|
||||
|
||||
// Prevent text outside the link from flashing when clicked.
|
||||
ForegroundColorSpan colorSpan = new ForegroundColorSpan(mTextView.getCurrentTextColor());
|
||||
ForegroundColorSpan colorSpan = new ForegroundColorSpan(mMessage.getCurrentTextColor());
|
||||
titleWithLink.setSpan(colorSpan, 0, title.length(), 0);
|
||||
|
||||
titleWithLink.setSpan(linkSpan, title.length() + 1, titleWithLink.length(), 0);
|
||||
mTextView.setText(titleWithLink);
|
||||
mTextView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
mMessage.setText(titleWithLink);
|
||||
mMessage.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
public void addButton(final String text, final String tag, final OnButtonClickListener listener) {
|
||||
@ -203,126 +178,23 @@ public class DoorHanger extends LinearLayout {
|
||||
}
|
||||
});
|
||||
|
||||
if (mChoicesLayout.getChildCount() == 0) {
|
||||
if (mButtonsContainer.getChildCount() == 0) {
|
||||
// If this is the first button we're adding, make the choices layout visible.
|
||||
mChoicesLayout.setVisibility(View.VISIBLE);
|
||||
mButtonsContainer.setVisibility(View.VISIBLE);
|
||||
// Make the divider above the buttons visible.
|
||||
View divider = findViewById(R.id.divider_choices);
|
||||
View divider = findViewById(R.id.divider_buttons);
|
||||
divider.setVisibility(View.VISIBLE);
|
||||
divider.setBackgroundColor(mDividerColor);
|
||||
} else {
|
||||
// Add a vertical divider between additional buttons.
|
||||
Divider divider = new Divider(getContext(), null);
|
||||
divider.setOrientation(Divider.Orientation.VERTICAL);
|
||||
divider.setBackgroundColor(mDividerColor);
|
||||
mChoicesLayout.addView(divider);
|
||||
mButtonsContainer.addView(divider);
|
||||
}
|
||||
|
||||
mChoicesLayout.addView(button, sButtonParams);
|
||||
mButtonsContainer.addView(button, sButtonParams);
|
||||
}
|
||||
|
||||
public void setOptions(final JSONObject options) {
|
||||
final int persistence = options.optInt("persistence");
|
||||
if (persistence > 0) {
|
||||
mPersistence = persistence;
|
||||
}
|
||||
|
||||
mPersistWhileVisible = options.optBoolean("persistWhileVisible");
|
||||
|
||||
final long timeout = options.optLong("timeout");
|
||||
if (timeout > 0) {
|
||||
mTimeout = timeout;
|
||||
}
|
||||
|
||||
final JSONObject link = options.optJSONObject("link");
|
||||
if (link != null) {
|
||||
try {
|
||||
final String linkLabel = link.getString("label");
|
||||
final String linkUrl = link.getString("url");
|
||||
addLink(linkLabel, linkUrl, " ");
|
||||
} catch (JSONException e) { }
|
||||
}
|
||||
|
||||
final JSONArray inputs = options.optJSONArray("inputs");
|
||||
if (inputs != null) {
|
||||
mInputs = new ArrayList<PromptInput>();
|
||||
|
||||
final ViewGroup group = (ViewGroup) findViewById(R.id.doorhanger_inputs);
|
||||
group.setVisibility(VISIBLE);
|
||||
|
||||
for (int i = 0; i < inputs.length(); i++) {
|
||||
try {
|
||||
PromptInput input = PromptInput.getInput(inputs.getJSONObject(i));
|
||||
mInputs.add(input);
|
||||
|
||||
View v = input.getView(getContext());
|
||||
styleInput(input, v);
|
||||
group.addView(v);
|
||||
} catch(JSONException ex) { }
|
||||
}
|
||||
}
|
||||
|
||||
final String checkBoxText = options.optString("checkbox");
|
||||
if (!TextUtils.isEmpty(checkBoxText)) {
|
||||
mCheckBox = (CheckBox) findViewById(R.id.doorhanger_checkbox);
|
||||
mCheckBox.setText(checkBoxText);
|
||||
mCheckBox.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void styleInput(PromptInput input, View view) {
|
||||
if (input instanceof PromptInput.MenulistInput) {
|
||||
styleSpinner(input, view);
|
||||
} else {
|
||||
// add some top and bottom padding to separate inputs
|
||||
view.setPadding(0, sInputPadding,
|
||||
0, sInputPadding);
|
||||
}
|
||||
}
|
||||
|
||||
private void styleSpinner(PromptInput input, View view) {
|
||||
PromptInput.MenulistInput spinInput = (PromptInput.MenulistInput) input;
|
||||
|
||||
/* Spinners have some intrinsic padding. To force the spinner's text to line up with
|
||||
* the doorhanger text, we have to take that padding into account.
|
||||
*
|
||||
* |-----A-------| <-- Normal doorhanger message
|
||||
* |-B-|---C+D---| <-- (optional) Spinner Label
|
||||
* |-B-|-C-|--D--| <-- Spinner
|
||||
*
|
||||
* A - Desired padding (sInputPadding)
|
||||
* B - Final padding applied to input element (sInputPadding - rect.left - textPadding).
|
||||
* C - Spinner background drawable padding (rect.left).
|
||||
* D - Spinner inner TextView padding (textPadding).
|
||||
*/
|
||||
|
||||
// First get the padding of the selected view inside the spinner. Since the spinner
|
||||
// hasn't been shown yet, we get this view directly from the adapter.
|
||||
Spinner spinner = spinInput.spinner;
|
||||
SpinnerAdapter adapter = spinner.getAdapter();
|
||||
View dropView = adapter.getView(0, null, spinner);
|
||||
int textPadding = 0;
|
||||
if (dropView != null) {
|
||||
textPadding = dropView.getPaddingLeft();
|
||||
}
|
||||
|
||||
// Then get the intrinsic padding built into the background image of the spinner.
|
||||
Rect rect = new Rect();
|
||||
spinner.getBackground().getPadding(rect);
|
||||
|
||||
// Set the difference in padding to the spinner view to align it with doorhanger text.
|
||||
view.setPadding(sInputPadding - rect.left - textPadding, 0, rect.right, sInputPadding);
|
||||
|
||||
if (spinInput.textView != null) {
|
||||
spinInput.textView.setTextColor(sSpinnerTextColor);
|
||||
spinInput.textView.setTextSize(sSpinnerTextSize);
|
||||
|
||||
// If this spinner has a label, offset it to also be aligned with the doorhanger text.
|
||||
spinInput.textView.setPadding(rect.left + textPadding, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Checks with persistence and timeout options to see if it's okay to remove a doorhanger.
|
||||
*
|
||||
@ -332,15 +204,15 @@ public class DoorHanger extends LinearLayout {
|
||||
public boolean shouldRemove(boolean isShowing) {
|
||||
if (mPersistWhileVisible && isShowing) {
|
||||
// We still want to decrement mPersistence, even if the popup is showing
|
||||
if (mPersistence != 0)
|
||||
mPersistence--;
|
||||
if (mPersistenceCount != 0)
|
||||
mPersistenceCount--;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If persistence is set to -1, the doorhanger will never be
|
||||
// automatically removed.
|
||||
if (mPersistence != 0) {
|
||||
mPersistence--;
|
||||
if (mPersistenceCount != 0) {
|
||||
mPersistenceCount--;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -350,4 +222,14 @@ public class DoorHanger extends LinearLayout {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: remove and expose through instance Button Handler.
|
||||
public List<PromptInput> getInputs() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public CheckBox getCheckBox() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
93
mobile/android/base/widget/DoorhangerConfig.java
Normal file
93
mobile/android/base/widget/DoorhangerConfig.java
Normal file
@ -0,0 +1,93 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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.widget;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import org.mozilla.gecko.widget.DoorHanger.Type;
|
||||
|
||||
public class DoorhangerConfig {
|
||||
|
||||
public static class Link {
|
||||
public final String label;
|
||||
public final String url;
|
||||
public final String delimiter;
|
||||
|
||||
private Link(String label, String url, String delimiter) {
|
||||
this.label = label;
|
||||
this.url = url;
|
||||
this.delimiter = delimiter;
|
||||
}
|
||||
}
|
||||
|
||||
private final int tabId;
|
||||
private final String id;
|
||||
private DoorHanger.Type type;
|
||||
private String message;
|
||||
private JSONObject options;
|
||||
private Link link;
|
||||
private JSONArray buttons;
|
||||
|
||||
public DoorhangerConfig() {
|
||||
// XXX: This should only be used by SiteIdentityPopup doorhangers which
|
||||
// don't need tab or id references, until bug 1141904 unifies doorhangers.
|
||||
this(-1, null);
|
||||
}
|
||||
|
||||
public DoorhangerConfig(int tabId, String id) {
|
||||
this.tabId = tabId;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getTabId() {
|
||||
return tabId;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setType(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setOptions(JSONObject options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public JSONObject getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public void setButtons(JSONArray buttons) {
|
||||
this.buttons = buttons;
|
||||
}
|
||||
|
||||
public JSONArray getButtons() {
|
||||
return buttons;
|
||||
}
|
||||
|
||||
public void setLink(String label, String url, String delimiter) {
|
||||
this.link = new Link(label, url, delimiter);
|
||||
}
|
||||
|
||||
public Link getLink() {
|
||||
return link;
|
||||
}
|
||||
}
|
79
mobile/android/base/widget/LoginDoorHanger.java
Normal file
79
mobile/android/base/widget/LoginDoorHanger.java
Normal file
@ -0,0 +1,79 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* 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.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import ch.boye.httpclientandroidlib.util.TextUtils;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.favicons.Favicons;
|
||||
import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
|
||||
|
||||
public class LoginDoorHanger extends DoorHanger {
|
||||
private static final String LOGTAG = "LoginDoorHanger";
|
||||
|
||||
final TextView mTitle;
|
||||
final TextView mLogin;
|
||||
|
||||
public LoginDoorHanger(Context context, DoorhangerConfig config) {
|
||||
super(context, config, Type.LOGIN);
|
||||
|
||||
mTitle = (TextView) findViewById(R.id.doorhanger_title);
|
||||
mLogin = (TextView) findViewById(R.id.doorhanger_login);
|
||||
|
||||
loadConfig(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadConfig(DoorhangerConfig config) {
|
||||
setOptions(config.getOptions());
|
||||
setMessage(config.getMessage());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setOptions(final JSONObject options) {
|
||||
super.setOptions(options);
|
||||
|
||||
final JSONObject titleObj = options.optJSONObject("title");
|
||||
if (titleObj != null) {
|
||||
|
||||
try {
|
||||
final String text = titleObj.getString("text");
|
||||
mTitle.setText(text);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "Error loading title from options JSON");
|
||||
}
|
||||
|
||||
final String resource = titleObj.optString("resource");
|
||||
if (resource != null) {
|
||||
Favicons.getSizedFaviconForPageFromLocal(mContext, resource, 32, new OnFaviconLoadedListener() {
|
||||
@Override
|
||||
public void onFaviconLoaded(String url, String faviconURL, Bitmap favicon) {
|
||||
if (favicon != null) {
|
||||
mTitle.setCompoundDrawablesWithIntrinsicBounds(new BitmapDrawable(mContext.getResources(), favicon), null, null, null);
|
||||
mTitle.setCompoundDrawablePadding((int) mContext.getResources().getDimension(R.dimen.doorhanger_drawable_padding));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
final String subtext = options.optString("subtext");
|
||||
if (!TextUtils.isEmpty(subtext)) {
|
||||
mLogin.setText(subtext);
|
||||
mLogin.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mLogin.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
@ -136,7 +136,8 @@ let Passwords = {
|
||||
let menuItems = [
|
||||
{ label: gStringBundle.GetStringFromName("passwordsMenu.copyPassword") },
|
||||
{ label: gStringBundle.GetStringFromName("passwordsMenu.copyUsername") },
|
||||
{ label: gStringBundle.GetStringFromName("passwordsMenu.details") } ];
|
||||
{ label: gStringBundle.GetStringFromName("passwordsMenu.details") },
|
||||
{ label: gStringBundle.GetStringFromName("passwordsMenu.delete") } ];
|
||||
|
||||
prompt.setSingleChoiceItems(menuItems);
|
||||
prompt.show((data) => {
|
||||
@ -152,6 +153,21 @@ let Passwords = {
|
||||
this._showDetails(loginItem);
|
||||
history.pushState({ id: login.guid }, document.title);
|
||||
break;
|
||||
case 3:
|
||||
let confirmPrompt = new Prompt({
|
||||
window: window,
|
||||
message: gStringBundle.GetStringFromName("passwordsDialog.confirmDelete"),
|
||||
buttons: [
|
||||
gStringBundle.GetStringFromName("passwordsDialog.confirm"),
|
||||
gStringBundle.GetStringFromName("passwordsDialog.cancel") ]
|
||||
});
|
||||
confirmPrompt.show((data) => {
|
||||
switch (data.button) {
|
||||
case 0:
|
||||
// Corresponds to "confirm" button.
|
||||
Services.logins.removeLogin(login);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2213,11 +2213,24 @@ var NativeWindow = {
|
||||
* persist across location changes.
|
||||
* timeout: A time in milliseconds. The notification will not
|
||||
* automatically dismiss before this time.
|
||||
*
|
||||
* checkbox: A string to appear next to a checkbox under the notification
|
||||
* message. The button callback functions will be called with
|
||||
* the checked state as an argument.
|
||||
*
|
||||
* title: An object that specifies text to display as the title, and
|
||||
* optionally a resource, such as a favicon cache url that can be
|
||||
* used to fetch a favicon from the FaviconCache. (This can be
|
||||
* generalized to other resources if the situation arises.)
|
||||
* { text: <title>,
|
||||
* resource: <resource_url> }
|
||||
*
|
||||
* subtext: A string to appear below the doorhanger message.
|
||||
*
|
||||
* @param aCategory
|
||||
* Doorhanger type to display (e.g., LOGIN)
|
||||
*/
|
||||
show: function(aMessage, aValue, aButtons, aTabID, aOptions) {
|
||||
show: function(aMessage, aValue, aButtons, aTabID, aOptions, aCategory) {
|
||||
if (aButtons == null) {
|
||||
aButtons = [];
|
||||
}
|
||||
@ -2236,7 +2249,8 @@ var NativeWindow = {
|
||||
buttons: aButtons,
|
||||
// use the current tab if none is provided
|
||||
tabID: aTabID || BrowserApp.selectedTab.id,
|
||||
options: aOptions || {}
|
||||
options: aOptions || {},
|
||||
category: aCategory
|
||||
};
|
||||
Messaging.sendRequest(json);
|
||||
},
|
||||
|
@ -63,8 +63,12 @@ LoginManagerPrompter.prototype = {
|
||||
if (!this.__strBundle) {
|
||||
var bunService = Cc["@mozilla.org/intl/stringbundle;1"].
|
||||
getService(Ci.nsIStringBundleService);
|
||||
this.__strBundle = bunService.createBundle(
|
||||
"chrome://passwordmgr/locale/passwordmgr.properties");
|
||||
this.__strBundle = {
|
||||
pwmgr : bunService.createBundle(
|
||||
"chrome://passwordmgr/locale/passwordmgr.properties"),
|
||||
brand : bunService.createBundle("chrome://branding/locale/brand.properties")
|
||||
};
|
||||
|
||||
if (!this.__strBundle)
|
||||
throw "String bundle for Login Manager not present!";
|
||||
}
|
||||
@ -136,9 +140,19 @@ LoginManagerPrompter.prototype = {
|
||||
* _showLoginNotification
|
||||
*
|
||||
* Displays a notification doorhanger.
|
||||
* @param aName
|
||||
* Name of notification
|
||||
* @param aTitle
|
||||
* Object with title and optional resource to display with the title, such as a favicon key
|
||||
* @param aBody
|
||||
* String message to be displayed in the doorhanger
|
||||
* @param aButtons
|
||||
* Buttons to display with the doorhanger
|
||||
* @param aSubtext
|
||||
* String to be displayed below the aBody message
|
||||
*
|
||||
*/
|
||||
_showLoginNotification : function (aName, aText, aButtons) {
|
||||
_showLoginNotification : function (aName, aTitle, aBody, aButtons, aSubtext) {
|
||||
this.log("Adding new " + aName + " notification bar");
|
||||
let notifyWin = this._window.top;
|
||||
let chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject;
|
||||
@ -155,12 +169,14 @@ LoginManagerPrompter.prototype = {
|
||||
|
||||
let options = {
|
||||
persistWhileVisible: true,
|
||||
timeout: Date.now() + 10000
|
||||
timeout: Date.now() + 10000,
|
||||
title: aTitle,
|
||||
subtext: aSubtext
|
||||
}
|
||||
|
||||
var nativeWindow = this._getNativeWindow();
|
||||
if (nativeWindow)
|
||||
nativeWindow.doorhanger.show(aText, aName, aButtons, tabID, options);
|
||||
nativeWindow.doorhanger.show(aBody, aName, aButtons, tabID, options, "LOGIN");
|
||||
},
|
||||
|
||||
|
||||
@ -173,15 +189,16 @@ LoginManagerPrompter.prototype = {
|
||||
*
|
||||
*/
|
||||
_showSaveLoginNotification : function (aLogin) {
|
||||
var displayHost = this._getShortDisplayHost(aLogin.hostname);
|
||||
var notificationText;
|
||||
if (aLogin.username) {
|
||||
var displayUser = this._sanitizeUsername(aLogin.username);
|
||||
notificationText = this._getLocalizedString("savePassword", [displayUser, displayHost]);
|
||||
} else {
|
||||
notificationText = this._getLocalizedString("savePasswordNoUser", [displayHost]);
|
||||
}
|
||||
let brandShortName = this._strBundle.brand.GetStringFromName("brandShortName");
|
||||
let notificationText = this._getLocalizedString("saveLogin", [brandShortName]);
|
||||
|
||||
let displayHost = this._getShortDisplayHost(aLogin.hostname);
|
||||
let title = { text: displayHost, resource: aLogin.hostname };
|
||||
let subtext = null;
|
||||
|
||||
if (aLogin.username) {
|
||||
subtext = this._sanitizeUsername(aLogin.username);
|
||||
}
|
||||
// The callbacks in |buttons| have a closure to access the variables
|
||||
// in scope here; set one to |this._pwmgr| so we can get back to pwmgr
|
||||
// without a getService() call.
|
||||
@ -190,22 +207,22 @@ LoginManagerPrompter.prototype = {
|
||||
|
||||
var buttons = [
|
||||
{
|
||||
label: this._getLocalizedString("saveButton"),
|
||||
label: this._getLocalizedString("neverButton"),
|
||||
callback: function() {
|
||||
promptHistogram.add(PROMPT_NEVER);
|
||||
pwmgr.setLoginSavingEnabled(aLogin.hostname, false);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this._getLocalizedString("rememberButton"),
|
||||
callback: function() {
|
||||
pwmgr.addLogin(aLogin);
|
||||
promptHistogram.add(PROMPT_ADD);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this._getLocalizedString("dontSaveButton"),
|
||||
callback: function() {
|
||||
promptHistogram.add(PROMPT_NOTNOW);
|
||||
// Don't set a permanent exception
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
this._showLoginNotification("password-save", notificationText, buttons);
|
||||
this._showLoginNotification("password-save", title, notificationText, buttons, subtext);
|
||||
},
|
||||
|
||||
/*
|
||||
@ -236,6 +253,9 @@ LoginManagerPrompter.prototype = {
|
||||
notificationText = this._getLocalizedString("updatePasswordNoUser");
|
||||
}
|
||||
|
||||
let displayHost = this._getShortDisplayHost(aOldLogin.hostname);
|
||||
let title = { text: displayHost, resource: aOldLogin.hostname };
|
||||
|
||||
// The callbacks in |buttons| have a closure to access the variables
|
||||
// in scope here; set one to |this._pwmgr| so we can get back to pwmgr
|
||||
// without a getService() call.
|
||||
@ -243,23 +263,23 @@ LoginManagerPrompter.prototype = {
|
||||
let promptHistogram = Services.telemetry.getHistogramById("PWMGR_PROMPT_UPDATE_ACTION");
|
||||
|
||||
var buttons = [
|
||||
{
|
||||
label: this._getLocalizedString("updateButton"),
|
||||
callback: function() {
|
||||
self._updateLogin(aOldLogin, aNewPassword);
|
||||
promptHistogram.add(PROMPT_UPDATE);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this._getLocalizedString("dontUpdateButton"),
|
||||
callback: function() {
|
||||
promptHistogram.add(PROMPT_NOTNOW);
|
||||
// do nothing
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this._getLocalizedString("updateButton"),
|
||||
callback: function() {
|
||||
self._updateLogin(aOldLogin, aNewPassword);
|
||||
promptHistogram.add(PROMPT_UPDATE);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
this._showLoginNotification("password-change", notificationText, buttons);
|
||||
this._showLoginNotification("password-change", title, notificationText, buttons);
|
||||
},
|
||||
|
||||
|
||||
@ -377,10 +397,10 @@ LoginManagerPrompter.prototype = {
|
||||
*/
|
||||
_getLocalizedString : function (key, formatArgs) {
|
||||
if (formatArgs)
|
||||
return this._strBundle.formatStringFromName(
|
||||
return this._strBundle.pwmgr.formatStringFromName(
|
||||
key, formatArgs, formatArgs.length);
|
||||
else
|
||||
return this._strBundle.GetStringFromName(key);
|
||||
return this._strBundle.pwmgr.GetStringFromName(key);
|
||||
},
|
||||
|
||||
|
||||
|
@ -88,6 +88,7 @@ SessionStore.prototype = {
|
||||
observerService.addObserver(this, "ClosedTabs:StopNotifications", true);
|
||||
observerService.addObserver(this, "last-pb-context-exited", true);
|
||||
observerService.addObserver(this, "Session:RestoreRecentTabs", true);
|
||||
observerService.addObserver(this, "Tabs:OpenMultiple", true);
|
||||
break;
|
||||
case "final-ui-startup":
|
||||
observerService.removeObserver(this, "final-ui-startup");
|
||||
@ -157,6 +158,11 @@ SessionStore.prototype = {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Tabs:OpenMultiple": {
|
||||
let data = JSON.parse(aData);
|
||||
this._openTabs(data);
|
||||
break;
|
||||
}
|
||||
case "application-background":
|
||||
// We receive this notification when Android's onPause callback is
|
||||
// executed. After onPause, the application may be terminated at any
|
||||
@ -910,6 +916,21 @@ SessionStore.prototype = {
|
||||
return shEntry;
|
||||
},
|
||||
|
||||
// This function iterates through a list of urls opening a new tab for each.
|
||||
_openTabs: function ss_openTabs(aData) {
|
||||
let window = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
for (let i = 0; i < aData.urls.length; i++) {
|
||||
let url = aData.urls[i];
|
||||
let params = {
|
||||
selected: (i == aData.urls.length - 1),
|
||||
isPrivate: false,
|
||||
desktopMode: false,
|
||||
};
|
||||
|
||||
let tab = window.BrowserApp.addTab(url, params);
|
||||
}
|
||||
},
|
||||
|
||||
// This function iterates through a list of tab data restoring session for each of them.
|
||||
_restoreTabs: function ss_restoreTabs(aData) {
|
||||
let window = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
|
@ -5,6 +5,11 @@
|
||||
passwordsMenu.copyPassword=Copy password
|
||||
passwordsMenu.copyUsername=Copy username
|
||||
passwordsMenu.details=Details
|
||||
passwordsMenu.delete=Delete
|
||||
|
||||
passwordsDialog.confirmDelete=Delete this login?
|
||||
passwordsDialog.confirm=OK
|
||||
passwordsDialog.cancel=Cancel
|
||||
|
||||
passwordsDetails.age=Age: %S days
|
||||
|
||||
|
@ -197,18 +197,17 @@ body {
|
||||
.content p > a:only-child > img:only-child,
|
||||
.content .wp-caption img,
|
||||
.content figure img {
|
||||
max-width: none !important;
|
||||
height: auto !important;
|
||||
display: block !important;
|
||||
margin-top: 0px !important;
|
||||
margin-bottom: 32px !important;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
/* If image is place inside one of these blocks
|
||||
there's no need to add margin at the bottom */
|
||||
.content .wp-caption img,
|
||||
.content figure img {
|
||||
margin-bottom: 0px !important;
|
||||
/* Account for body padding to make image full width */
|
||||
.content img[moz-reader-full-width] {
|
||||
width: calc(100% + 40px);
|
||||
margin-left: -20px;
|
||||
margin-right: -20px;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
/* Image caption text */
|
||||
|
@ -2,13 +2,10 @@
|
||||
# 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/.
|
||||
|
||||
# 1st string is the username for the login, 2nd is the login's hostname.
|
||||
# Note that long usernames may be truncated.
|
||||
savePassword=Save password for "%1$S" on %2$S?
|
||||
# String is the login's hostname
|
||||
savePasswordNoUser=Save password on %S?
|
||||
saveButton=Save
|
||||
dontSaveButton=Don't save
|
||||
# String will be replaced by brandShortName.
|
||||
saveLogin=Would you like %S to remember this login?
|
||||
rememberButton=Remember
|
||||
neverButton=Never
|
||||
|
||||
# String is the login's hostname
|
||||
updatePassword=Update saved password for %S?
|
||||
|
@ -10,6 +10,7 @@ COPY mozharness_configs /home/worker/mozharness_configs
|
||||
COPY buildprops.json /home/worker/buildprops.json
|
||||
ADD https://s3-us-west-2.amazonaws.com/test-caching/packages/linux64-stackwalk /usr/local/bin/linux64-minidump_stackwalk
|
||||
ADD https://raw.githubusercontent.com/taskcluster/buildbot-step/master/buildbot_step /home/worker/bin/buildbot_step
|
||||
COPY tc-vcs-config.yml /etc/taskcluster-vcs.yml
|
||||
|
||||
|
||||
# Run test setup script
|
||||
|
@ -1 +1 @@
|
||||
0.2.9
|
||||
0.2.10
|
||||
|
40
testing/docker/tester/tc-vcs-config.yml
Normal file
40
testing/docker/tester/tc-vcs-config.yml
Normal file
@ -0,0 +1,40 @@
|
||||
# Default configuration used by the tc-vs tools these can be overridden by
|
||||
# passing the config you wish to use over the command line...
|
||||
git: git
|
||||
hg: hg
|
||||
|
||||
repoCache:
|
||||
# Repo url to clone when running repo init..
|
||||
repoUrl: https://git.mozilla.org/external/google/gerrit/git-repo.git
|
||||
# Version of repo to utilize...
|
||||
repoRevision: master
|
||||
# The root where all downloaded cache files are stored on the local machine...
|
||||
cacheDir: '{{env.HOME}}/.tc-vcs-repo/'
|
||||
# Name/prefixed used as part of the base url.
|
||||
cacheName: sources/{{name}}.tar.gz
|
||||
# Command used to upload the tarball
|
||||
uploadTar: "curl --header 'Content-Type: application/x-tar' --header 'Content-Encoding: gzip' -X PUT --data-binary @'{{source}}' '{{url}}'"
|
||||
# Large http get requests are often slower using nodes built in http layer so
|
||||
# we utilize a subprocess which is responsible for fetching...
|
||||
get: curl --connect-timeout 30 --speed-limit 500000 -L -o {{dest}} {{url}}
|
||||
# Used to create clone tarball
|
||||
compress: tar -czf {{dest}} {{source}}
|
||||
# All cache urls use tar + gz this is the command used to extract those files
|
||||
# downloaded by the "get" command.
|
||||
extract: tar -x -z -C {{dest}} -f {{source}}
|
||||
|
||||
cloneCache:
|
||||
# The root where all downloaded cache files are stored on the local machine...
|
||||
cacheDir: '{{env.HOME}}/.tc-vcs/'
|
||||
# Command used to upload the tarball
|
||||
uploadTar: "curl --header 'Content-Type: application/x-tar' --header 'Content-Encoding: gzip' -X PUT --data-binary @'{{source}}' '{{url}}'"
|
||||
# Large http get requests are often slower using nodes built in http layer so
|
||||
# we utilize a subprocess which is responsible for fetching...
|
||||
get: curl --connect-timeout 30 --speed-limit 500000 -L -o {{dest}} {{url}}
|
||||
# Used to create clone tarball
|
||||
compress: tar -czf {{dest}} {{source}}
|
||||
# All cache urls use tar + gz this is the command used to extract those files
|
||||
# downloaded by the "get" command.
|
||||
extract: tar -x -z --strip-components 1 -C {{dest}} -f {{source}}
|
||||
# Name/prefixed used as part of the base url.
|
||||
cacheName: clones/{{name}}.tar.gz
|
71
testing/taskcluster/scripts/builder/build-emulator-x86.sh
Executable file
71
testing/taskcluster/scripts/builder/build-emulator-x86.sh
Executable file
@ -0,0 +1,71 @@
|
||||
#! /bin/bash -vex
|
||||
|
||||
# Ensure all the scripts in this dir are on the path....
|
||||
DIRNAME=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
PATH=$DIRNAME:$PATH
|
||||
|
||||
WORKSPACE=$1
|
||||
|
||||
### Check that require variables are defined
|
||||
test -d $WORKSPACE
|
||||
test $GECKO_HEAD_REPOSITORY # Should be an hg repository url to pull from
|
||||
test $GECKO_BASE_REPOSITORY # Should be an hg repository url to clone from
|
||||
test $GECKO_HEAD_REV # Should be an hg revision to pull down
|
||||
test $MOZHARNESS_REPOSITORY # mozharness repository
|
||||
test $MOZHARNESS_REV # mozharness revision
|
||||
test $TARGET
|
||||
|
||||
. setup-ccache.sh
|
||||
|
||||
# First check if the mozharness directory is available. This is intended to be
|
||||
# used locally in development to test mozharness changes:
|
||||
#
|
||||
# $ docker -v your_mozharness:/home/worker/mozharness ...
|
||||
#
|
||||
if [ ! -d mozharness ]; then
|
||||
tc-vcs checkout mozharness $MOZHARNESS_REPOSITORY $MOZHARNESS_REPOSITORY $MOZHARNESS_REV
|
||||
fi
|
||||
|
||||
# Figure out where the remote manifest is so we can use caches for it.
|
||||
|
||||
if [ -z "$MANIFEST" ]; then
|
||||
MANIFEST="$WORKSPACE/gecko/b2g/config/$TARGET/sources.xml"
|
||||
fi
|
||||
|
||||
tc-vcs repo-checkout $WORKSPACE/B2G https://git.mozilla.org/b2g/B2G.git $MANIFEST
|
||||
|
||||
# Ensure symlink has been created to gecko...
|
||||
rm -f $WORKSPACE/B2G/gecko
|
||||
ln -s $WORKSPACE/gecko $WORKSPACE/B2G/gecko
|
||||
|
||||
debug_flag=""
|
||||
if [ 0$B2G_DEBUG -ne 0 ]; then
|
||||
debug_flag='--debug'
|
||||
fi
|
||||
|
||||
./mozharness/scripts/b2g_build.py \
|
||||
--config b2g/taskcluster-emulator.py \
|
||||
"$debug_flag" \
|
||||
--disable-mock \
|
||||
--work-dir=$WORKSPACE/B2G \
|
||||
--log-level=debug \
|
||||
--target=$TARGET \
|
||||
--b2g-config-dir=$TARGET \
|
||||
--checkout-revision=$GECKO_HEAD_REV \
|
||||
--base-repo=$GECKO_BASE_REPOSITORY \
|
||||
--repo=$GECKO_HEAD_REPOSITORY
|
||||
|
||||
# Move files into artifact locations!
|
||||
mkdir -p $HOME/artifacts
|
||||
|
||||
ls -lah $WORKSPACE/B2G/out
|
||||
ls -lah $WORKSPACE/B2G/objdir-gecko/dist/
|
||||
|
||||
mv $WORKSPACE/B2G/sources.xml $HOME/artifacts/sources.xml
|
||||
mv $WORKSPACE/B2G/out/target/product/generic_x86/tests/gaia-tests.zip $HOME/artifacts/gaia-tests.zip
|
||||
mv $WORKSPACE/B2G/out/target/product/generic_x86/tests/b2g-*.zip $HOME/artifacts/b2g-tests.zip
|
||||
mv $WORKSPACE/B2G/out/emulator.tar.gz $HOME/artifacts/emulator.tar.gz
|
||||
mv $WORKSPACE/B2G/objdir-gecko/dist/b2g-*.crashreporter-symbols.zip $HOME/artifacts/b2g-crashreporter-symbols.zip
|
||||
|
||||
ccache -s
|
||||
|
@ -28,7 +28,11 @@ if [ ! -d mozharness ]; then
|
||||
fi
|
||||
|
||||
# Figure out where the remote manifest is so we can use caches for it.
|
||||
MANIFEST=$(repository-url.py $GECKO_HEAD_REPOSITORY $GECKO_HEAD_REV b2g/config/$TARGET/sources.xml)
|
||||
|
||||
if [ -z "$MANIFEST" ]; then
|
||||
MANIFEST="$WORKSPACE/gecko/b2g/config/$TARGET/sources.xml"
|
||||
fi
|
||||
|
||||
tc-vcs repo-checkout $WORKSPACE/B2G https://git.mozilla.org/b2g/B2G.git $MANIFEST
|
||||
|
||||
# Ensure symlink has been created to gecko...
|
||||
|
@ -9,6 +9,7 @@ flags:
|
||||
- emulator
|
||||
- emulator-jb
|
||||
- emulator-kk
|
||||
- emulator-x86-kk
|
||||
- emulator-l
|
||||
- linux32_gecko # b2g desktop linux 32 bit
|
||||
- linux64_gecko # b2g desktop linux 64 bit
|
||||
|
@ -36,6 +36,18 @@ builds:
|
||||
task: tasks/builds/b2g_emulator_kk_opt.yml
|
||||
debug:
|
||||
task: tasks/builds/b2g_emulator_kk_debug.yml
|
||||
emulator-x86-l:
|
||||
platfoms:
|
||||
- b2g
|
||||
types:
|
||||
opt:
|
||||
task: tasks/builds/b2g_emulator_x86_l_opt.yml
|
||||
emulator-x86-kk:
|
||||
platfoms:
|
||||
- b2g
|
||||
types:
|
||||
opt:
|
||||
task: tasks/builds/b2g_emulator_x86_kk_opt.yml
|
||||
emulator-jb:
|
||||
platfoms:
|
||||
- b2g
|
||||
@ -134,6 +146,10 @@ tests:
|
||||
task: tasks/tests/b2g_emulator_mochitest.yml
|
||||
tasks/builds/b2g_emulator_kk_opt.yml:
|
||||
task: tasks/tests/b2g_emulator_mochitest.yml
|
||||
tasks/builds/b2g_emulator_x86_l_opt.yml:
|
||||
task: tasks/tests/b2g_emulator_mochitest.yml
|
||||
tasks/builds/b2g_emulator_x86_kk_opt.yml:
|
||||
task: tasks/tests/b2g_emulator_mochitest.yml
|
||||
tasks/builds/b2g_emulator_ics_opt.yml:
|
||||
task: tasks/tests/b2g_emulator_mochitest.yml
|
||||
tasks/builds/b2g_emulator_ics_debug.yml:
|
||||
@ -150,6 +166,10 @@ tests:
|
||||
task: tasks/tests/mulet_reftests.yml
|
||||
tasks/builds/b2g_emulator_kk_opt.yml:
|
||||
task: tasks/tests/b2g_emulator_reftest.yml
|
||||
tasks/builds/b2g_emulator_x86_l_opt.yml:
|
||||
task: tasks/tests/b2g_emulator_reftest.yml
|
||||
tasks/builds/b2g_emulator_x86_kk_opt.yml:
|
||||
task: tasks/tests/b2g_emulator_reftest.yml
|
||||
tasks/builds/b2g_emulator_ics_opt.yml:
|
||||
task: tasks/tests/b2g_emulator_reftest.yml
|
||||
reftest-sanity-oop:
|
||||
@ -158,6 +178,8 @@ tests:
|
||||
task: tasks/tests/b2g_reftests_sanity_oop.yml
|
||||
xpcshell:
|
||||
allowed_build_tasks:
|
||||
tasks/builds/b2g_emulator_x86_kk_opt.yml:
|
||||
task: tasks/tests/b2g_emulator_xpcshell_chunked.yml
|
||||
tasks/builds/b2g_emulator_kk_opt.yml:
|
||||
task: tasks/tests/b2g_emulator_xpcshell_chunked.yml
|
||||
tasks/builds/b2g_emulator_ics_opt.yml:
|
||||
|
34
testing/taskcluster/tasks/builds/b2g_emulator_x86_base.yml
Normal file
34
testing/taskcluster/tasks/builds/b2g_emulator_x86_base.yml
Normal file
@ -0,0 +1,34 @@
|
||||
$inherits:
|
||||
from: 'tasks/build.yml'
|
||||
task:
|
||||
metadata:
|
||||
description: |
|
||||
Android emulators + b2g environment used in full stack testing.
|
||||
payload:
|
||||
env:
|
||||
TARGET: 'emulator'
|
||||
B2G_DEBUG: 0
|
||||
|
||||
# Emulators can take a very long time to build!
|
||||
maxRunTime: 14400
|
||||
|
||||
command:
|
||||
- /bin/bash
|
||||
- -c
|
||||
- >
|
||||
checkout-gecko workspace &&
|
||||
cd ./workspace/gecko/testing/taskcluster/scripts/builder &&
|
||||
buildbot_step 'Build' ./build-emulator-x86.sh $HOME/workspace
|
||||
|
||||
extra:
|
||||
treeherder:
|
||||
groupSymbol: x86
|
||||
# Rather then enforcing particular conventions we require that all build
|
||||
# tasks provide the "build" extra field to specify where the build and tests
|
||||
# files are located.
|
||||
locations:
|
||||
build: 'public/build/emulator.tar.gz'
|
||||
tests: 'public/build/b2g-tests.zip'
|
||||
symbols: 'public/build/b2g-crashreporter-symbols.zip'
|
||||
sources: 'public/build/sources.xml'
|
||||
|
22
testing/taskcluster/tasks/builds/b2g_emulator_x86_kk_opt.yml
Normal file
22
testing/taskcluster/tasks/builds/b2g_emulator_x86_kk_opt.yml
Normal file
@ -0,0 +1,22 @@
|
||||
$inherits:
|
||||
from: 'tasks/builds/b2g_emulator_x86_base.yml'
|
||||
task:
|
||||
workerType: emualtor-x86-kk
|
||||
scopes:
|
||||
- 'docker-worker:cache:workspace-emulator-kk-x86-opt'
|
||||
metadata:
|
||||
name: '[TC] B2G KK X86 Emulator (Opt)'
|
||||
|
||||
extra:
|
||||
treeherderEnv:
|
||||
- staging
|
||||
treeherder:
|
||||
# Disable "TC" prefix...
|
||||
machine:
|
||||
platform: b2g-emu-kk
|
||||
|
||||
payload:
|
||||
cache:
|
||||
workspace-emulator-kk-x86-opt: /home/worker/workspace
|
||||
env:
|
||||
TARGET: 'emulator-x86-kk'
|
34
testing/taskcluster/tasks/builds/b2g_emulator_x86_l_opt.yml
Normal file
34
testing/taskcluster/tasks/builds/b2g_emulator_x86_l_opt.yml
Normal file
@ -0,0 +1,34 @@
|
||||
$inherits:
|
||||
from: 'tasks/builds/b2g_emulator_base.yml'
|
||||
task:
|
||||
workerType: emulator-l
|
||||
scopes:
|
||||
- 'docker-worker:cache:workspace-emulator-l-x86-opt'
|
||||
metadata:
|
||||
name: '[TC] B2G X86 L Emulator (Opt)'
|
||||
|
||||
extra:
|
||||
treeherderEnv:
|
||||
- staging
|
||||
treeherder:
|
||||
# Disable "TC" prefix...
|
||||
groupSymbol: "X86"
|
||||
machine:
|
||||
platform: b2g-emu-l
|
||||
|
||||
payload:
|
||||
command:
|
||||
- /bin/bash
|
||||
- -c
|
||||
- >
|
||||
checkout-gecko workspace &&
|
||||
cd ./workspace/gecko/testing/taskcluster/scripts/builder &&
|
||||
buildbot_step 'Build' ./build-emulator-x86.sh $HOME/workspace
|
||||
|
||||
cache:
|
||||
workspace-emulator-l-x86-opt: /home/worker/workspace
|
||||
env:
|
||||
TARGET: 'emulator-x86-l'
|
||||
|
||||
|
||||
|
@ -14,6 +14,9 @@ task:
|
||||
TARGET: 'flame-kk'
|
||||
DEBUG: 0
|
||||
VARIANT: eng
|
||||
GAIA_OPTIMIZE: '1'
|
||||
B2G_SYSTEM_APPS: '1'
|
||||
B2G_UPDATER: '1'
|
||||
command:
|
||||
- >
|
||||
checkout-gecko workspace &&
|
||||
@ -28,8 +31,3 @@ task:
|
||||
platform: b2g-device-image
|
||||
locations:
|
||||
img: 'private/build/flame-kk.zip'
|
||||
|
||||
GAIA_OPTIMIZE: '1'
|
||||
B2G_SYSTEM_APPS: '1'
|
||||
B2G_UPDATER: '1'
|
||||
|
||||
|
@ -8,6 +8,7 @@ flags:
|
||||
- emulator
|
||||
- emulator-jb
|
||||
- emulator-kk
|
||||
- emulator-x86-kk
|
||||
- linux32_gecko # b2g desktop linux 32 bit
|
||||
- linux64_gecko # b2g desktop linux 64 bit
|
||||
- linux64-mulet # Firefox desktop - b2g gecko linux 64 bit
|
||||
|
@ -1,6 +1,7 @@
|
||||
---
|
||||
$inherits:
|
||||
from: 'tasks/test.yml'
|
||||
reruns: 2
|
||||
task:
|
||||
metadata:
|
||||
name: '[TC] - Gaia Build Unit Test'
|
||||
|
@ -1,6 +1,7 @@
|
||||
---
|
||||
$inherits:
|
||||
from: 'tasks/test.yml'
|
||||
reruns: 2
|
||||
task:
|
||||
metadata:
|
||||
name: '[TC] - Gaia JS Integration Test'
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user