зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound a=merge
This commit is contained in:
Коммит
fa7b4f1c63
|
@ -123,6 +123,11 @@ SettingsListener.observe('language.current', 'en-US', function(value) {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Debug flag
|
||||
SettingsListener.observe('ril.debugging.enabled', false, function(value) {
|
||||
Services.prefs.setBoolPref('ril.debugging.enabled', value);
|
||||
});
|
||||
})();
|
||||
|
||||
//=================== DeviceInfo ====================
|
||||
|
@ -617,4 +622,3 @@ for (let key in settingsToObserve) {
|
|||
setPref(prefName, value);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -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="cc235a867161e0000ea55a4f009b3be19021f066"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7894b929f1b0394f3c997f72a6482bc7813e758d"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -138,7 +138,7 @@
|
|||
<project name="platform/system/core" path="system/core" revision="53d584d4a4b4316e4de9ee5f210d662f89b44e7e"/>
|
||||
<project name="u-boot" path="u-boot" revision="6980cf8f8cf9c1d43ff92b7af13425a5ed531d12"/>
|
||||
<project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="6974f8e771d4d8e910357a6739ab124768891e8f"/>
|
||||
<project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="f56ab768cb9f1ad42fb0809ffec1424b1e693369"/>
|
||||
<project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="c985699a5140b6dd5c7a43295e4d4397ce9b1267"/>
|
||||
<project name="vendor/sprd/partner" path="vendor/sprd/partner" revision="8649c7145972251af11b0639997edfecabfc7c2e"/>
|
||||
<project name="vendor/sprd/proprietories" path="vendor/sprd/proprietories" revision="d2466593022f7078aaaf69026adf3367c2adb7bb"/>
|
||||
</manifest>
|
||||
|
|
|
@ -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="cc235a867161e0000ea55a4f009b3be19021f066"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7894b929f1b0394f3c997f72a6482bc7813e758d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</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="cc235a867161e0000ea55a4f009b3be19021f066"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7894b929f1b0394f3c997f72a6482bc7813e758d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/>
|
||||
|
|
|
@ -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="cc235a867161e0000ea55a4f009b3be19021f066"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7894b929f1b0394f3c997f72a6482bc7813e758d"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
[
|
||||
]
|
|
@ -0,0 +1,151 @@
|
|||
<?xml version="1.0" ?><manifest>
|
||||
<!--original fetch url was https://android.googlesource.com/-->
|
||||
<remote fetch="https://git.mozilla.org/external/aosp" name="aosp"/>
|
||||
<!--original fetch url was git://github.com/mozilla-b2g/-->
|
||||
<remote fetch="https://git.mozilla.org/b2g" name="b2g"/>
|
||||
<!--original fetch url was git://github.com/mozilla/-->
|
||||
<remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
|
||||
<!--original fetch url was git://github.com/apitrace/-->
|
||||
<remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
|
||||
<!--original fetch url was git://codeaurora.org/-->
|
||||
<remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
|
||||
<!--original fetch url was https://git.mozilla.org/releases-->
|
||||
<remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
|
||||
<!-- B2G specific things. -->
|
||||
<project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7894b929f1b0394f3c997f72a6482bc7813e758d"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
|
||||
<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="a1ddea3133e0807350326cee5dcf0d06fad00c08"/>
|
||||
<!-- 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"/>
|
||||
<project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="354496e8eddd28c743d8e02c02eeab02958367e6"/>
|
||||
<project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" revision="b37c91354272b7413a0dc058b7445e677921d39e"/>
|
||||
<project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" revision="a227c92e0170bcf2296a63386956946b0dd78ca7"/>
|
||||
<project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" revision="884626610186b6dbea52cec5194b1c4bcfe1cb98"/>
|
||||
<project groups="pdk,linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" revision="29f9b82faa1af9730f52e933dca848546cbea84c"/>
|
||||
<project groups="linux" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="0c2ee5a39ca67d5fceeeee4b55baa3e9c01f0dca"/>
|
||||
<project name="device/common" path="device/common" revision="20de8dac0204c1a15ba305d0913749d8736455e6"/>
|
||||
<project name="device/sample" path="device/sample" revision="e6dc0e5648eeab3c1f7b7bccf2d277ba89cc5dd4"/>
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="811d1ba9b99353d3d88378fd6d944d33f935a735"/>
|
||||
<project name="platform/bionic" path="bionic" revision="68e8555eea90ce70eabdaa8c9b2f00ddda103c4e"/>
|
||||
<project name="platform/bootable/recovery" path="bootable/recovery" revision="7ae254886b35f13b338b3c48bdc484e76329ba17"/>
|
||||
<project name="platform/external/aac" path="external/aac" revision="c5c637158b23360310d330905e354a1dcd1220a7"/>
|
||||
<project name="platform/external/bison" path="external/bison" revision="27b034c868a896f78154132910ebf279c14a7aea"/>
|
||||
<project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="3c0b9cd51d643e0bf6f01c6d8b07fce25e3b2778"/>
|
||||
<project name="platform/external/bsdiff" path="external/bsdiff" revision="7f634b9ed876d9d9b9a7e58c3e671b7a01ec411d"/>
|
||||
<project name="platform/external/bzip2" path="external/bzip2" revision="997e320d341281512b1b1c3a1087f87328c74453"/>
|
||||
<project name="platform/external/checkpolicy" path="external/checkpolicy" revision="f819e420440e14418927366148373537796173b7"/>
|
||||
<project name="platform/external/clang" path="external/clang" revision="5692adcdd1dec21baf7aef57249cc9a32de05314"/>
|
||||
<project name="platform/external/compiler-rt" path="external/compiler-rt" revision="e2565ddc676409d3cbf6b6a46c9fca6dfe953d5d"/>
|
||||
<project name="platform/external/dhcpcd" path="external/dhcpcd" revision="84e2cbce82c0e7374f75771c708336fcdf1ecefa"/>
|
||||
<project name="platform/external/dnsmasq" path="external/dnsmasq" revision="38895f25447b72f2749564fa4444616347d335e3"/>
|
||||
<project name="platform/external/e2fsprogs" path="external/e2fsprogs" revision="6bf16cd4eefe67583db6efd8ecf72571f5d8d249"/>
|
||||
<project name="platform/external/elfutils" path="external/elfutils" revision="17541cad868bfd18e6148d4fa3dc3a7d20a25732"/>
|
||||
<project name="platform/external/expat" path="external/expat" revision="f49624e0fef9915a3a8326b46e91cb4c464555e4"/>
|
||||
<project name="platform/external/f2fs-tools" path="external/f2fs-tools" revision="fc24ac8347630a14d62ffafa93a27ec3b81cc44b"/>
|
||||
<project name="platform/external/fdlibm" path="external/fdlibm" revision="de3b2c981c66345b51fbf0822e4058c36eecb9ac"/>
|
||||
<project name="platform/external/flac" path="external/flac" revision="4477379717f7b0fa20a8f0614f970c70d373cb7b"/>
|
||||
<project name="platform/external/freetype" path="external/freetype" revision="33a0a0c5b597e7b5ab2cada25ae411d9bb42ff26"/>
|
||||
<project name="platform/external/gcc-demangle" path="external/gcc-demangle" revision="2be5e099459d0deaee9f2f779870fdf003a105dc"/>
|
||||
<project name="platform/external/genext2fs" path="external/genext2fs" revision="30158c8359ddab07867c01a472b2a0afec3bd87b"/>
|
||||
<project name="platform/external/giflib" path="external/giflib" revision="81d8b32f20d35036f0ffd3f8388122a449252d28"/>
|
||||
<project name="platform/external/gtest" path="external/gtest" revision="f86535d49037cb8ba5fc65202a9d3ad7614e4647"/>
|
||||
<project name="platform/external/harfbuzz_ng" path="external/harfbuzz_ng" revision="37dfd64c0134cb4473b2750d1a54e29abeab0290"/>
|
||||
<project name="platform/external/icu" path="external/icu" revision="7a2a040f28d700f0f1538b442bef816b665214c9"/>
|
||||
<project name="platform/external/iproute2" path="external/iproute2" revision="3c143dd332acaaea50af2e9a3a0ea47cd235a807"/>
|
||||
<project name="platform/external/ipsec-tools" path="external/ipsec-tools" revision="003a6d9b6f990bfbf0e4f04dfdb2084eb52f2f05"/>
|
||||
<project name="platform/external/iptables" path="external/iptables" revision="33890e955960f8ac8f88c28beb779346b57e0c15"/>
|
||||
<project name="platform/external/jack" path="external/jack" revision="f416b56a51c2b47bdaa72b542973846b7b3d5cea"/>
|
||||
<project name="platform/external/jemalloc" path="external/jemalloc" revision="2dc8e0401cea426ec2524da2dacf6070b98d9bf7"/>
|
||||
<project name="platform/external/jhead" path="external/jhead" revision="cab94ace65c4ebb0ba466e2f884078454f5e14d8"/>
|
||||
<project name="platform/external/jpeg" path="external/jpeg" revision="c78f0d394e6a65cdf679a5c437a4092482a42972"/>
|
||||
<project name="platform/external/jsmn" path="external/jsmn" revision="b38b4fe9a246bf36b821c542d0712d8c38699ac0"/>
|
||||
<project name="platform/external/jsoncpp" path="external/jsoncpp" revision="d2c5c6cdb5e8315b8ad02f0f3259e67c89f82a21"/>
|
||||
<project name="platform/external/junit" path="external/junit" revision="ea4284c223ecedb4094af8fb1e44b8d7a453ad95"/>
|
||||
<project name="platform/external/libcxxabi" path="external/libcxxabi" revision="bb937c1004a8705f22422f8b96176e135471215c"/>
|
||||
<project name="platform/external/libcxx" path="external/libcxx" revision="4242ca3e7c035aebc9895d18c22fa3394577cbdf"/>
|
||||
<project name="platform/external/libgsm" path="external/libgsm" revision="e9d878fc0e5485544e54919a32d621d4281f8cc0"/>
|
||||
<project name="platform/external/liblzf" path="external/liblzf" revision="b61d5b6275ddb9688777dda6b3196fa6b671811e"/>
|
||||
<project name="platform/external/libnfc-nxp" path="external/libnfc-nxp" revision="84022905ba12dc0a71a38f652d4325447559ac0d"/>
|
||||
<project name="platform/external/libnl" path="external/libnl" revision="77a22ce82e6aca58206321558e5ef283c673e8c3"/>
|
||||
<project name="platform/external/libogg" path="external/libogg" revision="ce31af3deb35cfa7801907ab47dd567f7342f728"/>
|
||||
<project name="platform/external/libopus" path="external/libopus" revision="48641b8569f2e4706d0745b5392a1ea51959a7d2"/>
|
||||
<project name="platform/external/libpcap" path="external/libpcap" revision="2e6cfa7b97a305d36c9d7305e17bff1e89430c00"/>
|
||||
<project name="platform/external/libpng" path="external/libpng" revision="1ee9d65ed400cdd1865140e34ceac503c47f5697"/>
|
||||
<project name="platform/external/libselinux" path="external/libselinux" revision="5c3fe1dec7cd818dcd7af8742b7d9349359109dc"/>
|
||||
<project name="platform/external/libsepol" path="external/libsepol" revision="ebb551cc7b2db5af523758a5bae6b4336885f274"/>
|
||||
<project name="platform/external/libunwind" path="external/libunwind" revision="9a5b400cc8b62a8df95a22307b4ad9470e5699d2"/>
|
||||
<project name="platform/external/libvpx" path="external/libvpx" revision="ae97799c21f85b0b16e4c9720a45d5d10fccb552"/>
|
||||
<project name="platform/external/llvm" path="external/llvm" revision="4c69c3c662cf672e89f827aaab6bbb2eee4d3cea"/>
|
||||
<project name="platform/external/mdnsresponder" path="external/mdnsresponder" revision="c2361f084d4d2ef3ff5f89cd2b73122f6a729612"/>
|
||||
<project name="platform/external/mksh" path="external/mksh" revision="8b9324223df9b4e4754f9b13c1229f7e8c92fd5a"/>
|
||||
<project name="platform/external/netcat" path="external/netcat" revision="e81a29a522fe7df9c1803478de07d050dd82a71e"/>
|
||||
<project name="platform/external/openssl" path="external/openssl" revision="49e31f1f0e48744f3113bc36dee5182186251238"/>
|
||||
<project name="platform/external/pcre" path="external/pcre" revision="24cc1690dbc0525d42184db5781954414802413d"/>
|
||||
<project name="platform/external/protobuf" path="external/protobuf" revision="766926b875dac06bde2b96b0c3f8d28c8647a4c4"/>
|
||||
<project name="platform/external/safe-iop" path="external/safe-iop" revision="990aa0668849d1d88c4a12f9e0039a7981cf4c7a"/>
|
||||
<project name="platform/external/scrypt" path="external/scrypt" revision="4593ce16971f9704e7810052567aecf803f812b0"/>
|
||||
<project name="platform/external/sepolicy" path="external/sepolicy" revision="c105aabfdd2f3efe02ed8c5b51025b55e54b632d"/>
|
||||
<project name="platform/external/sfntly" path="external/sfntly" revision="3ab9ff62deba8ec0b6de91c675cac39e0b17fbd3"/>
|
||||
<project name="platform/external/skia" path="external/skia" revision="72b56c79228338ddd5ec5b70bf5d42154f592705"/>
|
||||
<project name="platform/external/sonivox" path="external/sonivox" revision="425b4dd3a2994e262f7c1f0e1ed5ebe3ece41eea"/>
|
||||
<project name="platform/external/speex" path="external/speex" revision="fc41a0b3eda8aba60d213288ac845ad7610144c9"/>
|
||||
<project name="platform/external/sqlite" path="external/sqlite" revision="933fe9d087995054be1a90c969bb2ae538f52a0f"/>
|
||||
<project name="platform/external/stlport" path="external/stlport" revision="d2c2e9e77ff8af17dbd8bafb10b6882d16f877fb"/>
|
||||
<project name="platform/external/strace" path="external/strace" revision="13aaaead6e65b6dcb55c4d6b16588577804f0d1a"/>
|
||||
<project name="platform/external/svox" path="external/svox" revision="04dc7d54a9f6c4a9c943cf6440ad45823fbc4e3f"/>
|
||||
<project name="platform/external/tagsoup" path="external/tagsoup" revision="ae474049a3f8129316266bc6aac3337c9a135a29"/>
|
||||
<project name="platform/external/tcpdump" path="external/tcpdump" revision="9f6a22df2778388d8992b6c3bbaef412b35d583b"/>
|
||||
<project name="platform/external/tinyalsa" path="external/tinyalsa" revision="c9c11a4e003f1ded33fbfde0a4bae032b0538235"/>
|
||||
<project name="platform/external/tinycompress" path="external/tinycompress" revision="cfc3e36e320bcb46da2dda9f94b2852cb76c61c5"/>
|
||||
<project name="platform/external/tinyxml2" path="external/tinyxml2" revision="cb31036a6e8213d59646dcee369e63dd4023b732"/>
|
||||
<project name="platform/external/tinyxml" path="external/tinyxml" revision="c758d7fa7252c2690838b18eb92c796dd1698a8e"/>
|
||||
<project name="platform/external/tremolo" path="external/tremolo" revision="2e50b37fd997644ebb1b769f6963977a129a0578"/>
|
||||
<project name="platform/external/webp" path="external/webp" revision="f6fc76c4baeeacfc933e75ad14e721b742c0c99b"/>
|
||||
<project name="platform/external/webrtc" path="external/webrtc" revision="701d8ae1fe80514e8be0bb8e481e309bed8baf92"/>
|
||||
<project name="platform/external/yaffs2" path="external/yaffs2" revision="2da1900eb304796e96c1b7575163b0489fc2eb80"/>
|
||||
<project name="platform/external/zlib" path="external/zlib" revision="78dd4cb86e1c4e6a644c2222abe0f53e739bf868"/>
|
||||
<project name="platform/external/zopfli" path="external/zopfli" revision="139e51d6095b5f89dfdd9bc5d33b35500cd7869a"/>
|
||||
<project name="platform/frameworks/native" path="frameworks/native" revision="a0c1c949e35a44aa9018d1b4803cb2d833c16d59"/>
|
||||
<project name="platform/frameworks/opt/emoji" path="frameworks/opt/emoji" revision="4af3e0b694fa015b2fed61f4508cf01fab2f58b9"/>
|
||||
<project name="platform/hardware/libhardware" path="hardware/libhardware" revision="7ed97eeed061e95b466f24693c2f0120f40e2bf5"/>
|
||||
<project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="a9a2f3f3f17b4e1ab0d3b00a67b4c5250e26194f"/>
|
||||
<project name="platform/libcore" path="libcore" revision="1a07f00d8163f497a785a3285ec55fe551ba95c1"/>
|
||||
<project name="platform/libnativehelper" path="libnativehelper" revision="4834b58ed7af3ee69523177e00e55603ac90ed50"/>
|
||||
<project name="platform/ndk" path="ndk" revision="869c05cab7b4315c2bc607493db3f5b18ead2580"/>
|
||||
<project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="da8b660db117e2a69a7624bfdca2f02cad397f2e"/>
|
||||
<project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="5fd638a4a0ff3677fc3c970fab038d6db1bb7665"/>
|
||||
<project name="platform/prebuilts/qemu-kernel" path="prebuilts/qemu-kernel" revision="530cad9b205442d2db29361e796299dc323ef9ce"/>
|
||||
<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/extras" path="system/extras" revision="18f7c51415917eb0e21b30f220db7bd0be4130a7"/>
|
||||
<project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="8f7d94ac711af4678169805137c6c42def39b3ed"/>
|
||||
<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"/>
|
||||
<project name="platform/system/vold" path="system/vold" revision="eb59d2afd5f6e1cbab2ef985a8dd1c7105b499e8"/>
|
||||
<project name="platform_frameworks_av" path="frameworks/av" remote="b2g" revision="ea531874885eed7f68802048218ed86dde927f58"/>
|
||||
<project name="platform_frameworks_base" path="frameworks/base" remote="b2g" revision="df7e0cfbbc7e954ed26c73ac17832a5ff035f046"/>
|
||||
<project name="platform_frameworks_wilhelm" path="frameworks/wilhelm" remote="b2g" revision="73f7e7f12c8c5459f7a39e2fa343f083c942864d"/>
|
||||
<project name="platform_system_core" path="system/core" remote="b2g" revision="4df51d9abf6cc9a6ec49b965e621699e0e6dc4fb"/>
|
||||
<default remote="caf" revision="refs/tags/android-5.0.0_r6" sync-j="4"/>
|
||||
<!-- Emulator specific things -->
|
||||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="ba62cc8b78c30d36181b8060a2016cc8da166236"/>
|
||||
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="1b9f134d62536c5727575d1f0a5bd4eeb4747d66"/>
|
||||
<!-- external/qemu for emulator-l need to be updated in bug-1121378 -->
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="7639c5c496ffd207bb627f2a59b2c5203ae6fefc"/>
|
||||
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="16abda2258c9aa1ed78f00bb0a9b2b43b4cb919e"/>
|
||||
<project name="android-sdk" path="sdk" remote="b2g" revision="94704df982a90d2ab74bd620d54155d148c415b2"/>
|
||||
<!-- hardware-ril for emulator-l need to be updated in bug-1113054 -->
|
||||
<project name="platform/hardware/ril" path="hardware/ril" revision="71dfa8228ad0d6cdf6bac0426ac59404ab74b7f3"/>
|
||||
</manifest>
|
|
@ -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="cc235a867161e0000ea55a4f009b3be19021f066"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7894b929f1b0394f3c997f72a6482bc7813e758d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
|
||||
<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="cc235a867161e0000ea55a4f009b3be19021f066"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7894b929f1b0394f3c997f72a6482bc7813e758d"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</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="cc235a867161e0000ea55a4f009b3be19021f066"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7894b929f1b0394f3c997f72a6482bc7813e758d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"git": {
|
||||
"git_revision": "cc235a867161e0000ea55a4f009b3be19021f066",
|
||||
"git_revision": "7894b929f1b0394f3c997f72a6482bc7813e758d",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "6b12bdb3c5f22c6ec98bd43cdcae2d0a6d6a0d1e",
|
||||
"revision": "a0be5b849eb84a913a5e2728c0d1c9006dfc874f",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</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="cc235a867161e0000ea55a4f009b3be19021f066"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7894b929f1b0394f3c997f72a6482bc7813e758d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/>
|
||||
|
|
|
@ -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="cc235a867161e0000ea55a4f009b3be19021f066"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7894b929f1b0394f3c997f72a6482bc7813e758d"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -30,6 +30,7 @@
|
|||
<project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="354496e8eddd28c743d8e02c02eeab02958367e6"/>
|
||||
<project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" revision="b37c91354272b7413a0dc058b7445e677921d39e"/>
|
||||
<project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" revision="a227c92e0170bcf2296a63386956946b0dd78ca7"/>
|
||||
<project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" revision="884626610186b6dbea52cec5194b1c4bcfe1cb98"/>
|
||||
<project groups="pdk,linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" revision="29f9b82faa1af9730f52e933dca848546cbea84c"/>
|
||||
<project groups="linux" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="0c2ee5a39ca67d5fceeeee4b55baa3e9c01f0dca"/>
|
||||
<project name="device/common" path="device/common" revision="20de8dac0204c1a15ba305d0913749d8736455e6"/>
|
||||
|
|
|
@ -32,11 +32,7 @@ let SidebarUI = {
|
|||
this._title = document.getElementById("sidebar-title");
|
||||
this._splitter = document.getElementById("sidebar-splitter");
|
||||
|
||||
if (window.opener && !window.opener.closed &&
|
||||
window.opener.document.documentURIObject.schemeIs("chrome") &&
|
||||
PrivateBrowsingUtils.isWindowPrivate(window) == PrivateBrowsingUtils.isWindowPrivate(window.opener)) {
|
||||
this.adoptFromWindow(window.opener);
|
||||
} else {
|
||||
if (!this.adoptFromWindow(window.opener)) {
|
||||
let commandID = this._box.getAttribute("sidebarcommand");
|
||||
if (commandID) {
|
||||
let command = document.getElementById(commandID);
|
||||
|
@ -67,24 +63,40 @@ let SidebarUI = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Adopt the status of the sidebar from another window.
|
||||
* Try and adopt the status of the sidebar from another window.
|
||||
* @param {Window} sourceWindow - Window to use as a source for sidebar status.
|
||||
* @return true if we adopted the state, or false if the caller should
|
||||
* initialize the state itself.
|
||||
*/
|
||||
adoptFromWindow(sourceWindow) {
|
||||
// No source window, or it being closed, or not chrome, or in a different
|
||||
// private-browsing context means we can't adopt.
|
||||
if (!sourceWindow || sourceWindow.closed ||
|
||||
!sourceWindow.document.documentURIObject.schemeIs("chrome") ||
|
||||
PrivateBrowsingUtils.isWindowPrivate(window) != PrivateBrowsingUtils.isWindowPrivate(sourceWindow)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the opener had a sidebar, open the same sidebar in our window.
|
||||
// The opener can be the hidden window too, if we're coming from the state
|
||||
// where no windows are open, and the hidden window has no sidebar box.
|
||||
let sourceUI = sourceWindow.SidebarUI;
|
||||
if (!sourceUI || sourceUI._box.hidden) {
|
||||
return;
|
||||
if (!sourceUI || !sourceUI._box) {
|
||||
// no source UI or no _box means we also can't adopt the state.
|
||||
return false;
|
||||
}
|
||||
if (sourceUI._box.hidden) {
|
||||
// just hidden means we have adopted the hidden state.
|
||||
return true;
|
||||
}
|
||||
|
||||
let commandID = sourceUI._box.getAttribute("sidebarcommand");
|
||||
let commandElem = document.getElementById(commandID);
|
||||
|
||||
// dynamically generated sidebars will fail this check.
|
||||
// dynamically generated sidebars will fail this check, but we still
|
||||
// consider it adopted.
|
||||
if (!commandElem) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
this._title.setAttribute("value",
|
||||
|
@ -101,6 +113,7 @@ let SidebarUI = {
|
|||
this._box.hidden = false;
|
||||
this._splitter.hidden = false;
|
||||
commandElem.setAttribute("checked", "true");
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,5 +16,7 @@ FIREFOX_PREFERENCES = {
|
|||
"loop.seenToS": "seen",
|
||||
|
||||
# this dialog is fragile, and likely to introduce intermittent failures
|
||||
"media.navigator.permission.disabled": True
|
||||
"media.navigator.permission.disabled": True,
|
||||
# Use fake streams only
|
||||
"media.navigator.streams.fake": True
|
||||
}
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
* 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/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const nsIPermissionManager = Components.interfaces.nsIPermissionManager;
|
||||
const nsICookiePermission = Components.interfaces.nsICookiePermission;
|
||||
|
||||
const NOTIFICATION_FLUSH_PERMISSIONS = "flush-pending-permissions";
|
||||
|
||||
function Permission(host, rawHost, type, capability)
|
||||
function Permission(host, rawHost, type, capability)
|
||||
{
|
||||
this.host = host;
|
||||
this.rawHost = rawHost;
|
||||
|
@ -17,18 +19,19 @@ function Permission(host, rawHost, type, capability)
|
|||
}
|
||||
|
||||
var gPermissionManager = {
|
||||
_type : "",
|
||||
_permissions : [],
|
||||
_pm : Components.classes["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Components.interfaces.nsIPermissionManager),
|
||||
_bundle : null,
|
||||
_tree : null,
|
||||
|
||||
_type : "",
|
||||
_permissions : [],
|
||||
_permissionsToAdd : new Map(),
|
||||
_permissionsToDelete : new Map(),
|
||||
_bundle : null,
|
||||
_tree : null,
|
||||
_observerRemoved : false,
|
||||
|
||||
_view: {
|
||||
_rowCount: 0,
|
||||
get rowCount()
|
||||
{
|
||||
return this._rowCount;
|
||||
get rowCount()
|
||||
{
|
||||
return this._rowCount;
|
||||
},
|
||||
getCellText: function (aRow, aColumn)
|
||||
{
|
||||
|
@ -56,7 +59,7 @@ var gPermissionManager = {
|
|||
return "";
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
_getCapabilityString: function (aCapability)
|
||||
{
|
||||
var stringKey = null;
|
||||
|
@ -76,44 +79,47 @@ var gPermissionManager = {
|
|||
}
|
||||
return this._bundle.getString(stringKey);
|
||||
},
|
||||
|
||||
|
||||
addPermission: function (aCapability)
|
||||
{
|
||||
var textbox = document.getElementById("url");
|
||||
var host = textbox.value.replace(/^\s*([-\w]*:\/+)?/, ""); // trim any leading space and scheme
|
||||
try {
|
||||
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService);
|
||||
var uri = ioService.newURI("http://"+host, null, null);
|
||||
var uri = Services.io.newURI("http://"+host, null, null);
|
||||
host = uri.host;
|
||||
} catch(ex) {
|
||||
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||||
.getService(Components.interfaces.nsIPromptService);
|
||||
var message = this._bundle.getString("invalidURI");
|
||||
var title = this._bundle.getString("invalidURITitle");
|
||||
promptService.alert(window, title, message);
|
||||
Services.prompt.alert(window, title, message);
|
||||
return;
|
||||
}
|
||||
|
||||
var capabilityString = this._getCapabilityString(aCapability);
|
||||
|
||||
// check whether the permission already exists, if not, add it
|
||||
var exists = false;
|
||||
let hostExists = false;
|
||||
let capabilityExists = false;
|
||||
for (var i = 0; i < this._permissions.length; ++i) {
|
||||
if (this._permissions[i].rawHost == host) {
|
||||
// Avoid calling the permission manager if the capability settings are
|
||||
// the same. Otherwise allow the call to the permissions manager to
|
||||
// update the listbox for us.
|
||||
exists = this._permissions[i].capability == capabilityString;
|
||||
hostExists = true;
|
||||
capabilityExists = this._permissions[i].capability == capabilityString;
|
||||
if (!capabilityExists) {
|
||||
this._permissions[i].capability = capabilityString;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
host = (host.charAt(0) == ".") ? host.substring(1,host.length) : host;
|
||||
var uri = ioService.newURI("http://" + host, null, null);
|
||||
this._pm.add(uri, this._type, aCapability);
|
||||
|
||||
let permissionParams = {host: host, type: this._type, capability: aCapability};
|
||||
if (!hostExists) {
|
||||
this._permissionsToAdd.set(host, permissionParams);
|
||||
this._addPermission(permissionParams);
|
||||
}
|
||||
else if (!capabilityExists) {
|
||||
this._permissionsToAdd.set(host, permissionParams);
|
||||
this._handleCapabilityChange();
|
||||
}
|
||||
|
||||
textbox.value = "";
|
||||
textbox.focus();
|
||||
|
||||
|
@ -123,14 +129,57 @@ var gPermissionManager = {
|
|||
// enable "remove all" button as needed
|
||||
document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0;
|
||||
},
|
||||
|
||||
|
||||
_removePermission: function(aPermission)
|
||||
{
|
||||
this._removePermissionFromList(aPermission.host);
|
||||
|
||||
// If this permission was added during this session, let's remove
|
||||
// it from the pending adds list to prevent calls to the
|
||||
// permission manager.
|
||||
let isNewPermission = this._permissionsToAdd.delete(aPermission.host);
|
||||
|
||||
if (!isNewPermission) {
|
||||
this._permissionsToDelete.set(aPermission.host, aPermission);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_handleCapabilityChange: function ()
|
||||
{
|
||||
// Re-do the sort, if the status changed from Block to Allow
|
||||
// or vice versa, since if we're sorted on status, we may no
|
||||
// longer be in order.
|
||||
if (this._lastPermissionSortColumn.id == "statusCol") {
|
||||
this._resortPermissions();
|
||||
}
|
||||
this._tree.treeBoxObject.invalidate();
|
||||
},
|
||||
|
||||
_addPermission: function(aPermission)
|
||||
{
|
||||
this._addPermissionToList(aPermission);
|
||||
++this._view._rowCount;
|
||||
this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, 1);
|
||||
// Re-do the sort, since we inserted this new item at the end.
|
||||
this._resortPermissions();
|
||||
},
|
||||
|
||||
_resortPermissions: function()
|
||||
{
|
||||
gTreeUtils.sort(this._tree, this._view, this._permissions,
|
||||
this._permissionsComparator,
|
||||
this._lastPermissionSortColumn,
|
||||
this._lastPermissionSortAscending);
|
||||
},
|
||||
|
||||
onHostInput: function (aSiteField)
|
||||
{
|
||||
document.getElementById("btnSession").disabled = !aSiteField.value;
|
||||
document.getElementById("btnBlock").disabled = !aSiteField.value;
|
||||
document.getElementById("btnAllow").disabled = !aSiteField.value;
|
||||
},
|
||||
|
||||
|
||||
onWindowKeyPress: function (aEvent)
|
||||
{
|
||||
if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE)
|
||||
|
@ -142,14 +191,14 @@ var gPermissionManager = {
|
|||
if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN)
|
||||
document.getElementById("btnAllow").click();
|
||||
},
|
||||
|
||||
|
||||
onLoad: function ()
|
||||
{
|
||||
this._bundle = document.getElementById("bundlePreferences");
|
||||
var params = window.arguments[0];
|
||||
this.init(params);
|
||||
},
|
||||
|
||||
|
||||
init: function (aParams)
|
||||
{
|
||||
if (this._type) {
|
||||
|
@ -159,14 +208,14 @@ var gPermissionManager = {
|
|||
|
||||
this._type = aParams.permissionType;
|
||||
this._manageCapability = aParams.manageCapability;
|
||||
|
||||
|
||||
var permissionsText = document.getElementById("permissionsText");
|
||||
while (permissionsText.hasChildNodes())
|
||||
permissionsText.removeChild(permissionsText.firstChild);
|
||||
permissionsText.appendChild(document.createTextNode(aParams.introText));
|
||||
|
||||
document.title = aParams.windowTitle;
|
||||
|
||||
|
||||
document.getElementById("btnBlock").hidden = !aParams.blockVisible;
|
||||
document.getElementById("btnSession").hidden = !aParams.sessionVisible;
|
||||
document.getElementById("btnAllow").hidden = !aParams.allowVisible;
|
||||
|
@ -182,23 +231,23 @@ var gPermissionManager = {
|
|||
var urlLabel = document.getElementById("urlLabel");
|
||||
urlLabel.hidden = !urlFieldVisible;
|
||||
|
||||
var os = Components.classes["@mozilla.org/observer-service;1"]
|
||||
.getService(Components.interfaces.nsIObserverService);
|
||||
os.notifyObservers(null, NOTIFICATION_FLUSH_PERMISSIONS, this._type);
|
||||
os.addObserver(this, "perm-changed", false);
|
||||
Services.obs.notifyObservers(null, NOTIFICATION_FLUSH_PERMISSIONS, this._type);
|
||||
Services.obs.addObserver(this, "perm-changed", false);
|
||||
|
||||
this._loadPermissions();
|
||||
|
||||
|
||||
urlField.focus();
|
||||
},
|
||||
|
||||
|
||||
uninit: function ()
|
||||
{
|
||||
var os = Components.classes["@mozilla.org/observer-service;1"]
|
||||
.getService(Components.interfaces.nsIObserverService);
|
||||
os.removeObserver(this, "perm-changed");
|
||||
if (!this._observerRemoved) {
|
||||
Services.obs.removeObserver(this, "perm-changed");
|
||||
|
||||
this._observerRemoved = true;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
observe: function (aSubject, aTopic, aData)
|
||||
{
|
||||
if (aTopic == "perm-changed") {
|
||||
|
@ -209,14 +258,7 @@ var gPermissionManager = {
|
|||
return;
|
||||
|
||||
if (aData == "added") {
|
||||
this._addPermissionToList(permission);
|
||||
++this._view._rowCount;
|
||||
this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, 1);
|
||||
// Re-do the sort, since we inserted this new item at the end.
|
||||
gTreeUtils.sort(this._tree, this._view, this._permissions,
|
||||
this._permissionsComparator,
|
||||
this._lastPermissionSortColumn,
|
||||
this._lastPermissionSortAscending);
|
||||
this._addPermission(permission);
|
||||
}
|
||||
else if (aData == "changed") {
|
||||
for (var i = 0; i < this._permissions.length; ++i) {
|
||||
|
@ -225,31 +267,14 @@ var gPermissionManager = {
|
|||
break;
|
||||
}
|
||||
}
|
||||
// Re-do the sort, if the status changed from Block to Allow
|
||||
// or vice versa, since if we're sorted on status, we may no
|
||||
// longer be in order.
|
||||
if (this._lastPermissionSortColumn.id == "statusCol") {
|
||||
gTreeUtils.sort(this._tree, this._view, this._permissions,
|
||||
this._permissionsComparator,
|
||||
this._lastPermissionSortColumn,
|
||||
this._lastPermissionSortAscending);
|
||||
}
|
||||
this._tree.treeBoxObject.invalidate();
|
||||
this._handleCapabilityChange();
|
||||
}
|
||||
else if (aData == "deleted") {
|
||||
for (var i = 0; i < this._permissions.length; i++) {
|
||||
if (this._permissions[i].host == permission.host) {
|
||||
this._permissions.splice(i, 1);
|
||||
this._view._rowCount--;
|
||||
this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, -1);
|
||||
this._tree.treeBoxObject.invalidate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._removePermissionFromList(permission);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
onPermissionSelected: function ()
|
||||
{
|
||||
var hasSelection = this._tree.view.selection.count > 0;
|
||||
|
@ -257,7 +282,7 @@ var gPermissionManager = {
|
|||
document.getElementById("removePermission").disabled = !hasRows || !hasSelection;
|
||||
document.getElementById("removeAllPermissions").disabled = !hasRows;
|
||||
},
|
||||
|
||||
|
||||
onPermissionDeleted: function ()
|
||||
{
|
||||
if (!this._view.rowCount)
|
||||
|
@ -266,12 +291,12 @@ var gPermissionManager = {
|
|||
gTreeUtils.deleteSelectedItems(this._tree, this._view, this._permissions, removedPermissions);
|
||||
for (var i = 0; i < removedPermissions.length; ++i) {
|
||||
var p = removedPermissions[i];
|
||||
this._pm.remove(p.host, p.type);
|
||||
}
|
||||
this._removePermission(p);
|
||||
}
|
||||
document.getElementById("removePermission").disabled = !this._permissions.length;
|
||||
document.getElementById("removeAllPermissions").disabled = !this._permissions.length;
|
||||
},
|
||||
|
||||
|
||||
onAllPermissionsDeleted: function ()
|
||||
{
|
||||
if (!this._view.rowCount)
|
||||
|
@ -280,12 +305,12 @@ var gPermissionManager = {
|
|||
gTreeUtils.deleteAll(this._tree, this._view, this._permissions, removedPermissions);
|
||||
for (var i = 0; i < removedPermissions.length; ++i) {
|
||||
var p = removedPermissions[i];
|
||||
this._pm.remove(p.host, p.type);
|
||||
}
|
||||
this._removePermission(p);
|
||||
}
|
||||
document.getElementById("removePermission").disabled = true;
|
||||
document.getElementById("removeAllPermissions").disabled = true;
|
||||
},
|
||||
|
||||
|
||||
onPermissionKeyPress: function (aEvent)
|
||||
{
|
||||
if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE
|
||||
|
@ -295,7 +320,7 @@ var gPermissionManager = {
|
|||
)
|
||||
this.onPermissionDeleted();
|
||||
},
|
||||
|
||||
|
||||
_lastPermissionSortColumn: "",
|
||||
_lastPermissionSortAscending: false,
|
||||
_permissionsComparator : function (a, b)
|
||||
|
@ -303,19 +328,38 @@ var gPermissionManager = {
|
|||
return a.toLowerCase().localeCompare(b.toLowerCase());
|
||||
},
|
||||
|
||||
|
||||
|
||||
onPermissionSort: function (aColumn)
|
||||
{
|
||||
this._lastPermissionSortAscending = gTreeUtils.sort(this._tree,
|
||||
this._view,
|
||||
this._lastPermissionSortAscending = gTreeUtils.sort(this._tree,
|
||||
this._view,
|
||||
this._permissions,
|
||||
aColumn,
|
||||
this._permissionsComparator,
|
||||
this._lastPermissionSortColumn,
|
||||
this._lastPermissionSortColumn,
|
||||
this._lastPermissionSortAscending);
|
||||
this._lastPermissionSortColumn = aColumn;
|
||||
},
|
||||
|
||||
|
||||
onApplyChanges: function()
|
||||
{
|
||||
// Stop observing permission changes since we are about
|
||||
// to write out the pending adds/deletes and don't need
|
||||
// to update the UI
|
||||
this.uninit();
|
||||
|
||||
for (let permissionParams of this._permissionsToAdd.values()) {
|
||||
let uri = Services.io.newURI("http://" + permissionParams.host, null, null);
|
||||
Services.perms.add(uri, permissionParams.type, permissionParams.capability);
|
||||
}
|
||||
|
||||
for (let p of this._permissionsToDelete.values()) {
|
||||
Services.perms.remove(p.host, p.type);
|
||||
}
|
||||
|
||||
window.close();
|
||||
},
|
||||
|
||||
_loadPermissions: function ()
|
||||
{
|
||||
this._tree = document.getElementById("permissionsTree");
|
||||
|
@ -323,12 +367,12 @@ var gPermissionManager = {
|
|||
|
||||
// load permissions into a table
|
||||
var count = 0;
|
||||
var enumerator = this._pm.enumerator;
|
||||
var enumerator = Services.perms.enumerator;
|
||||
while (enumerator.hasMoreElements()) {
|
||||
var nextPermission = enumerator.getNext().QueryInterface(Components.interfaces.nsIPermission);
|
||||
this._addPermissionToList(nextPermission);
|
||||
}
|
||||
|
||||
|
||||
this._view._rowCount = this._permissions.length;
|
||||
|
||||
// sort and display the table
|
||||
|
@ -338,7 +382,7 @@ var gPermissionManager = {
|
|||
// disable "remove all" button if there are none
|
||||
document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0;
|
||||
},
|
||||
|
||||
|
||||
_addPermissionToList: function (aPermission)
|
||||
{
|
||||
if (aPermission.type == this._type &&
|
||||
|
@ -352,9 +396,22 @@ var gPermissionManager = {
|
|||
aPermission.type,
|
||||
capabilityString);
|
||||
this._permissions.push(p);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
_removePermissionFromList: function (aHost)
|
||||
{
|
||||
for (let i = 0; i < this._permissions.length; ++i) {
|
||||
if (this._permissions[i].host == aHost) {
|
||||
this._permissions.splice(i, 1);
|
||||
this._view._rowCount--;
|
||||
this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, -1);
|
||||
this._tree.treeBoxObject.invalidate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setHost: function (aHost)
|
||||
{
|
||||
document.getElementById("url").value = aHost;
|
||||
|
|
|
@ -61,8 +61,8 @@
|
|||
<treechildren/>
|
||||
</tree>
|
||||
</vbox>
|
||||
<hbox align="end">
|
||||
<hbox class="actionButtons" flex="1">
|
||||
<vbox>
|
||||
<hbox class="actionButtons" align="left" flex="1">
|
||||
<button id="removePermission" disabled="true"
|
||||
accesskey="&removepermission.accesskey;"
|
||||
icon="remove" label="&removepermission.label;"
|
||||
|
@ -71,9 +71,13 @@
|
|||
icon="clear" label="&removeallpermissions.label;"
|
||||
accesskey="&removeallpermissions.accesskey;"
|
||||
oncommand="gPermissionManager.onAllPermissionsDeleted();"/>
|
||||
<spacer flex="1"/>
|
||||
<button oncommand="close();" icon="close"
|
||||
label="&button.close.label;" accesskey="&button.close.accesskey;"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
<spacer flex="1"/>
|
||||
<hbox class="actionButtons" align="right" flex="1">
|
||||
<button oncommand="close();" icon="close"
|
||||
label="&button.cancel.label;" accesskey="&button.cancel.accesskey;" />
|
||||
<button id="btnApplyChanges" oncommand="gPermissionManager.onApplyChanges();" icon="save"
|
||||
label="&button.ok.label;" accesskey="&button.ok.accesskey;"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</window>
|
||||
|
|
|
@ -4,182 +4,197 @@
|
|||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
function prefWindowObserver(subject, topic, data) {
|
||||
if (topic != "domwindowopened")
|
||||
return;
|
||||
|
||||
Services.ww.unregisterNotification(this);
|
||||
|
||||
let win = subject.QueryInterface(Ci.nsIDOMEventTarget);
|
||||
|
||||
win.addEventListener("load", function(event) {
|
||||
let historyMode = event.target.getElementById("historyMode");
|
||||
historyMode.value = "custom";
|
||||
historyMode.doCommand();
|
||||
Services.ww.registerNotification(cookiesWindowObserver);
|
||||
event.target.getElementById("cookieExceptions").doCommand();
|
||||
}, false);
|
||||
}
|
||||
|
||||
function cookiesWindowObserver(subject, topic, data) {
|
||||
if (topic != "domwindowopened")
|
||||
return;
|
||||
|
||||
Services.ww.unregisterNotification(this);
|
||||
|
||||
let win = subject.QueryInterface(Ci.nsIDOMEventTarget);
|
||||
|
||||
win.addEventListener("load", function(event) {
|
||||
SimpleTest.executeSoon(function() windowLoad(event, win, dialog));
|
||||
}, false);
|
||||
}
|
||||
|
||||
Services.ww.registerNotification(prefWindowObserver);
|
||||
|
||||
let dialog = openDialog("chrome://browser/content/preferences/preferences.xul",
|
||||
"Preferences", "chrome,titlebar,toolbar,centerscreen,dialog=no",
|
||||
"panePrivacy");
|
||||
testRunner.runTests();
|
||||
}
|
||||
|
||||
function windowLoad(event, win, dialog) {
|
||||
let doc = event.target;
|
||||
let tree = doc.getElementById("permissionsTree");
|
||||
let statusCol = tree.treeBoxObject.columns.getColumnAt(1);
|
||||
let url = doc.getElementById("url");
|
||||
let btnAllow = doc.getElementById("btnAllow");
|
||||
let btnBlock = doc.getElementById("btnBlock");
|
||||
let btnRemove = doc.getElementById("removePermission");
|
||||
let pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
let ioService = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService);
|
||||
const allowText = win.gPermissionManager._getCapabilityString(
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION);
|
||||
const denyText = win.gPermissionManager._getCapabilityString(
|
||||
Ci.nsIPermissionManager.DENY_ACTION);
|
||||
const allow = Ci.nsIPermissionManager.ALLOW_ACTION;
|
||||
const deny = Ci.nsIPermissionManager.DENY_ACTION;
|
||||
var testRunner = {
|
||||
|
||||
is(tree.view.rowCount, 0, "no cookie exceptions");
|
||||
tests:
|
||||
[
|
||||
{
|
||||
test: function(params) {
|
||||
params.url.value = "test.com";
|
||||
params.btnAllow.doCommand();
|
||||
is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
|
||||
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
|
||||
"permission text should be set correctly");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "cookie", host: "test.com", data: "added",
|
||||
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
|
||||
},
|
||||
{
|
||||
test: function(params) {
|
||||
params.url.value = "test.com";
|
||||
params.btnBlock.doCommand();
|
||||
is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
|
||||
"permission should change to deny in UI");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "cookie", host: "test.com", data: "changed",
|
||||
capability: Ci.nsIPermissionManager.DENY_ACTION }],
|
||||
},
|
||||
{
|
||||
test: function(params) {
|
||||
params.url.value = "test.com";
|
||||
params.btnAllow.doCommand();
|
||||
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
|
||||
"permission should revert back to allow");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "cookie", host: "test.com", data: "changed",
|
||||
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
|
||||
},
|
||||
{
|
||||
test: function(params) {
|
||||
params.url.value = "test.com";
|
||||
params.btnRemove.doCommand();
|
||||
is(params.tree.view.rowCount, 0, "exception should be removed");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "cookie", host: "test.com", data: "deleted" }],
|
||||
},
|
||||
{
|
||||
test: function(params) {
|
||||
let uri = params.ioService.newURI("http://test.com", null, null);
|
||||
params.pm.add(uri, "popup", Ci.nsIPermissionManager.DENY_ACTION);
|
||||
is(params.tree.view.rowCount, 0, "adding unrelated permission should not change display");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "popup", host: "test.com", data: "added",
|
||||
capability: Ci.nsIPermissionManager.DENY_ACTION }],
|
||||
cleanUp: function(params) {
|
||||
params.pm.remove("test.com", "popup");
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
let tests = [
|
||||
{
|
||||
test: function() {
|
||||
url.value = "test.com";
|
||||
btnAllow.doCommand();
|
||||
is(tree.view.rowCount, 1, "added exception shows up in treeview");
|
||||
is(tree.view.getCellText(0, statusCol), allowText,
|
||||
"permission text should be set correctly");
|
||||
},
|
||||
observances: [{ type: "cookie", host: "test.com", data: "added",
|
||||
capability: allow }]
|
||||
},
|
||||
{
|
||||
test: function() {
|
||||
url.value = "test.com";
|
||||
btnBlock.doCommand();
|
||||
is(tree.view.getCellText(0, statusCol), denyText,
|
||||
"permission should change to deny in UI");
|
||||
},
|
||||
observances: [{ type: "cookie", host: "test.com", data: "changed",
|
||||
capability: deny }],
|
||||
},
|
||||
{
|
||||
test: function() {
|
||||
url.value = "test.com";
|
||||
btnAllow.doCommand();
|
||||
is(tree.view.getCellText(0, statusCol), allowText,
|
||||
"permission should revert back to allow");
|
||||
},
|
||||
observances: [{ type: "cookie", host: "test.com", data: "changed",
|
||||
capability: allow }],
|
||||
},
|
||||
{
|
||||
test: function() {
|
||||
url.value = "test.com";
|
||||
btnRemove.doCommand();
|
||||
is(tree.view.rowCount, 0, "exception should be removed");
|
||||
},
|
||||
observances: [{ type: "cookie", host: "test.com", data: "deleted" }],
|
||||
},
|
||||
{
|
||||
test: function() {
|
||||
let uri = ioService.newURI("http://test.com", null, null);
|
||||
pm.add(uri, "popup", Ci.nsIPermissionManager.DENY_ACTION);
|
||||
is(tree.view.rowCount, 0, "adding unrelated permission should not change display");
|
||||
},
|
||||
observances: [{ type: "popup", host: "test.com", data: "added",
|
||||
capability: deny }],
|
||||
cleanUp: function() {
|
||||
pm.remove("test.com", "popup");
|
||||
},
|
||||
},
|
||||
{
|
||||
test: function() {
|
||||
url.value = "test.com";
|
||||
btnAllow.doCommand();
|
||||
pm.remove("test.com", "cookie");
|
||||
is(tree.view.rowCount, 0, "display should update when cookie permission is deleted");
|
||||
},
|
||||
observances: [{ type: "cookie", host: "test.com", data: "added",
|
||||
capability: allow },
|
||||
{ type: "cookie", host: "test.com", data: "deleted" }]
|
||||
},
|
||||
];
|
||||
_currentTest: -1,
|
||||
|
||||
let permObserver = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic != "perm-changed")
|
||||
return;
|
||||
runTests: function() {
|
||||
this._currentTest++;
|
||||
|
||||
if (tests[currentTest].observances.length == 0) {
|
||||
// Should fail here as we are not expecting a notification, but we don't.
|
||||
// See bug 1063410.
|
||||
return;
|
||||
info("Running test #" + (this._currentTest + 1) + "\n");
|
||||
let that = this;
|
||||
let p = this.runCurrentTest();
|
||||
p.then(function() {
|
||||
if (that._currentTest == that.tests.length - 1) {
|
||||
finish();
|
||||
}
|
||||
|
||||
let permission = aSubject.QueryInterface(Ci.nsIPermission);
|
||||
let expected = tests[currentTest].observances.shift();
|
||||
|
||||
is(aData, expected.data, "type of message should be the same");
|
||||
for each (let prop in ["type", "host", "capability"]) {
|
||||
if (expected[prop])
|
||||
is(permission[prop], expected[prop],
|
||||
"property: \"" + prop + "\" should be equal");
|
||||
else {
|
||||
that.runTests();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
if (tests[currentTest].observances.length == 0) {
|
||||
SimpleTest.executeSoon(function() {
|
||||
if (tests[currentTest].cleanUp)
|
||||
tests[currentTest].cleanUp();
|
||||
runCurrentTest: function() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
runNextTest();
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
let helperFunctions = {
|
||||
|
||||
let os = Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService);
|
||||
prefWindowObserver: function(subject, topic, data) {
|
||||
if (topic != "domwindowopened")
|
||||
return;
|
||||
|
||||
os.addObserver(permObserver, "perm-changed", false);
|
||||
Services.ww.unregisterNotification(helperFunctions.prefWindowObserver);
|
||||
|
||||
var currentTest = -1;
|
||||
function runNextTest() {
|
||||
currentTest++;
|
||||
if (currentTest == tests.length) {
|
||||
os.removeObserver(permObserver, "perm-changed");
|
||||
win.close();
|
||||
dialog.close();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
let win = subject.QueryInterface(Ci.nsIDOMEventTarget);
|
||||
|
||||
info("Running test #" + (currentTest + 1) + "\n");
|
||||
tests[currentTest].test();
|
||||
if (!tests[currentTest].observances)
|
||||
runNextTest();
|
||||
}
|
||||
win.addEventListener("load", function(event) {
|
||||
let historyMode = event.target.getElementById("historyMode");
|
||||
historyMode.value = "custom";
|
||||
historyMode.doCommand();
|
||||
Services.ww.registerNotification(helperFunctions.cookiesWindowObserver);
|
||||
event.target.getElementById("cookieExceptions").doCommand();
|
||||
}, false);
|
||||
},
|
||||
|
||||
runNextTest();
|
||||
}
|
||||
cookiesWindowObserver: function(subject, topic, data) {
|
||||
if (topic != "domwindowopened")
|
||||
return;
|
||||
|
||||
Services.ww.unregisterNotification(helperFunctions.cookiesWindowObserver);
|
||||
|
||||
let win = subject.QueryInterface(Ci.nsIDOMEventTarget);
|
||||
|
||||
win.addEventListener("load", function(event) {
|
||||
SimpleTest.executeSoon(function() helperFunctions.windowLoad(event, win));
|
||||
}, false);
|
||||
},
|
||||
|
||||
windowLoad: function(event, win) {
|
||||
let params = {
|
||||
doc: event.target,
|
||||
tree: event.target.getElementById("permissionsTree"),
|
||||
statusCol: event.target.getElementById("permissionsTree").treeBoxObject.columns.getColumnAt(1),
|
||||
url: event.target.getElementById("url"),
|
||||
btnAllow: event.target.getElementById("btnAllow"),
|
||||
btnBlock: event.target.getElementById("btnBlock"),
|
||||
btnApplyChanges: event.target.getElementById("btnApplyChanges"),
|
||||
btnRemove: event.target.getElementById("removePermission"),
|
||||
pm: Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager),
|
||||
ioService: Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService),
|
||||
allowText: win.gPermissionManager._getCapabilityString(
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION),
|
||||
denyText: win.gPermissionManager._getCapabilityString(
|
||||
Ci.nsIPermissionManager.DENY_ACTION),
|
||||
allow: Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
deny: Ci.nsIPermissionManager.DENY_ACTION,
|
||||
};
|
||||
|
||||
let permObserver = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic != "perm-changed")
|
||||
return;
|
||||
|
||||
if (testRunner.tests[testRunner._currentTest].observances.length == 0) {
|
||||
// Should fail here as we are not expecting a notification, but we don't.
|
||||
// See bug 1063410.
|
||||
return;
|
||||
}
|
||||
|
||||
let permission = aSubject.QueryInterface(Ci.nsIPermission);
|
||||
let expected = testRunner.tests[testRunner._currentTest].observances.shift();
|
||||
|
||||
is(aData, expected.data, "type of message should be the same");
|
||||
for each (let prop in ["type", "host", "capability"]) {
|
||||
if (expected[prop])
|
||||
is(permission[prop], expected[prop],
|
||||
"property: \"" + prop + "\" should be equal");
|
||||
}
|
||||
|
||||
os.removeObserver(permObserver, "perm-changed");
|
||||
|
||||
if (testRunner.tests[testRunner._currentTest].cleanup) {
|
||||
testRunner.tests[testRunner._currentTest].cleanup();
|
||||
}
|
||||
|
||||
testRunner.dialog.close(params);
|
||||
win.close();
|
||||
resolve();
|
||||
},
|
||||
};
|
||||
|
||||
let os = Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService);
|
||||
|
||||
os.addObserver(permObserver, "perm-changed", false);
|
||||
|
||||
if (testRunner._currentTest == 0) {
|
||||
is(params.tree.view.rowCount, 0, "no cookie exceptions");
|
||||
}
|
||||
|
||||
testRunner.tests[testRunner._currentTest].test(params);
|
||||
},
|
||||
};
|
||||
|
||||
Services.ww.registerNotification(helperFunctions.prefWindowObserver);
|
||||
|
||||
testRunner.dialog = openDialog("chrome://browser/content/preferences/preferences.xul",
|
||||
"Preferences", "chrome,titlebar,toolbar,centerscreen,dialog=no",
|
||||
"panePrivacy");
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
@ -12,8 +12,9 @@ const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
|
|||
let XMLHttpRequest = CC("@mozilla.org/xmlextras/xmlhttprequest;1");
|
||||
let strings = Services.strings.createBundle("chrome://browser/locale/devtools/app-manager.properties");
|
||||
|
||||
function AppValidator(project) {
|
||||
this.project = project;
|
||||
function AppValidator({ type, location }) {
|
||||
this.type = type;
|
||||
this.location = location;
|
||||
this.errors = [];
|
||||
this.warnings = [];
|
||||
}
|
||||
|
@ -27,7 +28,7 @@ AppValidator.prototype.warning = function (message) {
|
|||
};
|
||||
|
||||
AppValidator.prototype._getPackagedManifestFile = function () {
|
||||
let manifestFile = FileUtils.File(this.project.location);
|
||||
let manifestFile = FileUtils.File(this.location);
|
||||
if (!manifestFile.exists()) {
|
||||
this.error(strings.GetStringFromName("validator.nonExistingFolder"));
|
||||
return null;
|
||||
|
@ -149,12 +150,12 @@ AppValidator.prototype._fetchManifest = function (manifestURL) {
|
|||
|
||||
AppValidator.prototype._getManifest = function () {
|
||||
let manifestURL;
|
||||
if (this.project.type == "packaged") {
|
||||
if (this.type == "packaged") {
|
||||
manifestURL = this._getPackagedManifestURL();
|
||||
if (!manifestURL)
|
||||
return promise.resolve(null);
|
||||
} else if (this.project.type == "hosted") {
|
||||
manifestURL = this.project.location;
|
||||
} else if (this.type == "hosted") {
|
||||
manifestURL = this.location;
|
||||
try {
|
||||
Services.io.newURI(manifestURL, null, null);
|
||||
} catch(e) {
|
||||
|
@ -162,7 +163,7 @@ AppValidator.prototype._getManifest = function () {
|
|||
return promise.resolve(null);
|
||||
}
|
||||
} else {
|
||||
this.error(strings.formatStringFromName("validator.invalidProjectType", [this.project.type], 1));
|
||||
this.error(strings.formatStringFromName("validator.invalidProjectType", [this.type], 1));
|
||||
return promise.resolve(null);
|
||||
}
|
||||
return this._fetchManifest(manifestURL);
|
||||
|
@ -181,11 +182,11 @@ AppValidator.prototype.validateManifest = function (manifest) {
|
|||
};
|
||||
|
||||
AppValidator.prototype._getOriginURL = function () {
|
||||
if (this.project.type == "packaged") {
|
||||
if (this.type == "packaged") {
|
||||
let manifestURL = Services.io.newURI(this.manifestURL, null, null);
|
||||
return Services.io.newURI(".", null, manifestURL).spec;
|
||||
} else if (this.project.type == "hosted") {
|
||||
return Services.io.newURI(this.project.location, null, null).prePath;
|
||||
} else if (this.type == "hosted") {
|
||||
return Services.io.newURI(this.location, null, null).prePath;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -203,9 +204,9 @@ AppValidator.prototype.validateLaunchPath = function (manifest) {
|
|||
}
|
||||
let origin = this._getOriginURL();
|
||||
let path;
|
||||
if (this.project.type == "packaged") {
|
||||
if (this.type == "packaged") {
|
||||
path = "." + ( manifest.launch_path || "/index.html" );
|
||||
} else if (this.project.type == "hosted") {
|
||||
} else if (this.type == "hosted") {
|
||||
path = manifest.launch_path || "/";
|
||||
}
|
||||
let indexURL;
|
||||
|
@ -251,7 +252,7 @@ AppValidator.prototype.validateType = function (manifest) {
|
|||
let appType = manifest.type || "web";
|
||||
if (["web", "trusted", "privileged", "certified"].indexOf(appType) === -1) {
|
||||
this.error(strings.formatStringFromName("validator.invalidAppType", [appType], 1));
|
||||
} else if (this.project.type == "hosted" &&
|
||||
} else if (this.type == "hosted" &&
|
||||
["certified", "privileged"].indexOf(appType) !== -1) {
|
||||
this.error(strings.formatStringFromName("validator.invalidHostedPriviledges", [appType], 1));
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
let validator = createPackaged("wrong-launch-path");
|
||||
validator.validate().then(() => {
|
||||
is(validator.errors.length, 1, "app with wrong path got an error");
|
||||
let file = nsFile(validator.project.location);
|
||||
let file = nsFile(validator.location);
|
||||
file.append("wrong-path.html");
|
||||
let url = Services.io.newFileURI(file);
|
||||
is(validator.errors[0], strings.formatStringFromName("validator.accessFailedLaunchPath", [url.spec], 1),
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
// Expected values:
|
||||
let res1 = [
|
||||
{selector: "#element-size", value: "160" + "\u00D7" + "160"},
|
||||
{selector: ".size > span", value: "100" + "\u00D7" + "100"},
|
||||
{selector: "#element-size", value: "160" + "\u00D7" + "160.117"},
|
||||
{selector: ".size > span", value: "100" + "\u00D7" + "100.117"},
|
||||
{selector: ".margin.top > span", value: 30},
|
||||
{selector: ".margin.left > span", value: "auto"},
|
||||
{selector: ".margin.bottom > span", value: 30},
|
||||
|
@ -43,7 +43,7 @@ let res2 = [
|
|||
];
|
||||
|
||||
add_task(function*() {
|
||||
let style = "div { position: absolute; top: 42px; left: 42px; height: 100px; width: 100px; border: 10px solid black; padding: 20px; margin: 30px auto;}";
|
||||
let style = "div { position: absolute; top: 42px; left: 42px; height: 100.111px; width: 100px; border: 10px solid black; padding: 20px; margin: 30px auto;}";
|
||||
let html = "<style>" + style + "</style><div></div>"
|
||||
|
||||
yield addTab("data:text/html," + encodeURIComponent(html));
|
||||
|
|
|
@ -422,7 +422,7 @@ LayoutView.prototype = {
|
|||
// might be missing.
|
||||
continue;
|
||||
}
|
||||
let parsedValue = parseInt(layout[property]);
|
||||
let parsedValue = parseFloat(layout[property]);
|
||||
if (Number.isNaN(parsedValue)) {
|
||||
// Not a number. We use the raw string.
|
||||
// Useful for "position" for example.
|
||||
|
@ -451,9 +451,10 @@ LayoutView.prototype = {
|
|||
|
||||
width -= this.map.borderLeft.value + this.map.borderRight.value +
|
||||
this.map.paddingLeft.value + this.map.paddingRight.value;
|
||||
|
||||
width = parseFloat(width.toPrecision(6));
|
||||
height -= this.map.borderTop.value + this.map.borderBottom.value +
|
||||
this.map.paddingTop.value + this.map.paddingBottom.value;
|
||||
height = parseFloat(height.toPrecision(6));
|
||||
|
||||
let newValue = width + "\u00D7" + height;
|
||||
if (this.sizeLabel.textContent != newValue) {
|
||||
|
|
|
@ -129,6 +129,8 @@ function updateUI() {
|
|||
warningsNode.appendChild(li);
|
||||
}
|
||||
}
|
||||
|
||||
AppManager.update("details");
|
||||
}
|
||||
|
||||
function showPrepackageLog() {
|
||||
|
|
|
@ -153,11 +153,11 @@ function doOK() {
|
|||
target.remove(false);
|
||||
AppProjects.addPackaged(folder).then((project) => {
|
||||
window.arguments[0].location = project.location;
|
||||
AppManager.validateProject(project).then(() => {
|
||||
AppManager.validateAndUpdateProject(project).then(() => {
|
||||
if (project.manifest) {
|
||||
project.manifest.name = projectName;
|
||||
AppManager.writeManifest(project).then(() => {
|
||||
AppManager.validateProject(project).then(
|
||||
AppManager.validateAndUpdateProject(project).then(
|
||||
() => {window.close()}, bail)
|
||||
}, bail)
|
||||
} else {
|
||||
|
|
|
@ -134,7 +134,7 @@ let UI = {
|
|||
AppManager.selectedProject.type != "mainProcess" &&
|
||||
AppManager.selectedProject.type != "runtimeApp" &&
|
||||
AppManager.selectedProject.type != "tab") {
|
||||
AppManager.validateProject(AppManager.selectedProject);
|
||||
AppManager.validateAndUpdateProject(AppManager.selectedProject);
|
||||
}
|
||||
|
||||
// Hook to display promotional Developer Edition doorhanger. Only displayed once.
|
||||
|
@ -567,7 +567,7 @@ let UI = {
|
|||
menuindex: 1
|
||||
});
|
||||
this.projecteditor.on("onEditorSave", (editor, resource) => {
|
||||
AppManager.validateProject(AppManager.selectedProject);
|
||||
AppManager.validateAndUpdateProject(AppManager.selectedProject);
|
||||
});
|
||||
return this.projecteditor.loaded;
|
||||
},
|
||||
|
@ -667,9 +667,6 @@ let UI = {
|
|||
}
|
||||
}
|
||||
|
||||
// Validate project
|
||||
yield AppManager.validateProject(project);
|
||||
|
||||
// Select project
|
||||
AppManager.selectedProject = project;
|
||||
}),
|
||||
|
@ -1045,9 +1042,6 @@ let Cmds = {
|
|||
// Retrieve added project
|
||||
let project = AppProjects.get(ret.location);
|
||||
|
||||
// Validate project
|
||||
yield AppManager.validateProject(project);
|
||||
|
||||
// Select project
|
||||
AppManager.selectedProject = project;
|
||||
|
||||
|
@ -1106,7 +1100,7 @@ let Cmds = {
|
|||
// The result of the validation process (storing names, icons, …) is not stored in
|
||||
// the IndexedDB database when App Manager v1 is used.
|
||||
// We need to run the validation again and update the name and icon of the app.
|
||||
AppManager.validateProject(project).then(() => {
|
||||
AppManager.validateAndUpdateProject(project).then(() => {
|
||||
panelItemNode.setAttribute("label", project.name);
|
||||
panelItemNode.setAttribute("image", project.icon);
|
||||
});
|
||||
|
|
|
@ -278,36 +278,36 @@ let AppManager = exports.AppManager = {
|
|||
},
|
||||
|
||||
_selectedProject: null,
|
||||
set selectedProject(value) {
|
||||
set selectedProject(project) {
|
||||
// A regular comparison still sees a difference when equal in some cases
|
||||
if (JSON.stringify(this._selectedProject) !==
|
||||
JSON.stringify(value)) {
|
||||
|
||||
let cancelled = false;
|
||||
this.update("before-project", { cancel: () => { cancelled = true; } });
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._selectedProject = value;
|
||||
|
||||
// Clear out tab store's selected state, if any
|
||||
this.tabStore.selectedTab = null;
|
||||
|
||||
if (this.selectedProject) {
|
||||
if (this.selectedProject.type == "packaged" ||
|
||||
this.selectedProject.type == "hosted") {
|
||||
this.validateProject(this.selectedProject);
|
||||
}
|
||||
if (this.selectedProject.type == "tab") {
|
||||
this.tabStore.selectedTab = this.selectedProject.app;
|
||||
}
|
||||
}
|
||||
|
||||
this.update("project");
|
||||
|
||||
this.checkIfProjectIsRunning();
|
||||
if (JSON.stringify(this._selectedProject) ===
|
||||
JSON.stringify(project)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
this.update("before-project", { cancel: () => { cancelled = true; } });
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._selectedProject = project;
|
||||
|
||||
// Clear out tab store's selected state, if any
|
||||
this.tabStore.selectedTab = null;
|
||||
|
||||
if (project) {
|
||||
if (project.type == "packaged" ||
|
||||
project.type == "hosted") {
|
||||
this.validateAndUpdateProject(project);
|
||||
}
|
||||
if (project.type == "tab") {
|
||||
this.tabStore.selectedTab = project.app;
|
||||
}
|
||||
}
|
||||
|
||||
this.update("project");
|
||||
this.checkIfProjectIsRunning();
|
||||
},
|
||||
get selectedProject() {
|
||||
return this._selectedProject;
|
||||
|
@ -323,6 +323,19 @@ let AppManager = exports.AppManager = {
|
|||
return AppProjects.remove(location);
|
||||
},
|
||||
|
||||
packageProject: Task.async(function*(project) {
|
||||
if (!project) {
|
||||
return;
|
||||
}
|
||||
if (project.type == "packaged" ||
|
||||
project.type == "hosted") {
|
||||
yield ProjectBuilding.build({
|
||||
project: project,
|
||||
logger: this.update.bind(this, "pre-package")
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
_selectedRuntime: null,
|
||||
set selectedRuntime(value) {
|
||||
this._selectedRuntime = value;
|
||||
|
@ -481,12 +494,9 @@ let AppManager = exports.AppManager = {
|
|||
return Task.spawn(function* () {
|
||||
let self = AppManager;
|
||||
|
||||
let packageDir = yield ProjectBuilding.build({
|
||||
project: project,
|
||||
logger: self.update.bind(self, "pre-package")
|
||||
});
|
||||
|
||||
yield self.validateProject(project);
|
||||
// Package and validate project
|
||||
yield self.packageProject(project);
|
||||
yield self.validateAndUpdateProject(project);
|
||||
|
||||
if (project.errorsCount > 0) {
|
||||
self.reportError("error_cantInstallValidationErrors");
|
||||
|
@ -501,7 +511,7 @@ let AppManager = exports.AppManager = {
|
|||
|
||||
let response;
|
||||
if (project.type == "packaged") {
|
||||
packageDir = packageDir || project.location;
|
||||
let packageDir = yield ProjectBuilding.getPackageDir(project);
|
||||
console.log("Installing app from " + packageDir);
|
||||
|
||||
response = yield self._appsFront.installPackaged(packageDir,
|
||||
|
@ -557,14 +567,20 @@ let AppManager = exports.AppManager = {
|
|||
|
||||
/* PROJECT VALIDATION */
|
||||
|
||||
validateProject: function(project) {
|
||||
validateAndUpdateProject: function(project) {
|
||||
if (!project) {
|
||||
return promise.reject();
|
||||
}
|
||||
|
||||
return Task.spawn(function* () {
|
||||
|
||||
let validation = new AppValidator(project);
|
||||
let packageDir = yield ProjectBuilding.getPackageDir(project);
|
||||
let validation = new AppValidator({
|
||||
type: project.type,
|
||||
// Build process may place the manifest in a non-root directory
|
||||
location: packageDir
|
||||
});
|
||||
|
||||
yield validation.validate();
|
||||
|
||||
if (validation.manifest) {
|
||||
|
@ -584,7 +600,7 @@ let AppManager = exports.AppManager = {
|
|||
let origin = Services.io.newURI(manifestURL.prePath, null, null);
|
||||
project.icon = Services.io.newURI(iconPath, null, origin).spec;
|
||||
} else if (project.type == "packaged") {
|
||||
let projectFolder = FileUtils.File(project.location);
|
||||
let projectFolder = FileUtils.File(packageDir);
|
||||
let folderURI = Services.io.newFileURI(projectFolder).spec;
|
||||
project.icon = folderURI + iconPath.replace(/^\/|\\/, "");
|
||||
}
|
||||
|
|
|
@ -14,10 +14,10 @@ const ProjectBuilding = exports.ProjectBuilding = {
|
|||
let manifestPath = OS.Path.join(project.location, "package.json");
|
||||
let exists = yield OS.File.exists(manifestPath);
|
||||
if (!exists) {
|
||||
return;
|
||||
// No explicit manifest, try to generate one if possible
|
||||
return this.generatePackageManifest(project);
|
||||
}
|
||||
|
||||
let Decoder = new TextDecoder();
|
||||
let data = yield OS.File.read(manifestPath);
|
||||
data = new TextDecoder().decode(data);
|
||||
let manifest;
|
||||
|
@ -30,6 +30,31 @@ const ProjectBuilding = exports.ProjectBuilding = {
|
|||
return manifest;
|
||||
}),
|
||||
|
||||
/**
|
||||
* For common frameworks in the community, attempt to detect the build
|
||||
* settings if none are defined. This makes it much easier to get started
|
||||
* with WebIDE. Later on, perhaps an add-on could define such things for
|
||||
* different frameworks.
|
||||
*/
|
||||
generatePackageManifest: Task.async(function*(project) {
|
||||
// Cordova
|
||||
let cordovaConfigPath = OS.Path.join(project.location, "config.xml");
|
||||
let exists = yield OS.File.exists(cordovaConfigPath);
|
||||
if (!exists) {
|
||||
return;
|
||||
}
|
||||
let data = yield OS.File.read(cordovaConfigPath);
|
||||
data = new TextDecoder().decode(data);
|
||||
if (data.contains("cordova.apache.org")) {
|
||||
return {
|
||||
"webide": {
|
||||
"prepackage": "cordova prepare",
|
||||
"packageDir": "./platforms/firefoxos/www"
|
||||
}
|
||||
};
|
||||
}
|
||||
}),
|
||||
|
||||
hasPrepackage: Task.async(function* (project) {
|
||||
let manifest = yield ProjectBuilding.fetchPackageManifest(project);
|
||||
return manifest && manifest.webide && "prepackage" in manifest.webide;
|
||||
|
@ -37,22 +62,19 @@ const ProjectBuilding = exports.ProjectBuilding = {
|
|||
|
||||
// If the app depends on some build step, run it before pushing the app
|
||||
build: Task.async(function* ({ project, logger }) {
|
||||
if (!this.hasPrepackage(project)) {
|
||||
if (!(yield this.hasPrepackage(project))) {
|
||||
return;
|
||||
}
|
||||
|
||||
let manifest = yield ProjectBuilding.fetchPackageManifest(project);
|
||||
|
||||
logger("start");
|
||||
let packageDir;
|
||||
try {
|
||||
packageDir = yield this._build(project, manifest, logger);
|
||||
yield this._build(project, manifest, logger);
|
||||
logger("succeed");
|
||||
} catch(e) {
|
||||
logger("failed", e);
|
||||
}
|
||||
|
||||
return packageDir;
|
||||
}),
|
||||
|
||||
_build: Task.async(function* (project, manifest, logger) {
|
||||
|
@ -61,6 +83,15 @@ const ProjectBuilding = exports.ProjectBuilding = {
|
|||
|
||||
let command, cwd, args = [], env = [];
|
||||
|
||||
// Copy frequently used env vars
|
||||
let envService = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
["HOME", "PATH"].forEach(key => {
|
||||
let value = envService.get(key);
|
||||
if (value) {
|
||||
env.push(key + "=" + value);
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof(manifest.prepackage) === "string") {
|
||||
command = manifest.prepackage.replace(/%project%/g, project.location);
|
||||
} else if (manifest.prepackage.command) {
|
||||
|
@ -69,16 +100,9 @@ const ProjectBuilding = exports.ProjectBuilding = {
|
|||
args = manifest.prepackage.args || [];
|
||||
args = args.map(a => a.replace(/%project%/g, project.location));
|
||||
|
||||
env = manifest.prepackage.env || [];
|
||||
env = env.concat(manifest.prepackage.env || []);
|
||||
env = env.map(a => a.replace(/%project%/g, project.location));
|
||||
|
||||
// Gaia build system crashes if HOME env variable isn't set...
|
||||
let envService = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
let home = envService.get("HOME");
|
||||
if (home) {
|
||||
env.push("HOME=" + home);
|
||||
}
|
||||
|
||||
if (manifest.prepackage.cwd) {
|
||||
// Normalize path for Windows support (converts / to \)
|
||||
let path = OS.Path.normalize(manifest.prepackage.cwd);
|
||||
|
@ -110,7 +134,6 @@ const ProjectBuilding = exports.ProjectBuilding = {
|
|||
// Otherwise, on Linux and Mac, SHELL env variable should refer to
|
||||
// the user chosen shell program.
|
||||
// (We do not check for OS, as on windows, with cygwin, ComSpec isn't set)
|
||||
let envService = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
let shell = envService.get("ComSpec") || envService.get("SHELL");
|
||||
args.unshift(command);
|
||||
|
||||
|
@ -155,17 +178,22 @@ const ProjectBuilding = exports.ProjectBuilding = {
|
|||
throw new Error("Unable to run pre-package command '" + command + "' " +
|
||||
args.join(" ") + ":\n" + (e.message || e));
|
||||
}
|
||||
|
||||
if (manifest.packageDir) {
|
||||
let packageDir = OS.Path.join(project.location, manifest.packageDir);
|
||||
// On Windows, replace / by \\
|
||||
packageDir = OS.Path.normalize(packageDir);
|
||||
let exists = yield OS.File.exists(packageDir);
|
||||
if (exists) {
|
||||
return packageDir;
|
||||
}
|
||||
throw new Error("Unable to resolve application package directory: '" + manifest.packageDir + "'");
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
getPackageDir: Task.async(function*(project) {
|
||||
let manifest = yield ProjectBuilding.fetchPackageManifest(project);
|
||||
if (!manifest || !manifest.webide || !manifest.webide.packageDir) {
|
||||
return project.location;
|
||||
}
|
||||
manifest = manifest.webide;
|
||||
|
||||
let packageDir = OS.Path.join(project.location, manifest.packageDir);
|
||||
// On Windows, replace / by \\
|
||||
packageDir = OS.Path.normalize(packageDir);
|
||||
let exists = yield OS.File.exists(packageDir);
|
||||
if (exists) {
|
||||
return packageDir;
|
||||
}
|
||||
throw new Error("Unable to resolve application package directory: '" + manifest.packageDir + "'");
|
||||
})
|
||||
};
|
||||
|
|
|
@ -103,8 +103,10 @@ function nextTick() {
|
|||
}
|
||||
|
||||
function waitForUpdate(win, update) {
|
||||
info("Wait: " + update);
|
||||
let deferred = promise.defer();
|
||||
win.AppManager.on("app-manager-update", function onUpdate(e, what) {
|
||||
info("Got: " + what);
|
||||
if (what !== update) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
let AppManager = win.AppManager;
|
||||
|
||||
function isProjectMarkedAsValid() {
|
||||
let details = win.UI.projecteditor.window.frames[0];
|
||||
let details = win.frames[0];
|
||||
return !details.document.body.classList.contains("error");
|
||||
}
|
||||
|
||||
|
@ -40,13 +40,10 @@
|
|||
let packagedAppLocation = getTestFilePath("build_app" + testSuffix + "1");
|
||||
|
||||
yield win.Cmds.importPackagedApp(packagedAppLocation);
|
||||
yield waitForUpdate(win, "details");
|
||||
|
||||
let project = win.AppManager.selectedProject;
|
||||
|
||||
let deferred = promise.defer();
|
||||
win.UI.projecteditor.once("onEditorCreated", deferred.resolve);
|
||||
yield deferred.promise;
|
||||
|
||||
ok(!project.manifest, "manifest includes name");
|
||||
is(project.name, "--", "Display name uses manifest name");
|
||||
|
||||
|
@ -55,18 +52,19 @@
|
|||
loggedMessages.push(msg);
|
||||
}
|
||||
|
||||
let packageDir = yield ProjectBuilding.build({
|
||||
yield ProjectBuilding.build({
|
||||
project,
|
||||
logger
|
||||
});
|
||||
ok(!packageDir, "no custom packagedir");
|
||||
let packageDir = yield ProjectBuilding.getPackageDir(project);
|
||||
is(packageDir, packagedAppLocation, "no custom packagedir");
|
||||
is(loggedMessages[0], "start", "log messages are correct");
|
||||
ok(loggedMessages[1].indexOf("Running pre-package hook") != -1, "log messages are correct");
|
||||
is(loggedMessages[2], "Terminated with error code: 0", "log messages are correct");
|
||||
is(loggedMessages[3], "succeed", "log messages are correct");
|
||||
|
||||
// Trigger validation
|
||||
yield AppManager.validateProject(AppManager.selectedProject);
|
||||
yield AppManager.validateAndUpdateProject(AppManager.selectedProject);
|
||||
yield nextTick();
|
||||
|
||||
ok("name" in project.manifest, "manifest includes name");
|
||||
|
@ -79,14 +77,16 @@
|
|||
packagedAppLocation = getTestFilePath("build_app" + testSuffix + "2");
|
||||
|
||||
yield win.Cmds.importPackagedApp(packagedAppLocation);
|
||||
yield waitForUpdate(win, "project-validated");
|
||||
|
||||
project = win.AppManager.selectedProject;
|
||||
|
||||
loggedMessages = [];
|
||||
packageDir = yield ProjectBuilding.build({
|
||||
yield ProjectBuilding.build({
|
||||
project,
|
||||
logger
|
||||
});
|
||||
packageDir = yield ProjectBuilding.getPackageDir(project);
|
||||
is(OS.Path.normalize(packageDir),
|
||||
OS.Path.join(packagedAppLocation, "stage"), "custom packagedir");
|
||||
is(loggedMessages[0], "start", "log messages are correct");
|
||||
|
@ -96,6 +96,7 @@
|
|||
|
||||
// Switch to the package dir in order to verify the generated webapp.manifest
|
||||
yield win.Cmds.importPackagedApp(packageDir);
|
||||
yield waitForUpdate(win, "project-validated");
|
||||
|
||||
project = win.AppManager.selectedProject;
|
||||
|
||||
|
@ -115,5 +116,3 @@
|
|||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
|
|
@ -28,14 +28,17 @@
|
|||
|
||||
info("to call importPackagedApp(" + packagedAppLocation + ")");
|
||||
yield win.Cmds.importPackagedApp(packagedAppLocation);
|
||||
yield waitForUpdate(win, "project-validated");
|
||||
yield nextTick();
|
||||
|
||||
info("to call importHostedApp(" + hostedAppManifest + ")");
|
||||
yield win.Cmds.importHostedApp(hostedAppManifest);
|
||||
yield waitForUpdate(win, "project-validated");
|
||||
yield nextTick();
|
||||
|
||||
info("to call importPackagedApp(" + packagedAppLocation + ") again");
|
||||
yield win.Cmds.importPackagedApp(packagedAppLocation);
|
||||
yield waitForUpdate(win, "project-validated");
|
||||
|
||||
let project = win.AppManager.selectedProject;
|
||||
is(project.location, packagedAppLocation, "Correctly reselected existing packaged app.");
|
||||
|
@ -43,6 +46,7 @@
|
|||
|
||||
info("to call importHostedApp(" + hostedAppManifest + ") again");
|
||||
yield win.Cmds.importHostedApp(hostedAppManifest);
|
||||
yield waitForUpdate(win, "project-validated");
|
||||
project = win.AppManager.selectedProject;
|
||||
is(project.location, hostedAppManifest, "Correctly reselected existing hosted app.");
|
||||
yield nextTick();
|
||||
|
@ -71,4 +75,3 @@
|
|||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
ok(!win.UI._busyPromise, "UI is not busy");
|
||||
|
||||
yield win.Cmds.importPackagedApp(packagedAppLocation);
|
||||
yield waitForUpdate(win, "project-validated");
|
||||
|
||||
let project = win.AppManager.selectedProject;
|
||||
is(project.location, packagedAppLocation, "Location is valid");
|
||||
|
@ -40,6 +41,7 @@
|
|||
|
||||
let hostedAppManifest = TEST_BASE + "hosted_app.manifest";
|
||||
yield win.Cmds.importHostedApp(hostedAppManifest);
|
||||
yield waitForUpdate(win, "project-validated");
|
||||
|
||||
project = win.AppManager.selectedProject;
|
||||
is(project.location, hostedAppManifest, "Location is valid");
|
||||
|
@ -49,6 +51,7 @@
|
|||
|
||||
hostedAppManifest = TEST_BASE + "/app";
|
||||
yield win.Cmds.importHostedApp(hostedAppManifest);
|
||||
yield waitForUpdate(win, "project-validated");
|
||||
|
||||
project = win.AppManager.selectedProject;
|
||||
ok(project.location.endsWith('manifest.webapp'), "The manifest was found and the project was updated");
|
||||
|
|
|
@ -25,20 +25,17 @@
|
|||
let AppManager = win.AppManager;
|
||||
|
||||
function isProjectMarkedAsValid() {
|
||||
let details = win.UI.projecteditor.window.frames[0];
|
||||
let details = win.frames[0];
|
||||
return !details.document.body.classList.contains("error");
|
||||
}
|
||||
|
||||
let packagedAppLocation = getTestFilePath("app");
|
||||
|
||||
yield win.Cmds.importPackagedApp(packagedAppLocation);
|
||||
yield waitForUpdate(win, "details");
|
||||
|
||||
let project = win.AppManager.selectedProject;
|
||||
|
||||
let deferred = promise.defer();
|
||||
win.UI.projecteditor.once("onEditorCreated", deferred.resolve);
|
||||
yield deferred.promise;
|
||||
|
||||
ok("name" in project.manifest, "manifest includes name");
|
||||
is(project.name, project.manifest.name, "Display name uses manifest name");
|
||||
ok(isProjectMarkedAsValid(), "project is marked as valid");
|
||||
|
@ -66,7 +63,7 @@
|
|||
yield OS.File.writeAtomic(manifestPath, data , {tmpPath: manifestPath + ".tmp"});
|
||||
|
||||
// Trigger validation
|
||||
yield AppManager.validateProject(AppManager.selectedProject);
|
||||
yield AppManager.validateAndUpdateProject(AppManager.selectedProject);
|
||||
yield nextTick();
|
||||
|
||||
ok(!("name" in project.manifest), "manifest has been updated");
|
||||
|
@ -78,7 +75,7 @@
|
|||
yield AppManager.writeManifest(project);
|
||||
|
||||
// Trigger validation
|
||||
yield AppManager.validateProject(AppManager.selectedProject);
|
||||
yield AppManager.validateAndUpdateProject(AppManager.selectedProject);
|
||||
yield nextTick();
|
||||
|
||||
ok("name" in project.manifest, "manifest includes name");
|
||||
|
@ -97,5 +94,3 @@
|
|||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
let packagedAppLocation = getTestFilePath("app");
|
||||
|
||||
yield win.Cmds.importPackagedApp(packagedAppLocation);
|
||||
yield waitForUpdate(win, "project-validated");
|
||||
|
||||
let panelNode = win.document.querySelector("#runtime-panel");
|
||||
let items = panelNode.querySelectorAll(".runtime-panel-item-usb");
|
||||
|
|
|
@ -113,6 +113,7 @@
|
|||
return Task.spawn(function*() {
|
||||
let packagedAppLocation = getTestFilePath("app");
|
||||
yield win.Cmds.importPackagedApp(packagedAppLocation);
|
||||
yield waitForUpdate(win, "project-validated");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,10 @@ function sendMessage(action, data, sync, callbackCookie) {
|
|||
return Components.utils.cloneInto(result, content);
|
||||
}
|
||||
|
||||
function enableDebug() {
|
||||
sendAsyncMessage('Shumway:enableDebug', null);
|
||||
}
|
||||
|
||||
addMessageListener('Shumway:init', function (message) {
|
||||
sendAsyncMessage('Shumway:running', {}, {
|
||||
externalInterface: externalInterfaceWrapper
|
||||
|
@ -55,6 +59,7 @@ addMessageListener('Shumway:init', function (message) {
|
|||
// up Xray wrappers.
|
||||
shumwayComAdapter = Components.utils.createObjectIn(content, {defineAs: 'ShumwayCom'});
|
||||
Components.utils.exportFunction(sendMessage, shumwayComAdapter, {defineAs: 'sendMessage'});
|
||||
Components.utils.exportFunction(enableDebug, shumwayComAdapter, {defineAs: 'enableDebug'});
|
||||
Object.defineProperties(shumwayComAdapter, {
|
||||
onLoadFileCallback: { value: null, writable: true },
|
||||
onExternalCallback: { value: null, writable: true },
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright 2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Simple class for synchronous XHR communication.
|
||||
// See also examples/inspector/debug/server.js.
|
||||
|
||||
var PingPongConnection = (function () {
|
||||
function PingPongConnection(url, onlySend) {
|
||||
this.url = url;
|
||||
this.onData = null;
|
||||
this.onError = null;
|
||||
this.currentXhr = null;
|
||||
this.closed = false;
|
||||
|
||||
if (!onlySend) {
|
||||
this.idle();
|
||||
}
|
||||
}
|
||||
|
||||
PingPongConnection.prototype = {
|
||||
idle: function () {
|
||||
function requestIncoming(connection) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', connection.url + '?idle', true);
|
||||
xhr.onload = function () {
|
||||
if (xhr.status === 204 &&
|
||||
xhr.getResponseHeader('X-PingPong-Error') === 'timeout') {
|
||||
requestIncoming(connection);
|
||||
return;
|
||||
}
|
||||
if (xhr.status === 200) {
|
||||
var result;
|
||||
if (connection.onData) {
|
||||
var response = xhr.responseText;
|
||||
result = connection.onData(response ? JSON.parse(response) : undefined);
|
||||
}
|
||||
if (xhr.getResponseHeader('X-PingPong-Async') === '1') {
|
||||
requestIncoming(connection);
|
||||
} else {
|
||||
sendResponse(connection, result);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (connection.onError) {
|
||||
connection.onError(xhr.statusText);
|
||||
}
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
if (connection.onError) {
|
||||
connection.onError(xhr.error);
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
connection.currentXhr = xhr;
|
||||
}
|
||||
function sendResponse(connection, result) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', connection.url + '?response', false);
|
||||
xhr.onload = function () {
|
||||
if (xhr.status !== 204) {
|
||||
if (connection.onError) {
|
||||
connection.onError(xhr.statusText);
|
||||
}
|
||||
}
|
||||
requestIncoming(connection);
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
if (connection.onError) {
|
||||
connection.onError(xhr.error);
|
||||
}
|
||||
};
|
||||
xhr.send(result === undefined ? '' : JSON.stringify(result));
|
||||
connection.currentXhr = xhr;
|
||||
}
|
||||
requestIncoming(this);
|
||||
},
|
||||
send: function (data, async, timeout) {
|
||||
if (this.closed) {
|
||||
throw new Error('connection closed');
|
||||
}
|
||||
|
||||
async = !!async;
|
||||
timeout |= 0;
|
||||
|
||||
var encoded = data === undefined ? '' : JSON.stringify(data);
|
||||
if (async) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', this.url + '?async', true);
|
||||
xhr.send(encoded);
|
||||
return;
|
||||
} else {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', this.url, false);
|
||||
if (timeout > 0) {
|
||||
xhr.setRequestHeader('X-PingPong-Timeout', timeout);
|
||||
}
|
||||
xhr.send(encoded);
|
||||
if (xhr.status === 204 &&
|
||||
xhr.getResponseHeader('X-PingPong-Error') === 'timeout') {
|
||||
throw new Error('sync request timeout');
|
||||
}
|
||||
var response = xhr.responseText;
|
||||
return response ? JSON.parse(response) : undefined;
|
||||
}
|
||||
},
|
||||
close: function () {
|
||||
if (this.currentXhr) {
|
||||
this.currentXhr.abort();
|
||||
this.currentXhr = null;
|
||||
}
|
||||
this.closed = true;
|
||||
}
|
||||
};
|
||||
|
||||
return PingPongConnection;
|
||||
})();
|
|
@ -36,11 +36,23 @@ limitations under the License.
|
|||
line-height: 0;
|
||||
border: 0px none;
|
||||
}
|
||||
|
||||
body.remoteStopped {
|
||||
background-color: red;
|
||||
}
|
||||
body.remoteDebug {
|
||||
background-color: green;
|
||||
}
|
||||
body.remoteReload {
|
||||
background-color: yellow;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<iframe id="viewer" src="resource://shumway/web/viewer.html" width="100%" height="100%" mozbrowser remote="true"></iframe>
|
||||
<script src="chrome://shumway/content/pingpong.js"></script>
|
||||
<script src="chrome://shumway/content/viewerDebugger.js"></script>
|
||||
<script src="chrome://shumway/content/viewerWrapper.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright 2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
Components.utils.import('resource://gre/modules/Services.jsm');
|
||||
|
||||
var DebugUtils = (function () {
|
||||
var baseUrl = null;
|
||||
|
||||
function getBaseUrl() {
|
||||
if (baseUrl === null) {
|
||||
try {
|
||||
baseUrl = Services.prefs.getCharPref('shumway.debug.url');
|
||||
} catch (e) {
|
||||
baseUrl = 'http://localhost:8010';
|
||||
}
|
||||
}
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
var uniqueId = (Date.now() % 888888) * 2 + 1;
|
||||
|
||||
function getEnabledDebuggerId(swfUrl) {
|
||||
return new Promise(function (resolve) {
|
||||
var url = getBaseUrl() + '/debugController/' + uniqueId;
|
||||
var connection = new PingPongConnection(url);
|
||||
connection.onData = function (data) {
|
||||
if (data.action === 'setDebugger' && data.swfUrl === swfUrl) {
|
||||
resolve(data.debuggerId);
|
||||
}
|
||||
};
|
||||
try {
|
||||
connection.send({action: 'getDebugger', swfUrl: swfUrl, swfId: uniqueId}, true);
|
||||
} catch (e) {
|
||||
// ignoring failed send request
|
||||
}
|
||||
setTimeout(function () {
|
||||
resolve(0);
|
||||
connection.close();
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
|
||||
function enableDebug(swfUrl) {
|
||||
var url = getBaseUrl() + '/debugController/' + uniqueId;
|
||||
var connection = new PingPongConnection(url, true);
|
||||
try {
|
||||
connection.send({action: 'enableDebugging', swfUrl: swfUrl}, true);
|
||||
} catch (e) {
|
||||
// ignoring failed send request
|
||||
}
|
||||
connection.close();
|
||||
}
|
||||
|
||||
function createDebuggerConnection(swfUrl) {
|
||||
return getEnabledDebuggerId(swfUrl).then(function (debuggerId) {
|
||||
if (!debuggerId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var url = getBaseUrl() + '/debug/' + uniqueId + '/' + debuggerId;
|
||||
console.log('Starting remote debugger with ' + url);
|
||||
return new PingPongConnection(url);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
get isEnabled() {
|
||||
try {
|
||||
return Services.prefs.getBoolPref('shumway.debug.enabled');
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
enableDebug: enableDebug,
|
||||
createDebuggerConnection: createDebuggerConnection
|
||||
};
|
||||
})();
|
|
@ -53,6 +53,7 @@ function runViewer() {
|
|||
// ShumwayStreamConverter.
|
||||
var shumwayComAdapter = Components.utils.createObjectIn(childWindow, {defineAs: 'ShumwayCom'});
|
||||
Components.utils.exportFunction(sendMessage, shumwayComAdapter, {defineAs: 'sendMessage'});
|
||||
Components.utils.exportFunction(enableDebug, shumwayComAdapter, {defineAs: 'enableDebug'});
|
||||
Object.defineProperties(shumwayComAdapter, {
|
||||
onLoadFileCallback: { value: null, writable: true },
|
||||
onExternalCallback: { value: null, writable: true },
|
||||
|
@ -117,6 +118,10 @@ function runViewer() {
|
|||
return window.notifyShumwayMessage(detail);
|
||||
});
|
||||
|
||||
messageManager.addMessageListener('Shumway:enableDebug', function (message) {
|
||||
enableDebug();
|
||||
});
|
||||
|
||||
window.onExternalCallback = function (call) {
|
||||
return externalInterface.callback(JSON.stringify(call));
|
||||
};
|
||||
|
@ -135,7 +140,76 @@ function runViewer() {
|
|||
messageManager.sendAsyncMessage('Shumway:init', {});
|
||||
}
|
||||
|
||||
|
||||
function handleDebug(connection) {
|
||||
viewer.parentNode.removeChild(viewer); // we don't need viewer anymore
|
||||
document.body.className = 'remoteDebug';
|
||||
|
||||
function sendMessage(data) {
|
||||
var detail = {
|
||||
action: data.action,
|
||||
data: data.data,
|
||||
sync: data.sync
|
||||
};
|
||||
if (data.callback) {
|
||||
detail.callback = true;
|
||||
detail.cookie = data.cookie;
|
||||
}
|
||||
return window.notifyShumwayMessage(detail);
|
||||
}
|
||||
|
||||
connection.onData = function (data) {
|
||||
switch (data.action) {
|
||||
case 'sendMessage':
|
||||
return sendMessage(data.detail);
|
||||
case 'reload':
|
||||
document.body.className = 'remoteReload';
|
||||
setTimeout(function () {
|
||||
window.top.location.reload();
|
||||
}, 1000);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
window.onExternalCallback = function (call) {
|
||||
return connection.send({action: 'onExternalCallback', detail: call});
|
||||
};
|
||||
|
||||
window.onMessageCallback = function (response) {
|
||||
return connection.send({action: 'onMessageCallback', detail: response});
|
||||
};
|
||||
|
||||
window.onLoadFileCallback = function (args) {
|
||||
if (args.array) {
|
||||
args.array = Array.prototype.slice.call(args.array, 0);
|
||||
}
|
||||
return connection.send({action: 'onLoadFileCallback', detail: args}, true);
|
||||
};
|
||||
|
||||
connection.send({action: 'runViewer'}, true);
|
||||
}
|
||||
|
||||
function enableDebug() {
|
||||
DebugUtils.enableDebug(window.swfUrlLoading);
|
||||
setTimeout(function () {
|
||||
window.top.location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
promise.then(function (oop) {
|
||||
if (DebugUtils.isEnabled) {
|
||||
DebugUtils.createDebuggerConnection(window.swfUrlLoading).then(function (debuggerConnection) {
|
||||
if (debuggerConnection) {
|
||||
handleDebug(debuggerConnection);
|
||||
} else if (oop) {
|
||||
handlerOOP();
|
||||
} else {
|
||||
handler();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (oop) {
|
||||
handlerOOP();
|
||||
} else {
|
||||
|
|
|
@ -411,7 +411,7 @@ ChromeActions.prototype = {
|
|||
var position = e.loaded;
|
||||
var data = new Uint8Array(xhr.response);
|
||||
notifyLoadFileListener({callback:"loadFile", sessionId: sessionId,
|
||||
topic: "progress", array: data, loaded: e.loaded, total: e.total});
|
||||
topic: "progress", array: data, loaded: position, total: e.total});
|
||||
lastPosition = position;
|
||||
if (limit && e.total >= limit) {
|
||||
xhr.abort();
|
||||
|
@ -755,6 +755,59 @@ function activateShumwayScripts(window, requestListener) {
|
|||
return requestListener.receive.apply(requestListener, arguments);
|
||||
};
|
||||
window.wrappedJSObject.runViewer();
|
||||
|
||||
var parentWindow = window.parent;
|
||||
var viewerWindow = window.viewer.contentWindow;
|
||||
|
||||
function activate(e) {
|
||||
e.preventDefault();
|
||||
viewerWindow.removeEventListener('mousedown', activate, true);
|
||||
|
||||
parentWindow.addEventListener('keydown', forwardKeyEvent, true);
|
||||
parentWindow.addEventListener('keyup', forwardKeyEvent, true);
|
||||
|
||||
sendFocusEvent('focus');
|
||||
|
||||
parentWindow.addEventListener('blur', deactivate, true);
|
||||
parentWindow.addEventListener('mousedown', deactivate, true);
|
||||
viewerWindow.addEventListener('unload', deactivate, true);
|
||||
}
|
||||
|
||||
function deactivate() {
|
||||
parentWindow.removeEventListener('blur', deactivate, true);
|
||||
parentWindow.removeEventListener('mousedown', deactivate, true);
|
||||
viewerWindow.removeEventListener('unload', deactivate, true);
|
||||
|
||||
parentWindow.removeEventListener('keydown', forwardKeyEvent, true);
|
||||
parentWindow.removeEventListener('keyup', forwardKeyEvent, true);
|
||||
|
||||
sendFocusEvent('blur');
|
||||
|
||||
viewerWindow.addEventListener('mousedown', activate, true);
|
||||
}
|
||||
|
||||
function forwardKeyEvent(e) {
|
||||
var event = viewerWindow.document.createEvent('KeyboardEvent');
|
||||
event.initKeyEvent(e.type,
|
||||
e.bubbles,
|
||||
e.cancelable,
|
||||
e.view,
|
||||
e.ctrlKey,
|
||||
e.altKey,
|
||||
e.shiftKey,
|
||||
e.metaKey,
|
||||
e.keyCode,
|
||||
e.charCode);
|
||||
viewerWindow.dispatchEvent(event);
|
||||
}
|
||||
|
||||
function sendFocusEvent(type) {
|
||||
var event = viewerWindow.document.createEvent("UIEvent");
|
||||
event.initEvent(type, false, true);
|
||||
viewerWindow.dispatchEvent(event);
|
||||
}
|
||||
|
||||
viewerWindow.addEventListener('mousedown', activate, true);
|
||||
}
|
||||
|
||||
if (window.document.readyState === "interactive" ||
|
||||
|
@ -1034,6 +1087,8 @@ ShumwayStreamConverterBase.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
domWindow.swfUrlLoading = actions.url;
|
||||
|
||||
// Report telemetry on amount of swfs on the page
|
||||
if (actions.isOverlay) {
|
||||
// Looking for last actions with same baseUrl
|
||||
|
|
Двоичный файл не отображается.
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,2 +1,2 @@
|
|||
0.9.3775
|
||||
a82ac47
|
||||
0.10.182
|
||||
0195a96
|
||||
|
|
Двоичный файл не отображается.
|
@ -111,6 +111,7 @@ limitations under the License.
|
|||
<menuitem label="Open in Inspector" id="inspectorMenu"></menuitem>
|
||||
<menuitem label="Report Problems" id="reportMenu"></menuitem>
|
||||
<menuitem label="Reload in Adobe Flash Player" id="fallbackMenu" hidden></menuitem>
|
||||
<menuitem label="Debug this SWF" id="debugMenu"></menuitem>
|
||||
<menuitem label="About Shumway %version%..." id="aboutMenu"></menuitem>
|
||||
</menu>
|
||||
</section>
|
||||
|
|
|
@ -93,7 +93,7 @@ var viewerPlayerglobalInfo = {
|
|||
catalog: SHUMWAY_ROOT + "playerglobal/playerglobal.json"
|
||||
};
|
||||
|
||||
var builtinPath = SHUMWAY_ROOT + "avm2/generated/builtin/builtin.abc";
|
||||
var builtinPath = SHUMWAY_ROOT + "libs/builtin.abc";
|
||||
|
||||
var playerWindow;
|
||||
var playerWindowLoaded = new Promise(function(resolve) {
|
||||
|
@ -165,6 +165,13 @@ function runViewer() {
|
|||
var version = Shumway.version || '';
|
||||
document.getElementById('aboutMenu').label =
|
||||
document.getElementById('aboutMenu').label.replace('%version%', version);
|
||||
|
||||
var debugMenuEnabled = FirefoxCom.requestSync('getBoolPref', {pref: 'shumway.debug.enabled', def: false});
|
||||
if (debugMenuEnabled) {
|
||||
document.getElementById('debugMenu').addEventListener('click', enableDebug);
|
||||
} else {
|
||||
document.getElementById('debugMenu').remove();
|
||||
}
|
||||
}
|
||||
|
||||
function showURL() {
|
||||
|
@ -205,6 +212,10 @@ function showAbout() {
|
|||
window.open('http://areweflashyet.com/');
|
||||
}
|
||||
|
||||
function enableDebug() {
|
||||
ShumwayCom.enableDebug();
|
||||
}
|
||||
|
||||
var movieUrl, movieParams, objectParams;
|
||||
|
||||
window.addEventListener("message", function handlerMessage(e) {
|
||||
|
|
|
@ -22,8 +22,7 @@ var viewerPlayerglobalInfo = {
|
|||
catalog: SHUMWAY_ROOT + "playerglobal/playerglobal.json"
|
||||
};
|
||||
|
||||
var avm2Root = SHUMWAY_ROOT + "avm2/";
|
||||
var builtinPath = avm2Root + "generated/builtin/builtin.abc";
|
||||
var builtinPath = SHUMWAY_ROOT + "libs/builtin.abc";
|
||||
|
||||
window.print = function(msg) {
|
||||
console.log(msg);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!ENTITY window.title "Exceptions">
|
||||
<!ENTITY window.width "36em">
|
||||
<!ENTITY window.width "45em">
|
||||
|
||||
<!ENTITY treehead.sitename.label "Site">
|
||||
<!ENTITY treehead.status.label "Status">
|
||||
|
@ -21,6 +21,8 @@
|
|||
<!ENTITY allow.accesskey "A">
|
||||
<!ENTITY windowClose.key "w">
|
||||
|
||||
<!ENTITY button.close.label "Close">
|
||||
<!ENTITY button.close.accesskey "C">
|
||||
<!ENTITY button.cancel.label "Cancel">
|
||||
<!ENTITY button.cancel.accesskey "C">
|
||||
<!ENTITY button.ok.label "Save Changes">
|
||||
<!ENTITY button.ok.accesskey "S">
|
||||
|
||||
|
|
|
@ -77,6 +77,11 @@ BluetoothOppManager::Observe(nsISupports* aSubject,
|
|||
const char16_t* aData)
|
||||
{
|
||||
MOZ_ASSERT(sBluetoothOppManager);
|
||||
// if state of any volume was changed
|
||||
if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
|
||||
HandleVolumeStateChanged(aSubject);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
||||
HandleShutdown();
|
||||
|
@ -204,6 +209,10 @@ BluetoothOppManager::~BluetoothOppManager()
|
|||
if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
|
||||
BT_WARNING("Failed to remove shutdown observer!");
|
||||
}
|
||||
|
||||
if (NS_FAILED(obs->RemoveObserver(this, NS_VOLUME_STATE_CHANGED))) {
|
||||
BT_WARNING("Failed to remove volume observer!");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -216,6 +225,11 @@ BluetoothOppManager::Init()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (NS_FAILED(obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, false))) {
|
||||
BT_WARNING("Failed to add ns volume observer!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* We don't start listening here as BluetoothServiceBluedroid calls Listen()
|
||||
* immediately when BT stops.
|
||||
|
@ -283,6 +297,40 @@ BluetoothOppManager::HandleShutdown()
|
|||
sBluetoothOppManager = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothOppManager::HandleVolumeStateChanged(nsISupports* aSubject)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// We will disconnect OPP connection no matter which volume becomes non
|
||||
// STATE_MOUNTED, since user may select files scattered in multiple volumes
|
||||
// in a single transfer, (e.g., user selects 2 files to transfer, one is in
|
||||
// internal volume and the other is in external volume).
|
||||
|
||||
if (!mConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get volume interface and state
|
||||
nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
|
||||
if (!vol) {
|
||||
return;
|
||||
}
|
||||
int32_t state;
|
||||
vol->GetState(&state);
|
||||
if (nsIVolume::STATE_MOUNTED != state) {
|
||||
// Here is for unexpected physical removal of any storage.
|
||||
// By checking the volume state change, we should release the unavailable
|
||||
// storage to prevent being killed by vold.
|
||||
// Disconnect any connected OPP connection since volume state becomes
|
||||
// non STATE_MOUNTED. Then |OnSocketDisconnect| would also be called to
|
||||
// handle remaining files during send(|DiscardBlobsToSend|) or
|
||||
// receive(|DeleteReceivedFile|).
|
||||
BT_LOGR("Volume state is not STATE_MOUNTED. Abort any ongoing OPP connection.");
|
||||
Disconnect(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothOppManager::Listen()
|
||||
{
|
||||
|
@ -1099,7 +1147,9 @@ BluetoothOppManager::ReceiveSocketData(BluetoothSocket* aSocket,
|
|||
void
|
||||
BluetoothOppManager::SendConnectRequest()
|
||||
{
|
||||
if (mConnected) return;
|
||||
if (mConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Section 3.3.1 "Connect", IrOBEX 1.2
|
||||
// [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
|
||||
|
@ -1119,7 +1169,9 @@ void
|
|||
BluetoothOppManager::SendPutHeaderRequest(const nsAString& aFileName,
|
||||
int aFileSize)
|
||||
{
|
||||
if (!mConnected) return;
|
||||
if (!mConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
|
||||
|
||||
|
@ -1150,7 +1202,10 @@ void
|
|||
BluetoothOppManager::SendPutRequest(uint8_t* aFileBody,
|
||||
int aFileBodyLength)
|
||||
{
|
||||
if (!mConnected) return;
|
||||
if (!mConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
int packetLeftSpace = mRemoteMaxPacketLength - kPutRequestHeaderSize;
|
||||
if (aFileBodyLength > packetLeftSpace) {
|
||||
BT_WARNING("Not allowed such a small MaxPacketLength value");
|
||||
|
@ -1174,7 +1229,9 @@ BluetoothOppManager::SendPutRequest(uint8_t* aFileBody,
|
|||
void
|
||||
BluetoothOppManager::SendPutFinalRequest()
|
||||
{
|
||||
if (!mConnected) return;
|
||||
if (!mConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Section 2.2.9, "End-of-Body", IrObex 1.2
|
||||
|
@ -1197,7 +1254,9 @@ BluetoothOppManager::SendPutFinalRequest()
|
|||
void
|
||||
BluetoothOppManager::SendDisconnectRequest()
|
||||
{
|
||||
if (!mConnected) return;
|
||||
if (!mConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Section 3.3.2 "Disconnect", IrOBEX 1.2
|
||||
// [opcode:1][length:2][Headers:var]
|
||||
|
@ -1230,7 +1289,9 @@ BluetoothOppManager::GetAddress(nsAString& aDeviceAddress)
|
|||
void
|
||||
BluetoothOppManager::ReplyToConnect()
|
||||
{
|
||||
if (mConnected) return;
|
||||
if (mConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Section 3.3.1 "Connect", IrOBEX 1.2
|
||||
// [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
|
||||
|
@ -1249,7 +1310,9 @@ BluetoothOppManager::ReplyToConnect()
|
|||
void
|
||||
BluetoothOppManager::ReplyToDisconnectOrAbort()
|
||||
{
|
||||
if (!mConnected) return;
|
||||
if (!mConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Section 3.3.2 "Disconnect" and Section 3.3.5 "Abort", IrOBEX 1.2
|
||||
// The format of response packet of "Disconnect" and "Abort" are the same
|
||||
|
@ -1263,7 +1326,9 @@ BluetoothOppManager::ReplyToDisconnectOrAbort()
|
|||
void
|
||||
BluetoothOppManager::ReplyToPut(bool aFinal, bool aContinue)
|
||||
{
|
||||
if (!mConnected) return;
|
||||
if (!mConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The received length can be reset here because this is where we reply to a
|
||||
// complete put packet.
|
||||
|
|
|
@ -77,6 +77,7 @@ private:
|
|||
BluetoothOppManager();
|
||||
bool Init();
|
||||
void HandleShutdown();
|
||||
void HandleVolumeStateChanged(nsISupports* aSubject);
|
||||
|
||||
void StartFileTransfer();
|
||||
void StartSendingNextFile();
|
||||
|
|
|
@ -50,23 +50,21 @@ BluetoothPairingListener::~BluetoothPairingListener()
|
|||
}
|
||||
|
||||
void
|
||||
BluetoothPairingListener::DispatchPairingEvent(BluetoothDevice* aDevice,
|
||||
BluetoothPairingListener::DispatchPairingEvent(const nsAString& aName,
|
||||
const nsAString& aAddress,
|
||||
const nsAString& aPasskey,
|
||||
const nsAString& aType)
|
||||
{
|
||||
MOZ_ASSERT(aDevice && !aType.IsEmpty());
|
||||
|
||||
nsString address;
|
||||
aDevice->GetAddress(address);
|
||||
MOZ_ASSERT(!aName.IsEmpty() && !aAddress.IsEmpty() && !aType.IsEmpty());
|
||||
|
||||
nsRefPtr<BluetoothPairingHandle> handle =
|
||||
BluetoothPairingHandle::Create(GetOwner(),
|
||||
address,
|
||||
aAddress,
|
||||
aType,
|
||||
aPasskey);
|
||||
|
||||
BluetoothPairingEventInit init;
|
||||
init.mDevice = aDevice;
|
||||
init.mDeviceName = aName;
|
||||
init.mHandle = handle;
|
||||
|
||||
nsRefPtr<BluetoothPairingEvent> event =
|
||||
|
@ -96,20 +94,13 @@ BluetoothPairingListener::Notify(const BluetoothSignal& aData)
|
|||
arr[2].value().type() == BluetoothValue::TnsString && // passkey
|
||||
arr[3].value().type() == BluetoothValue::TnsString); // type
|
||||
|
||||
nsString deviceAddress = arr[0].value().get_nsString();
|
||||
nsString deviceName = arr[1].value().get_nsString();
|
||||
nsString address = arr[0].value().get_nsString();
|
||||
nsString name = arr[1].value().get_nsString();
|
||||
nsString passkey = arr[2].value().get_nsString();
|
||||
nsString type = arr[3].value().get_nsString();
|
||||
|
||||
// Create a temporary device with deviceAddress and deviceName
|
||||
InfallibleTArray<BluetoothNamedValue> props;
|
||||
BT_APPEND_NAMED_VALUE(props, "Address", deviceAddress);
|
||||
BT_APPEND_NAMED_VALUE(props, "Name", deviceName);
|
||||
nsRefPtr<BluetoothDevice> device =
|
||||
BluetoothDevice::Create(GetOwner(), props);
|
||||
|
||||
// Notify pairing listener of pairing requests
|
||||
DispatchPairingEvent(device, passkey, type);
|
||||
DispatchPairingEvent(name, address, passkey, type);
|
||||
} else {
|
||||
BT_WARNING("Not handling pairing listener signal: %s",
|
||||
NS_ConvertUTF16toUTF8(aData.name()).get());
|
||||
|
|
|
@ -25,7 +25,8 @@ public:
|
|||
static already_AddRefed<BluetoothPairingListener>
|
||||
Create(nsPIDOMWindow* aWindow);
|
||||
|
||||
void DispatchPairingEvent(BluetoothDevice* aDevice,
|
||||
void DispatchPairingEvent(const nsAString& aName,
|
||||
const nsAString& aAddress,
|
||||
const nsAString& aPasskey,
|
||||
const nsAString& aType);
|
||||
|
||||
|
|
|
@ -1328,14 +1328,15 @@ public:
|
|||
already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aOnSuccess,
|
||||
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aOnFailure,
|
||||
uint64_t aWindowId, nsACString& aAudioLoopbackDev,
|
||||
nsACString& aVideoLoopbackDev)
|
||||
nsACString& aVideoLoopbackDev, bool aUseFakeDevices)
|
||||
: mConstraints(aConstraints)
|
||||
, mOnSuccess(aOnSuccess)
|
||||
, mOnFailure(aOnFailure)
|
||||
, mManager(MediaManager::GetInstance())
|
||||
, mWindowId(aWindowId)
|
||||
, mLoopbackAudioDevice(aAudioLoopbackDev)
|
||||
, mLoopbackVideoDevice(aVideoLoopbackDev) {}
|
||||
, mLoopbackVideoDevice(aVideoLoopbackDev)
|
||||
, mUseFakeDevices(aUseFakeDevices) {}
|
||||
|
||||
void // NS_IMETHOD
|
||||
Run()
|
||||
|
@ -1343,7 +1344,7 @@ public:
|
|||
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
|
||||
|
||||
nsRefPtr<MediaEngine> backend;
|
||||
if (mConstraints.mFake)
|
||||
if (mConstraints.mFake || mUseFakeDevices)
|
||||
backend = new MediaEngineDefault(mConstraints.mFakeTracks);
|
||||
else
|
||||
backend = mManager->GetBackend(mWindowId);
|
||||
|
@ -1387,6 +1388,7 @@ private:
|
|||
// automated media tests only.
|
||||
nsCString mLoopbackAudioDevice;
|
||||
nsCString mLoopbackVideoDevice;
|
||||
bool mUseFakeDevices;
|
||||
};
|
||||
|
||||
MediaManager::MediaManager()
|
||||
|
@ -1596,10 +1598,15 @@ MediaManager::GetUserMedia(
|
|||
if (!Preferences::GetBool("media.navigator.video.enabled", true)) {
|
||||
c.mVideo.SetAsBoolean() = false;
|
||||
}
|
||||
bool fake = true;
|
||||
if (!c.mFake &&
|
||||
!Preferences::GetBool("media.navigator.streams.fake", false)) {
|
||||
fake = false;
|
||||
}
|
||||
|
||||
// Pass callbacks and MediaStreamListener along to GetUserMediaTask.
|
||||
nsAutoPtr<GetUserMediaTask> task;
|
||||
if (c.mFake) {
|
||||
if (fake) {
|
||||
// Fake stream from default backend.
|
||||
task = new GetUserMediaTask(c, onSuccess.forget(),
|
||||
onFailure.forget(), windowID, listener, mPrefs, new MediaEngineDefault(c.mFakeTracks));
|
||||
|
@ -1711,7 +1718,7 @@ MediaManager::GetUserMedia(
|
|||
|
||||
// XXX No full support for picture in Desktop yet (needs proper UI)
|
||||
if (privileged ||
|
||||
(c.mFake && !Preferences::GetBool("media.navigator.permission.fake"))) {
|
||||
(fake && !Preferences::GetBool("media.navigator.permission.fake"))) {
|
||||
MediaManager::GetMessageLoop()->PostTask(FROM_HERE, task.forget());
|
||||
} else {
|
||||
bool isHTTPS = false;
|
||||
|
@ -1805,12 +1812,14 @@ MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
|
|||
Preferences::GetCString("media.audio_loopback_dev");
|
||||
nsAdoptingCString loopbackVideoDevice =
|
||||
Preferences::GetCString("media.video_loopback_dev");
|
||||
bool useFakeStreams =
|
||||
Preferences::GetBool("media.navigator.streams.fake", false);
|
||||
|
||||
MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
|
||||
new GetUserMediaDevicesTask(
|
||||
aConstraints, onSuccess.forget(), onFailure.forget(),
|
||||
(aInnerWindowID ? aInnerWindowID : aWindow->WindowID()),
|
||||
loopbackAudioDevice, loopbackVideoDevice));
|
||||
loopbackAudioDevice, loopbackVideoDevice, useFakeStreams));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
optional BluetoothPairingEventInit eventInitDict)]
|
||||
interface BluetoothPairingEvent : Event
|
||||
{
|
||||
readonly attribute BluetoothDevice device;
|
||||
readonly attribute DOMString deviceName;
|
||||
readonly attribute BluetoothPairingHandle handle;
|
||||
};
|
||||
|
||||
dictionary BluetoothPairingEventInit : EventInit
|
||||
{
|
||||
required BluetoothDevice device;
|
||||
required DOMString deviceName;
|
||||
required BluetoothPairingHandle handle;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
PATTERN_FILES = $(strip $(wildcard $(srcdir)/*/hyphenation/*.dic))
|
||||
|
||||
ifneq (,$(PATTERN_FILES))
|
||||
libs::
|
||||
$(INSTALL) $(PATTERN_FILES) $(FINAL_TARGET)/hyphenation
|
||||
endif
|
|
@ -4,45 +4,3 @@
|
|||
# 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/.
|
||||
|
||||
locales = [
|
||||
'af',
|
||||
'bg',
|
||||
'ca',
|
||||
'cy',
|
||||
'da',
|
||||
'de-1901',
|
||||
'de-1996',
|
||||
'de-CH',
|
||||
# 'en-US', # en-US is renamed -- see below.
|
||||
'eo',
|
||||
'es',
|
||||
'et',
|
||||
'fi',
|
||||
'fr',
|
||||
'gl',
|
||||
'hr',
|
||||
'hsb',
|
||||
'hu',
|
||||
'ia',
|
||||
'is',
|
||||
'it',
|
||||
'kmr',
|
||||
'la',
|
||||
'lt',
|
||||
'mn',
|
||||
'nb',
|
||||
'nl',
|
||||
'nn',
|
||||
'pl',
|
||||
'pt',
|
||||
'ru',
|
||||
'sh',
|
||||
'sl',
|
||||
'sv',
|
||||
'tr',
|
||||
'uk',
|
||||
]
|
||||
filename = '{locale}/hyphenation/hyph_{locale}.dic'
|
||||
FINAL_TARGET_FILES.hyphenation += [filename.format(locale=locale) for locale in locales]
|
||||
# en-US is a special case: the dic file is named like en_US.
|
||||
FINAL_TARGET_FILES.hyphenation += ['en-US/hyphenation/hyph_en_US.dic']
|
||||
|
|
|
@ -133,15 +133,12 @@ import android.view.ViewGroup;
|
|||
import android.view.ViewStub;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.widget.ListView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.Toast;
|
||||
import android.widget.ViewFlipper;
|
||||
|
||||
import com.readystatesoftware.systembartint.SystemBarTintManager;
|
||||
|
||||
public class BrowserApp extends GeckoApp
|
||||
implements TabsPanel.TabsLayoutChangeListener,
|
||||
PropertyAnimator.PropertyAnimationListener,
|
||||
|
@ -245,8 +242,6 @@ public class BrowserApp extends GeckoApp
|
|||
|
||||
private ReadingListHelper mReadingListHelper;
|
||||
|
||||
private SystemBarTintManager mTintManager;
|
||||
|
||||
// The tab to be selected on editing mode exit.
|
||||
private Integer mTargetTabForEditingMode;
|
||||
|
||||
|
@ -687,8 +682,6 @@ public class BrowserApp extends GeckoApp
|
|||
|
||||
final Context appContext = getApplicationContext();
|
||||
|
||||
setupSystemUITinting();
|
||||
|
||||
mBrowserChrome = (ViewGroup) findViewById(R.id.browser_chrome);
|
||||
mActionBarFlipper = (ViewFlipper) findViewById(R.id.browser_actionbar);
|
||||
mActionBar = (ActionModeCompatView) findViewById(R.id.actionbar);
|
||||
|
@ -838,31 +831,6 @@ public class BrowserApp extends GeckoApp
|
|||
}
|
||||
}
|
||||
|
||||
private void setupSystemUITinting() {
|
||||
if (!Versions.feature19Plus) {
|
||||
return;
|
||||
}
|
||||
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
|
||||
mTintManager = new SystemBarTintManager(this);
|
||||
mTintManager.setTintColor(getResources().getColor(R.color.background_tabs));
|
||||
updateSystemUITinting(mRootLayout.getSystemUiVisibility());
|
||||
|
||||
mRootLayout.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
|
||||
@Override
|
||||
public void onSystemUiVisibilityChange(int visibility) {
|
||||
updateSystemUITinting(visibility);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateSystemUITinting(int visibility) {
|
||||
final boolean shouldTint = (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0 &&
|
||||
(visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0;
|
||||
mTintManager.setStatusBarTintEnabled(shouldTint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and show the firstrun pane if the browser has never been launched and
|
||||
* is not opening an external link from another application.
|
||||
|
@ -2649,11 +2617,6 @@ public class BrowserApp extends GeckoApp
|
|||
view.getHitRect(mTempRect);
|
||||
mTempRect.offset(-view.getScrollX(), -view.getScrollY());
|
||||
|
||||
if (mTintManager != null) {
|
||||
SystemBarTintManager.SystemBarConfig config = mTintManager.getConfig();
|
||||
mTempRect.offset(0, -config.getPixelInsetTop(false));
|
||||
}
|
||||
|
||||
int[] viewCoords = new int[2];
|
||||
view.getLocationOnScreen(viewCoords);
|
||||
|
||||
|
|
|
@ -571,7 +571,7 @@ final class GeckoEditable
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setUpdateGecko(boolean update) {
|
||||
public void setUpdateGecko(boolean update, boolean force) {
|
||||
if (!onIcThread()) {
|
||||
// Android may be holding an old InputConnection; ignore
|
||||
if (DEBUG) {
|
||||
|
@ -580,7 +580,7 @@ final class GeckoEditable
|
|||
return;
|
||||
}
|
||||
if (update) {
|
||||
icUpdateGecko(false);
|
||||
icUpdateGecko(force);
|
||||
}
|
||||
mUpdateGecko = update;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import android.text.Editable;
|
|||
interface GeckoEditableClient {
|
||||
void sendEvent(GeckoEvent event);
|
||||
Editable getEditable();
|
||||
void setUpdateGecko(boolean update);
|
||||
void setUpdateGecko(boolean update, boolean force);
|
||||
void setSuppressKeyUp(boolean suppress);
|
||||
Handler getInputConnectionHandler();
|
||||
boolean setInputConnectionHandler(Handler handler);
|
||||
|
|
|
@ -250,7 +250,7 @@ class GeckoInputConnection
|
|||
@Override
|
||||
public synchronized boolean beginBatchEdit() {
|
||||
mBatchEditCount++;
|
||||
mEditableClient.setUpdateGecko(false);
|
||||
mEditableClient.setUpdateGecko(false, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -259,6 +259,10 @@ class GeckoInputConnection
|
|||
if (mBatchEditCount > 0) {
|
||||
mBatchEditCount--;
|
||||
if (mBatchEditCount == 0) {
|
||||
// Force Gecko update for cancelled auto-correction of single-
|
||||
// character words to prevent character duplication. (bug 1133802)
|
||||
boolean forceUpdate = !mBatchTextChanged && !mBatchSelectionChanged;
|
||||
|
||||
if (mBatchTextChanged) {
|
||||
notifyTextChange();
|
||||
mBatchTextChanged = false;
|
||||
|
@ -269,7 +273,7 @@ class GeckoInputConnection
|
|||
Selection.getSelectionEnd(editable));
|
||||
mBatchSelectionChanged = false;
|
||||
}
|
||||
mEditableClient.setUpdateGecko(true);
|
||||
mEditableClient.setUpdateGecko(true, forceUpdate);
|
||||
}
|
||||
} else {
|
||||
Log.w(LOGTAG, "endBatchEdit() called, but mBatchEditCount == 0?!");
|
||||
|
|
|
@ -43,6 +43,7 @@ class TextSelection extends Layer implements GeckoEventListener {
|
|||
private final DrawListener mDrawListener;
|
||||
private boolean mDraggingHandles;
|
||||
|
||||
private int selectionID; // Unique ID provided for each selection action.
|
||||
private float mViewLeft;
|
||||
private float mViewTop;
|
||||
private float mViewZoom;
|
||||
|
@ -131,6 +132,7 @@ class TextSelection extends Layer implements GeckoEventListener {
|
|||
public void run() {
|
||||
try {
|
||||
if (event.equals("TextSelection:ShowHandles")) {
|
||||
selectionID = message.getInt("selectionID");
|
||||
final JSONArray handles = message.getJSONArray("handles");
|
||||
for (int i=0; i < handles.length(); i++) {
|
||||
String handle = handles.getString(i);
|
||||
|
@ -315,7 +317,17 @@ class TextSelection extends Layer implements GeckoEventListener {
|
|||
public void onDestroyActionMode(ActionModeCompat mode) {
|
||||
mActionMode = null;
|
||||
mCallback = null;
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("TextSelection:End", null));
|
||||
final JSONObject args = new JSONObject();
|
||||
try {
|
||||
args.put("selectionID", selectionID);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "Error building JSON arguments for TextSelection:End", e);
|
||||
return;
|
||||
}
|
||||
|
||||
final GeckoEvent event =
|
||||
GeckoEvent.createBroadcastEvent("TextSelection:End", args.toString());
|
||||
GeckoAppShell.sendEventToGecko(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -628,7 +628,6 @@ gtjar.sources += [ thirdparty_source_dir + f for f in [
|
|||
'com/nineoldandroids/view/ViewPropertyAnimatorHC.java',
|
||||
'com/nineoldandroids/view/ViewPropertyAnimatorICS.java',
|
||||
'com/nineoldandroids/view/ViewPropertyAnimatorPreHC.java',
|
||||
'com/readystatesoftware/systembartint/SystemBarTintManager.java',
|
||||
'com/squareup/picasso/Action.java',
|
||||
'com/squareup/picasso/AssetBitmapHunter.java',
|
||||
'com/squareup/picasso/BitmapHunter.java',
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
xmlns:gecko="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/root_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ViewStub android:id="@+id/tabs_panel"
|
||||
android:layout="@layout/tabs_panel_view"
|
||||
|
|
|
@ -102,7 +102,8 @@ function testLTR_selectAll() {
|
|||
|
||||
]).then(function() {
|
||||
// Close selection and complete test.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
ok(!sh.isSelectionActive(),
|
||||
|
@ -171,7 +172,8 @@ function testRTL_selectAll() {
|
|||
|
||||
]).then(function() {
|
||||
// Close selection and complete test.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
ok(!sh.isSelectionActive(),
|
||||
|
@ -216,7 +218,8 @@ function testLTR_dragFocusHandleToSelf() {
|
|||
let focusDragSelectionText = sh._getSelectedText();
|
||||
|
||||
// Complete test, and report.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
ok(true, "testLTR_dragFocusHandleToSelf - Test Starts."),
|
||||
|
@ -275,7 +278,8 @@ function testLTR_dragAnchorHandleToSelf() {
|
|||
let anchorDragSelectionText = sh._getSelectedText();
|
||||
|
||||
// Complete test, and report.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
ok(true, "testLTR_dragAnchorHandleToSelf - Test Starts."),
|
||||
|
@ -333,7 +337,8 @@ function testRTL_dragFocusHandleToSelf() {
|
|||
let focusDragSelectionText = sh._getSelectedText();
|
||||
|
||||
// Complete test, and report.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
ok(true, "testRTL_dragFocusHandleToSelf - Test Starts."),
|
||||
|
@ -392,7 +397,8 @@ function testRTL_dragAnchorHandleToSelf() {
|
|||
let anchorDragSelectionText = sh._getSelectedText();
|
||||
|
||||
// Complete test, and report.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
ok(true, "testRTL_dragAnchorHandleToSelf - Test Starts."),
|
||||
|
|
|
@ -208,7 +208,12 @@ function testCloseSelection() {
|
|||
// Various ways to close an active selection.
|
||||
]).then(function() {
|
||||
sh.startSelection(inputNode);
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: -1}));
|
||||
return ok(sh.isSelectionActive(), "unrelated TextSelection:End should not close active selection");
|
||||
}).then(function() {
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
return ok(!sh.isSelectionActive(), "TextSelection:End should close active selection");
|
||||
|
||||
}).then(function() {
|
||||
|
@ -310,7 +315,8 @@ function testAttachCaret() {
|
|||
]);
|
||||
|
||||
}).then(function() {
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
ok(!sh.isSelectionActive(), "Selection should not be active at end of testAttachCaret"),
|
||||
|
|
|
@ -77,7 +77,8 @@ function testLTR_selectionPoints() {
|
|||
let midpointSelText = sh._getSelectedText();
|
||||
|
||||
// Close selection and complete test.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
selectionExists(selection, "LTR Selection existed at points"),
|
||||
|
@ -121,7 +122,8 @@ function testRTL_selectionPoints() {
|
|||
let midpointSelText = sh._getSelectedText();
|
||||
|
||||
// Close selection and complete test.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
selectionExists(selection, "RTL Selection existed at points"),
|
||||
|
@ -186,7 +188,8 @@ function test_selectionLineHeight() {
|
|||
|
||||
]).then(function() {
|
||||
// Complete test, and report.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
greaterThan(selectionLineHeight, 0, "Distance from one line to another " +
|
||||
|
@ -237,7 +240,8 @@ function testLTR_moveFocusHandleDown() {
|
|||
focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
|
||||
|
||||
// Complete test, and report.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
ok(true, "testLTR_moveFocusHandleDown - Test Starts."),
|
||||
|
@ -302,7 +306,8 @@ function testLTR_moveFocusHandleUp() {
|
|||
focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
|
||||
|
||||
// Complete test, and report.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
ok(true, "testLTR_moveFocusHandleUp - Test Starts."),
|
||||
|
@ -370,7 +375,8 @@ function testLTR_moveAnchorHandleUp() {
|
|||
focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
|
||||
|
||||
// Complete test, and report.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
ok(true, "testLTR_moveAnchorHandleUp - Test Starts."),
|
||||
|
@ -434,7 +440,8 @@ function testLTR_moveAnchorHandleDown() {
|
|||
focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
|
||||
|
||||
// Complete test, and report.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
ok(true, "testLTR_moveAnchorHandleDown - Test Starts."),
|
||||
|
@ -502,7 +509,8 @@ function testRTL_moveFocusHandleDown() {
|
|||
focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
|
||||
|
||||
// Complete test, and report.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
ok(true, "testRTL_moveFocusHandleDown - Test Starts."),
|
||||
|
@ -566,7 +574,8 @@ function testRTL_moveFocusHandleUp() {
|
|||
focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
|
||||
|
||||
// Complete test, and report.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
ok(true, "testRTL_moveFocusHandleUp - Test Starts."),
|
||||
|
@ -634,7 +643,8 @@ function testRTL_moveAnchorHandleUp() {
|
|||
focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
|
||||
|
||||
// Complete test, and report.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
ok(true, "testRTL_moveAnchorHandleUp - Test Starts."),
|
||||
|
@ -698,7 +708,8 @@ function testRTL_moveAnchorHandleDown() {
|
|||
focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
|
||||
|
||||
// Complete test, and report.
|
||||
Services.obs.notifyObservers(null, "TextSelection:End", {});
|
||||
Services.obs.notifyObservers(null, "TextSelection:End",
|
||||
JSON.stringify({selectionID: sh._selectionID}));
|
||||
|
||||
return Promise.all([
|
||||
ok(true, "testRTL_moveAnchorHandleDown - Test Starts."),
|
||||
|
|
|
@ -44,6 +44,7 @@ var SelectionHandler = {
|
|||
_focusIsRTL: false,
|
||||
|
||||
_activeType: 0, // TYPE_NONE
|
||||
_selectionID: 0, // Unique Selection ID
|
||||
|
||||
_draggingHandles: false, // True while user drags text selection handles
|
||||
_dragStartAnchorOffset: null, // Editables need initial pos during HandleMove events
|
||||
|
@ -134,10 +135,19 @@ var SelectionHandler = {
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "Tab:Selected":
|
||||
case "TextSelection:End":
|
||||
this._closeSelection();
|
||||
break;
|
||||
|
||||
case "TextSelection:End":
|
||||
let data = JSON.parse(aData);
|
||||
// End the requested selection only.
|
||||
if (this._selectionID === data.selectionID) {
|
||||
this._closeSelection();
|
||||
}
|
||||
break;
|
||||
|
||||
case "TextSelection:Action":
|
||||
for (let type in this.actions) {
|
||||
if (this.actions[type].id == aData) {
|
||||
|
@ -365,6 +375,7 @@ var SelectionHandler = {
|
|||
// Determine position and show handles, open actionbar
|
||||
this._positionHandles(positions);
|
||||
Messaging.sendRequest({
|
||||
selectionID: this._selectionID,
|
||||
type: "TextSelection:ShowHandles",
|
||||
handles: [this.HANDLE_TYPE_ANCHOR, this.HANDLE_TYPE_FOCUS]
|
||||
});
|
||||
|
@ -756,6 +767,7 @@ var SelectionHandler = {
|
|||
// Determine position and show caret, open actionbar
|
||||
this._positionHandles();
|
||||
Messaging.sendRequest({
|
||||
selectionID: this._selectionID,
|
||||
type: "TextSelection:ShowHandles",
|
||||
handles: [this.HANDLE_TYPE_CARET]
|
||||
});
|
||||
|
@ -777,6 +789,7 @@ var SelectionHandler = {
|
|||
aElement.focus();
|
||||
}
|
||||
|
||||
this._selectionID++;
|
||||
this._stopDraggingHandles();
|
||||
this._contentWindow = aElement.ownerDocument.defaultView;
|
||||
this._targetIsRTL = (this._contentWindow.getComputedStyle(aElement, "").direction == "rtl");
|
||||
|
|
|
@ -1,565 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013 readyState Software Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.readystatesoftware.systembartint;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout.LayoutParams;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Class to manage status and navigation bar tint effects when using KitKat
|
||||
* translucent system UI modes.
|
||||
*
|
||||
*/
|
||||
public class SystemBarTintManager {
|
||||
|
||||
static {
|
||||
// Android allows a system property to override the presence of the navigation bar.
|
||||
// Used by the emulator.
|
||||
// See https://github.com/android/platform_frameworks_base/blob/master/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java#L1076
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
try {
|
||||
Class c = Class.forName("android.os.SystemProperties");
|
||||
Method m = c.getDeclaredMethod("get", String.class);
|
||||
m.setAccessible(true);
|
||||
sNavBarOverride = (String) m.invoke(null, "qemu.hw.mainkeys");
|
||||
} catch (Throwable e) {
|
||||
sNavBarOverride = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The default system bar tint color value.
|
||||
*/
|
||||
public static final int DEFAULT_TINT_COLOR = 0x99000000;
|
||||
|
||||
private static String sNavBarOverride;
|
||||
|
||||
private final SystemBarConfig mConfig;
|
||||
private boolean mStatusBarAvailable;
|
||||
private boolean mNavBarAvailable;
|
||||
private boolean mStatusBarTintEnabled;
|
||||
private boolean mNavBarTintEnabled;
|
||||
private View mStatusBarTintView;
|
||||
private View mNavBarTintView;
|
||||
|
||||
/**
|
||||
* Constructor. Call this in the host activity onCreate method after its
|
||||
* content view has been set. You should always create new instances when
|
||||
* the host activity is recreated.
|
||||
*
|
||||
* @param activity The host activity.
|
||||
*/
|
||||
@TargetApi(19)
|
||||
public SystemBarTintManager(Activity activity) {
|
||||
|
||||
Window win = activity.getWindow();
|
||||
ViewGroup decorViewGroup = (ViewGroup) win.getDecorView();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
// check theme attrs
|
||||
int[] attrs = {android.R.attr.windowTranslucentStatus,
|
||||
android.R.attr.windowTranslucentNavigation};
|
||||
TypedArray a = activity.obtainStyledAttributes(attrs);
|
||||
try {
|
||||
mStatusBarAvailable = a.getBoolean(0, false);
|
||||
mNavBarAvailable = a.getBoolean(1, false);
|
||||
} finally {
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
// check window flags
|
||||
WindowManager.LayoutParams winParams = win.getAttributes();
|
||||
int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
|
||||
if ((winParams.flags & bits) != 0) {
|
||||
mStatusBarAvailable = true;
|
||||
}
|
||||
bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
|
||||
if ((winParams.flags & bits) != 0) {
|
||||
mNavBarAvailable = true;
|
||||
}
|
||||
}
|
||||
|
||||
mConfig = new SystemBarConfig(activity, mStatusBarAvailable, mNavBarAvailable);
|
||||
// device might not have virtual navigation keys
|
||||
if (!mConfig.hasNavigtionBar()) {
|
||||
mNavBarAvailable = false;
|
||||
}
|
||||
|
||||
if (mStatusBarAvailable) {
|
||||
setupStatusBarView(activity, decorViewGroup);
|
||||
}
|
||||
if (mNavBarAvailable) {
|
||||
setupNavBarView(activity, decorViewGroup);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable tinting of the system status bar.
|
||||
*
|
||||
* If the platform is running Jelly Bean or earlier, or translucent system
|
||||
* UI modes have not been enabled in either the theme or via window flags,
|
||||
* then this method does nothing.
|
||||
*
|
||||
* @param enabled True to enable tinting, false to disable it (default).
|
||||
*/
|
||||
public void setStatusBarTintEnabled(boolean enabled) {
|
||||
mStatusBarTintEnabled = enabled;
|
||||
if (mStatusBarAvailable) {
|
||||
mStatusBarTintView.setVisibility(enabled ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable tinting of the system navigation bar.
|
||||
*
|
||||
* If the platform does not have soft navigation keys, is running Jelly Bean
|
||||
* or earlier, or translucent system UI modes have not been enabled in either
|
||||
* the theme or via window flags, then this method does nothing.
|
||||
*
|
||||
* @param enabled True to enable tinting, false to disable it (default).
|
||||
*/
|
||||
public void setNavigationBarTintEnabled(boolean enabled) {
|
||||
mNavBarTintEnabled = enabled;
|
||||
if (mNavBarAvailable) {
|
||||
mNavBarTintView.setVisibility(enabled ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified color tint to all system UI bars.
|
||||
*
|
||||
* @param color The color of the background tint.
|
||||
*/
|
||||
public void setTintColor(int color) {
|
||||
setStatusBarTintColor(color);
|
||||
setNavigationBarTintColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified drawable or color resource to all system UI bars.
|
||||
*
|
||||
* @param res The identifier of the resource.
|
||||
*/
|
||||
public void setTintResource(int res) {
|
||||
setStatusBarTintResource(res);
|
||||
setNavigationBarTintResource(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified drawable to all system UI bars.
|
||||
*
|
||||
* @param drawable The drawable to use as the background, or null to remove it.
|
||||
*/
|
||||
public void setTintDrawable(Drawable drawable) {
|
||||
setStatusBarTintDrawable(drawable);
|
||||
setNavigationBarTintDrawable(drawable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified alpha to all system UI bars.
|
||||
*
|
||||
* @param alpha The alpha to use
|
||||
*/
|
||||
public void setTintAlpha(float alpha) {
|
||||
setStatusBarAlpha(alpha);
|
||||
setNavigationBarAlpha(alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified color tint to the system status bar.
|
||||
*
|
||||
* @param color The color of the background tint.
|
||||
*/
|
||||
public void setStatusBarTintColor(int color) {
|
||||
if (mStatusBarAvailable) {
|
||||
mStatusBarTintView.setBackgroundColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified drawable or color resource to the system status bar.
|
||||
*
|
||||
* @param res The identifier of the resource.
|
||||
*/
|
||||
public void setStatusBarTintResource(int res) {
|
||||
if (mStatusBarAvailable) {
|
||||
mStatusBarTintView.setBackgroundResource(res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified drawable to the system status bar.
|
||||
*
|
||||
* @param drawable The drawable to use as the background, or null to remove it.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setStatusBarTintDrawable(Drawable drawable) {
|
||||
if (mStatusBarAvailable) {
|
||||
mStatusBarTintView.setBackgroundDrawable(drawable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified alpha to the system status bar.
|
||||
*
|
||||
* @param alpha The alpha to use
|
||||
*/
|
||||
@TargetApi(11)
|
||||
public void setStatusBarAlpha(float alpha) {
|
||||
if (mStatusBarAvailable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
mStatusBarTintView.setAlpha(alpha);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified color tint to the system navigation bar.
|
||||
*
|
||||
* @param color The color of the background tint.
|
||||
*/
|
||||
public void setNavigationBarTintColor(int color) {
|
||||
if (mNavBarAvailable) {
|
||||
mNavBarTintView.setBackgroundColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified drawable or color resource to the system navigation bar.
|
||||
*
|
||||
* @param res The identifier of the resource.
|
||||
*/
|
||||
public void setNavigationBarTintResource(int res) {
|
||||
if (mNavBarAvailable) {
|
||||
mNavBarTintView.setBackgroundResource(res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified drawable to the system navigation bar.
|
||||
*
|
||||
* @param drawable The drawable to use as the background, or null to remove it.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setNavigationBarTintDrawable(Drawable drawable) {
|
||||
if (mNavBarAvailable) {
|
||||
mNavBarTintView.setBackgroundDrawable(drawable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified alpha to the system navigation bar.
|
||||
*
|
||||
* @param alpha The alpha to use
|
||||
*/
|
||||
@TargetApi(11)
|
||||
public void setNavigationBarAlpha(float alpha) {
|
||||
if (mNavBarAvailable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
mNavBarTintView.setAlpha(alpha);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the system bar configuration.
|
||||
*
|
||||
* @return The system bar configuration for the current device configuration.
|
||||
*/
|
||||
public SystemBarConfig getConfig() {
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is tinting enabled for the system status bar?
|
||||
*
|
||||
* @return True if enabled, False otherwise.
|
||||
*/
|
||||
public boolean isStatusBarTintEnabled() {
|
||||
return mStatusBarTintEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is tinting enabled for the system navigation bar?
|
||||
*
|
||||
* @return True if enabled, False otherwise.
|
||||
*/
|
||||
public boolean isNavBarTintEnabled() {
|
||||
return mNavBarTintEnabled;
|
||||
}
|
||||
|
||||
private void setupStatusBarView(Context context, ViewGroup decorViewGroup) {
|
||||
mStatusBarTintView = new View(context);
|
||||
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mConfig.getStatusBarHeight());
|
||||
params.gravity = Gravity.TOP;
|
||||
if (mNavBarAvailable && !mConfig.isNavigationAtBottom()) {
|
||||
params.rightMargin = mConfig.getNavigationBarWidth();
|
||||
}
|
||||
mStatusBarTintView.setLayoutParams(params);
|
||||
mStatusBarTintView.setBackgroundColor(DEFAULT_TINT_COLOR);
|
||||
mStatusBarTintView.setVisibility(View.GONE);
|
||||
decorViewGroup.addView(mStatusBarTintView);
|
||||
}
|
||||
|
||||
private void setupNavBarView(Context context, ViewGroup decorViewGroup) {
|
||||
mNavBarTintView = new View(context);
|
||||
LayoutParams params;
|
||||
if (mConfig.isNavigationAtBottom()) {
|
||||
params = new LayoutParams(LayoutParams.MATCH_PARENT, mConfig.getNavigationBarHeight());
|
||||
params.gravity = Gravity.BOTTOM;
|
||||
} else {
|
||||
params = new LayoutParams(mConfig.getNavigationBarWidth(), LayoutParams.MATCH_PARENT);
|
||||
params.gravity = Gravity.RIGHT;
|
||||
}
|
||||
mNavBarTintView.setLayoutParams(params);
|
||||
mNavBarTintView.setBackgroundColor(DEFAULT_TINT_COLOR);
|
||||
mNavBarTintView.setVisibility(View.GONE);
|
||||
decorViewGroup.addView(mNavBarTintView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class which describes system bar sizing and other characteristics for the current
|
||||
* device configuration.
|
||||
*
|
||||
*/
|
||||
public static class SystemBarConfig {
|
||||
|
||||
private static final String STATUS_BAR_HEIGHT_RES_NAME = "status_bar_height";
|
||||
private static final String NAV_BAR_HEIGHT_RES_NAME = "navigation_bar_height";
|
||||
private static final String NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME = "navigation_bar_height_landscape";
|
||||
private static final String NAV_BAR_WIDTH_RES_NAME = "navigation_bar_width";
|
||||
private static final String SHOW_NAV_BAR_RES_NAME = "config_showNavigationBar";
|
||||
|
||||
private final boolean mTranslucentStatusBar;
|
||||
private final boolean mTranslucentNavBar;
|
||||
private final int mStatusBarHeight;
|
||||
private final int mActionBarHeight;
|
||||
private final boolean mHasNavigationBar;
|
||||
private final int mNavigationBarHeight;
|
||||
private final int mNavigationBarWidth;
|
||||
private final boolean mInPortrait;
|
||||
private final float mSmallestWidthDp;
|
||||
|
||||
private SystemBarConfig(Activity activity, boolean translucentStatusBar, boolean traslucentNavBar) {
|
||||
Resources res = activity.getResources();
|
||||
mInPortrait = (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
|
||||
mSmallestWidthDp = getSmallestWidthDp(activity);
|
||||
mStatusBarHeight = getInternalDimensionSize(res, STATUS_BAR_HEIGHT_RES_NAME);
|
||||
mActionBarHeight = getActionBarHeight(activity);
|
||||
mNavigationBarHeight = getNavigationBarHeight(activity);
|
||||
mNavigationBarWidth = getNavigationBarWidth(activity);
|
||||
mHasNavigationBar = (mNavigationBarHeight > 0);
|
||||
mTranslucentStatusBar = translucentStatusBar;
|
||||
mTranslucentNavBar = traslucentNavBar;
|
||||
}
|
||||
|
||||
@TargetApi(14)
|
||||
private int getActionBarHeight(Context context) {
|
||||
int result = 0;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
TypedValue tv = new TypedValue();
|
||||
context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true);
|
||||
result = TypedValue.complexToDimensionPixelSize(tv.data, context.getResources().getDisplayMetrics());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@TargetApi(14)
|
||||
private int getNavigationBarHeight(Context context) {
|
||||
Resources res = context.getResources();
|
||||
int result = 0;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
if (hasNavBar(context)) {
|
||||
String key;
|
||||
if (mInPortrait) {
|
||||
key = NAV_BAR_HEIGHT_RES_NAME;
|
||||
} else {
|
||||
key = NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME;
|
||||
}
|
||||
return getInternalDimensionSize(res, key);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@TargetApi(14)
|
||||
private int getNavigationBarWidth(Context context) {
|
||||
Resources res = context.getResources();
|
||||
int result = 0;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
if (hasNavBar(context)) {
|
||||
return getInternalDimensionSize(res, NAV_BAR_WIDTH_RES_NAME);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@TargetApi(14)
|
||||
private boolean hasNavBar(Context context) {
|
||||
Resources res = context.getResources();
|
||||
int resourceId = res.getIdentifier(SHOW_NAV_BAR_RES_NAME, "bool", "android");
|
||||
if (resourceId != 0) {
|
||||
boolean hasNav = res.getBoolean(resourceId);
|
||||
// check override flag (see static block)
|
||||
if ("1".equals(sNavBarOverride)) {
|
||||
hasNav = false;
|
||||
} else if ("0".equals(sNavBarOverride)) {
|
||||
hasNav = true;
|
||||
}
|
||||
return hasNav;
|
||||
} else { // fallback
|
||||
return !ViewConfiguration.get(context).hasPermanentMenuKey();
|
||||
}
|
||||
}
|
||||
|
||||
private int getInternalDimensionSize(Resources res, String key) {
|
||||
int result = 0;
|
||||
int resourceId = res.getIdentifier(key, "dimen", "android");
|
||||
if (resourceId > 0) {
|
||||
result = res.getDimensionPixelSize(resourceId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private float getSmallestWidthDp(Activity activity) {
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
activity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
|
||||
} else {
|
||||
// TODO this is not correct, but we don't really care pre-kitkat
|
||||
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
||||
}
|
||||
float widthDp = metrics.widthPixels / metrics.density;
|
||||
float heightDp = metrics.heightPixels / metrics.density;
|
||||
return Math.min(widthDp, heightDp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should a navigation bar appear at the bottom of the screen in the current
|
||||
* device configuration? A navigation bar may appear on the right side of
|
||||
* the screen in certain configurations.
|
||||
*
|
||||
* @return True if navigation should appear at the bottom of the screen, False otherwise.
|
||||
*/
|
||||
public boolean isNavigationAtBottom() {
|
||||
return (mSmallestWidthDp >= 600 || mInPortrait);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the system status bar.
|
||||
*
|
||||
* @return The height of the status bar (in pixels).
|
||||
*/
|
||||
public int getStatusBarHeight() {
|
||||
return mStatusBarHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the action bar.
|
||||
*
|
||||
* @return The height of the action bar (in pixels).
|
||||
*/
|
||||
public int getActionBarHeight() {
|
||||
return mActionBarHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this device have a system navigation bar?
|
||||
*
|
||||
* @return True if this device uses soft key navigation, False otherwise.
|
||||
*/
|
||||
public boolean hasNavigtionBar() {
|
||||
return mHasNavigationBar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the system navigation bar.
|
||||
*
|
||||
* @return The height of the navigation bar (in pixels). If the device does not have
|
||||
* soft navigation keys, this will always return 0.
|
||||
*/
|
||||
public int getNavigationBarHeight() {
|
||||
return mNavigationBarHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of the system navigation bar when it is placed vertically on the screen.
|
||||
*
|
||||
* @return The width of the navigation bar (in pixels). If the device does not have
|
||||
* soft navigation keys, this will always return 0.
|
||||
*/
|
||||
public int getNavigationBarWidth() {
|
||||
return mNavigationBarWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the layout inset for any system UI that appears at the top of the screen.
|
||||
*
|
||||
* @param withActionBar True to include the height of the action bar, False otherwise.
|
||||
* @return The layout inset (in pixels).
|
||||
*/
|
||||
public int getPixelInsetTop(boolean withActionBar) {
|
||||
return (mTranslucentStatusBar ? mStatusBarHeight : 0) + (withActionBar ? mActionBarHeight : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the layout inset for any system UI that appears at the bottom of the screen.
|
||||
*
|
||||
* @return The layout inset (in pixels).
|
||||
*/
|
||||
public int getPixelInsetBottom() {
|
||||
if (mTranslucentNavBar && isNavigationAtBottom()) {
|
||||
return mNavigationBarHeight;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the layout inset for any system UI that appears at the right of the screen.
|
||||
*
|
||||
* @return The layout inset (in pixels).
|
||||
*/
|
||||
public int getPixelInsetRight() {
|
||||
if (mTranslucentNavBar && !isNavigationAtBottom()) {
|
||||
return mNavigationBarWidth;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
/* 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/. */
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'Services',
|
||||
'resource://gre/modules/Services.jsm');
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'Preferences',
|
||||
'resource://gre/modules/Preferences.jsm');
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'FileUtils',
|
||||
'resource://gre/modules/FileUtils.jsm');
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'Log',
|
||||
'resource://gre/modules/Log.jsm');
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'Task',
|
||||
'resource://gre/modules/Task.jsm');
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'OS',
|
||||
'resource://gre/modules/osfile.jsm');
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'CommonUtils',
|
||||
'resource://services-common/utils.js');
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"LogManager",
|
||||
];
|
||||
|
||||
const DEFAULT_MAX_ERROR_AGE = 20 * 24 * 60 * 60; // 20 days
|
||||
|
||||
// "shared" logs (ie, where the same log name is used by multiple LogManager
|
||||
// instances) are a fact of life here - eg, FirefoxAccounts logs are used by
|
||||
// both Sync and Reading-list.
|
||||
// However, different instances have different pref branches, so we need to
|
||||
// handle when one pref branch says "Debug" and the other says "Error"
|
||||
// So we (a) keep singleton console and dump appenders and (b) keep track
|
||||
// of the minimum (ie, most verbose) level and use that.
|
||||
// This avoids (a) the most recent setter winning (as that is indeterminate)
|
||||
// and (b) multiple dump/console appenders being added to the same log multiple
|
||||
// times, which would cause messages to appear twice.
|
||||
|
||||
// Singletons used by each instance.
|
||||
let formatter;
|
||||
let dumpAppender;
|
||||
let consoleAppender;
|
||||
|
||||
// A set of all preference roots used by all instances.
|
||||
let allBranches = new Set();
|
||||
|
||||
// The public LogManager object.
|
||||
function LogManager(prefRoot, logNames, logFilePrefix) {
|
||||
this.init(prefRoot, logNames, logFilePrefix);
|
||||
}
|
||||
|
||||
LogManager.prototype = {
|
||||
REASON_SUCCESS: "success",
|
||||
REASON_ERROR: "error",
|
||||
|
||||
_cleaningUpFileLogs: false,
|
||||
|
||||
_prefObservers: [],
|
||||
|
||||
init(prefRoot, logNames, logFilePrefix) {
|
||||
if (prefRoot instanceof Preferences) {
|
||||
this._prefs = prefRoot;
|
||||
} else {
|
||||
this._prefs = new Preferences(prefRoot);
|
||||
}
|
||||
|
||||
this.logFilePrefix = logFilePrefix;
|
||||
if (!formatter) {
|
||||
// Create a formatter and various appenders to attach to the logs.
|
||||
formatter = new Log.BasicFormatter();
|
||||
consoleAppender = new Log.ConsoleAppender(formatter);
|
||||
dumpAppender = new Log.DumpAppender(formatter);
|
||||
}
|
||||
|
||||
allBranches.add(this._prefs._branchStr);
|
||||
// We create a preference observer for all our prefs so they are magically
|
||||
// reflected if the pref changes after creation.
|
||||
let setupAppender = (appender, prefName, defaultLevel, findSmallest = false) => {
|
||||
let observer = newVal => {
|
||||
let level = Log.Level[newVal] || defaultLevel;
|
||||
if (findSmallest) {
|
||||
// We need to find the smallest value from all prefs controlling this appender.
|
||||
for (let branch of allBranches) {
|
||||
let lookPrefBranch = new Preferences(branch);
|
||||
let lookVal = Log.Level[lookPrefBranch.get(prefName)];
|
||||
if (lookVal && lookVal < level) {
|
||||
level = lookVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
appender.level = level;
|
||||
}
|
||||
this._prefs.observe(prefName, observer, this);
|
||||
this._prefObservers.push([prefName, observer]);
|
||||
// and call the observer now with the current pref value.
|
||||
observer(this._prefs.get(prefName));
|
||||
return observer;
|
||||
}
|
||||
|
||||
this._observeConsolePref = setupAppender(consoleAppender, "log.appender.console", Log.Level.Error, true);
|
||||
this._observeDumpPref = setupAppender(dumpAppender, "log.appender.dump", Log.Level.Error, true);
|
||||
|
||||
// The file appender doesn't get the special singleton behaviour.
|
||||
let fapp = this._fileAppender = new Log.StorageStreamAppender(formatter);
|
||||
// the stream gets a default of Debug as the user must go out of there way
|
||||
// to see the stuff spewed to it.
|
||||
this._observeStreamPref = setupAppender(fapp, "log.appender.file.level", Log.Level.Debug);
|
||||
|
||||
// now attach the appenders to all our logs.
|
||||
for (let logName of logNames) {
|
||||
let log = Log.repository.getLogger(logName);
|
||||
for (let appender of [fapp, dumpAppender, consoleAppender]) {
|
||||
log.addAppender(appender);
|
||||
}
|
||||
}
|
||||
// and use the first specified log as a "root" for our log.
|
||||
this._log = Log.repository.getLogger(logNames[0] + ".LogManager");
|
||||
},
|
||||
|
||||
/**
|
||||
* Cleanup this instance
|
||||
*/
|
||||
finalize() {
|
||||
for (let [name, pref] of this._prefObservers) {
|
||||
this._prefs.ignore(name, pref, this);
|
||||
}
|
||||
this._prefObservers = [];
|
||||
try {
|
||||
allBranches.delete(this._prefs._branchStr);
|
||||
} catch (e) {}
|
||||
this._prefs = null;
|
||||
},
|
||||
|
||||
get _logFileDirectory() {
|
||||
// At this point we don't allow a custom directory for the logs so
|
||||
// about:sync-log can be used. We could fix this later if necessary.
|
||||
return FileUtils.getDir("ProfD", ["weave", "logs"]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy an input stream to the named file, doing everything off the main
|
||||
* thread.
|
||||
* outputFile is an nsIFile, but is used only for the name.
|
||||
* Returns a promise that is resolved with the file modification date on
|
||||
* completion or rejected if there is an error.
|
||||
*/
|
||||
_copyStreamToFile: Task.async(function* (inputStream, outputFile) {
|
||||
// The log data could be large, so we don't want to pass it all in a single
|
||||
// message, so use BUFFER_SIZE chunks.
|
||||
const BUFFER_SIZE = 8192;
|
||||
|
||||
// get a binary stream
|
||||
let binaryStream = Cc['@mozilla.org/binaryinputstream;1'].createInstance(Ci.nsIBinaryInputStream);
|
||||
binaryStream.setInputStream(inputStream);
|
||||
yield OS.File.makeDir(outputFile.parent.path, { ignoreExisting: true });
|
||||
let output = yield OS.File.open(outputFile.path, { write: true} );
|
||||
try {
|
||||
while (true) {
|
||||
let available = binaryStream.available();
|
||||
if (!available) {
|
||||
break;
|
||||
}
|
||||
let chunk = binaryStream.readByteArray(Math.min(available, BUFFER_SIZE));
|
||||
yield output.write(new Uint8Array(chunk));
|
||||
}
|
||||
} finally {
|
||||
inputStream.close();
|
||||
binaryStream.close();
|
||||
yield output.close();
|
||||
}
|
||||
this._log.trace("finished copy to", outputFile.path);
|
||||
return (yield OS.File.stat(outputFile.path)).lastModificationDate;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Possibly generate a log file for all accumulated log messages and refresh
|
||||
* the input & output streams.
|
||||
* Returns a promise that resolves on completion or rejects if the file could
|
||||
* not be written.
|
||||
*/
|
||||
resetFileLog(reason) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
let flushToFile;
|
||||
let reasonPrefix;
|
||||
switch (reason) {
|
||||
case this.REASON_SUCCESS:
|
||||
flushToFile = this._prefs.get("log.appender.file.logOnSuccess");
|
||||
reasonPrefix = "success";
|
||||
break;
|
||||
case this.REASON_ERROR:
|
||||
flushToFile = this._prefs.get("log.appender.file.logOnError");
|
||||
reasonPrefix = "error";
|
||||
break;
|
||||
default:
|
||||
return reject(new Error("Invalid reason"));
|
||||
}
|
||||
|
||||
let inStream = this._fileAppender.getInputStream();
|
||||
this._fileAppender.reset();
|
||||
if (flushToFile && inStream) {
|
||||
this._log.debug("Flushing file log");
|
||||
let filename = this.logFilePrefix + "-" + reasonPrefix + "-" + Date.now() + ".txt";
|
||||
let file = this._logFileDirectory;
|
||||
file.append(filename);
|
||||
this._log.trace("Beginning stream copy to " + file.leafName + ": " +
|
||||
Date.now());
|
||||
this._copyStreamToFile(inStream, file).then(
|
||||
modDate => {
|
||||
this._log.trace("onCopyComplete: " + Date.now());
|
||||
this._log.trace("Output file timestamp: " + modDate + " (" + modDate.getTime() + ")");
|
||||
},
|
||||
err => {
|
||||
this._log.error("Failed to copy log stream to file", err)
|
||||
reject(err)
|
||||
}
|
||||
).then(
|
||||
() => {
|
||||
// It's not completely clear to markh why we only do log cleanups
|
||||
// for errors, but for now the Sync semantics have been copied...
|
||||
// (one theory is that only cleaning up on error makes it less
|
||||
// likely old error logs would be removed, but that's not true if
|
||||
// there are occasional errors - let's address this later!)
|
||||
if (reason == this.REASON_ERROR &&
|
||||
!this._cleaningUpFileLogs) {
|
||||
this._log.trace("Scheduling cleanup.");
|
||||
// Note we don't return or wait on this promise - it continues
|
||||
// in the background
|
||||
this.cleanupLogs().then(null, err => {
|
||||
this._log.error("Failed to cleanup logs", err);
|
||||
});
|
||||
}
|
||||
resolve();
|
||||
}
|
||||
);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
} catch (ex) {
|
||||
this._log.error("Failed to resetFileLog", ex)
|
||||
reject(ex);
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds all logs older than maxErrorAge and deletes them using async I/O.
|
||||
*/
|
||||
cleanupLogs: Task.async(function* () {
|
||||
this._cleaningUpFileLogs = true;
|
||||
let iterator = new OS.File.DirectoryIterator(this._logFileDirectory.path);
|
||||
let maxAge = this._prefs.get("log.appender.file.maxErrorAge", DEFAULT_MAX_ERROR_AGE);
|
||||
let threshold = Date.now() - 1000 * maxAge;
|
||||
|
||||
this._log.debug("Log cleanup threshold time: " + threshold);
|
||||
yield iterator.forEach(Task.async(function* (entry) {
|
||||
if (!entry.name.startsWith(this.logFilePrefix + "-")) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// need to call .stat() as the enumerator doesn't give that to us on *nix.
|
||||
let info = yield OS.File.stat(entry.path);
|
||||
if (info.lastModificationDate.getTime() >= threshold) {
|
||||
return;
|
||||
}
|
||||
this._log.trace(" > Cleanup removing " + entry.name +
|
||||
" (" + info.lastModificationDate.getTime() + ")");
|
||||
yield OS.File.remove(entry.path);
|
||||
this._log.trace("Deleted " + entry.name);
|
||||
} catch (ex) {
|
||||
this._log.debug("Encountered error trying to clean up old log file "
|
||||
+ entry.name, ex);
|
||||
}
|
||||
}.bind(this)));
|
||||
this._cleaningUpFileLogs = false;
|
||||
this._log.debug("Done deleting files.");
|
||||
// This notification is used only for tests.
|
||||
Services.obs.notifyObservers(null, "services-tests:common:log-manager:cleanup-logs", null);
|
||||
}),
|
||||
}
|
|
@ -13,6 +13,7 @@ EXTRA_COMPONENTS += [
|
|||
EXTRA_JS_MODULES['services-common'] += [
|
||||
'hawkclient.js',
|
||||
'hawkrequest.js',
|
||||
'logmanager.js',
|
||||
'storageservice.js',
|
||||
'stringbundle.js',
|
||||
'tokenserverclient.js',
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
const modules = [
|
||||
"async.js",
|
||||
"bagheeraclient.js",
|
||||
"logmanager.js",
|
||||
"rest.js",
|
||||
"storageservice.js",
|
||||
"stringbundle.js",
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// NOTE: The sync test_errorhandler_* tests have quite good coverage for
|
||||
// other aspects of this.
|
||||
|
||||
Cu.import("resource://services-common/logmanager.js");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// Returns an array of [consoleAppender, dumpAppender, [fileAppenders]] for
|
||||
// the specified log. Note that fileAppenders will usually have length=1
|
||||
function getAppenders(log) {
|
||||
let capps = log.appenders.filter(app => app instanceof Log.ConsoleAppender);
|
||||
equal(capps.length, 1, "should only have one console appender");
|
||||
let dapps = log.appenders.filter(app => app instanceof Log.DumpAppender);
|
||||
equal(dapps.length, 1, "should only have one dump appender");
|
||||
let fapps = log.appenders.filter(app => app instanceof Log.StorageStreamAppender);
|
||||
return [capps[0], dapps[0], fapps];
|
||||
}
|
||||
|
||||
// Test that the correct thing happens when no prefs exist for the log manager.
|
||||
add_test(function test_noPrefs() {
|
||||
// tell the log manager to init with a pref branch that doesn't exist.
|
||||
let lm = new LogManager("no-such-branch.", ["TestLog"], "test");
|
||||
|
||||
let log = Log.repository.getLogger("TestLog");
|
||||
let [capp, dapp, fapps] = getAppenders(log);
|
||||
// the "dump" and "console" appenders should get Error level
|
||||
equal(capp.level, Log.Level.Error);
|
||||
equal(dapp.level, Log.Level.Error);
|
||||
// and the file (stream) appender gets Dump by default
|
||||
equal(fapps.length, 1, "only 1 file appender");
|
||||
equal(fapps[0].level, Log.Level.Debug);
|
||||
lm.finalize();
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Test that changes to the prefs used by the log manager are updated dynamically.
|
||||
add_test(function test_PrefChanges() {
|
||||
Services.prefs.setCharPref("log-manager.test.log.appender.console", "Trace");
|
||||
Services.prefs.setCharPref("log-manager.test.log.appender.dump", "Trace");
|
||||
Services.prefs.setCharPref("log-manager.test.log.appender.file.level", "Trace");
|
||||
let lm = new LogManager("log-manager.test.", ["TestLog2"], "test");
|
||||
|
||||
let log = Log.repository.getLogger("TestLog2");
|
||||
let [capp, dapp, [fapp]] = getAppenders(log);
|
||||
equal(capp.level, Log.Level.Trace);
|
||||
equal(dapp.level, Log.Level.Trace);
|
||||
equal(fapp.level, Log.Level.Trace);
|
||||
// adjust the prefs and they should magically be reflected in the appenders.
|
||||
Services.prefs.setCharPref("log-manager.test.log.appender.console", "Debug");
|
||||
Services.prefs.setCharPref("log-manager.test.log.appender.dump", "Debug");
|
||||
Services.prefs.setCharPref("log-manager.test.log.appender.file.level", "Debug");
|
||||
equal(capp.level, Log.Level.Debug);
|
||||
equal(dapp.level, Log.Level.Debug);
|
||||
equal(fapp.level, Log.Level.Debug);
|
||||
// and invalid values should cause them to fallback to their defaults.
|
||||
Services.prefs.setCharPref("log-manager.test.log.appender.console", "xxx");
|
||||
Services.prefs.setCharPref("log-manager.test.log.appender.dump", "xxx");
|
||||
Services.prefs.setCharPref("log-manager.test.log.appender.file.level", "xxx");
|
||||
equal(capp.level, Log.Level.Error);
|
||||
equal(dapp.level, Log.Level.Error);
|
||||
equal(fapp.level, Log.Level.Debug);
|
||||
lm.finalize();
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
// Test that the same log used by multiple log managers does the right thing.
|
||||
add_test(function test_SharedLogs() {
|
||||
// create the prefs for the first instance.
|
||||
Services.prefs.setCharPref("log-manager-1.test.log.appender.console", "Trace");
|
||||
Services.prefs.setCharPref("log-manager-1.test.log.appender.dump", "Trace");
|
||||
Services.prefs.setCharPref("log-manager-1.test.log.appender.file.level", "Trace");
|
||||
let lm1 = new LogManager("log-manager-1.test.", ["TestLog3"], "test");
|
||||
|
||||
// and the second.
|
||||
Services.prefs.setCharPref("log-manager-2.test.log.appender.console", "Debug");
|
||||
Services.prefs.setCharPref("log-manager-2.test.log.appender.dump", "Debug");
|
||||
Services.prefs.setCharPref("log-manager-2.test.log.appender.file.level", "Debug");
|
||||
let lm2 = new LogManager("log-manager-2.test.", ["TestLog3"], "test");
|
||||
|
||||
let log = Log.repository.getLogger("TestLog3");
|
||||
let [capp, dapp, fapps] = getAppenders(log);
|
||||
|
||||
// console and dump appenders should be "trace" as it is more verbose than
|
||||
// "debug"
|
||||
equal(capp.level, Log.Level.Trace);
|
||||
equal(dapp.level, Log.Level.Trace);
|
||||
|
||||
// Set the prefs on the -1 branch to "Error" - it should then end up with
|
||||
// "Debug" from the -2 branch.
|
||||
Services.prefs.setCharPref("log-manager-1.test.log.appender.console", "Error");
|
||||
Services.prefs.setCharPref("log-manager-1.test.log.appender.dump", "Error");
|
||||
Services.prefs.setCharPref("log-manager-1.test.log.appender.file.level", "Error");
|
||||
|
||||
equal(capp.level, Log.Level.Debug);
|
||||
equal(dapp.level, Log.Level.Debug);
|
||||
|
||||
lm1.finalize();
|
||||
lm2.finalize();
|
||||
|
||||
run_next_test();
|
||||
});
|
|
@ -29,6 +29,7 @@ skip-if = toolkit == 'gonk'
|
|||
[test_bagheera_client.js]
|
||||
[test_hawkclient.js]
|
||||
[test_hawkrequest.js]
|
||||
[test_logmanager.js]
|
||||
[test_observers.js]
|
||||
[test_restrequest.js]
|
||||
[test_tokenauthenticatedrequest.js]
|
||||
|
|
|
@ -13,8 +13,7 @@ Cu.import("resource://gre/modules/Log.jsm");
|
|||
Cu.import("resource://services-sync/constants.js");
|
||||
Cu.import("resource://services-sync/engines.js");
|
||||
Cu.import("resource://services-sync/util.js");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://services-common/logmanager.js");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Status",
|
||||
"resource://services-sync/status.js");
|
||||
|
@ -539,9 +538,6 @@ SyncScheduler.prototype = {
|
|||
},
|
||||
};
|
||||
|
||||
const LOG_PREFIX_SUCCESS = "success-";
|
||||
const LOG_PREFIX_ERROR = "error-";
|
||||
|
||||
this.ErrorHandler = function ErrorHandler(service) {
|
||||
this.service = service;
|
||||
this.init();
|
||||
|
@ -574,33 +570,14 @@ ErrorHandler.prototype = {
|
|||
initLogs: function initLogs() {
|
||||
this._log = Log.repository.getLogger("Sync.ErrorHandler");
|
||||
this._log.level = Log.Level[Svc.Prefs.get("log.logger.service.main")];
|
||||
this._cleaningUpFileLogs = false;
|
||||
|
||||
let root = Log.repository.getLogger("Sync");
|
||||
root.level = Log.Level[Svc.Prefs.get("log.rootLogger")];
|
||||
|
||||
let formatter = new Log.BasicFormatter();
|
||||
let capp = new Log.ConsoleAppender(formatter);
|
||||
capp.level = Log.Level[Svc.Prefs.get("log.appender.console")];
|
||||
root.addAppender(capp);
|
||||
let logs = ["Sync", "FirefoxAccounts", "Hawk", "Common.TokenServerClient",
|
||||
"Sync.SyncMigration"];
|
||||
|
||||
let dapp = new Log.DumpAppender(formatter);
|
||||
dapp.level = Log.Level[Svc.Prefs.get("log.appender.dump")];
|
||||
root.addAppender(dapp);
|
||||
|
||||
let fapp = this._logAppender = new Log.StorageStreamAppender(formatter);
|
||||
fapp.level = Log.Level[Svc.Prefs.get("log.appender.file.level")];
|
||||
root.addAppender(fapp);
|
||||
|
||||
// Arrange for a number of other sync-related logs to also go to our
|
||||
// appenders.
|
||||
for (let extra of ["FirefoxAccounts", "Hawk", "Common.TokenServerClient",
|
||||
"Sync.SyncMigration"]) {
|
||||
let log = Log.repository.getLogger(extra);
|
||||
for (let appender of [fapp, dapp, capp]) {
|
||||
log.addAppender(appender);
|
||||
}
|
||||
}
|
||||
this._logManager = new LogManager(Svc.Prefs, logs, "sync");
|
||||
},
|
||||
|
||||
observe: function observe(subject, topic, data) {
|
||||
|
@ -625,8 +602,7 @@ ErrorHandler.prototype = {
|
|||
this._log.debug(engine_name + " failed: " + Utils.exceptionStr(exception));
|
||||
break;
|
||||
case "weave:service:login:error":
|
||||
this.resetFileLog(Svc.Prefs.get("log.appender.file.logOnError"),
|
||||
LOG_PREFIX_ERROR);
|
||||
this.resetFileLog(this._logManager.REASON_ERROR);
|
||||
|
||||
if (this.shouldReportError()) {
|
||||
this.notifyOnNextTick("weave:ui:login:error");
|
||||
|
@ -641,8 +617,7 @@ ErrorHandler.prototype = {
|
|||
this.service.logout();
|
||||
}
|
||||
|
||||
this.resetFileLog(Svc.Prefs.get("log.appender.file.logOnError"),
|
||||
LOG_PREFIX_ERROR);
|
||||
this.resetFileLog(this._logManager.REASON_ERROR);
|
||||
|
||||
if (this.shouldReportError()) {
|
||||
this.notifyOnNextTick("weave:ui:sync:error");
|
||||
|
@ -668,8 +643,7 @@ ErrorHandler.prototype = {
|
|||
|
||||
if (Status.service == SYNC_FAILED_PARTIAL) {
|
||||
this._log.debug("Some engines did not sync correctly.");
|
||||
this.resetFileLog(Svc.Prefs.get("log.appender.file.logOnError"),
|
||||
LOG_PREFIX_ERROR);
|
||||
this.resetFileLog(this._logManager.REASON_ERROR);
|
||||
|
||||
if (this.shouldReportError()) {
|
||||
this.dontIgnoreErrors = false;
|
||||
|
@ -677,8 +651,7 @@ ErrorHandler.prototype = {
|
|||
break;
|
||||
}
|
||||
} else {
|
||||
this.resetFileLog(Svc.Prefs.get("log.appender.file.logOnSuccess"),
|
||||
LOG_PREFIX_SUCCESS);
|
||||
this.resetFileLog(this._logManager.REASON_SUCCESS);
|
||||
}
|
||||
this.dontIgnoreErrors = false;
|
||||
this.notifyOnNextTick("weave:ui:sync:finish");
|
||||
|
@ -705,95 +678,22 @@ ErrorHandler.prototype = {
|
|||
Utils.nextTick(this.service.sync, this.service);
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds all logs older than maxErrorAge and deletes them without tying up I/O.
|
||||
*/
|
||||
cleanupLogs: function cleanupLogs() {
|
||||
let direntries = FileUtils.getDir("ProfD", ["weave", "logs"]).directoryEntries;
|
||||
let oldLogs = [];
|
||||
let index = 0;
|
||||
let threshold = Date.now() - 1000 * Svc.Prefs.get("log.appender.file.maxErrorAge");
|
||||
|
||||
this._log.debug("Log cleanup threshold time: " + threshold);
|
||||
while (direntries.hasMoreElements()) {
|
||||
let logFile = direntries.getNext().QueryInterface(Ci.nsIFile);
|
||||
if (logFile.lastModifiedTime < threshold) {
|
||||
this._log.trace(" > Noting " + logFile.leafName +
|
||||
" for cleanup (" + logFile.lastModifiedTime + ")");
|
||||
oldLogs.push(logFile);
|
||||
}
|
||||
}
|
||||
|
||||
// Deletes a file from oldLogs each tick until there are none left.
|
||||
let errorHandler = this;
|
||||
function deleteFile() {
|
||||
if (index >= oldLogs.length) {
|
||||
errorHandler._log.debug("Done deleting files.");
|
||||
errorHandler._cleaningUpFileLogs = false;
|
||||
Svc.Obs.notify("weave:service:cleanup-logs");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let file = oldLogs[index];
|
||||
file.remove(false);
|
||||
errorHandler._log.trace("Deleted " + file.leafName + ".");
|
||||
} catch (ex) {
|
||||
errorHandler._log._debug("Encountered error trying to clean up old log file '"
|
||||
+ oldLogs[index].leafName + "':"
|
||||
+ Utils.exceptionStr(ex));
|
||||
}
|
||||
index++;
|
||||
Utils.nextTick(deleteFile);
|
||||
}
|
||||
|
||||
if (oldLogs.length > 0) {
|
||||
this._cleaningUpFileLogs = true;
|
||||
Utils.nextTick(deleteFile);
|
||||
} else {
|
||||
this._log.debug("No logs to clean up.");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate a log file for the sync that just completed
|
||||
* and refresh the input & output streams.
|
||||
*
|
||||
* @param flushToFile
|
||||
* the log file to be flushed/reset
|
||||
*
|
||||
* @param filenamePrefix
|
||||
* a value of either LOG_PREFIX_SUCCESS or LOG_PREFIX_ERROR
|
||||
* to be used as the log filename prefix
|
||||
* @param reason
|
||||
* A constant from the LogManager that indicates the reason for the
|
||||
* reset.
|
||||
*/
|
||||
resetFileLog: function resetFileLog(flushToFile, filenamePrefix) {
|
||||
let inStream = this._logAppender.getInputStream();
|
||||
this._logAppender.reset();
|
||||
if (flushToFile && inStream) {
|
||||
this._log.debug("Flushing file log.");
|
||||
try {
|
||||
let filename = filenamePrefix + Date.now() + ".txt";
|
||||
let file = FileUtils.getFile("ProfD", ["weave", "logs", filename]);
|
||||
let outStream = FileUtils.openFileOutputStream(file);
|
||||
|
||||
this._log.trace("Beginning stream copy to " + file.leafName + ": " +
|
||||
Date.now());
|
||||
NetUtil.asyncCopy(inStream, outStream, function onCopyComplete() {
|
||||
this._log.trace("onCopyComplete: " + Date.now());
|
||||
this._log.trace("Output file timestamp: " + file.lastModifiedTime);
|
||||
Svc.Obs.notify("weave:service:reset-file-log");
|
||||
this._log.trace("Notified: " + Date.now());
|
||||
if (filenamePrefix == LOG_PREFIX_ERROR &&
|
||||
!this._cleaningUpFileLogs) {
|
||||
this._log.trace("Scheduling cleanup.");
|
||||
Utils.nextTick(this.cleanupLogs, this);
|
||||
}
|
||||
}.bind(this));
|
||||
} catch (ex) {
|
||||
Svc.Obs.notify("weave:service:reset-file-log");
|
||||
}
|
||||
} else {
|
||||
resetFileLog: function resetFileLog(reason) {
|
||||
let onComplete = () => {
|
||||
Svc.Obs.notify("weave:service:reset-file-log");
|
||||
}
|
||||
this._log.trace("Notified: " + Date.now());
|
||||
};
|
||||
// Note we do not return the promise here - the caller doesn't need to wait
|
||||
// for this to complete.
|
||||
this._logManager.resetFileLog(reason).then(onComplete, onComplete);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,8 +14,6 @@ Cu.import("resource://gre/modules/FileUtils.jsm");
|
|||
|
||||
const FAKE_SERVER_URL = "http://dummy:9000/";
|
||||
const logsdir = FileUtils.getDir("ProfD", ["weave", "logs"], true);
|
||||
const LOG_PREFIX_SUCCESS = "success-";
|
||||
const LOG_PREFIX_ERROR = "error-";
|
||||
|
||||
const PROLONGED_ERROR_DURATION =
|
||||
(Svc.Prefs.get('errorhandler.networkFailureReportTimeout') * 2) * 1000;
|
||||
|
@ -1772,8 +1770,7 @@ add_task(function test_sync_engine_generic_fail() {
|
|||
let entries = logsdir.directoryEntries;
|
||||
do_check_true(entries.hasMoreElements());
|
||||
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
|
||||
do_check_eq(logfile.leafName.slice(0, LOG_PREFIX_ERROR.length),
|
||||
LOG_PREFIX_ERROR);
|
||||
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName);
|
||||
|
||||
clean();
|
||||
server.stop(deferred.resolve);
|
||||
|
@ -1804,8 +1801,7 @@ add_test(function test_logs_on_sync_error_despite_shouldReportError() {
|
|||
let entries = logsdir.directoryEntries;
|
||||
do_check_true(entries.hasMoreElements());
|
||||
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
|
||||
do_check_eq(logfile.leafName.slice(0, LOG_PREFIX_ERROR.length),
|
||||
LOG_PREFIX_ERROR);
|
||||
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName);
|
||||
|
||||
clean();
|
||||
run_next_test();
|
||||
|
@ -1832,8 +1828,7 @@ add_test(function test_logs_on_login_error_despite_shouldReportError() {
|
|||
let entries = logsdir.directoryEntries;
|
||||
do_check_true(entries.hasMoreElements());
|
||||
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
|
||||
do_check_eq(logfile.leafName.slice(0, LOG_PREFIX_ERROR.length),
|
||||
LOG_PREFIX_ERROR);
|
||||
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName);
|
||||
|
||||
clean();
|
||||
run_next_test();
|
||||
|
@ -1867,8 +1862,7 @@ add_task(function test_engine_applyFailed() {
|
|||
let entries = logsdir.directoryEntries;
|
||||
do_check_true(entries.hasMoreElements());
|
||||
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
|
||||
do_check_eq(logfile.leafName.slice(0, LOG_PREFIX_ERROR.length),
|
||||
LOG_PREFIX_ERROR);
|
||||
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName);
|
||||
|
||||
clean();
|
||||
server.stop(deferred.resolve);
|
||||
|
|
|
@ -10,8 +10,6 @@ Cu.import("resource://gre/modules/NetUtil.jsm");
|
|||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const logsdir = FileUtils.getDir("ProfD", ["weave", "logs"], true);
|
||||
const LOG_PREFIX_SUCCESS = "success-";
|
||||
const LOG_PREFIX_ERROR = "error-";
|
||||
|
||||
// Delay to wait before cleanup, to allow files to age.
|
||||
// This is so large because the file timestamp granularity is per-second, and
|
||||
|
@ -32,6 +30,7 @@ function setLastSync(lastSyncValue) {
|
|||
function run_test() {
|
||||
initTestLogging("Trace");
|
||||
|
||||
Log.repository.getLogger("Sync.LogManager").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.Service").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace;
|
||||
Log.repository.getLogger("Sync.ErrorHandler").level = Log.Level.Trace;
|
||||
|
@ -41,7 +40,7 @@ function run_test() {
|
|||
|
||||
add_test(function test_noOutput() {
|
||||
// Ensure that the log appender won't print anything.
|
||||
errorHandler._logAppender.level = Log.Level.Fatal + 1;
|
||||
errorHandler._logManager._fileAppender.level = Log.Level.Fatal + 1;
|
||||
|
||||
// Clear log output from startup.
|
||||
Svc.Prefs.set("log.appender.file.logOnSuccess", false);
|
||||
|
@ -53,7 +52,7 @@ add_test(function test_noOutput() {
|
|||
Svc.Obs.add("weave:service:reset-file-log", function onResetFileLog() {
|
||||
Svc.Obs.remove("weave:service:reset-file-log", onResetFileLog);
|
||||
|
||||
errorHandler._logAppender.level = Log.Level.Trace;
|
||||
errorHandler._logManager._fileAppender.level = Log.Level.Trace;
|
||||
Svc.Prefs.resetBranch("");
|
||||
run_next_test();
|
||||
});
|
||||
|
@ -109,8 +108,7 @@ add_test(function test_logOnSuccess_true() {
|
|||
do_check_true(entries.hasMoreElements());
|
||||
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
|
||||
do_check_eq(logfile.leafName.slice(-4), ".txt");
|
||||
do_check_eq(logfile.leafName.slice(0, LOG_PREFIX_SUCCESS.length),
|
||||
LOG_PREFIX_SUCCESS);
|
||||
do_check_true(logfile.leafName.startsWith("sync-success-"), logfile.leafName);
|
||||
do_check_false(entries.hasMoreElements());
|
||||
|
||||
// Ensure the log message was actually written to file.
|
||||
|
@ -162,6 +160,13 @@ add_test(function test_sync_error_logOnError_true() {
|
|||
const MESSAGE = "this WILL show up";
|
||||
log.info(MESSAGE);
|
||||
|
||||
// We need to wait until the log cleanup started by this test is complete
|
||||
// or the next test will fail as it is ongoing.
|
||||
Svc.Obs.add("services-tests:common:log-manager:cleanup-logs", function onCleanupLogs() {
|
||||
Svc.Obs.remove("services-tests:common:log-manager:cleanup-logs", onCleanupLogs);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:service:reset-file-log", function onResetFileLog() {
|
||||
Svc.Obs.remove("weave:service:reset-file-log", onResetFileLog);
|
||||
|
||||
|
@ -170,8 +175,7 @@ add_test(function test_sync_error_logOnError_true() {
|
|||
do_check_true(entries.hasMoreElements());
|
||||
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
|
||||
do_check_eq(logfile.leafName.slice(-4), ".txt");
|
||||
do_check_eq(logfile.leafName.slice(0, LOG_PREFIX_ERROR.length),
|
||||
LOG_PREFIX_ERROR);
|
||||
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName);
|
||||
do_check_false(entries.hasMoreElements());
|
||||
|
||||
// Ensure the log message was actually written to file.
|
||||
|
@ -188,7 +192,6 @@ add_test(function test_sync_error_logOnError_true() {
|
|||
}
|
||||
|
||||
Svc.Prefs.resetBranch("");
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -224,6 +227,13 @@ add_test(function test_login_error_logOnError_true() {
|
|||
const MESSAGE = "this WILL show up";
|
||||
log.info(MESSAGE);
|
||||
|
||||
// We need to wait until the log cleanup started by this test is complete
|
||||
// or the next test will fail as it is ongoing.
|
||||
Svc.Obs.add("services-tests:common:log-manager:cleanup-logs", function onCleanupLogs() {
|
||||
Svc.Obs.remove("services-tests:common:log-manager:cleanup-logs", onCleanupLogs);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
Svc.Obs.add("weave:service:reset-file-log", function onResetFileLog() {
|
||||
Svc.Obs.remove("weave:service:reset-file-log", onResetFileLog);
|
||||
|
||||
|
@ -232,8 +242,7 @@ add_test(function test_login_error_logOnError_true() {
|
|||
do_check_true(entries.hasMoreElements());
|
||||
let logfile = entries.getNext().QueryInterface(Ci.nsILocalFile);
|
||||
do_check_eq(logfile.leafName.slice(-4), ".txt");
|
||||
do_check_eq(logfile.leafName.slice(0, LOG_PREFIX_ERROR.length),
|
||||
LOG_PREFIX_ERROR);
|
||||
do_check_true(logfile.leafName.startsWith("sync-error-"), logfile.leafName);
|
||||
do_check_false(entries.hasMoreElements());
|
||||
|
||||
// Ensure the log message was actually written to file.
|
||||
|
@ -250,7 +259,6 @@ add_test(function test_login_error_logOnError_true() {
|
|||
}
|
||||
|
||||
Svc.Prefs.resetBranch("");
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -273,7 +281,7 @@ add_test(function test_logErrorCleanup_age() {
|
|||
_("Making some files.");
|
||||
for (let i = 0; i < numLogs; i++) {
|
||||
let now = Date.now();
|
||||
let filename = LOG_PREFIX_ERROR + now + "" + i + ".txt";
|
||||
let filename = "sync-error-" + now + "" + i + ".txt";
|
||||
let newLog = FileUtils.getFile("ProfD", ["weave", "logs", filename]);
|
||||
let foStream = FileUtils.openFileOutputStream(newLog);
|
||||
foStream.write(errString, errString.length);
|
||||
|
@ -282,8 +290,8 @@ add_test(function test_logErrorCleanup_age() {
|
|||
oldLogs.push(newLog.leafName);
|
||||
}
|
||||
|
||||
Svc.Obs.add("weave:service:cleanup-logs", function onCleanupLogs() {
|
||||
Svc.Obs.remove("weave:service:cleanup-logs", onCleanupLogs);
|
||||
Svc.Obs.add("services-tests:common:log-manager:cleanup-logs", function onCleanupLogs() {
|
||||
Svc.Obs.remove("services-tests:common:log-manager:cleanup-logs", onCleanupLogs);
|
||||
|
||||
// Only the newest created log file remains.
|
||||
let entries = logsdir.directoryEntries;
|
||||
|
|
|
@ -146,7 +146,10 @@ function summarizeObject(obj) {
|
|||
return obj;
|
||||
}
|
||||
|
||||
let Scheduler = {
|
||||
// In order to expose Scheduler to the unfiltered Cu.import return value variant
|
||||
// on B2G we need to save it to `this`. This does not make it public;
|
||||
// EXPORTED_SYMBOLS still controls that in all cases.
|
||||
let Scheduler = this.Scheduler = {
|
||||
|
||||
/**
|
||||
* |true| once we have sent at least one message to the worker.
|
||||
|
@ -268,36 +271,39 @@ let Scheduler = {
|
|||
// Grab the kill queue to make sure that we
|
||||
// cannot be interrupted by another call to `kill`.
|
||||
let killQueue = this._killQueue;
|
||||
|
||||
// Deactivate the queue, to ensure that no message is sent
|
||||
// to an obsolete worker (we reactivate it in the `finally`).
|
||||
// This needs to be done right now so that we maintain relative
|
||||
// ordering with calls to post(), etc.
|
||||
let deferred = Promise.defer();
|
||||
let savedQueue = this.queue;
|
||||
this.queue = deferred.promise;
|
||||
|
||||
return this._killQueue = Task.spawn(function*() {
|
||||
|
||||
yield killQueue;
|
||||
// From this point, and until the end of the Task, we are the
|
||||
// only call to `kill`, regardless of any `yield`.
|
||||
|
||||
yield this.queue;
|
||||
|
||||
// Enter critical section: no yield in this block
|
||||
// (we want to make sure that we remain the only
|
||||
// request in the queue).
|
||||
|
||||
if (!this.launched || this.shutdown || !this._worker) {
|
||||
// Nothing to kill
|
||||
this.shutdown = this.shutdown || shutdown;
|
||||
this._worker = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Deactivate the queue, to ensure that no message is sent
|
||||
// to an obsolete worker (we reactivate it in the |finally|).
|
||||
let deferred = Promise.defer();
|
||||
this.queue = deferred.promise;
|
||||
|
||||
|
||||
// Exit critical section
|
||||
|
||||
let message = ["Meta_shutdown", [reset]];
|
||||
yield savedQueue;
|
||||
|
||||
try {
|
||||
// Enter critical section: no yield in this block
|
||||
// (we want to make sure that we remain the only
|
||||
// request in the queue).
|
||||
|
||||
if (!this.launched || this.shutdown || !this._worker) {
|
||||
// Nothing to kill
|
||||
this.shutdown = this.shutdown || shutdown;
|
||||
this._worker = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Exit critical section
|
||||
|
||||
let message = ["Meta_shutdown", [reset]];
|
||||
|
||||
Scheduler.latestReceived = [];
|
||||
Scheduler.latestSent = [Date.now(), ...message];
|
||||
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
"use strict";
|
||||
|
||||
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
// We want the actual global to get at the internals since Scheduler is not
|
||||
// exported.
|
||||
var AsyncFrontGlobal = Components.utils.import(
|
||||
"resource://gre/modules/osfile/osfile_async_front.jsm",
|
||||
null);
|
||||
var Scheduler = AsyncFrontGlobal.Scheduler;
|
||||
|
||||
/**
|
||||
* Verify that Scheduler.kill() interacts with other OS.File requests correctly,
|
||||
* and that no requests are lost. This is relevant because on B2G we
|
||||
* auto-kill the worker periodically, making it very possible for valid requests
|
||||
* to be interleaved with the automatic kill().
|
||||
*
|
||||
* This test is being created with the fix for Bug 1125989 where `kill` queue
|
||||
* management was found to be buggy. It is a glass-box test that explicitly
|
||||
* re-creates the observed failure situation; it is not guaranteed to prevent
|
||||
* all future regressions. The following is a detailed explanation of the test
|
||||
* for your benefit if this test ever breaks or you are wondering what was the
|
||||
* point of all this. You might want to skim the code below first.
|
||||
*
|
||||
* OS.File maintains a `queue` of operations to be performed. This queue is
|
||||
* nominally implemented as a chain of promises. Every time a new job is
|
||||
* OS.File.push()ed, it effectively becomes the new `queue` promise. (An
|
||||
* extra promise is interposed with a rejection handler to avoid the rejection
|
||||
* cascading, but that does not matter for our purposes.)
|
||||
*
|
||||
* The flaw in `kill` was that it would wait for the `queue` to complete before
|
||||
* replacing `queue`. As a result, another OS.File operation could use `push`
|
||||
* (by way of OS.File.post()) to also use .then() on the same `queue` promise.
|
||||
* Accordingly, assuming that promise was not yet resolved (due to a pending
|
||||
* OS.File request), when it was resolved, both the task scheduled in `kill`
|
||||
* and in `post` would be triggered. Both of those tasks would run until
|
||||
* encountering a call to worker.post().
|
||||
*
|
||||
* Re-creating this race is not entirely trivial because of the large number of
|
||||
* promises used by the code causing control flow to repeatedly be deferred. In
|
||||
* a slightly simpler world we could run the follwing in the same turn of the
|
||||
* event loop and trigger the problem.
|
||||
* - any OS.File request
|
||||
* - Scheduler.kill()
|
||||
* - any OS.File request
|
||||
*
|
||||
* However, we need the Scheduler.kill task to reach the point where it is
|
||||
* waiting on the same `queue` that another task has been scheduled against.
|
||||
* Since the `kill` task yields on the `killQueue` promise prior to yielding
|
||||
* on `queue`, however, some turns of the event loop are required. Happily,
|
||||
* for us, as discussed above, the problem triggers when we have two promises
|
||||
* scheduled on the `queue`, so we can just wait to schedule the second OS.File
|
||||
* request on the queue. (Note that because of the additional then() added to
|
||||
* eat rejections, there is an important difference between the value of
|
||||
* `queue` and the value returned by the first OS.File request.)
|
||||
*/
|
||||
add_task(function* test_kill_race() {
|
||||
// Ensure the worker has been created and that SET_DEBUG has taken effect.
|
||||
// We have chosen OS.File.exists for our tests because it does not trigger
|
||||
// a rejection and we absolutely do not care what the operation is other
|
||||
// than it does not invoke a native fast-path.
|
||||
yield OS.File.exists('foo.foo');
|
||||
|
||||
do_print('issuing first request');
|
||||
let firstRequest = OS.File.exists('foo.bar');
|
||||
let secondRequest;
|
||||
let secondResolved = false;
|
||||
|
||||
// As noted in our big block comment, we want to wait to schedule the
|
||||
// second request so that it races `kill`'s call to `worker.post`. Having
|
||||
// ourselves wait on the same promise, `queue`, and registering ourselves
|
||||
// before we issue the kill request means we will get run before the `kill`
|
||||
// task resumes and allow us to precisely create the desired race.
|
||||
Scheduler.queue.then(function() {
|
||||
do_print('issuing second request');
|
||||
secondRequest = OS.File.exists('foo.baz');
|
||||
secondRequest.then(function() {
|
||||
secondResolved = true;
|
||||
});
|
||||
});
|
||||
|
||||
do_print('issuing kill request');
|
||||
let killRequest = Scheduler.kill({ reset: true, shutdown: false });
|
||||
|
||||
// Wait on the killRequest so that we can schedule a new OS.File request
|
||||
// after it completes...
|
||||
yield killRequest;
|
||||
// ...because our ordering guarantee ensures that there is at most one
|
||||
// worker (and this usage here should not be vulnerable even with the
|
||||
// bug present), so when this completes the secondRequest has either been
|
||||
// resolved or lost.
|
||||
yield OS.File.exists('foo.goz');
|
||||
|
||||
ok(secondResolved,
|
||||
'The second request was resolved so we avoided the bug. Victory!');
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
|
@ -39,6 +39,7 @@ support-files =
|
|||
[test_queue.js]
|
||||
[test_loader.js]
|
||||
[test_constants.js]
|
||||
[test_osfile_kill.js]
|
||||
|
||||
# Unimplemented on Windows (bug 1022816).
|
||||
# Spurious failure on Android test farm due to non-POSIX behavior of
|
||||
|
|
|
@ -1319,7 +1319,7 @@ BoxModelHighlighter.prototype = Heritage.extend(AutoRefreshHighlighter.prototype
|
|||
}
|
||||
|
||||
let rect = this.currentQuads.border.bounds;
|
||||
let dim = Math.ceil(rect.width) + " \u00D7 " + Math.ceil(rect.height);
|
||||
let dim = parseFloat(rect.width.toPrecision(6)) + " \u00D7 " + parseFloat(rect.height.toPrecision(6));
|
||||
|
||||
let elementId = this.ID_CLASS_PREFIX + "nodeinfobar-";
|
||||
this.markup.setTextContentForElement(elementId + "tagname", tagName);
|
||||
|
|
|
@ -43,7 +43,12 @@ types.addDictType("AllocationsRecordingOptions", {
|
|||
// The probability we sample any given allocation when recording
|
||||
// allocations. Must be between 0.0 and 1.0. Defaults to 1.0, or sampling
|
||||
// every allocation.
|
||||
probability: "number"
|
||||
probability: "number",
|
||||
|
||||
// The maximum number of of allocation events to keep in the allocations
|
||||
// log. If new allocations arrive, when we are already at capacity, the oldest
|
||||
// allocation event is lost. This number must fit in a 32 bit signed integer.
|
||||
maxLogLength: "number"
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -164,6 +169,9 @@ let MemoryActor = protocol.ActorClass({
|
|||
this.dbg.memory.allocationSamplingProbability = options.probability != null
|
||||
? options.probability
|
||||
: 1.0;
|
||||
if (options.maxLogLength != null) {
|
||||
this.dbg.memory.maxAllocationsLogLength = options.maxLogLength;
|
||||
}
|
||||
this.dbg.memory.trackingAllocationSites = true;
|
||||
|
||||
return Date.now();
|
||||
|
@ -253,6 +261,15 @@ let MemoryActor = protocol.ActorClass({
|
|||
* profiling and done only when necessary.
|
||||
*/
|
||||
getAllocations: method(expectState("attached", function() {
|
||||
if (this.dbg.memory.allocationsLogOverflowed) {
|
||||
// Since the last time we drained the allocations log, there have been
|
||||
// more allocations than the log's capacity, and we lost some data. There
|
||||
// isn't anything actionable we can do about this, but put a message in
|
||||
// the browser console so we at least know that it occurred.
|
||||
reportException("MemoryActor.prototype.getAllocations",
|
||||
"Warning: allocations log overflowed and lost some data.");
|
||||
}
|
||||
|
||||
const allocations = this.dbg.memory.drainAllocationsLog()
|
||||
const packet = {
|
||||
allocations: [],
|
||||
|
|
|
@ -747,8 +747,8 @@ var PageStyleActor = protocol.ActorClass({
|
|||
// the size of the element.
|
||||
|
||||
let clientRect = node.rawNode.getBoundingClientRect();
|
||||
layout.width = Math.ceil(clientRect.width);
|
||||
layout.height = Math.ceil(clientRect.height);
|
||||
layout.width = parseFloat(clientRect.width.toPrecision(6));
|
||||
layout.height = parseFloat(clientRect.height.toPrecision(6));
|
||||
|
||||
// We compute and update the values of margins & co.
|
||||
let style = CssLogic.getComputedStyle(node.rawNode);
|
||||
|
@ -776,7 +776,7 @@ var PageStyleActor = protocol.ActorClass({
|
|||
|
||||
for (let i in this.map) {
|
||||
let property = this.map[i].property;
|
||||
this.map[i].value = parseInt(style.getPropertyValue(property));
|
||||
this.map[i].value = parseFloat(style.getPropertyValue(property));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ skip-if = buildapp == 'mulet'
|
|||
[test_memory_allocations_03.html]
|
||||
[test_memory_allocations_04.html]
|
||||
[test_memory_allocations_05.html]
|
||||
[test_memory_allocations_06.html]
|
||||
[test_memory_attach_01.html]
|
||||
[test_memory_attach_02.html]
|
||||
[test_memory_census.html]
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Bug 1132764 - Test controlling the maximum allocations log length over the RDP.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Memory monitoring actor test</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script src="memory-helpers.js" type="application/javascript;version=1.8"></script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
Task.spawn(function* () {
|
||||
var { memory, client } = yield startServerAndGetSelectedTabMemory();
|
||||
yield memory.attach();
|
||||
|
||||
var allocs = [];
|
||||
function allocator() {
|
||||
allocs.push(new Object);
|
||||
}
|
||||
|
||||
yield memory.startRecordingAllocations({
|
||||
maxLogLength: 1
|
||||
});
|
||||
|
||||
allocator();
|
||||
allocator();
|
||||
allocator();
|
||||
|
||||
var response = yield memory.getAllocations();
|
||||
yield memory.stopRecordingAllocations();
|
||||
|
||||
is(response.allocations.length, 1,
|
||||
"There should only be one entry in the allocations log.");
|
||||
|
||||
yield memory.detach();
|
||||
destroyServerAndFinish(client);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -548,6 +548,7 @@ xul|*#categories[keyboard-navigation="true"]:-moz-focusring > xul|*.category[cur
|
|||
padding-bottom: 2px;
|
||||
-moz-padding-start: 9px;
|
||||
margin: 0;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
*|*.category-icon {
|
||||
|
@ -569,6 +570,7 @@ xul|*#categories[keyboard-navigation="true"]:-moz-focusring > xul|*.category[cur
|
|||
font-weight: normal;
|
||||
line-height: 40px;
|
||||
margin: 0;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
/* File fields */
|
||||
|
|
Загрузка…
Ссылка в новой задаче