зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound a=merge CLOSED TREE
This commit is contained in:
Коммит
ed4c2aa486
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="401e981f51cf047292d101c785be8ec48bf75e8c"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -23,7 +23,7 @@
|
|||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0024095d96fdccc076e511cfa7a4287cb1e0fd83"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
|
||||
|
@ -129,7 +129,7 @@
|
|||
<!-- Stock Android things -->
|
||||
<project name="platform/external/icu4c" path="external/icu4c" revision="2bb01561780583cc37bc667f0ea79f48a122d8a2"/>
|
||||
<!-- dolphin specific things -->
|
||||
<project name="device/sprd" path="device/sprd" revision="b94ac10f073ed2503505e59f5097d4e286423037"/>
|
||||
<project name="device/sprd" path="device/sprd" revision="56f0085d1105fd6519d5d3773f91d48b2ad66d5c"/>
|
||||
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="4e58336019b5cbcfd134caf55b142236cf986618"/>
|
||||
<project name="platform/frameworks/av" path="frameworks/av" revision="4387fe988e5a1001f29ce05fcfda03ed2d32137b"/>
|
||||
<project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>
|
||||
|
|
|
@ -19,13 +19,13 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="401e981f51cf047292d101c785be8ec48bf75e8c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0024095d96fdccc076e511cfa7a4287cb1e0fd83"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
<project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="401e981f51cf047292d101c785be8ec48bf75e8c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0024095d96fdccc076e511cfa7a4287cb1e0fd83"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="401e981f51cf047292d101c785be8ec48bf75e8c"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -23,7 +23,7 @@
|
|||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0024095d96fdccc076e511cfa7a4287cb1e0fd83"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
|
||||
|
|
|
@ -19,13 +19,13 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="401e981f51cf047292d101c785be8ec48bf75e8c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0024095d96fdccc076e511cfa7a4287cb1e0fd83"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
<project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="401e981f51cf047292d101c785be8ec48bf75e8c"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -23,7 +23,7 @@
|
|||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0024095d96fdccc076e511cfa7a4287cb1e0fd83"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="401e981f51cf047292d101c785be8ec48bf75e8c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0024095d96fdccc076e511cfa7a4287cb1e0fd83"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "03effd58034893d2a12907faa6a8a41d3e923b50",
|
||||
"revision": "1764121939b82f4ded4ca30761df48c3841028b0",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
<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="ebc90190771a945d405f5d36efd813db6f77f965"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="401e981f51cf047292d101c785be8ec48bf75e8c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0024095d96fdccc076e511cfa7a4287cb1e0fd83"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
<project name="platform_bionic" path="bionic" remote="b2g" revision="1a2a32eda22ef2cd18f57f423a5e7b22a105a6f8"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="401e981f51cf047292d101c785be8ec48bf75e8c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="401e981f51cf047292d101c785be8ec48bf75e8c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0024095d96fdccc076e511cfa7a4287cb1e0fd83"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ebc90190771a945d405f5d36efd813db6f77f965"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="401e981f51cf047292d101c785be8ec48bf75e8c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0024095d96fdccc076e511cfa7a4287cb1e0fd83"/>
|
||||
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
|
|
|
@ -2562,7 +2562,7 @@ let BrowserOnClick = {
|
|||
let mm = window.messageManager;
|
||||
mm.addMessageListener("Browser:CertExceptionError", this);
|
||||
mm.addMessageListener("Browser:SiteBlockedError", this);
|
||||
mm.addMessageListener("Browser:NetworkError", this);
|
||||
mm.addMessageListener("Browser:EnableOnlineMode", this);
|
||||
mm.addMessageListener("Browser:SendSSLErrorReport", this);
|
||||
mm.addMessageListener("Browser:SetSSLErrorReportAuto", this);
|
||||
},
|
||||
|
@ -2571,7 +2571,7 @@ let BrowserOnClick = {
|
|||
let mm = window.messageManager;
|
||||
mm.removeMessageListener("Browser:CertExceptionError", this);
|
||||
mm.removeMessageListener("Browser:SiteBlockedError", this);
|
||||
mm.removeMessageListener("Browser:NetworkError", this);
|
||||
mm.removeMessageListener("Browser:EnableOnlineMode", this);
|
||||
mm.removeMessageListener("Browser:SendSSLErrorReport", this);
|
||||
mm.removeMessageListener("Browser:SetSSLErrorReportAuto", this);
|
||||
},
|
||||
|
@ -2605,9 +2605,12 @@ let BrowserOnClick = {
|
|||
this.onAboutBlocked(msg.data.elementId, msg.data.isMalware,
|
||||
msg.data.isTopFrame, msg.data.location);
|
||||
break;
|
||||
case "Browser:NetworkError":
|
||||
// Reset network state, the error page will refresh on its own.
|
||||
Services.io.offline = false;
|
||||
case "Browser:EnableOnlineMode":
|
||||
if (Services.io.offline) {
|
||||
// Reset network state and refresh the page.
|
||||
Services.io.offline = false;
|
||||
msg.target.reload();
|
||||
}
|
||||
break;
|
||||
case "Browser:SendSSLErrorReport":
|
||||
this.onSSLErrorReport(msg.target, msg.data.elementId,
|
||||
|
|
|
@ -568,7 +568,8 @@ let ClickEventHandler = {
|
|||
this.onAboutBlocked(originalTarget, ownerDoc);
|
||||
return;
|
||||
} else if (ownerDoc.documentURI.startsWith("about:neterror")) {
|
||||
this.onAboutNetError(originalTarget, ownerDoc);
|
||||
this.onAboutNetError(event, ownerDoc.documentURI);
|
||||
return;
|
||||
}
|
||||
|
||||
let [href, node] = this._hrefAndLinkNodeForClickEvent(event);
|
||||
|
@ -635,12 +636,18 @@ let ClickEventHandler = {
|
|||
});
|
||||
},
|
||||
|
||||
onAboutNetError: function (targetElement, ownerDoc) {
|
||||
let elmId = targetElement.getAttribute("id");
|
||||
if (elmId != "errorTryAgain" || !/e=netOffline/.test(ownerDoc.documentURI)) {
|
||||
onAboutNetError: function (event, documentURI) {
|
||||
let elmId = event.originalTarget.getAttribute("id");
|
||||
if (elmId != "errorTryAgain" || !/e=netOffline/.test(documentURI)) {
|
||||
return;
|
||||
}
|
||||
sendSyncMessage("Browser:NetworkError", {});
|
||||
// browser front end will handle clearing offline mode and refreshing
|
||||
// the page *if* we're in offline mode now. Otherwise let the error page
|
||||
// handle the click.
|
||||
if (Services.io.offline) {
|
||||
event.preventDefault();
|
||||
sendAsyncMessage("Browser:EnableOnlineMode", {});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -56,12 +56,12 @@ function checkPage() {
|
|||
// the actual example.com.
|
||||
Services.prefs.setIntPref("network.proxy.type", proxyPrefValue);
|
||||
|
||||
Services.obs.addObserver(function observer(aSubject, aTopic) {
|
||||
ok(!Services.io.offline, "After clicking the Try Again button, we're back " +
|
||||
"online.");
|
||||
finish();
|
||||
}, "network:offline-status-changed", false);
|
||||
gBrowser.contentDocument.getElementById("errorTryAgain").click();
|
||||
|
||||
ok(!Services.io.offline, "After clicking the Try Again button, we're back " +
|
||||
"online.");
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
|
|
|
@ -37,8 +37,8 @@ function runTests() {
|
|||
yield setLinks("-1");
|
||||
|
||||
// Test with enhanced = false
|
||||
NewTabUtils.allPages.enhanced = false;
|
||||
yield addNewTabPageTab();
|
||||
yield customizeNewTabPage("classic");
|
||||
let {type, enhanced, title} = getData(0);
|
||||
is(type, "organic", "directory link is organic");
|
||||
isnot(enhanced, "", "directory link has enhanced image");
|
||||
|
@ -47,8 +47,8 @@ function runTests() {
|
|||
is(getData(1), null, "history link pushed out by directory link");
|
||||
|
||||
// Test with enhanced = true
|
||||
NewTabUtils.allPages.enhanced = true;
|
||||
yield addNewTabPageTab();
|
||||
yield customizeNewTabPage("enhanced");
|
||||
({type, enhanced, title} = getData(0));
|
||||
is(type, "organic", "directory link is still organic");
|
||||
isnot(enhanced, "", "directory link still has enhanced image");
|
||||
|
@ -67,12 +67,15 @@ function runTests() {
|
|||
is(getData(1), null, "directory link pushed out by pinned history link");
|
||||
|
||||
// Test pinned link with enhanced = false
|
||||
NewTabUtils.allPages.enhanced = false;
|
||||
yield addNewTabPageTab();
|
||||
yield customizeNewTabPage("classic");
|
||||
({type, enhanced, title} = getData(0));
|
||||
isnot(type, "enhanced", "history link is not enhanced");
|
||||
is(enhanced, "", "history link has no enhanced image");
|
||||
is(title, "site#-1");
|
||||
|
||||
is(getData(1), null, "directory link still pushed out by pinned history link");
|
||||
|
||||
ok(getContentDocument().getElementById("newtab-intro-what"),
|
||||
"'What is this page?' link exists");
|
||||
}
|
||||
|
|
|
@ -684,3 +684,34 @@ function whenSearchInitDone() {
|
|||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the newtab customization option and waits for the panel to open and close
|
||||
*
|
||||
* @param {string} aTheme
|
||||
* Can be any of("blank"|"classic"|"enhanced")
|
||||
*/
|
||||
function customizeNewTabPage(aTheme) {
|
||||
let document = getContentDocument();
|
||||
let panel = document.getElementById("newtab-customize-panel");
|
||||
let customizeButton = document.getElementById("newtab-customize-button");
|
||||
|
||||
// Attache onShown the listener on panel
|
||||
panel.addEventListener("popupshown", function onShown() {
|
||||
panel.removeEventListener("popupshown", onShown);
|
||||
|
||||
// Get the element for the specific option and click on it,
|
||||
// then trigger an escape to close the panel
|
||||
document.getElementById("newtab-customize-" + aTheme).click();
|
||||
executeSoon(() => { panel.hidePopup(); });
|
||||
});
|
||||
|
||||
// Attache the listener for panel closing, this will resolve the promise
|
||||
panel.addEventListener("popuphidden", function onHidden() {
|
||||
panel.removeEventListener("popuphidden", onHidden);
|
||||
executeSoon(TestRunner.next);
|
||||
});
|
||||
|
||||
// Click on the customize button to display the panel
|
||||
customizeButton.click();
|
||||
}
|
||||
|
|
|
@ -193,21 +193,26 @@ PlayerWidget.prototype = {
|
|||
});
|
||||
let titleHTML = "";
|
||||
|
||||
// Name
|
||||
// Name.
|
||||
if (state.name) {
|
||||
// Css animations have names
|
||||
// Css animations have names.
|
||||
titleHTML += L10N.getStr("player.animationNameLabel");
|
||||
titleHTML += "<strong>" + state.name + "</strong>";
|
||||
} else {
|
||||
// Css transitions don't
|
||||
// Css transitions don't.
|
||||
titleHTML += L10N.getStr("player.transitionNameLabel");
|
||||
}
|
||||
|
||||
// Duration and iteration count
|
||||
// Duration, delay and iteration count.
|
||||
titleHTML += "<span class='meta-data'>";
|
||||
titleHTML += L10N.getStr("player.animationDurationLabel");
|
||||
titleHTML += "<strong>" + L10N.getFormatStr("player.timeLabel",
|
||||
this.getFormattedTime(state.duration)) + "</strong>";
|
||||
if (state.delay) {
|
||||
titleHTML += L10N.getStr("player.animationDelayLabel");
|
||||
titleHTML += "<strong>" + L10N.getFormatStr("player.timeLabel",
|
||||
this.getFormattedTime(state.delay)) + "</strong>";
|
||||
}
|
||||
titleHTML += L10N.getStr("player.animationIterationCountLabel");
|
||||
let count = state.iterationCount || L10N.getStr("player.infiniteIterationCount");
|
||||
titleHTML += "<strong>" + count + "</strong>";
|
||||
|
@ -215,7 +220,7 @@ PlayerWidget.prototype = {
|
|||
|
||||
titleEl.innerHTML = titleHTML;
|
||||
|
||||
// Timeline widget
|
||||
// Timeline widget.
|
||||
let timelineEl = createNode({
|
||||
parent: this.el,
|
||||
attributes: {
|
||||
|
@ -223,7 +228,7 @@ PlayerWidget.prototype = {
|
|||
}
|
||||
});
|
||||
|
||||
// Playback control buttons container
|
||||
// Playback control buttons container.
|
||||
let playbackControlsEl = createNode({
|
||||
parent: timelineEl,
|
||||
attributes: {
|
||||
|
@ -241,7 +246,7 @@ PlayerWidget.prototype = {
|
|||
}
|
||||
});
|
||||
|
||||
// Sliders container
|
||||
// Sliders container.
|
||||
let slidersContainerEl = createNode({
|
||||
parent: timelineEl,
|
||||
attributes: {
|
||||
|
@ -249,9 +254,9 @@ PlayerWidget.prototype = {
|
|||
}
|
||||
});
|
||||
|
||||
let max = state.duration; // Infinite iterations
|
||||
let max = state.duration; // Infinite iterations.
|
||||
if (state.iterationCount) {
|
||||
// Finite iterations
|
||||
// Finite iterations.
|
||||
max = state.iterationCount * state.duration;
|
||||
}
|
||||
|
||||
|
@ -267,6 +272,7 @@ PlayerWidget.prototype = {
|
|||
"min": "0",
|
||||
"max": max,
|
||||
"step": "10",
|
||||
"value": "0",
|
||||
// The currentTime isn't settable yet, so disable the timeline slider
|
||||
"disabled": "true"
|
||||
}
|
||||
|
@ -280,7 +286,7 @@ PlayerWidget.prototype = {
|
|||
}
|
||||
});
|
||||
this.timeDisplayEl.textContent = L10N.getFormatStr("player.timeLabel",
|
||||
this.getFormattedTime());
|
||||
this.getFormattedTime(0));
|
||||
|
||||
this.containerEl.appendChild(this.el);
|
||||
},
|
||||
|
@ -290,14 +296,11 @@ PlayerWidget.prototype = {
|
|||
* @param {Number} time Defaults to the player's currentTime.
|
||||
* @return {String} The formatted time, e.g. "10.55"
|
||||
*/
|
||||
getFormattedTime: function(time=this.player.state.currentTime) {
|
||||
let str = time/1000 + "";
|
||||
str = str.split(".");
|
||||
if (str.length === 1) {
|
||||
return str[0] + ".00";
|
||||
} else {
|
||||
return str[0] + "." + str[1].substring(0, 2);
|
||||
}
|
||||
getFormattedTime: function(time) {
|
||||
return (time/1000).toLocaleString(undefined, {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -390,6 +393,12 @@ PlayerWidget.prototype = {
|
|||
displayTime: function(time) {
|
||||
let state = this.player.state;
|
||||
|
||||
// If the animation is delayed, don't start displaying the time until the
|
||||
// delay has passed.
|
||||
if (state.delay) {
|
||||
time = Math.max(0, time - state.delay);
|
||||
}
|
||||
|
||||
this.timeDisplayEl.textContent = L10N.getFormatStr("player.timeLabel",
|
||||
this.getFormattedTime(time));
|
||||
if (!state.iterationCount && time !== state.duration) {
|
||||
|
|
|
@ -11,8 +11,10 @@ support-files =
|
|||
[browser_animation_play_pause_button.js]
|
||||
[browser_animation_playerFronts_are_refreshed.js]
|
||||
[browser_animation_playerWidgets_destroy.js]
|
||||
[browser_animation_playerWidgets_meta_data.js]
|
||||
[browser_animation_refresh_when_active.js]
|
||||
[browser_animation_same_nb_of_playerWidgets_and_playerFronts.js]
|
||||
[browser_animation_shows_player_on_valid_node.js]
|
||||
[browser_animation_timeline_animates.js]
|
||||
[browser_animation_timeline_waits_for_delay.js]
|
||||
[browser_animation_ui_updates_when_animation_changes.js]
|
|
@ -0,0 +1,49 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that player widgets show the right player meta-data (name, duration,
|
||||
// iteration count, delay).
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
|
||||
let {inspector, panel} = yield openAnimationInspector();
|
||||
|
||||
info("Select the simple animated node");
|
||||
yield selectNode(".animated", inspector);
|
||||
|
||||
let titleEl = panel.playerWidgets[0].el.querySelector(".animation-title");
|
||||
ok(titleEl,
|
||||
"The player widget has a title element, where meta-data should be displayed");
|
||||
|
||||
let nameEl = titleEl.querySelector("strong");
|
||||
ok(nameEl, "The first <strong> tag was retrieved, it should contain the name");
|
||||
is(nameEl.textContent, "simple-animation", "The animation name is correct");
|
||||
|
||||
let metaDataEl = titleEl.querySelector(".meta-data");
|
||||
ok(metaDataEl, "The meta-data element exists");
|
||||
|
||||
let metaDataEls = metaDataEl.querySelectorAll("strong");
|
||||
is(metaDataEls.length, 2, "2 meta-data elements were found");
|
||||
is(metaDataEls[0].textContent, "2.00s",
|
||||
"The first meta-data is the duration, and is correct");
|
||||
|
||||
info("Select the node with the delayed animation");
|
||||
yield selectNode(".delayed", inspector);
|
||||
|
||||
titleEl = panel.playerWidgets[0].el.querySelector(".animation-title");
|
||||
nameEl = titleEl.querySelector("strong");
|
||||
is(nameEl.textContent, "simple-animation", "The animation name is correct");
|
||||
|
||||
metaDataEls = titleEl.querySelectorAll(".meta-data strong");
|
||||
is(metaDataEls.length, 3,
|
||||
"3 meta-data elements were found for the delayed animation");
|
||||
is(metaDataEls[0].textContent, "3.00s",
|
||||
"The first meta-data is the duration, and is correct");
|
||||
is(metaDataEls[1].textContent, "60.00s",
|
||||
"The second meta-data is the delay, and is correct");
|
||||
is(metaDataEls[2].textContent, "10",
|
||||
"The third meta-data is the iteration count, and is correct");
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that the currentTime timeline doesn't move if the animation is currently
|
||||
// waiting for an animation-delay.
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
|
||||
let {inspector, panel} = yield openAnimationInspector();
|
||||
|
||||
info("Select the delayed animation node");
|
||||
yield selectNode(".delayed", inspector);
|
||||
|
||||
let widget = panel.playerWidgets[0];
|
||||
|
||||
let timeline = widget.currentTimeEl;
|
||||
is(timeline.value, 0, "The timeline is at 0 since the animation hasn't started");
|
||||
|
||||
let timeLabel = widget.timeDisplayEl;
|
||||
is(timeLabel.textContent, "0.00s", "The current time is 0");
|
||||
});
|
|
@ -4,8 +4,8 @@
|
|||
<meta charset="UTF-8">
|
||||
<style>
|
||||
.ball {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
background: #f06;
|
||||
|
||||
|
@ -13,25 +13,33 @@
|
|||
}
|
||||
|
||||
.still {
|
||||
top: 50px;
|
||||
left: 50px;
|
||||
top: 0;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.animated {
|
||||
top: 200px;
|
||||
left: 200px;
|
||||
top: 100px;
|
||||
left: 10px;
|
||||
|
||||
animation: simple-animation 2s infinite alternate;
|
||||
}
|
||||
|
||||
.multi {
|
||||
top: 100px;
|
||||
left: 400px;
|
||||
top: 200px;
|
||||
left: 10px;
|
||||
|
||||
animation: simple-animation 2s infinite alternate,
|
||||
other-animation 5s infinite alternate;
|
||||
}
|
||||
|
||||
.delayed {
|
||||
top: 300px;
|
||||
left: 10px;
|
||||
background: rebeccapurple;
|
||||
|
||||
animation: simple-animation 3s 60s 10;
|
||||
}
|
||||
|
||||
@keyframes simple-animation {
|
||||
100% {
|
||||
transform: translateX(300px);
|
||||
|
@ -50,5 +58,6 @@
|
|||
<div class="ball still"></div>
|
||||
<div class="ball animated"></div>
|
||||
<div class="ball multi"></div>
|
||||
<div class="ball delayed"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -30,6 +30,7 @@ support-files =
|
|||
[browser_canvas-frontend-img-thumbnails-01.js]
|
||||
[browser_canvas-frontend-img-thumbnails-02.js]
|
||||
[browser_canvas-frontend-open.js]
|
||||
skip-if = e10s # bug 1102301 - leaks while running as a standalone directory in e10s mode
|
||||
[browser_canvas-frontend-record-01.js]
|
||||
[browser_canvas-frontend-record-02.js]
|
||||
[browser_canvas-frontend-record-03.js]
|
||||
|
|
|
@ -118,7 +118,7 @@ skip-if = e10s && debug
|
|||
[browser_dbg_auto-pretty-print-02.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_bfcache.js]
|
||||
skip-if = e10s # TODO
|
||||
skip-if = e10s || true # bug 1113935
|
||||
[browser_dbg_blackboxing-01.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_blackboxing-02.js]
|
||||
|
|
|
@ -12,24 +12,19 @@ const TAB_URL_2 = EXAMPLE_URL + "doc_recursion-stack.html";
|
|||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gSources;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL_1).then(([aTab, aDebuggee, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
const test = Task.async(function* () {
|
||||
info("Starting browser_dbg_bfcache.js's `test`.");
|
||||
|
||||
testFirstPage()
|
||||
.then(testLocationChange)
|
||||
.then(testBack)
|
||||
.then(testForward)
|
||||
.then(() => closeDebuggerAndFinish(gPanel))
|
||||
.then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
});
|
||||
}
|
||||
([gTab, gDebuggee, gPanel]) = yield initDebugger(TAB_URL_1);
|
||||
gDebugger = gPanel.panelWin;
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
|
||||
yield testFirstPage();
|
||||
yield testLocationChange();
|
||||
yield testBack();
|
||||
yield testForward();
|
||||
return closeDebuggerAndFinish(gPanel);
|
||||
});
|
||||
|
||||
function testFirstPage() {
|
||||
info("Testing first page.");
|
||||
|
@ -38,33 +33,35 @@ function testFirstPage() {
|
|||
// this function to return first.
|
||||
executeSoon(() => gDebuggee.firstCall());
|
||||
|
||||
return waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1).then(() => {
|
||||
validateFirstPage();
|
||||
});
|
||||
return waitForSourceAndCaretAndScopes(gPanel, "-02.js", 1)
|
||||
.then(validateFirstPage);
|
||||
}
|
||||
|
||||
function testLocationChange() {
|
||||
info("Navigating to a different page.");
|
||||
|
||||
return navigateActiveTabTo(gPanel, TAB_URL_2, gDebugger.EVENTS.SOURCES_ADDED).then(() => {
|
||||
validateSecondPage();
|
||||
});
|
||||
return navigateActiveTabTo(gPanel,
|
||||
TAB_URL_2,
|
||||
gDebugger.EVENTS.SOURCES_ADDED)
|
||||
.then(validateSecondPage);
|
||||
}
|
||||
|
||||
function testBack() {
|
||||
info("Going back.");
|
||||
|
||||
return navigateActiveTabInHistory(gPanel, "back", gDebugger.EVENTS.SOURCES_ADDED).then(() => {
|
||||
validateFirstPage();
|
||||
});
|
||||
return navigateActiveTabInHistory(gPanel,
|
||||
"back",
|
||||
gDebugger.EVENTS.SOURCES_ADDED)
|
||||
.then(validateFirstPage);
|
||||
}
|
||||
|
||||
function testForward() {
|
||||
info("Going forward.");
|
||||
|
||||
return navigateActiveTabInHistory(gPanel, "forward", gDebugger.EVENTS.SOURCES_ADDED).then(() => {
|
||||
validateSecondPage();
|
||||
});
|
||||
return navigateActiveTabInHistory(gPanel,
|
||||
"forward",
|
||||
gDebugger.EVENTS.SOURCES_ADDED)
|
||||
.then(validateSecondPage);
|
||||
}
|
||||
|
||||
function validateFirstPage() {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
const {Cu} = require("chrome");
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
|
@ -18,6 +19,23 @@ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
|||
/**
|
||||
* ToolSidebar provides methods to register tabs in the sidebar.
|
||||
* It's assumed that the sidebar contains a xul:tabbox.
|
||||
* Typically, you'll want the tabbox parameter to be a XUL tabbox like this:
|
||||
*
|
||||
* <tabbox id="inspector-sidebar" handleCtrlTab="false" class="devtools-sidebar-tabs">
|
||||
* <tabs/>
|
||||
* <tabpanels flex="1"/>
|
||||
* </tabbox>
|
||||
*
|
||||
* The ToolSidebar API has a method to add new tabs, so the tabs and tabpanels
|
||||
* nodes can be empty. But they can also already contain items before the
|
||||
* ToolSidebar is created.
|
||||
*
|
||||
* Tabs added through the addTab method are only identified by an ID and a URL
|
||||
* which is used as the href of an iframe node that is inserted in the newly
|
||||
* created tabpanel.
|
||||
* Tabs already present before the ToolSidebar is created may contain anything.
|
||||
* However, these tabs must have ID attributes if it is required for the various
|
||||
* methods that accept an ID as argument to work here.
|
||||
*
|
||||
* @param {Node} tabbox
|
||||
* <tabbox> node;
|
||||
|
@ -25,38 +43,168 @@ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
|||
* Related ToolPanel instance;
|
||||
* @param {String} uid
|
||||
* Unique ID
|
||||
* @param {Boolean} showTabstripe
|
||||
* Show the tabs.
|
||||
* @param {Object} options
|
||||
* - hideTabstripe: Should the tabs be hidden. Defaults to false
|
||||
* - showAllTabsMenu: Should a drop-down menu be displayed in case tabs
|
||||
* become hidden. Defaults to false.
|
||||
* - disableTelemetry: By default, switching tabs on and off in the sidebar
|
||||
* will record tool usage in telemetry, pass this option to true to avoid it.
|
||||
*
|
||||
* Events raised:
|
||||
* - new-tab-registered : After a tab has been added via addTab. The tab ID
|
||||
* is passed with the event. This however, is raised before the tab iframe
|
||||
* is fully loaded.
|
||||
* - <tabid>-ready : After the tab iframe has been loaded
|
||||
* - <tabid>-selected : After tab <tabid> was selected
|
||||
* - select : Same as above, but for any tab, the ID is passed with the event
|
||||
* - <tabid>-unselected : After tab <tabid> is unselected
|
||||
*/
|
||||
function ToolSidebar(tabbox, panel, uid, showTabstripe=true)
|
||||
{
|
||||
function ToolSidebar(tabbox, panel, uid, options={}) {
|
||||
EventEmitter.decorate(this);
|
||||
|
||||
this._tabbox = tabbox;
|
||||
this._uid = uid;
|
||||
this._panelDoc = this._tabbox.ownerDocument;
|
||||
this._toolPanel = panel;
|
||||
this._options = options;
|
||||
|
||||
this._onTabBoxOverflow = this._onTabBoxOverflow.bind(this);
|
||||
this._onTabBoxUnderflow = this._onTabBoxUnderflow.bind(this);
|
||||
|
||||
try {
|
||||
this._width = Services.prefs.getIntPref("devtools.toolsidebar-width." + this._uid);
|
||||
} catch(e) {}
|
||||
|
||||
this._telemetry = new Telemetry();
|
||||
if (!options.disableTelemetry) {
|
||||
this._telemetry = new Telemetry();
|
||||
}
|
||||
|
||||
this._tabbox.tabpanels.addEventListener("select", this, true);
|
||||
|
||||
this._tabs = new Map();
|
||||
|
||||
if (!showTabstripe) {
|
||||
// Check for existing tabs in the DOM and add them.
|
||||
this.addExistingTabs();
|
||||
|
||||
if (this._options.hideTabstripe) {
|
||||
this._tabbox.setAttribute("hidetabs", "true");
|
||||
}
|
||||
|
||||
if (this._options.showAllTabsMenu) {
|
||||
this.addAllTabsMenu();
|
||||
}
|
||||
|
||||
this._toolPanel.emit("sidebar-created", this);
|
||||
}
|
||||
|
||||
exports.ToolSidebar = ToolSidebar;
|
||||
|
||||
ToolSidebar.prototype = {
|
||||
TAB_ID_PREFIX: "sidebar-tab-",
|
||||
|
||||
TABPANEL_ID_PREFIX: "sidebar-panel-",
|
||||
|
||||
/**
|
||||
* Add a "…" button at the end of the tabstripe that toggles a dropdown menu
|
||||
* containing the list of all tabs if any become hidden due to lack of room.
|
||||
*
|
||||
* If the ToolSidebar was created with the "showAllTabsMenu" option set to
|
||||
* true, this is already done automatically. If not, you may call this
|
||||
* function at any time to add the menu.
|
||||
*/
|
||||
addAllTabsMenu: function() {
|
||||
if (this._allTabsBtn) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tabs = this._tabbox.tabs;
|
||||
|
||||
// Create a toolbar and insert it first in the tabbox
|
||||
let allTabsToolbar = this._panelDoc.createElementNS(XULNS, "toolbar");
|
||||
this._tabbox.insertBefore(allTabsToolbar, tabs);
|
||||
|
||||
// Move the tabs inside and make them flex
|
||||
allTabsToolbar.appendChild(tabs);
|
||||
tabs.setAttribute("flex", "1");
|
||||
|
||||
// Create the dropdown menu next to the tabs
|
||||
this._allTabsBtn = this._panelDoc.createElementNS(XULNS, "toolbarbutton");
|
||||
this._allTabsBtn.setAttribute("class", "devtools-sidebar-alltabs");
|
||||
this._allTabsBtn.setAttribute("type", "menu");
|
||||
this._allTabsBtn.setAttribute("label", l10n("sidebar.showAllTabs.label"));
|
||||
this._allTabsBtn.setAttribute("tooltiptext", l10n("sidebar.showAllTabs.tooltip"));
|
||||
this._allTabsBtn.setAttribute("hidden", "true");
|
||||
allTabsToolbar.appendChild(this._allTabsBtn);
|
||||
|
||||
let menuPopup = this._panelDoc.createElementNS(XULNS, "menupopup");
|
||||
this._allTabsBtn.appendChild(menuPopup);
|
||||
|
||||
// Listening to tabs overflow event to toggle the alltabs button
|
||||
tabs.addEventListener("overflow", this._onTabBoxOverflow, false);
|
||||
tabs.addEventListener("underflow", this._onTabBoxUnderflow, false);
|
||||
|
||||
// Add menuitems to the alltabs menu if there are already tabs in the
|
||||
// sidebar
|
||||
for (let [id, tab] of this._tabs) {
|
||||
this._addItemToAllTabsMenu(id, tab, tab.hasAttribute("selected"));
|
||||
}
|
||||
},
|
||||
|
||||
removeAllTabsMenu: function() {
|
||||
if (!this._allTabsBtn) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tabs = this._tabbox.tabs;
|
||||
|
||||
tabs.removeEventListener("overflow", this._onTabBoxOverflow, false);
|
||||
tabs.removeEventListener("underflow", this._onTabBoxUnderflow, false);
|
||||
|
||||
// Moving back the tabs as a first child of the tabbox
|
||||
this._tabbox.insertBefore(tabs, this._tabbox.tabpanels);
|
||||
this._tabbox.querySelector("toolbar").remove();
|
||||
|
||||
this._allTabsBtn = null;
|
||||
},
|
||||
|
||||
_onTabBoxOverflow: function() {
|
||||
this._allTabsBtn.removeAttribute("hidden");
|
||||
},
|
||||
|
||||
_onTabBoxUnderflow: function() {
|
||||
this._allTabsBtn.setAttribute("hidden", "true");
|
||||
},
|
||||
|
||||
/**
|
||||
* Add an item in the allTabs menu for a given tab.
|
||||
*/
|
||||
_addItemToAllTabsMenu: function(id, tab, selected=false) {
|
||||
if (!this._allTabsBtn) {
|
||||
return;
|
||||
}
|
||||
|
||||
let item = this._panelDoc.createElementNS(XULNS, "menuitem");
|
||||
item.setAttribute("id", "sidebar-alltabs-item-" + id);
|
||||
item.setAttribute("label", tab.getAttribute("label"));
|
||||
item.setAttribute("type", "checkbox");
|
||||
if (selected) {
|
||||
item.setAttribute("checked", true);
|
||||
}
|
||||
// The auto-checking of menuitems in this menu doesn't work, so let's do
|
||||
// it manually
|
||||
item.setAttribute("autocheck", false);
|
||||
|
||||
this._allTabsBtn.querySelector("menupopup").appendChild(item);
|
||||
|
||||
item.addEventListener("click", () => {
|
||||
this._tabbox.selectedTab = tab;
|
||||
}, false);
|
||||
|
||||
tab.allTabsMenuItem = item;
|
||||
|
||||
return item;
|
||||
},
|
||||
|
||||
/**
|
||||
* Register a tab. A tab is a document.
|
||||
* The document must have a title, which will be used as the name of the tab.
|
||||
|
@ -64,22 +212,31 @@ ToolSidebar.prototype = {
|
|||
* @param {string} tab uniq id
|
||||
* @param {string} url
|
||||
*/
|
||||
addTab: function ToolSidebar_addTab(id, url, selected=false) {
|
||||
addTab: function(id, url, selected=false) {
|
||||
let iframe = this._panelDoc.createElementNS(XULNS, "iframe");
|
||||
iframe.className = "iframe-" + id;
|
||||
iframe.setAttribute("flex", "1");
|
||||
iframe.setAttribute("src", url);
|
||||
iframe.tooltip = "aHTMLTooltip";
|
||||
|
||||
let tab = this._tabbox.tabs.appendItem();
|
||||
// Creating the tab and adding it to the tabbox
|
||||
let tab = this._panelDoc.createElementNS(XULNS, "tab");
|
||||
this._tabbox.tabs.appendChild(tab);
|
||||
tab.setAttribute("label", ""); // Avoid showing "undefined" while the tab is loading
|
||||
tab.setAttribute("id", "sidebar-tab-" + id);
|
||||
tab.setAttribute("id", this.TAB_ID_PREFIX + id);
|
||||
|
||||
// Add the tab to the allTabs menu if exists
|
||||
let allTabsItem = this._addItemToAllTabsMenu(id, tab, selected);
|
||||
|
||||
let onIFrameLoaded = (event) => {
|
||||
let doc = event.target;
|
||||
let win = doc.defaultView;
|
||||
tab.setAttribute("label", doc.title);
|
||||
|
||||
if (allTabsItem) {
|
||||
allTabsItem.setAttribute("label", doc.title);
|
||||
}
|
||||
|
||||
iframe.removeEventListener("load", onIFrameLoaded, true);
|
||||
if ("setPanel" in win) {
|
||||
win.setPanel(this._toolPanel, iframe);
|
||||
|
@ -90,7 +247,7 @@ ToolSidebar.prototype = {
|
|||
iframe.addEventListener("load", onIFrameLoaded, true);
|
||||
|
||||
let tabpanel = this._panelDoc.createElementNS(XULNS, "tabpanel");
|
||||
tabpanel.setAttribute("id", "sidebar-panel-" + id);
|
||||
tabpanel.setAttribute("id", this.TABPANEL_ID_PREFIX + id);
|
||||
tabpanel.appendChild(iframe);
|
||||
this._tabbox.tabpanels.appendChild(tabpanel);
|
||||
|
||||
|
@ -99,7 +256,7 @@ ToolSidebar.prototype = {
|
|||
tabpanel.appendChild(this._tooltip);
|
||||
this._tooltip.page = true;
|
||||
|
||||
tab.linkedPanel = "sidebar-panel-" + id;
|
||||
tab.linkedPanel = this.TABPANEL_ID_PREFIX + id;
|
||||
|
||||
// We store the index of this tab.
|
||||
this._tabs.set(id, tab);
|
||||
|
@ -116,37 +273,78 @@ ToolSidebar.prototype = {
|
|||
this.emit("new-tab-registered", id);
|
||||
},
|
||||
|
||||
untitledTabsIndex: 0,
|
||||
|
||||
/**
|
||||
* Search for existing tabs in the markup that aren't know yet and add them.
|
||||
*/
|
||||
addExistingTabs: function() {
|
||||
let knownTabs = [...this._tabs.values()];
|
||||
|
||||
for (let tab of this._tabbox.tabs.querySelectorAll("tab")) {
|
||||
if (knownTabs.indexOf(tab) !== -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find an ID for this unknown tab
|
||||
let id = tab.getAttribute("id") || "untitled-tab-" + (this.untitledTabsIndex++);
|
||||
|
||||
// Register the tab
|
||||
this._tabs.set(id, tab);
|
||||
this.emit("new-tab-registered", id);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove an existing tab.
|
||||
* @param {String} tabId The ID of the tab that was used to register it, or
|
||||
* the tab id attribute value if the tab existed before the sidebar got created.
|
||||
* @param {String} tabPanelId Optional. If provided, this ID will be used
|
||||
* instead of the tabId to retrieve and remove the corresponding <tabpanel>
|
||||
*/
|
||||
removeTab: Task.async(function*(id) {
|
||||
let tab = this._tabbox.tabs.querySelector("tab#sidebar-tab-" + id);
|
||||
removeTab: Task.async(function*(tabId, tabPanelId) {
|
||||
// Remove the tab if it can be found
|
||||
let tab = this.getTab(tabId);
|
||||
if (!tab) {
|
||||
return;
|
||||
}
|
||||
|
||||
let win = this.getWindowForTab(id);
|
||||
if ("destroy" in win) {
|
||||
let win = this.getWindowForTab(tabId);
|
||||
if (win && ("destroy" in win)) {
|
||||
yield win.destroy();
|
||||
}
|
||||
|
||||
tab.remove();
|
||||
|
||||
let panel = this.getTab(id);
|
||||
// Also remove the tabpanel
|
||||
let panel = this.getTabPanel(tabPanelId || tabId);
|
||||
if (panel) {
|
||||
panel.remove();
|
||||
}
|
||||
|
||||
this._tabs.delete(id);
|
||||
|
||||
this.emit("tab-unregistered", id);
|
||||
this._tabs.delete(tabId);
|
||||
this.emit("tab-unregistered", tabId);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Show or hide a specific tab
|
||||
*/
|
||||
toggleTab: function(id, isVisible) {
|
||||
let tab = this.getTab(id);
|
||||
if (!tab) {
|
||||
return;
|
||||
}
|
||||
tab.hidden = !isVisible;
|
||||
if (this._allTabsBtn) {
|
||||
this._allTabsBtn.querySelector("#sidebar-alltabs-item-" + id).hidden = !isVisible;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Select a specific tab.
|
||||
*/
|
||||
select: function ToolSidebar_select(id) {
|
||||
let tab = this._tabs.get(id);
|
||||
select: function(id) {
|
||||
let tab = this.getTab(id);
|
||||
if (tab) {
|
||||
this._tabbox.selectedTab = tab;
|
||||
}
|
||||
|
@ -155,7 +353,7 @@ ToolSidebar.prototype = {
|
|||
/**
|
||||
* Return the id of the selected tab.
|
||||
*/
|
||||
getCurrentTabID: function ToolSidebar_getCurrentTabID() {
|
||||
getCurrentTabID: function() {
|
||||
let currentID = null;
|
||||
for (let [id, tab] of this._tabs) {
|
||||
if (this._tabbox.tabs.selectedItem == tab) {
|
||||
|
@ -167,42 +365,75 @@ ToolSidebar.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Returns the requested tab based on the id.
|
||||
*
|
||||
* @param String id
|
||||
* unique id of the requested tab.
|
||||
* Returns the requested tab panel based on the id.
|
||||
* @param {String} id
|
||||
* @return {DOMNode}
|
||||
*/
|
||||
getTab: function ToolSidebar_getTab(id) {
|
||||
return this._tabbox.tabpanels.querySelector("#sidebar-panel-" + id);
|
||||
getTabPanel: function(id) {
|
||||
// Search with and without the ID prefix as there might have been existing
|
||||
// tabpanels by the time the sidebar got created
|
||||
return this._tabbox.tabpanels.querySelector("#" + this.TABPANEL_ID_PREFIX + id + ", #" + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the tab based on the provided id, if one was registered with this id.
|
||||
* @param {String} id
|
||||
* @return {DOMNode}
|
||||
*/
|
||||
getTab: function(id) {
|
||||
return this._tabs.get(id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler.
|
||||
*/
|
||||
handleEvent: function ToolSidebar_eventHandler(event) {
|
||||
if (event.type == "select") {
|
||||
if (this._currentTool == this.getCurrentTabID()) {
|
||||
// Tool hasn't changed.
|
||||
return;
|
||||
}
|
||||
handleEvent: function(event) {
|
||||
if (event.type !== "select" || this._destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
let previousTool = this._currentTool;
|
||||
this._currentTool = this.getCurrentTabID();
|
||||
if (previousTool) {
|
||||
if (this._currentTool == this.getCurrentTabID()) {
|
||||
// Tool hasn't changed.
|
||||
return;
|
||||
}
|
||||
|
||||
let previousTool = this._currentTool;
|
||||
this._currentTool = this.getCurrentTabID();
|
||||
if (previousTool) {
|
||||
if (this._telemetry) {
|
||||
this._telemetry.toolClosed(previousTool);
|
||||
this.emit(previousTool + "-unselected");
|
||||
}
|
||||
this.emit(previousTool + "-unselected");
|
||||
}
|
||||
|
||||
if (this._telemetry) {
|
||||
this._telemetry.toolOpened(this._currentTool);
|
||||
this.emit(this._currentTool + "-selected");
|
||||
this.emit("select", this._currentTool);
|
||||
}
|
||||
|
||||
this.emit(this._currentTool + "-selected");
|
||||
this.emit("select", this._currentTool);
|
||||
|
||||
// Handlers for "select"/"...-selected"/"...-unselected" events might have
|
||||
// destroyed the sidebar in the meantime.
|
||||
if (this._destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle menuitem selection if the allTabsMenu is there by unchecking all
|
||||
// items except the selected one.
|
||||
let tab = this._tabbox.selectedTab;
|
||||
if (tab.allTabsMenuItem) {
|
||||
for (let otherItem of this._allTabsBtn.querySelectorAll("menuitem")) {
|
||||
otherItem.removeAttribute("checked");
|
||||
}
|
||||
tab.allTabsMenuItem.setAttribute("checked", true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle sidebar's visibility state.
|
||||
*/
|
||||
toggle: function ToolSidebar_toggle() {
|
||||
toggle: function() {
|
||||
if (this._tabbox.hasAttribute("hidden")) {
|
||||
this.show();
|
||||
} else {
|
||||
|
@ -213,7 +444,7 @@ ToolSidebar.prototype = {
|
|||
/**
|
||||
* Show the sidebar.
|
||||
*/
|
||||
show: function ToolSidebar_show() {
|
||||
show: function() {
|
||||
if (this._width) {
|
||||
this._tabbox.width = this._width;
|
||||
}
|
||||
|
@ -225,7 +456,7 @@ ToolSidebar.prototype = {
|
|||
/**
|
||||
* Show the sidebar.
|
||||
*/
|
||||
hide: function ToolSidebar_hide() {
|
||||
hide: function() {
|
||||
Services.prefs.setIntPref("devtools.toolsidebar-width." + this._uid, this._tabbox.width);
|
||||
this._tabbox.setAttribute("hidden", "true");
|
||||
|
||||
|
@ -235,12 +466,16 @@ ToolSidebar.prototype = {
|
|||
/**
|
||||
* Return the window containing the tab content.
|
||||
*/
|
||||
getWindowForTab: function ToolSidebar_getWindowForTab(id) {
|
||||
getWindowForTab: function(id) {
|
||||
if (!this._tabs.has(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let panel = this._panelDoc.getElementById(this._tabs.get(id).linkedPanel);
|
||||
// Get the tabpanel and make sure it contains an iframe
|
||||
let panel = this.getTabPanel(id);
|
||||
if (!panel || !panel.firstChild || !panel.firstChild.contentWindow) {
|
||||
return;
|
||||
}
|
||||
return panel.firstChild.contentWindow;
|
||||
},
|
||||
|
||||
|
@ -249,12 +484,16 @@ ToolSidebar.prototype = {
|
|||
*/
|
||||
destroy: Task.async(function*() {
|
||||
if (this._destroyed) {
|
||||
return promise.resolve(null);
|
||||
return;
|
||||
}
|
||||
this._destroyed = true;
|
||||
|
||||
Services.prefs.setIntPref("devtools.toolsidebar-width." + this._uid, this._tabbox.width);
|
||||
|
||||
if (this._allTabsBtn) {
|
||||
this.removeAllTabsMenu();
|
||||
}
|
||||
|
||||
this._tabbox.tabpanels.removeEventListener("select", this, true);
|
||||
|
||||
// Note that we check for the existence of this._tabbox.tabpanels at each
|
||||
|
@ -263,7 +502,7 @@ ToolSidebar.prototype = {
|
|||
while (this._tabbox.tabpanels && this._tabbox.tabpanels.hasChildNodes()) {
|
||||
let panel = this._tabbox.tabpanels.firstChild;
|
||||
let win = panel.firstChild.contentWindow;
|
||||
if ("destroy" in win) {
|
||||
if (win && ("destroy" in win)) {
|
||||
yield win.destroy();
|
||||
}
|
||||
panel.remove();
|
||||
|
@ -273,7 +512,7 @@ ToolSidebar.prototype = {
|
|||
this._tabbox.tabs.removeChild(this._tabbox.tabs.firstChild);
|
||||
}
|
||||
|
||||
if (this._currentTool) {
|
||||
if (this._currentTool && this._telemetry) {
|
||||
this._telemetry.toolClosed(this._currentTool);
|
||||
}
|
||||
|
||||
|
@ -283,7 +522,21 @@ ToolSidebar.prototype = {
|
|||
this._tabbox = null;
|
||||
this._panelDoc = null;
|
||||
this._toolPanel = null;
|
||||
|
||||
return promise.resolve(null);
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "l10n", function() {
|
||||
let bundle = Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties");
|
||||
let l10n = function(aName, ...aArgs) {
|
||||
try {
|
||||
if (aArgs.length == 0) {
|
||||
return bundle.GetStringFromName(aName);
|
||||
} else {
|
||||
return bundle.formatStringFromName(aName, aArgs, aArgs.length);
|
||||
}
|
||||
} catch (ex) {
|
||||
Services.console.logStringMessage("Error reading '" + aName + "'");
|
||||
}
|
||||
};
|
||||
return l10n;
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@ support-files =
|
|||
browser_toolbox_options_disable_js.html
|
||||
browser_toolbox_options_disable_js_iframe.html
|
||||
browser_toolbox_options_disable_cache.sjs
|
||||
browser_toolbox_sidebar_tool.xul
|
||||
head.js
|
||||
helper_disable_cache.js
|
||||
doc_theme.css
|
||||
|
@ -39,6 +40,8 @@ skip-if = e10s # Bug 1070837 - devtools/framework/toolbox.js |doc| getter not e1
|
|||
skip-if = e10s # Bug 1069044 - destroyInspector may hang during shutdown
|
||||
[browser_toolbox_sidebar.js]
|
||||
[browser_toolbox_sidebar_events.js]
|
||||
[browser_toolbox_sidebar_existing_tabs.js]
|
||||
[browser_toolbox_sidebar_overflow_menu.js]
|
||||
[browser_toolbox_tabsswitch_shortcuts.js]
|
||||
[browser_toolbox_tool_ready.js]
|
||||
[browser_toolbox_tool_remote_reopen.js]
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
|
||||
// Tests that disabling JavaScript for a tab works as it should.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/framework/" +
|
||||
"test/browser_toolbox_options_disable_js.html";
|
||||
const TEST_URI = URL_ROOT + "browser_toolbox_options_disable_js.html";
|
||||
|
||||
let doc;
|
||||
let toolbox;
|
||||
|
|
|
@ -111,7 +111,7 @@ function test() {
|
|||
|
||||
is(label, 4, "Found the right amount of tabs.");
|
||||
is(panel.sidebar._tabbox.selectedPanel, panels[0], "First tab is selected");
|
||||
ok(panel.sidebar.getCurrentTabID(), "tab1", "getCurrentTabID() is correct");
|
||||
is(panel.sidebar.getCurrentTabID(), "tab1", "getCurrentTabID() is correct");
|
||||
|
||||
panel.sidebar.once("tab1-unselected", function() {
|
||||
ok(true, "received 'unselected' event");
|
||||
|
@ -154,6 +154,7 @@ function test() {
|
|||
panel.sidebar = new ToolSidebar(tabbox, panel, "testbug865688", true);
|
||||
panel.sidebar.show();
|
||||
is(panel.panelDoc.getElementById("sidebar").width, 420, "Width restored")
|
||||
|
||||
finishUp(panel);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test that the sidebar widget auto-registers existing tabs.
|
||||
|
||||
const Cu = Components.utils;
|
||||
const {ToolSidebar} = devtools.require("devtools/framework/sidebar");
|
||||
|
||||
const testToolURL = "data:text/xml;charset=utf8,<?xml version='1.0'?>" +
|
||||
"<?xml-stylesheet href='chrome://browser/skin/devtools/common.css' type='text/css'?>" +
|
||||
"<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'>" +
|
||||
"<hbox flex='1'><description flex='1'>test tool</description>" +
|
||||
"<splitter class='devtools-side-splitter'/>" +
|
||||
"<tabbox flex='1' id='sidebar' class='devtools-sidebar-tabs'>" +
|
||||
"<tabs><tab id='tab1' label='tab 1'></tab><tab id='tab2' label='tab 2'></tab></tabs>" +
|
||||
"<tabpanels flex='1'><tabpanel id='tabpanel1'>tab 1</tabpanel><tabpanel id='tabpanel2'>tab 2</tabpanel></tabpanels>" +
|
||||
"</tabbox></hbox></window>";
|
||||
|
||||
const testToolDefinition = {
|
||||
id: "testTool",
|
||||
url: testToolURL,
|
||||
label: "Test Tool",
|
||||
isTargetSupported: () => true,
|
||||
build: (iframeWindow, toolbox) => {
|
||||
return promise.resolve({
|
||||
target: toolbox.target,
|
||||
toolbox: toolbox,
|
||||
isReady: true,
|
||||
destroy: () => {},
|
||||
panelDoc: iframeWindow.document,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
add_task(function*() {
|
||||
let tab = yield addTab("about:blank");
|
||||
|
||||
let target = TargetFactory.forTab(tab);
|
||||
|
||||
gDevTools.registerTool(testToolDefinition);
|
||||
let toolbox = yield gDevTools.showToolbox(target, testToolDefinition.id);
|
||||
|
||||
let toolPanel = toolbox.getPanel(testToolDefinition.id);
|
||||
let tabbox = toolPanel.panelDoc.getElementById("sidebar");
|
||||
|
||||
info("Creating the sidebar widget");
|
||||
let sidebar = new ToolSidebar(tabbox, toolPanel, "bug1101569");
|
||||
|
||||
info("Checking that existing tabs have been registered");
|
||||
ok(sidebar.getTab("tab1"), "Existing tab 1 was found");
|
||||
ok(sidebar.getTab("tab2"), "Existing tab 2 was found");
|
||||
ok(sidebar.getTabPanel("tabpanel1"), "Existing tabpanel 1 was found");
|
||||
ok(sidebar.getTabPanel("tabpanel2"), "Existing tabpanel 2 was found");
|
||||
|
||||
info("Checking that the sidebar API works with existing tabs");
|
||||
|
||||
sidebar.select("tab2");
|
||||
is(tabbox.selectedTab, tabbox.querySelector("#tab2"),
|
||||
"Existing tabs can be selected");
|
||||
|
||||
sidebar.select("tab1");
|
||||
is(tabbox.selectedTab, tabbox.querySelector("#tab1"),
|
||||
"Existing tabs can be selected");
|
||||
|
||||
is(sidebar.getCurrentTabID(), "tab1", "getCurrentTabID returns the expected id");
|
||||
|
||||
info("Removing a tab");
|
||||
sidebar.removeTab("tab2", "tabpanel2");
|
||||
ok(!sidebar.getTab("tab2"), "Tab 2 was removed correctly");
|
||||
ok(!sidebar.getTabPanel("tabpanel2"), "Tabpanel 2 was removed correctly");
|
||||
|
||||
sidebar.destroy();
|
||||
gDevTools.unregisterTool(testToolDefinition.id);
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
|
@ -0,0 +1,76 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test that the sidebar widget correctly displays the "all tabs..." button
|
||||
// when the tabs overflow.
|
||||
|
||||
const {ToolSidebar} = devtools.require("devtools/framework/sidebar");
|
||||
|
||||
const testToolDefinition = {
|
||||
id: "testTool",
|
||||
url: CHROME_URL_ROOT + "browser_toolbox_sidebar_tool.xul",
|
||||
label: "Test Tool",
|
||||
isTargetSupported: () => true,
|
||||
build: (iframeWindow, toolbox) => {
|
||||
return {
|
||||
target: toolbox.target,
|
||||
toolbox: toolbox,
|
||||
isReady: true,
|
||||
destroy: () => {},
|
||||
panelDoc: iframeWindow.document,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
add_task(function*() {
|
||||
let tab = yield addTab("about:blank");
|
||||
let target = TargetFactory.forTab(tab);
|
||||
|
||||
gDevTools.registerTool(testToolDefinition);
|
||||
let toolbox = yield gDevTools.showToolbox(target, testToolDefinition.id);
|
||||
|
||||
let toolPanel = toolbox.getPanel(testToolDefinition.id);
|
||||
let tabbox = toolPanel.panelDoc.getElementById("sidebar");
|
||||
|
||||
info("Creating the sidebar widget");
|
||||
let sidebar = new ToolSidebar(tabbox, toolPanel, "bug1101569", {
|
||||
showAllTabsMenu: true
|
||||
});
|
||||
|
||||
let allTabsMenu = toolPanel.panelDoc.querySelector(".devtools-sidebar-alltabs");
|
||||
ok(allTabsMenu, "The all-tabs menu is available");
|
||||
is(allTabsMenu.getAttribute("hidden"), "true", "The menu is hidden for now");
|
||||
|
||||
info("Adding 10 tabs to the sidebar widget");
|
||||
for (let nb = 0; nb < 10; nb ++) {
|
||||
let url = `data:text/html;charset=utf8,<title>tab ${nb}</title><p>Test tab ${nb}</p>`;
|
||||
sidebar.addTab("tab" + nb, url, nb === 0);
|
||||
}
|
||||
|
||||
info("Fake an overflow event so that the all-tabs menu is visible");
|
||||
sidebar._onTabBoxOverflow();
|
||||
ok(!allTabsMenu.hasAttribute("hidden"), "The all-tabs menu is now shown");
|
||||
|
||||
info("Select each tab, one by one");
|
||||
for (let nb = 0; nb < 10; nb ++) {
|
||||
let id = "tab" + nb;
|
||||
|
||||
info("Found tab item nb " + nb);
|
||||
let item = allTabsMenu.querySelector("#sidebar-alltabs-item-" + id);
|
||||
|
||||
info("Click on the tab");
|
||||
EventUtils.sendMouseEvent({type: "click"}, item, toolPanel.panelDoc.defaultView);
|
||||
|
||||
is(tabbox.selectedTab.id, "sidebar-tab-" + id,
|
||||
"The selected tab is now nb " + nb);
|
||||
}
|
||||
|
||||
info("Fake an underflow event so that the all-tabs menu gets hidden");
|
||||
sidebar._onTabBoxUnderflow();
|
||||
is(allTabsMenu.getAttribute("hidden"), "true", "The all-tabs menu is hidden");
|
||||
|
||||
yield sidebar.destroy();
|
||||
gDevTools.unregisterTool(testToolDefinition.id);
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- 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/. -->
|
||||
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/devtools/widgets.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/devtools/widgets.css" type="text/css"?>
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript;version=1.8" src="chrome://browser/content/devtools/theme-switching.js"/>
|
||||
<box flex="1" class="devtools-responsive-container theme-body">
|
||||
<vbox flex="1" class="devtools-main-content" id="content">test</vbox>
|
||||
<splitter class="devtools-side-splitter"/>
|
||||
<tabbox flex="1" id="sidebar" class="devtools-sidebar-tabs">
|
||||
<tabs/>
|
||||
<tabpanels flex="1"/>
|
||||
</tabbox>
|
||||
</box>
|
||||
</window>
|
|
@ -8,6 +8,9 @@ const { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {})
|
|||
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
|
||||
const URL_ROOT = "http://example.com/browser/browser/devtools/framework/test/";
|
||||
const CHROME_URL_ROOT = "chrome://mochitests/content/browser/browser/devtools/framework/test/";
|
||||
|
||||
let TargetFactory = devtools.TargetFactory;
|
||||
|
||||
// All test are asynchronous
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
"use strict";
|
||||
|
||||
// Common code shared by browser_toolbox_options_disable_cache-*.js
|
||||
const TEST_URI = "http://mochi.test:8888/browser/browser/devtools/framework/" +
|
||||
"test/browser_toolbox_options_disable_cache.sjs";
|
||||
const TEST_URI = URL_ROOT + "browser_toolbox_options_disable_cache.sjs";
|
||||
let tabs = [
|
||||
{
|
||||
title: "Tab 0",
|
||||
|
|
|
@ -347,8 +347,6 @@ InspectorPanel.prototype = {
|
|||
"animationinspector" == defaultTab);
|
||||
}
|
||||
|
||||
let ruleViewTab = this.sidebar.getTab("ruleview");
|
||||
|
||||
this.sidebar.show();
|
||||
},
|
||||
|
||||
|
|
|
@ -2129,7 +2129,7 @@ function ElementEditor(aContainer, aNode) {
|
|||
}
|
||||
|
||||
// Make the new attribute space editable.
|
||||
editableField({
|
||||
this.newAttr.editMode = editableField({
|
||||
element: this.newAttr,
|
||||
trigger: "dblclick",
|
||||
stopOnReturn: true,
|
||||
|
@ -2235,7 +2235,7 @@ ElementEditor.prototype = {
|
|||
}
|
||||
|
||||
// Make the attribute editable.
|
||||
editableField({
|
||||
attr.editMode = editableField({
|
||||
element: inner,
|
||||
trigger: "dblclick",
|
||||
stopOnReturn: true,
|
||||
|
@ -2257,7 +2257,7 @@ ElementEditor.prototype = {
|
|||
aEditor.input.select();
|
||||
}
|
||||
},
|
||||
done: (aVal, aCommit) => {
|
||||
done: (aVal, aCommit, direction) => {
|
||||
if (!aCommit || aVal === initial) {
|
||||
return;
|
||||
}
|
||||
|
@ -2269,6 +2269,7 @@ ElementEditor.prototype = {
|
|||
// parsed out of the input element. Restore original attribute if
|
||||
// parsing fails.
|
||||
try {
|
||||
this.refocusOnEdit(aAttr.name, attr, direction);
|
||||
this._saveAttribute(aAttr.name, undoMods);
|
||||
doMods.removeAttribute(aAttr.name);
|
||||
this._applyAttributes(aVal, attr, doMods, undoMods);
|
||||
|
@ -2347,6 +2348,97 @@ ElementEditor.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Listen to mutations, and when the attribute list is regenerated
|
||||
* try to focus on the attribute after the one that's being edited now.
|
||||
* If the attribute order changes, go to the beginning of the attribute list.
|
||||
*/
|
||||
refocusOnEdit: function(attrName, attrNode, direction) {
|
||||
// Only allow one refocus on attribute change at a time, so when there's
|
||||
// more than 1 request in parallel, the last one wins.
|
||||
if (this._editedAttributeObserver) {
|
||||
this.markup._inspector.off("markupmutation", this._editedAttributeObserver);
|
||||
this._editedAttributeObserver = null;
|
||||
}
|
||||
|
||||
let container = this.markup.getContainer(this.node);
|
||||
|
||||
let activeAttrs = [...this.attrList.childNodes].filter(el => el.style.display != "none");
|
||||
let attributeIndex = activeAttrs.indexOf(attrNode);
|
||||
|
||||
let onMutations = this._editedAttributeObserver = (e, mutations) => {
|
||||
let isDeletedAttribute = false;
|
||||
let isNewAttribute = false;
|
||||
for (let mutation of mutations) {
|
||||
let inContainer = this.markup.getContainer(mutation.target) === container;
|
||||
if (!inContainer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let isOriginalAttribute = mutation.attributeName === attrName;
|
||||
|
||||
isDeletedAttribute = isDeletedAttribute || isOriginalAttribute && mutation.newValue === null;
|
||||
isNewAttribute = isNewAttribute || mutation.attributeName !== attrName;
|
||||
}
|
||||
let isModifiedOrder = isDeletedAttribute && isNewAttribute;
|
||||
this._editedAttributeObserver = null;
|
||||
|
||||
// "Deleted" attributes are merely hidden, so filter them out.
|
||||
let visibleAttrs = [...this.attrList.childNodes].filter(el => el.style.display != "none");
|
||||
let activeEditor;
|
||||
if (visibleAttrs.length > 0) {
|
||||
if (!direction) {
|
||||
// No direction was given; stay on current attribute.
|
||||
activeEditor = visibleAttrs[attributeIndex];
|
||||
} else if (isModifiedOrder) {
|
||||
// The attribute was renamed, reordering the existing attributes.
|
||||
// So let's go to the beginning of the attribute list for consistency.
|
||||
activeEditor = visibleAttrs[0];
|
||||
} else {
|
||||
let newAttributeIndex;
|
||||
if (isDeletedAttribute) {
|
||||
newAttributeIndex = attributeIndex;
|
||||
} else {
|
||||
if (direction == Ci.nsIFocusManager.MOVEFOCUS_FORWARD) {
|
||||
newAttributeIndex = attributeIndex + 1;
|
||||
} else if (direction == Ci.nsIFocusManager.MOVEFOCUS_BACKWARD) {
|
||||
newAttributeIndex = attributeIndex - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// The number of attributes changed (deleted), or we moved through the array
|
||||
// so check we're still within bounds.
|
||||
if (newAttributeIndex >= 0 && newAttributeIndex <= visibleAttrs.length - 1) {
|
||||
activeEditor = visibleAttrs[newAttributeIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Either we have no attributes left,
|
||||
// or we just edited the last attribute and want to move on.
|
||||
if (!activeEditor) {
|
||||
activeEditor = this.newAttr;
|
||||
}
|
||||
|
||||
// Refocus was triggered by tab or shift-tab.
|
||||
// Continue in edit mode.
|
||||
if (direction) {
|
||||
activeEditor.editMode();
|
||||
} else {
|
||||
// Refocus was triggered by enter.
|
||||
// Exit edit mode (but restore focus).
|
||||
let editable = activeEditor === this.newAttr ? activeEditor : activeEditor.querySelector(".editable");
|
||||
editable.focus();
|
||||
}
|
||||
|
||||
this.markup.emit("refocusedonedit");
|
||||
};
|
||||
|
||||
// Start listening for mutations until we find an attributes change
|
||||
// that modifies this attribute.
|
||||
this.markup._inspector.once("markupmutation", onMutations);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the tag name editor has is done editing.
|
||||
*/
|
||||
|
|
|
@ -63,6 +63,7 @@ skip-if = e10s # Bug 1040751 - CodeMirror editor.destroy() isn't e10s compatible
|
|||
[browser_markupview_html_edit_02.js]
|
||||
[browser_markupview_html_edit_03.js]
|
||||
[browser_markupview_image_tooltip.js]
|
||||
[browser_markupview_keybindings_01.js]
|
||||
[browser_markupview_mutation_01.js]
|
||||
[browser_markupview_mutation_02.js]
|
||||
[browser_markupview_navigation.js]
|
||||
|
@ -83,6 +84,7 @@ skip-if = e10s # Bug 1036409 - The last selected node isn't reselected
|
|||
[browser_markupview_tag_edit_09.js]
|
||||
[browser_markupview_tag_edit_10.js]
|
||||
[browser_markupview_tag_edit_11.js]
|
||||
[browser_markupview_tag_edit_12.js]
|
||||
[browser_markupview_textcontent_edit_01.js]
|
||||
[browser_markupview_toggle_01.js]
|
||||
[browser_markupview_toggle_02.js]
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests tabbing through attributes on a node
|
||||
|
||||
const TEST_URL = "data:text/html;charset=utf8,<div a b c d e id='test'></div>";
|
||||
|
||||
add_task(function*() {
|
||||
let {inspector} = yield addTab(TEST_URL).then(openInspector);
|
||||
|
||||
info("Focusing the tag editor of the test element");
|
||||
let {editor} = yield getContainerForSelector("div", inspector);
|
||||
editor.tag.focus();
|
||||
|
||||
info("Pressing tab and expecting to focus the ID attribute, always first");
|
||||
EventUtils.sendKey("tab", inspector.panelWin);
|
||||
checkFocusedAttribute("id");
|
||||
|
||||
info("Hit enter to turn the attribute to edit mode");
|
||||
EventUtils.sendKey("return", inspector.panelWin);
|
||||
checkFocusedAttribute("id", true);
|
||||
|
||||
// Check the order of the other attributes in the DOM to the check they appear
|
||||
// correctly in the markup-view
|
||||
let attributes = [...getNode("div").attributes].filter(attr => attr.name !== "id");
|
||||
|
||||
info("Tabbing forward through attributes in edit mode");
|
||||
for (let {name} of attributes) {
|
||||
collapseSelectionAndTab(inspector);
|
||||
checkFocusedAttribute(name, true);
|
||||
}
|
||||
|
||||
info("Tabbing backward through attributes in edit mode");
|
||||
|
||||
// Just reverse the attributes other than id and remove the first one since
|
||||
// it's already focused now.
|
||||
let reverseAttributes = attributes.reverse();
|
||||
reverseAttributes.shift();
|
||||
|
||||
for (let {name} of reverseAttributes) {
|
||||
collapseSelectionAndShiftTab(inspector);
|
||||
checkFocusedAttribute(name, true);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,115 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that focus position is correct when tabbing through and editing
|
||||
// attributes.
|
||||
|
||||
const TEST_URL = "data:text/html;charset=utf8,<div id='attr' c='3' b='2' a='1'></div><div id='delattr' last='1' tobeinvalid='2'></div>";
|
||||
|
||||
add_task(function*() {
|
||||
let {inspector} = yield addTab(TEST_URL).then(openInspector);
|
||||
|
||||
yield testAttributeEditing(inspector);
|
||||
yield testAttributeDeletion(inspector);
|
||||
});
|
||||
|
||||
function* testAttributeEditing(inspector) {
|
||||
info("Testing focus position after attribute editing");
|
||||
|
||||
// Modifying attributes reorders them in the internal representation to move
|
||||
// the modified attribute to the end. breadcrumbs.js will update attributes
|
||||
// to match original order if you selectNode before modifying attributes.
|
||||
// So, hacky workaround for consistency with manual testing.
|
||||
// Should be removed after Bug 1093593.
|
||||
yield selectNode("#attr", inspector);
|
||||
|
||||
info("Setting the first non-id attribute in edit mode");
|
||||
yield activateFirstAttribute("#attr", inspector); // focuses id
|
||||
collapseSelectionAndTab(inspector); // focuses the first attr after id
|
||||
|
||||
// Detect the attributes order from the DOM, instead of assuming an order in
|
||||
// the test, because the NamedNodeMap returned by element.attributes doesn't
|
||||
// guaranty any specific order.
|
||||
// Filter out the id attribute as the markup-view places it first anyway.
|
||||
let attrs = getNodeAttributesOtherThanId("#attr");
|
||||
|
||||
info("Editing this attribute, keeping the same name, and tabbing to the next");
|
||||
yield editAttributeAndTab(attrs[0].name + '="99"', inspector);
|
||||
checkFocusedAttribute(attrs[1].name, true);
|
||||
|
||||
info("Editing the new focused attribute, keeping the name, and tabbing to the previous");
|
||||
yield editAttributeAndTab(attrs[1].name + '="99"', inspector, true);
|
||||
checkFocusedAttribute(attrs[0].name, true);
|
||||
|
||||
info("Editing attribute name, changes attribute order");
|
||||
yield editAttributeAndTab("d='4'", inspector);
|
||||
checkFocusedAttribute("id", true);
|
||||
|
||||
// Escape of the currently focused field for the next test
|
||||
EventUtils.sendKey("escape", inspector.panelWin);
|
||||
}
|
||||
|
||||
function* testAttributeDeletion(inspector) {
|
||||
info("Testing focus position after attribute deletion");
|
||||
|
||||
// Modifying attributes reorders them in the internal representation to move
|
||||
// the modified attribute to the end. breadcrumbs.js will update attributes
|
||||
// to match original order if you selectNode before modifying attributes.
|
||||
// So, hacky workaround for consistency with manual testing.
|
||||
// Should be removed after Bug 1093593.
|
||||
yield selectNode("#delattr", inspector);
|
||||
|
||||
info("Setting the first non-id attribute in edit mode");
|
||||
yield activateFirstAttribute("#delattr", inspector); // focuses id
|
||||
collapseSelectionAndTab(inspector); // focuses the first attr after id
|
||||
|
||||
// Detect the attributes order from the DOM, instead of assuming an order in
|
||||
// the test, because the NamedNodeMap returned by element.attributes doesn't
|
||||
// guaranty any specific order.
|
||||
// Filter out the id attribute as the markup-view places it first anyway.
|
||||
let attrs = getNodeAttributesOtherThanId("#delattr");
|
||||
|
||||
info("Entering an invalid attribute to delete the attribute");
|
||||
yield editAttributeAndTab('"', inspector);
|
||||
checkFocusedAttribute(attrs[1].name, true);
|
||||
|
||||
info("Deleting the last attribute");
|
||||
yield editAttributeAndTab(" ", inspector);
|
||||
|
||||
// Check we're on the newattr element
|
||||
let focusedAttr = Services.focus.focusedElement;
|
||||
ok(focusedAttr.classList.contains("styleinspector-propertyeditor"), "in newattr");
|
||||
is(focusedAttr.tagName, "input", "newattr is active");
|
||||
}
|
||||
|
||||
function* editAttributeAndTab(newValue, inspector, goPrevious) {
|
||||
var onEditMutation = inspector.markup.once("refocusedonedit");
|
||||
inspector.markup.doc.activeElement.value = newValue;
|
||||
if (goPrevious) {
|
||||
EventUtils.synthesizeKey("VK_TAB", { shiftKey: true },
|
||||
inspector.panelWin);
|
||||
} else {
|
||||
EventUtils.sendKey("tab", inspector.panelWin);
|
||||
}
|
||||
yield onEditMutation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a markup container, focus and turn in edit mode its first attribute
|
||||
* field.
|
||||
*/
|
||||
function* activateFirstAttribute(container, inspector) {
|
||||
let {editor} = yield getContainerForSelector(container, inspector);
|
||||
editor.tag.focus();
|
||||
|
||||
// Go to "id" attribute and trigger edit mode.
|
||||
EventUtils.sendKey("tab", inspector.panelWin);
|
||||
EventUtils.sendKey("return", inspector.panelWin);
|
||||
}
|
||||
|
||||
function getNodeAttributesOtherThanId(selector) {
|
||||
return [...getNode(selector).attributes].filter(attr => attr.name !== "id");
|
||||
}
|
|
@ -526,3 +526,38 @@ function promiseNextTick() {
|
|||
executeSoon(deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapses the current text selection in an input field and tabs to the next
|
||||
* field.
|
||||
*/
|
||||
function collapseSelectionAndTab(inspector) {
|
||||
EventUtils.sendKey("tab", inspector.panelWin); // collapse selection and move caret to end
|
||||
EventUtils.sendKey("tab", inspector.panelWin); // next element
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapses the current text selection in an input field and tabs to the
|
||||
* previous field.
|
||||
*/
|
||||
function collapseSelectionAndShiftTab(inspector) {
|
||||
EventUtils.synthesizeKey("VK_TAB", { shiftKey: true },
|
||||
inspector.panelWin); // collapse selection and move caret to end
|
||||
EventUtils.synthesizeKey("VK_TAB", { shiftKey: true },
|
||||
inspector.panelWin); // previous element
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the current focused element is an attribute element in the markup
|
||||
* view.
|
||||
* @param {String} attrName The attribute name expected to be found
|
||||
* @param {Boolean} editMode Whether or not the attribute should be in edit mode
|
||||
*/
|
||||
function checkFocusedAttribute(attrName, editMode) {
|
||||
let focusedAttr = Services.focus.focusedElement;
|
||||
is(focusedAttr ? focusedAttr.parentNode.dataset.attr : undefined,
|
||||
attrName, attrName + " attribute editor is currently focused.");
|
||||
is(focusedAttr ? focusedAttr.tagName : undefined,
|
||||
editMode ? "input": "span",
|
||||
editMode ? attrName + " is in edit mode" : attrName + " is not in edit mode");
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
*
|
||||
* editableField({
|
||||
* element: spanToEdit,
|
||||
* done: function(value, commit) {
|
||||
* done: function(value, commit, direction) {
|
||||
* if (commit) {
|
||||
* spanToEdit.textContent = value;
|
||||
* }
|
||||
|
@ -62,9 +62,11 @@ Cu.import("resource://gre/modules/devtools/event-emitter.js");
|
|||
* with the current value of the text input.
|
||||
* {function} done:
|
||||
* Called when input is committed or blurred. Called with
|
||||
* current value and a boolean telling the caller whether to
|
||||
* commit the change. This function is called before the editor
|
||||
* has been torn down.
|
||||
* current value, a boolean telling the caller whether to
|
||||
* commit the change, and the direction of the next element to be
|
||||
* selected. Direction may be one of nsIFocusManager.MOVEFOCUS_FORWARD,
|
||||
* nsIFocusManager.MOVEFOCUS_BACKWARD, or null (no movement).
|
||||
* This function is called before the editor has been torn down.
|
||||
* {function} destroy:
|
||||
* Called when the editor is destroyed and has been torn down.
|
||||
* {string} advanceChars:
|
||||
|
@ -103,6 +105,7 @@ exports.editableField = editableField;
|
|||
* defaults to "click"
|
||||
* @param {function} aCallback
|
||||
* Called when the editor is activated.
|
||||
* @return {function} function which calls aCallback
|
||||
*/
|
||||
function editableItem(aOptions, aCallback)
|
||||
{
|
||||
|
@ -148,6 +151,13 @@ function editableItem(aOptions, aCallback)
|
|||
// Mark the element editable field for tab
|
||||
// navigation while editing.
|
||||
element._editable = true;
|
||||
|
||||
// Save the trigger type so we can dispatch this later
|
||||
element._trigger = trigger;
|
||||
|
||||
return function turnOnEditMode() {
|
||||
aCallback(element);
|
||||
}
|
||||
}
|
||||
|
||||
exports.editableItem = this.editableItem;
|
||||
|
@ -769,7 +779,7 @@ InplaceEditor.prototype = {
|
|||
/**
|
||||
* Call the client's done handler and clear out.
|
||||
*/
|
||||
_apply: function InplaceEditor_apply(aEvent)
|
||||
_apply: function InplaceEditor_apply(aEvent, direction)
|
||||
{
|
||||
if (this._applied) {
|
||||
return;
|
||||
|
@ -779,7 +789,7 @@ InplaceEditor.prototype = {
|
|||
|
||||
if (this.done) {
|
||||
let val = this.input.value.trim();
|
||||
return this.done(this.cancelled ? this.initial : val, !this.cancelled);
|
||||
return this.done(this.cancelled ? this.initial : val, !this.cancelled, direction);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -945,7 +955,7 @@ InplaceEditor.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
this._apply();
|
||||
this._apply(aEvent, direction);
|
||||
|
||||
// Close the popup if open
|
||||
if (this.popup && this.popup.isOpen) {
|
||||
|
@ -958,9 +968,11 @@ InplaceEditor.prototype = {
|
|||
let next = moveFocus(this.doc.defaultView, direction);
|
||||
|
||||
// If the next node to be focused has been tagged as an editable
|
||||
// node, send it a click event to trigger
|
||||
// node, trigger editing using the configured event
|
||||
if (next && next.ownerDocument === this.doc && next._editable) {
|
||||
next.click();
|
||||
let e = this.doc.createEvent('Event');
|
||||
e.initEvent(next._trigger, true, true);
|
||||
next.dispatchEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -441,7 +441,7 @@ StyleSheetEditor.prototype = {
|
|||
* Toggled the disabled state of the underlying stylesheet.
|
||||
*/
|
||||
toggleDisabled: function() {
|
||||
this.styleSheet.toggleDisabled();
|
||||
this.styleSheet.toggleDisabled().then(null, Cu.reportError);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -483,7 +483,8 @@ StyleSheetEditor.prototype = {
|
|||
|
||||
let transitionsEnabled = Services.prefs.getBoolPref(TRANSITION_PREF);
|
||||
|
||||
this.styleSheet.update(this._state.text, transitionsEnabled);
|
||||
this.styleSheet.update(this._state.text, transitionsEnabled)
|
||||
.then(null, Cu.reportError);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -54,18 +54,21 @@ add_task(function*() {
|
|||
yield TimelineController.toggleRecording();
|
||||
ok(true, "Recording has ended.");
|
||||
|
||||
is(TimelineController.getMarkers().length, 0,
|
||||
"There are no markers available.");
|
||||
// TODO: Re-enable this assertion as part of bug 1120830
|
||||
// is(TimelineController.getMarkers().length, 0,
|
||||
// "There are no markers available.");
|
||||
isnot(TimelineController.getMemory().length, 0,
|
||||
"There are some memory measurements available.");
|
||||
|
||||
is(TimelineView.markersOverview.selectionEnabled, true,
|
||||
"The selection should now be enabled for the markers overview.");
|
||||
is(TimelineView.markersOverview.hasSelection(), false,
|
||||
"The markers overview should not have a selection after recording.");
|
||||
// TODO: Re-enable this assertion as part of bug 1120830
|
||||
// is(TimelineView.markersOverview.hasSelection(), false,
|
||||
// "The markers overview should not have a selection after recording.");
|
||||
|
||||
is(TimelineView.memoryOverview.selectionEnabled, true,
|
||||
"The selection should now be enabled for the memory overview.");
|
||||
is(TimelineView.memoryOverview.hasSelection(), false,
|
||||
"The memory overview should not have a selection after recording.");
|
||||
// TODO: Re-enable this assertion as part of bug 1120830
|
||||
// is(TimelineView.memoryOverview.hasSelection(), false,
|
||||
// "The memory overview should not have a selection after recording.");
|
||||
});
|
||||
|
|
|
@ -3555,8 +3555,8 @@ JSTerm.prototype = {
|
|||
deferred.resolve(window);
|
||||
};
|
||||
|
||||
let tab = this.sidebar.getTab("variablesview");
|
||||
if (tab) {
|
||||
let tabPanel = this.sidebar.getTabPanel("variablesview");
|
||||
if (tabPanel) {
|
||||
if (this.sidebar.getCurrentTabID() == "variablesview") {
|
||||
onTabReady();
|
||||
}
|
||||
|
|
|
@ -589,9 +589,6 @@ Section "-Application" APP_IDX
|
|||
${EndUnless}
|
||||
${EndIf}
|
||||
|
||||
; Add the Firewall entries during install
|
||||
Call AddFirewallEntries
|
||||
|
||||
!ifdef MOZ_MAINTENANCE_SERVICE
|
||||
${If} $TmpVal == "HKLM"
|
||||
; Add the registry keys for allowed certificates.
|
||||
|
@ -627,6 +624,9 @@ Section "-InstallEndCleanup"
|
|||
${GetShortcutsLogPath} $0
|
||||
WriteIniStr "$0" "TASKBAR" "Migrated" "true"
|
||||
|
||||
; Add the Firewall entries during install
|
||||
Call AddFirewallEntries
|
||||
|
||||
; Refresh desktop icons
|
||||
System::Call "shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_DWORDFLUSH}, i 0, i 0)"
|
||||
|
||||
|
|
|
@ -938,7 +938,7 @@ FunctionEnd
|
|||
!macroend
|
||||
!define MountRegistryIntoHKU "!insertmacro MountRegistryIntoHKU"
|
||||
!define un.MountRegistryIntoHKU "!insertmacro MountRegistryIntoHKU"
|
||||
;
|
||||
|
||||
; Unmounts all user ntuser.dat files into the registry as a subkey of HKU
|
||||
!macro UnmountRegistryIntoHKU
|
||||
; $0 is used as an index for HKEY_USERS enumeration
|
||||
|
@ -1556,6 +1556,72 @@ FunctionEnd
|
|||
!macroend
|
||||
!define PushFilesToCheck "!insertmacro PushFilesToCheck"
|
||||
|
||||
|
||||
; Pushes the string "true" to the top of the stack if the Firewall service is
|
||||
; running and pushes the string "false" to the top of the stack if it isn't.
|
||||
!define SC_MANAGER_ALL_ACCESS 0x3F
|
||||
!define SERVICE_QUERY_CONFIG 0x0001
|
||||
!define SERVICE_QUERY_STATUS 0x0004
|
||||
!define SERVICE_RUNNING 0x4
|
||||
|
||||
!macro IsFirewallSvcRunning
|
||||
Push $R9
|
||||
Push $R8
|
||||
Push $R7
|
||||
Push $R6
|
||||
Push "false"
|
||||
|
||||
System::Call 'advapi32::OpenSCManagerW(n, n, i ${SC_MANAGER_ALL_ACCESS}) i.R6'
|
||||
${If} $R6 != 0
|
||||
; MpsSvc is the Firewall service on Windows Vista and above.
|
||||
; When opening the service with SERVICE_QUERY_CONFIG the return value will
|
||||
; be 0 if the service is not installed.
|
||||
System::Call 'advapi32::OpenServiceW(i R6, t "MpsSvc", i ${SERVICE_QUERY_CONFIG}) i.R7'
|
||||
${If} $R7 != 0
|
||||
System::Call 'advapi32::CloseServiceHandle(i R7) n'
|
||||
; Open the service with SERVICE_QUERY_CONFIG so its status can be queried.
|
||||
System::Call 'advapi32::OpenServiceW(i R6, t "MpsSvc", i ${SERVICE_QUERY_STATUS}) i.R7'
|
||||
${Else}
|
||||
; SharedAccess is the Firewall service on Windows XP.
|
||||
; When opening the service with SERVICE_QUERY_CONFIG the return value will
|
||||
; be 0 if the service is not installed.
|
||||
System::Call 'advapi32::OpenServiceW(i R6, t "SharedAccess", i ${SERVICE_QUERY_CONFIG}) i.R7'
|
||||
${If} $R7 != 0
|
||||
System::Call 'advapi32::CloseServiceHandle(i R7) n'
|
||||
; Open the service with SERVICE_QUERY_CONFIG so its status can be
|
||||
; queried.
|
||||
System::Call 'advapi32::OpenServiceW(i R6, t "SharedAccess", i ${SERVICE_QUERY_STATUS}) i.R7'
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
; Did the calls to OpenServiceW succeed?
|
||||
${If} $R7 != 0
|
||||
System::Call '*(i,i,i,i,i,i,i) i.R9'
|
||||
; Query the current status of the service.
|
||||
System::Call 'advapi32::QueryServiceStatus(i R7, i $R9) i'
|
||||
System::Call '*$R9(i, i.R8)'
|
||||
System::Free $R9
|
||||
System::Call 'advapi32::CloseServiceHandle(i R7) n'
|
||||
IntFmt $R8 "0x%X" $R8
|
||||
${If} $R8 == ${SERVICE_RUNNING}
|
||||
Pop $R9
|
||||
Push "true"
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
System::Call 'advapi32::CloseServiceHandle(i R6) n'
|
||||
${EndIf}
|
||||
|
||||
Exch 1
|
||||
Pop $R6
|
||||
Exch 1
|
||||
Pop $R7
|
||||
Exch 1
|
||||
Pop $R8
|
||||
Exch 1
|
||||
Pop $R9
|
||||
!macroend
|
||||
!define IsFirewallSvcRunning "!insertmacro IsFirewallSvcRunning"
|
||||
!define un.IsFirewallSvcRunning "!insertmacro IsFirewallSvcRunning"
|
||||
|
||||
; Sets this installation as the default browser by setting the registry keys
|
||||
; under HKEY_CURRENT_USER via registry calls and using the AppAssocReg NSIS
|
||||
; plugin for Vista and above. This is a function instead of a macro so it is
|
||||
|
@ -1619,8 +1685,13 @@ Function FixShortcutAppModelIDs
|
|||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
; Helper for adding Firewall exceptions during install and after app update.
|
||||
Function AddFirewallEntries
|
||||
liteFirewallW::AddRule "$INSTDIR\${FileMainEXE}" "${BrandShortName} ($INSTDIR)"
|
||||
${IsFirewallSvcRunning}
|
||||
Pop $0
|
||||
${If} "$0" == "true"
|
||||
liteFirewallW::AddRule "$INSTDIR\${FileMainEXE}" "${BrandShortName} ($INSTDIR)"
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
; The !ifdef NO_LOG prevents warnings when compiling the installer.nsi due to
|
||||
|
|
|
@ -464,8 +464,6 @@ Section "Uninstall"
|
|||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
liteFirewallW::RemoveRule "$INSTDIR\${FileMainEXE}" "${BrandShortName} ($INSTDIR)"
|
||||
|
||||
; Refresh desktop icons otherwise the start menu internet item won't be
|
||||
; removed and other ugly things will happen like recreation of the app's
|
||||
; clients registry key by the OS under some conditions.
|
||||
|
@ -489,6 +487,11 @@ Section "Uninstall"
|
|||
Call un.UninstallServiceIfNotUsed
|
||||
!endif
|
||||
|
||||
${un.IsFirewallSvcRunning}
|
||||
Pop $0
|
||||
${If} "$0" == "true"
|
||||
liteFirewallW::RemoveRule "$INSTDIR\${FileMainEXE}" "${BrandShortName} ($INSTDIR)"
|
||||
${EndIf}
|
||||
SectionEnd
|
||||
|
||||
################################################################################
|
||||
|
|
|
@ -26,6 +26,11 @@ player.transitionNameLabel=Transition
|
|||
# displayed before the animation duration.
|
||||
player.animationDurationLabel=Duration:
|
||||
|
||||
# LOCALIZATION NOTE (player.animationDelayLabel):
|
||||
# This string is displayed in each animation player widget. It is the label
|
||||
# displayed before the animation delay.
|
||||
player.animationDelayLabel=Delay:
|
||||
|
||||
# LOCALIZATION NOTE (player.animationIterationCountLabel):
|
||||
# This string is displayed in each animation player widget. It is the label
|
||||
# displayed before the number of times the animation is set to repeat.
|
||||
|
|
|
@ -71,6 +71,16 @@ browserConsoleCmd.commandkey=j
|
|||
# This is the tooltip of the pick button in the toolbox toolbar
|
||||
pickButton.tooltip=Pick an element from the page
|
||||
|
||||
# LOCALIZATION NOTE (sidebar.showAllTabs.label)
|
||||
# This is the label shown in the show all tabs button in the tabbed side
|
||||
# bar, when there's no enough space to show all tabs at once
|
||||
sidebar.showAllTabs.label=…
|
||||
|
||||
# LOCALIZATION NOTE (sidebar.showAllTabs.tooltip)
|
||||
# This is the tooltip shown when hover over the '…' button in the tabbed side
|
||||
# bar, when there's no enough space to show all tabs at once
|
||||
sidebar.showAllTabs.tooltip=All tabs
|
||||
|
||||
# LOCALIZATION NOTE (options.darkTheme.label)
|
||||
# Used as a label for dark theme
|
||||
options.darkTheme.label=Dark theme
|
||||
|
|
|
@ -142,6 +142,7 @@ function PreviewController(win, tab) {
|
|||
this.preview = this.win.createTabPreview(this);
|
||||
|
||||
this.linkedBrowser.addEventListener("MozAfterPaint", this, false);
|
||||
this.linkedBrowser.addEventListener("resize", this, false);
|
||||
this.tab.addEventListener("TabAttrModified", this, false);
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "canvasPreview", function () {
|
||||
|
@ -171,6 +172,7 @@ PreviewController.prototype = {
|
|||
Ci.nsIDOMEventListener]),
|
||||
destroy: function () {
|
||||
this.tab.removeEventListener("TabAttrModified", this, false);
|
||||
this.linkedBrowser.removeEventListener("resize", this, false);
|
||||
this.linkedBrowser.removeEventListener("MozAfterPaint", this, false);
|
||||
|
||||
// Break cycles, otherwise we end up leaking the window with everything
|
||||
|
@ -201,9 +203,17 @@ PreviewController.prototype = {
|
|||
// Resizes the canvasPreview to 0x0, essentially freeing its memory.
|
||||
// updateCanvasPreview() will detect the size mismatch as a resize event
|
||||
// the next time it is called.
|
||||
resetCanvasPreview: function () {
|
||||
this.canvasPreview.width = 0;
|
||||
this.canvasPreview.height = 0;
|
||||
resetCanvasPreview: function () this.resizeCanvasPreview(0, 0),
|
||||
|
||||
resizeCanvasPreview: function (width, height) {
|
||||
this.canvasPreview.width = width;
|
||||
this.canvasPreview.height = height;
|
||||
},
|
||||
|
||||
get wasResizedSinceLastPreview () {
|
||||
let bx = this.linkedBrowser.boxObject;
|
||||
return bx.width != this.canvasPreview.width ||
|
||||
bx.height != this.canvasPreview.height;
|
||||
},
|
||||
|
||||
get zoom() {
|
||||
|
@ -219,13 +229,15 @@ PreviewController.prototype = {
|
|||
updateCanvasPreview: function () {
|
||||
let win = this.linkedBrowser.contentWindow;
|
||||
let bx = this.linkedBrowser.boxObject;
|
||||
// If we resized then we need to flush layout so that the previews are up
|
||||
// to date. Layout flushing for resizes is deferred for background tabs so
|
||||
// we may need to force it. (bug 526620)
|
||||
let flushLayout = this.wasResizedSinceLastPreview;
|
||||
// Check for resize
|
||||
if (bx.width != this.canvasPreview.width ||
|
||||
bx.height != this.canvasPreview.height) {
|
||||
if (flushLayout) {
|
||||
// Invalidate the entire area and repaint
|
||||
this.onTabPaint({left:0, top:0, right:win.innerWidth, bottom:win.innerHeight});
|
||||
this.canvasPreview.width = bx.width;
|
||||
this.canvasPreview.height = bx.height;
|
||||
this.resizeCanvasPreview(bx.width, bx.height);
|
||||
}
|
||||
|
||||
// Draw dirty regions
|
||||
|
@ -233,6 +245,9 @@ PreviewController.prototype = {
|
|||
let scale = this.zoom;
|
||||
|
||||
let flags = this.canvasPreviewFlags;
|
||||
if (flushLayout)
|
||||
flags &= ~Ci.nsIDOMCanvasRenderingContext2D.DRAWWINDOW_DO_NOT_FLUSH;
|
||||
|
||||
// The dirty region may include parts that are offscreen so we clip to the
|
||||
// canvas area.
|
||||
this.dirtyRegion.intersectRect(0, 0, win.innerWidth, win.innerHeight);
|
||||
|
@ -372,6 +387,19 @@ PreviewController.prototype = {
|
|||
case "TabAttrModified":
|
||||
this.updateTitleAndTooltip();
|
||||
break;
|
||||
case "resize":
|
||||
// We need to invalidate our window's other tabs' previews since layout
|
||||
// due to resizing is delayed for background tabs. Note that this
|
||||
// resize may not be the first after the main window has been resized -
|
||||
// the user may be switching to our tab which forces the resize.
|
||||
this.win.previews.forEach(function (p) {
|
||||
let controller = p.controller.wrappedJSObject;
|
||||
if (controller.wasResizedSinceLastPreview) {
|
||||
controller.resetCanvasPreview();
|
||||
p.invalidate();
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
there are overrides for each platform in their devedition.css files. */
|
||||
|
||||
:root {
|
||||
--space-above-tabbar: 1px;
|
||||
--tab-toolbar-navbar-overlap: 0px;
|
||||
--space-above-tabbar: 0px;
|
||||
--toolbarbutton-text-shadow: none;
|
||||
--panel-ui-button-background-size: 1px calc(100% - 1px);
|
||||
--panel-ui-button-background-position: 1px 0px;
|
||||
|
@ -32,9 +33,9 @@
|
|||
--tab-separator-color: #474C50;
|
||||
--tab-selection-color: #f5f7fa;
|
||||
--tab-selection-background-color: #1a4666;
|
||||
--tab-selection-box-shadow: 0 2px 0 #d7f1ff inset,
|
||||
0 8px 3px -5px #2b82bf inset,
|
||||
0 -1px 0 rgba(0,0,0,.2) inset;
|
||||
--tab-selection-box-shadow: 0 2px 0 #D7F1FF inset,
|
||||
0 -2px 0 rgba(0,0,0,.05) inset,
|
||||
0 -1px 0 rgba(0,0,0,.3) inset;
|
||||
--pinned-tab-glow: radial-gradient(22px at center calc(100% - 2px), rgba(76,158,217,0.9) 13%, rgba(0,0,0,0.4) 16%, transparent 70%);
|
||||
|
||||
/* Toolbar buttons */
|
||||
|
@ -92,9 +93,9 @@
|
|||
--tab-separator-color: #C6C6C7;
|
||||
--tab-selection-color: #f5f7fa;
|
||||
--tab-selection-background-color: #4c9ed9;
|
||||
--tab-selection-box-shadow: 0 2px 0 #d7f1ff inset,
|
||||
0 8px 3px -5px #319BDB inset,
|
||||
0 -1px 0 #2A7CB1 inset;
|
||||
--tab-selection-box-shadow: 0 2px 0 #9FDFFF inset,
|
||||
0 -2px 0 rgba(0,0,0,.05) inset,
|
||||
0 -1px 0 rgba(0,0,0,.2) inset;
|
||||
--pinned-tab-glow: radial-gradient(22px at center calc(100% - 2px), rgba(76,158,217,0.9) 13%, transparent 16%);
|
||||
|
||||
|
||||
|
@ -177,7 +178,7 @@
|
|||
#navigator-toolbox > toolbar:not(#TabsToolbar):not(#toolbar-menubar),
|
||||
.browserContainer > findbar,
|
||||
#browser-bottombox {
|
||||
background: var(--chrome-secondary-background-color) !important;
|
||||
background-color: var(--chrome-secondary-background-color) !important;
|
||||
color: var(--chrome-color);
|
||||
}
|
||||
|
||||
|
@ -227,9 +228,10 @@ window:not([chromehidden~="toolbar"]) #urlbar-wrapper {
|
|||
/* Nav bar specific stuff */
|
||||
#nav-bar {
|
||||
margin-top: 0 !important;
|
||||
border: none !important;
|
||||
border-top: none !important;
|
||||
border-bottom: none !important;
|
||||
border-radius: 0 !important;
|
||||
box-shadow: 0 1px var(--chrome-nav-bar-separator-color) inset !important;
|
||||
box-shadow: 0 -1px var(--chrome-nav-bar-separator-color) !important;
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
|
@ -288,7 +290,6 @@ searchbar:not([oneoffui]) .search-go-button {
|
|||
}
|
||||
|
||||
.tabbrowser-tab[pinned][titlechanged]:not([selected="true"]) > .tab-stack > .tab-content {
|
||||
/* The -2px in `calc` is the height of `tabToolbarNavbarOverlap` plus a 1px offset from the center */
|
||||
background-image: var(--pinned-tab-glow);
|
||||
background-position: center;
|
||||
background-size: 100%;
|
||||
|
@ -311,13 +312,6 @@ searchbar:not([oneoffui]) .search-go-button {
|
|||
width: 36px;
|
||||
}
|
||||
|
||||
/* Override @tabToolbarNavbarOverlap@ */
|
||||
#TabsToolbar .toolbarbutton-1,
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-up,
|
||||
.tabbrowser-arrowscrollbox > .scrollbutton-down {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#TabsToolbar > #new-tab-button:hover,
|
||||
.tabs-newtab-button:hover {
|
||||
/* Important needed because !important is used in browser.css */
|
||||
|
|
|
@ -191,7 +191,8 @@
|
|||
|
||||
.theme-toolbar,
|
||||
.devtools-toolbar,
|
||||
.devtools-sidebar-tabs > tabs,
|
||||
.devtools-sidebar-tabs tabs,
|
||||
.devtools-sidebar-alltabs,
|
||||
.CodeMirror-dialog { /* General toolbar styling */
|
||||
color: var(--theme-body-color-alt);
|
||||
background-color: var(--theme-toolbar-background);
|
||||
|
|
|
@ -194,7 +194,8 @@
|
|||
|
||||
.theme-toolbar,
|
||||
.devtools-toolbar,
|
||||
.devtools-sidebar-tabs > tabs,
|
||||
.devtools-sidebar-tabs tabs,
|
||||
.devtools-sidebar-alltabs,
|
||||
.CodeMirror-dialog { /* General toolbar styling */
|
||||
color: var(--theme-body-color-alt);
|
||||
background-color: var(--theme-toolbar-background);
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
|
||||
/* Toolbars */
|
||||
.devtools-toolbar,
|
||||
.devtools-sidebar-tabs > tabs {
|
||||
.devtools-sidebar-tabs tabs,
|
||||
.devtools-sidebar-alltabs {
|
||||
-moz-appearance: none;
|
||||
padding: 0;
|
||||
border-width: 0;
|
||||
|
@ -416,19 +417,30 @@
|
|||
color: var(--theme-body-color);
|
||||
}
|
||||
|
||||
.devtools-sidebar-tabs > tabs {
|
||||
.devtools-sidebar-tabs tabs {
|
||||
position: static;
|
||||
font: inherit;
|
||||
margin-bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.devtools-sidebar-tabs > tabs > .tabs-right,
|
||||
.devtools-sidebar-tabs > tabs > .tabs-left {
|
||||
.devtools-sidebar-alltabs {
|
||||
margin: 0;
|
||||
border-width: 0 0 1px 0;
|
||||
-moz-border-start-width: 1px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.devtools-sidebar-alltabs dropmarker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.devtools-sidebar-tabs > tabs > tab {
|
||||
.devtools-sidebar-tabs tabs > .tabs-right,
|
||||
.devtools-sidebar-tabs tabs > .tabs-left {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.devtools-sidebar-tabs tabs > tab {
|
||||
-moz-appearance: none;
|
||||
/* We want to match the height of a toolbar with a toolbarbutton
|
||||
* First, we need to replicated the padding of toolbar (4px),
|
||||
|
@ -449,70 +461,70 @@
|
|||
text-shadow: none;
|
||||
}
|
||||
|
||||
.devtools-sidebar-tabs > tabs > tab:first-child {
|
||||
.devtools-sidebar-tabs tabs > tab:first-child {
|
||||
-moz-border-start-width: 0;
|
||||
}
|
||||
|
||||
.theme-dark .devtools-sidebar-tabs > tabs > tab {
|
||||
.theme-dark .devtools-sidebar-tabs tabs > tab {
|
||||
border-image: @smallSeparatorDark@ 1 1;
|
||||
}
|
||||
|
||||
.theme-dark .devtools-sidebar-tabs > tabs > tab:hover {
|
||||
.theme-dark .devtools-sidebar-tabs tabs > tab:hover {
|
||||
background: hsla(206,37%,4%,.2);
|
||||
border-image: @smallSeparatorDark@ 1 1;
|
||||
}
|
||||
|
||||
.theme-dark .devtools-sidebar-tabs > tabs > tab:hover:active {
|
||||
.theme-dark .devtools-sidebar-tabs tabs > tab:hover:active {
|
||||
background: hsla(206,37%,4%,.4);
|
||||
border-image: @smallSeparatorDark@ 1 1;
|
||||
}
|
||||
|
||||
.theme-dark .devtools-sidebar-tabs > tabs > tab[selected] + tab {
|
||||
.theme-dark .devtools-sidebar-tabs tabs > tab[selected] + tab {
|
||||
border-image: @solidSeparatorDark@ 1 1;
|
||||
}
|
||||
|
||||
.theme-dark .devtools-sidebar-tabs > tabs > tab[selected] + tab:hover {
|
||||
.theme-dark .devtools-sidebar-tabs tabs > tab[selected] + tab:hover {
|
||||
background: hsla(206,37%,4%,.2);
|
||||
border-image: @solidSeparatorDark@ 1 1;
|
||||
}
|
||||
|
||||
.theme-dark .devtools-sidebar-tabs > tabs > tab[selected] + tab:hover:active {
|
||||
.theme-dark .devtools-sidebar-tabs tabs > tab[selected] + tab:hover:active {
|
||||
background: hsla(206,37%,4%,.4);
|
||||
border-image: @solidSeparatorDark@ 1 1;
|
||||
}
|
||||
|
||||
.theme-dark .devtools-sidebar-tabs > tabs > tab[selected],
|
||||
.theme-dark .devtools-sidebar-tabs > tabs > tab[selected]:hover:active {
|
||||
.theme-dark .devtools-sidebar-tabs tabs > tab[selected],
|
||||
.theme-dark .devtools-sidebar-tabs tabs > tab[selected]:hover:active {
|
||||
color: var(--theme-selection-color);
|
||||
background: #1d4f73;
|
||||
border-image: @solidSeparatorDark@ 1 1;
|
||||
}
|
||||
|
||||
.theme-light .devtools-sidebar-tabs > tabs > tab {
|
||||
.theme-light .devtools-sidebar-tabs tabs > tab {
|
||||
border-image: @smallSeparatorLight@ 1 1;
|
||||
}
|
||||
|
||||
.theme-light .devtools-sidebar-tabs > tabs > tab:hover {
|
||||
.theme-light .devtools-sidebar-tabs tabs > tab:hover {
|
||||
background: #ddd;
|
||||
border-image: @smallSeparatorLight@ 1 1;
|
||||
}
|
||||
|
||||
.theme-light .devtools-sidebar-tabs > tabs > tab:hover:active {
|
||||
.theme-light .devtools-sidebar-tabs tabs > tab:hover:active {
|
||||
background: #ddd;
|
||||
border-image: @smallSeparatorLight@ 1 1;
|
||||
}
|
||||
|
||||
.theme-light .devtools-sidebar-tabs > tabs > tab[selected] + tab {
|
||||
.theme-light .devtools-sidebar-tabs tabs > tab[selected] + tab {
|
||||
border-image: @solidSeparatorLight@;
|
||||
}
|
||||
|
||||
.theme-light .devtools-sidebar-tabs > tabs > tab[selected] + tab:hover {
|
||||
.theme-light .devtools-sidebar-tabs tabs > tab[selected] + tab:hover {
|
||||
background: #ddd;
|
||||
border-image: @solidSeparatorLight@;
|
||||
}
|
||||
|
||||
.theme-light .devtools-sidebar-tabs > tabs > tab[selected],
|
||||
.theme-light .devtools-sidebar-tabs > tabs > tab[selected]:hover:active {
|
||||
.theme-light .devtools-sidebar-tabs tabs > tab[selected],
|
||||
.theme-light .devtools-sidebar-tabs tabs > tab[selected]:hover:active {
|
||||
color: var(--theme-selection-color);
|
||||
background: #4c9ed9;
|
||||
border-image: @solidSeparatorLight@;
|
||||
|
@ -734,7 +746,7 @@
|
|||
-moz-box-align: center;
|
||||
min-width: 32px;
|
||||
min-height: 24px;
|
||||
max-width: 127px;
|
||||
max-width: 110px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-style: solid;
|
||||
|
|
|
@ -944,7 +944,7 @@ ContactDB.prototype = {
|
|||
if (DEBUG) debug("No object ID passed");
|
||||
return;
|
||||
}
|
||||
this.newTxn("readwrite", STORE_NAME, function(txn, store) {
|
||||
this.newTxn("readwrite", SAVED_GETALL_STORE_NAME, function(txn, store) {
|
||||
store.openCursor().onsuccess = function(e) {
|
||||
let cursor = e.target.result;
|
||||
if (cursor) {
|
||||
|
|
|
@ -4,3 +4,7 @@
|
|||
skip-if = os == "android"
|
||||
|
||||
[test_contacts_upgrade.xul]
|
||||
skip-if = os == "android"
|
||||
|
||||
[test_contacts_cache.xul]
|
||||
skip-if = os == "android"
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
|
||||
<window title="Mozilla Bug 1114520"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
<![CDATA[
|
||||
"use strict";
|
||||
|
||||
const { 'utils': Cu } = Components;
|
||||
Cu.import("resource://gre/modules/ContactDB.jsm", window);
|
||||
Cu.importGlobalProperties(["indexedDB"]);
|
||||
|
||||
let contactsDB = new ContactDB();
|
||||
contactsDB.init();
|
||||
|
||||
function makeFailure(reason, skipDelete) {
|
||||
return function() {
|
||||
ok(false, reason);
|
||||
if (skipDelete) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
deleteDatabase(SimpleTest.finish);
|
||||
};
|
||||
};
|
||||
|
||||
function deleteDatabase(then) {
|
||||
contactsDB.close();
|
||||
let req = indexedDB.deleteDatabase(DB_NAME);
|
||||
req.onsuccess = then;
|
||||
req.onblocked = makeFailure("blocked", true);
|
||||
req.onupgradeneeded = makeFailure("onupgradeneeded", true);
|
||||
req.onerror = makeFailure("onerror", true);
|
||||
};
|
||||
|
||||
let CONTACT_PROPS = {
|
||||
id: "ab74671e36be41b680f8f030e7e24ea2",
|
||||
properties: {
|
||||
name: ["Magnificentest foo bar the third"],
|
||||
givenName: ["foo"],
|
||||
familyName: ["bar"]
|
||||
}
|
||||
};
|
||||
|
||||
let ANOTHER_CONTACT_PROPS = {
|
||||
id: "b461d53d548b4e8aaa8256911a415f79",
|
||||
properties: {
|
||||
name: ["Magnificentest foo bar the fourth"],
|
||||
givenName: ["foo"],
|
||||
familyName: ["bar"]
|
||||
}
|
||||
};
|
||||
|
||||
let Tests = [function() {
|
||||
info("Save contact");
|
||||
contactsDB.saveContact(CONTACT_PROPS, function() {
|
||||
ok(true, "Saved contact successfully");
|
||||
next();
|
||||
});
|
||||
}, function() {
|
||||
info("Save another contact");
|
||||
contactsDB.saveContact(ANOTHER_CONTACT_PROPS, function() {
|
||||
ok(true, "Saved contact successfully");
|
||||
next();
|
||||
});
|
||||
}, function() {
|
||||
info("Get all contacts so cache is built");
|
||||
contactsDB.getAll(function(contacts) {
|
||||
ok(true, "Got all contacts " + contacts.length);
|
||||
next();
|
||||
}, function(e) {
|
||||
makeFailure("Unexpected error getting contacts " + e);
|
||||
}, {"sortBy":"givenName","sortOrder":"ascending"});
|
||||
}, function() {
|
||||
info("Contacts cache should have both ids");
|
||||
let contactsCount = 0;
|
||||
contactsDB.newTxn("readonly", SAVED_GETALL_STORE_NAME, function(txn, store) {
|
||||
store.openCursor().onsuccess = function(e) {
|
||||
let cursor = e.target.result;
|
||||
if (!cursor) {
|
||||
makeFailure("Wrong cache");
|
||||
return;
|
||||
}
|
||||
ok(cursor.value.length == 2, "Both contacts ids are in the cache");
|
||||
next();
|
||||
};
|
||||
}, null, makeFailure);
|
||||
}, function() {
|
||||
info("Remove contact " + CONTACT_PROPS.id);
|
||||
contactsDB.removeContact(CONTACT_PROPS.id, function() {
|
||||
ok(true, "Removed contact");
|
||||
next();
|
||||
}, function() {
|
||||
makeFailure("Unexpected error removing contact " + e);
|
||||
});
|
||||
}, function() {
|
||||
info("Contacts cache should have only one id");
|
||||
contactsDB.newTxn("readonly", SAVED_GETALL_STORE_NAME, function(txn, store) {
|
||||
store.openCursor().onsuccess = function(e) {
|
||||
let cursor = e.target.result;
|
||||
if (!cursor) {
|
||||
makeFailure;
|
||||
return;
|
||||
}
|
||||
ok(cursor.value.length == 1, "Only one contacts id is in the cache");
|
||||
ok(cursor.value[0] == ANOTHER_CONTACT_PROPS.id, "And it is the right id");
|
||||
next();
|
||||
};
|
||||
}, null, makeFailure);
|
||||
}, function() {
|
||||
deleteDatabase(next);
|
||||
}];
|
||||
|
||||
function next() {
|
||||
let step = Tests.shift();
|
||||
if (step) {
|
||||
step();
|
||||
} else {
|
||||
info("All done");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
next();
|
||||
|
||||
]]>
|
||||
</script>
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1114520"
|
||||
target="_blank">Mozilla Bug 1114520</a>
|
||||
</body>
|
||||
</window>
|
|
@ -0,0 +1,68 @@
|
|||
/* 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 "NetIdManager.h"
|
||||
|
||||
NetIdManager::NetIdManager()
|
||||
: mNextNetId(MIN_NET_ID)
|
||||
{
|
||||
}
|
||||
|
||||
int NetIdManager::getNextNetId()
|
||||
{
|
||||
// Modified from
|
||||
// http://androidxref.com/5.0.0_r2/xref/frameworks/base/services/
|
||||
// core/java/com/android/server/ConnectivityService.java#764
|
||||
|
||||
int netId = mNextNetId;
|
||||
if (++mNextNetId > MAX_NET_ID) {
|
||||
mNextNetId = MIN_NET_ID;
|
||||
}
|
||||
|
||||
return netId;
|
||||
}
|
||||
|
||||
void NetIdManager::acquire(const nsString& aInterfaceName,
|
||||
NetIdInfo* aNetIdInfo)
|
||||
{
|
||||
// Lookup or create one.
|
||||
if (!mInterfaceToNetIdHash.Get(aInterfaceName, aNetIdInfo)) {
|
||||
aNetIdInfo->mNetId = getNextNetId();
|
||||
aNetIdInfo->mRefCnt = 1;
|
||||
} else {
|
||||
aNetIdInfo->mRefCnt++;
|
||||
}
|
||||
|
||||
// Update hash and return.
|
||||
mInterfaceToNetIdHash.Put(aInterfaceName, *aNetIdInfo);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool NetIdManager::lookup(const nsString& aInterfaceName,
|
||||
NetIdInfo* aNetIdInfo)
|
||||
{
|
||||
return mInterfaceToNetIdHash.Get(aInterfaceName, aNetIdInfo);
|
||||
}
|
||||
|
||||
bool NetIdManager::release(const nsString& aInterfaceName,
|
||||
NetIdInfo* aNetIdInfo)
|
||||
{
|
||||
if (!mInterfaceToNetIdHash.Get(aInterfaceName, aNetIdInfo)) {
|
||||
return false; // No such key.
|
||||
}
|
||||
|
||||
aNetIdInfo->mRefCnt--;
|
||||
|
||||
// Update the hash if still be referenced.
|
||||
if (aNetIdInfo->mRefCnt > 0) {
|
||||
mInterfaceToNetIdHash.Put(aInterfaceName, *aNetIdInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
// No longer be referenced. Remove the entry.
|
||||
mInterfaceToNetIdHash.Remove(aInterfaceName);
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef NetIdManager_h
|
||||
#define NetIdManager_h
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
||||
// NetId is a logical network identifier defined by netd.
|
||||
// A network is typically a physical one (i.e. PhysicalNetwork.cpp)
|
||||
// for netd but it could be a virtual network as well.
|
||||
// We currently use physical network only and use one-to-one
|
||||
// network-interface mapping.
|
||||
|
||||
class NetIdManager {
|
||||
public:
|
||||
// keep in sync with system/netd/NetworkController.cpp
|
||||
enum {
|
||||
MIN_NET_ID = 100,
|
||||
MAX_NET_ID = 65535,
|
||||
};
|
||||
|
||||
// We need to count the number of references since different
|
||||
// application like data and mms may use the same interface.
|
||||
struct NetIdInfo {
|
||||
int mNetId;
|
||||
int mRefCnt;
|
||||
};
|
||||
|
||||
public:
|
||||
NetIdManager();
|
||||
|
||||
bool lookup(const nsString& aInterfaceName, NetIdInfo* aNetIdInfo);
|
||||
void acquire(const nsString& aInterfaceName, NetIdInfo* aNetIdInfo);
|
||||
bool release(const nsString& aInterfaceName, NetIdInfo* aNetIdInfo);
|
||||
|
||||
private:
|
||||
int getNextNetId();
|
||||
int mNextNetId;
|
||||
nsDataHashtable<nsStringHashKey, NetIdInfo> mInterfaceToNetIdHash;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -308,6 +308,17 @@ NetworkManager.prototype = {
|
|||
debug("Network '" + networkId + "' registered.");
|
||||
},
|
||||
|
||||
_addSubnetRoutes: function(network) {
|
||||
let ips = {};
|
||||
let prefixLengths = {};
|
||||
let length = network.getAddresses(ips, prefixLengths);
|
||||
for (let i = 0; i < length; i++) {
|
||||
debug('Adding subnet routes: ' + ips.value[i] + '/' + prefixLengths.value[i]);
|
||||
gNetworkService.modifyRoute(Ci.nsINetworkService.MODIFY_ROUTE_ADD,
|
||||
network.name, ips.value[i], prefixLengths.value[i]);
|
||||
}
|
||||
},
|
||||
|
||||
updateNetworkInterface: function(network) {
|
||||
if (!(network instanceof Ci.nsINetworkInterface)) {
|
||||
throw Components.Exception("Argument must be nsINetworkInterface.",
|
||||
|
@ -320,35 +331,50 @@ NetworkManager.prototype = {
|
|||
}
|
||||
debug("Network " + network.type + "/" + network.name +
|
||||
" changed state to " + network.state);
|
||||
|
||||
// Note that since Lollipop we need to allocate and initialize
|
||||
// something through netd, so we add createNetwork/destroyNetwork
|
||||
// to deal with that explicitly.
|
||||
|
||||
switch (network.state) {
|
||||
case Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED:
|
||||
// Add host route for data calls
|
||||
if (this.isNetworkTypeMobile(network.type)) {
|
||||
gNetworkService.removeHostRoutes(network.name);
|
||||
this.setHostRoutes(network);
|
||||
}
|
||||
// Dun type is a special case where we add the default route to a
|
||||
// secondary table.
|
||||
if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
|
||||
this.setSecondaryDefaultRoute(network);
|
||||
}
|
||||
// Remove pre-created default route and let setAndConfigureActive()
|
||||
// to set default route only on preferred network
|
||||
gNetworkService.removeDefaultRoute(network);
|
||||
this.setAndConfigureActive();
|
||||
|
||||
// Update data connection when Wifi connected/disconnected
|
||||
if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI && this.mRil) {
|
||||
for (let i = 0; i < this.mRil.numRadioInterfaces; i++) {
|
||||
this.mRil.getRadioInterface(i).updateRILNetworkInterface();
|
||||
gNetworkService.createNetwork(network.name, () => {
|
||||
// Add host route for data calls
|
||||
if (this.isNetworkTypeMobile(network.type)) {
|
||||
gNetworkService.removeHostRoutes(network.name);
|
||||
this.setHostRoutes(network);
|
||||
}
|
||||
}
|
||||
// Dun type is a special case where we add the default route to a
|
||||
// secondary table.
|
||||
if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
|
||||
this.setSecondaryDefaultRoute(network);
|
||||
}
|
||||
// Remove pre-created default route and let setAndConfigureActive()
|
||||
// to set default route only on preferred network
|
||||
gNetworkService.removeDefaultRoute(network);
|
||||
|
||||
this.onConnectionChanged(network);
|
||||
this._addSubnetRoutes(network);
|
||||
this.setAndConfigureActive();
|
||||
|
||||
// Update data connection when Wifi connected/disconnected
|
||||
if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI && this.mRil) {
|
||||
for (let i = 0; i < this.mRil.numRadioInterfaces; i++) {
|
||||
this.mRil.getRadioInterface(i).updateRILNetworkInterface();
|
||||
}
|
||||
}
|
||||
|
||||
this.onConnectionChanged(network);
|
||||
|
||||
// Probing the public network accessibility after routing table is ready
|
||||
CaptivePortalDetectionHelper
|
||||
.notify(CaptivePortalDetectionHelper.EVENT_CONNECT, this.active);
|
||||
|
||||
// Notify outer modules like MmsService to start the transaction after
|
||||
// the configuration of the network interface is done.
|
||||
Services.obs.notifyObservers(network, TOPIC_CONNECTION_STATE_CHANGED,
|
||||
this.convertConnectionType(network));
|
||||
});
|
||||
|
||||
// Probing the public network accessibility after routing table is ready
|
||||
CaptivePortalDetectionHelper
|
||||
.notify(CaptivePortalDetectionHelper.EVENT_CONNECT, this.active);
|
||||
break;
|
||||
case Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED:
|
||||
// Remove host route for data calls
|
||||
|
@ -378,13 +404,14 @@ NetworkManager.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
gNetworkService.destroyNetwork(network.name, () => {
|
||||
// Notify outer modules like MmsService to start the transaction after
|
||||
// the configuration of the network interface is done.
|
||||
Services.obs.notifyObservers(network, TOPIC_CONNECTION_STATE_CHANGED,
|
||||
this.convertConnectionType(network));
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
// Notify outer modules like MmsService to start the transaction after
|
||||
// the configuration of the network interface is done.
|
||||
Services.obs.notifyObservers(network, TOPIC_CONNECTION_STATE_CHANGED,
|
||||
this.convertConnectionType(network));
|
||||
},
|
||||
|
||||
unregisterNetworkInterface: function(network) {
|
||||
|
@ -435,14 +462,22 @@ NetworkManager.prototype = {
|
|||
},
|
||||
|
||||
_updateRoutes: function(doAdd, ipAddresses, networkName, gateways) {
|
||||
let getMaxPrefixLength = (aIp) => {
|
||||
return aIp.match(this.REGEXP_IPV4) ? IPV4_MAX_PREFIX_LENGTH : IPV6_MAX_PREFIX_LENGTH;
|
||||
}
|
||||
|
||||
let promises = [];
|
||||
|
||||
ipAddresses.forEach((aIpAddress) => {
|
||||
let gateway = this.selectGateway(gateways, aIpAddress);
|
||||
if (gateway) {
|
||||
promises.push((doAdd)
|
||||
? gNetworkService.addHostRoute(networkName, gateway, aIpAddress)
|
||||
: gNetworkService.removeHostRoute(networkName, gateway, aIpAddress));
|
||||
? gNetworkService.modifyRoute(Ci.nsINetworkService.MODIFY_ROUTE_ADD,
|
||||
networkName, aIpAddress,
|
||||
getMaxPrefixLength(aIpAddress), gateway)
|
||||
: gNetworkService.modifyRoute(Ci.nsINetworkService.MODIFY_ROUTE_REMOVE,
|
||||
networkName, aIpAddress,
|
||||
getMaxPrefixLength(aIpAddress), gateway));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1271,6 +1306,10 @@ NetworkManager.prototype = {
|
|||
|
||||
_setDefaultRouteAndDNS: function(network, oldInterface) {
|
||||
gNetworkService.setDefaultRoute(network, oldInterface, function(success) {
|
||||
if (!success) {
|
||||
gNetworkService.destroyNetwork(network, function() {});
|
||||
return;
|
||||
}
|
||||
gNetworkService.setDNS(network, function(result) {
|
||||
gNetworkService.setNetworkProxy(network);
|
||||
});
|
||||
|
|
|
@ -255,22 +255,12 @@ NetworkService.prototype = {
|
|||
},
|
||||
|
||||
resetRoutingTable: function(network) {
|
||||
let ips = {};
|
||||
let prefixLengths = {};
|
||||
let length = network.getAddresses(ips, prefixLengths);
|
||||
let options = {
|
||||
cmd: "removeNetworkRoute",
|
||||
ifname: network.name
|
||||
};
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
let ip = ips.value[i];
|
||||
let prefixLength = prefixLengths.value[i];
|
||||
|
||||
let options = {
|
||||
cmd: "removeNetworkRoute",
|
||||
ifname: network.name,
|
||||
ip: ip,
|
||||
prefixLength: prefixLength
|
||||
};
|
||||
this.controlMessage(options);
|
||||
}
|
||||
this.controlMessage(options);
|
||||
},
|
||||
|
||||
setDNS: function(networkInterface, callback) {
|
||||
|
@ -312,8 +302,20 @@ NetworkService.prototype = {
|
|||
this.controlMessage(options);
|
||||
},
|
||||
|
||||
_setHostRoute: function(doAdd, interfaceName, gateway, host) {
|
||||
let command = doAdd ? "addHostRoute" : "removeHostRoute";
|
||||
modifyRoute: function(action, interfaceName, host, prefixLength, gateway) {
|
||||
let command;
|
||||
|
||||
switch (action) {
|
||||
case Ci.nsINetworkService.MODIFY_ROUTE_ADD:
|
||||
command = 'addHostRoute';
|
||||
break;
|
||||
case Ci.nsINetworkService.MODIFY_ROUTE_REMOVE:
|
||||
command = 'removeHostRoute';
|
||||
break;
|
||||
default:
|
||||
if (DEBUG) debug('Unknown action: ' + action);
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
if (DEBUG) debug(command + " " + host + " on " + interfaceName);
|
||||
let deferred = Promise.defer();
|
||||
|
@ -321,6 +323,7 @@ NetworkService.prototype = {
|
|||
cmd: command,
|
||||
ifname: interfaceName,
|
||||
gateway: gateway,
|
||||
prefixLength: prefixLength,
|
||||
ip: host
|
||||
};
|
||||
this.controlMessage(options, function(data) {
|
||||
|
@ -333,14 +336,6 @@ NetworkService.prototype = {
|
|||
return deferred.promise;
|
||||
},
|
||||
|
||||
addHostRoute: function(interfaceName, gateway, host) {
|
||||
return this._setHostRoute(true, interfaceName, gateway, host);
|
||||
},
|
||||
|
||||
removeHostRoute: function(interfaceName, gateway, host) {
|
||||
return this._setHostRoute(false, interfaceName, gateway, host);
|
||||
},
|
||||
|
||||
removeHostRoutes: function(ifname) {
|
||||
if(DEBUG) debug("Going to remove all host routes on " + ifname);
|
||||
let options = {
|
||||
|
@ -567,6 +562,28 @@ NetworkService.prototype = {
|
|||
});
|
||||
},
|
||||
|
||||
createNetwork: function(interfaceName, callback) {
|
||||
let params = {
|
||||
cmd: "createNetwork",
|
||||
ifname: interfaceName
|
||||
};
|
||||
|
||||
this.controlMessage(params, function(result) {
|
||||
callback.nativeCommandResult(!result.error);
|
||||
});
|
||||
},
|
||||
|
||||
destroyNetwork: function(interfaceName, callback) {
|
||||
let params = {
|
||||
cmd: "destroyNetwork",
|
||||
ifname: interfaceName
|
||||
};
|
||||
|
||||
this.controlMessage(params, function(result) {
|
||||
callback.nativeCommandResult(!result.error);
|
||||
});
|
||||
},
|
||||
|
||||
shutdown: false,
|
||||
|
||||
observe: function observe(aSubject, aTopic, aData) {
|
||||
|
|
|
@ -248,12 +248,6 @@ const CommandFunc NetworkUtils::sNetworkInterfaceSetAlarmChain[] = {
|
|||
NetworkUtils::networkInterfaceAlarmSuccess
|
||||
};
|
||||
|
||||
const CommandFunc NetworkUtils::sSetDnsChain[] = {
|
||||
NetworkUtils::setDefaultInterface,
|
||||
NetworkUtils::setInterfaceDns,
|
||||
NetworkUtils::setDnsSuccess,
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to get the mask from given prefix length.
|
||||
*/
|
||||
|
@ -918,13 +912,34 @@ void NetworkUtils::setDefaultInterface(CommandChain* aChain,
|
|||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::removeDefaultRoute(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
// FIXME: (Bug 1121795) We only remove the first gateway to the default route.
|
||||
// For dual stack (ipv4/ipv6) device, one of the gateway would
|
||||
// not be added to the default route.
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1, "network route remove %d %s 0.0.0.0/0 %s",
|
||||
GET_FIELD(mNetId), GET_CHAR(mIfname), GET_CHAR(mGateways[0]));
|
||||
|
||||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::setInterfaceDns(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
int written = snprintf(command, sizeof command, "resolver setifdns %s %s",
|
||||
GET_CHAR(mIfname), GET_CHAR(mDomain));
|
||||
int written;
|
||||
|
||||
if (SDK_VERSION >= 20) {
|
||||
written = snprintf(command, sizeof command, "resolver setnetdns %d %s",
|
||||
GET_FIELD(mNetId), GET_CHAR(mDomain));
|
||||
} else {
|
||||
written = snprintf(command, sizeof command, "resolver setifdns %s %s",
|
||||
GET_CHAR(mIfname), GET_CHAR(mDomain));
|
||||
}
|
||||
|
||||
nsTArray<nsString>& dnses = GET_FIELD(mDnses);
|
||||
uint32_t length = dnses.Length();
|
||||
|
@ -949,6 +964,188 @@ void NetworkUtils::setInterfaceDns(CommandChain* aChain,
|
|||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::clearAddrForInterface(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1, "interface clearaddrs %s", GET_CHAR(mIfname));
|
||||
|
||||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::createNetwork(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1, "network create %d", GET_FIELD(mNetId));
|
||||
|
||||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::destroyNetwork(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1, "network destroy %d", GET_FIELD(mNetId));
|
||||
|
||||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::addInterfaceToNetwork(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1, "network interface add %d %s",
|
||||
GET_FIELD(mNetId), GET_CHAR(mIfname));
|
||||
|
||||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::addRouteToInterface(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
struct MyCallback {
|
||||
static void callback(CommandCallback::CallbackType aOriginalCallback,
|
||||
CommandChain* aChain,
|
||||
bool aError,
|
||||
mozilla::dom::NetworkResultOptions& aResult)
|
||||
{
|
||||
NS_ConvertUTF16toUTF8 reason(aResult.mResultReason);
|
||||
NU_DBG("addRouteToInterface's reason: %s", reason.get());
|
||||
if (aError && reason.EqualsASCII("addRoute() failed (File exists)")) {
|
||||
NU_DBG("Ignore \"File exists\" error when adding host route.");
|
||||
return aOriginalCallback(aChain, false, aResult);
|
||||
}
|
||||
aOriginalCallback(aChain, aError, aResult);
|
||||
}
|
||||
};
|
||||
|
||||
CommandCallback wrappedCallback(MyCallback::callback, aCallback);
|
||||
modifyRouteOnInterface(aChain, wrappedCallback, aResult, true);
|
||||
}
|
||||
|
||||
void NetworkUtils::removeRouteFromInterface(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
modifyRouteOnInterface(aChain, aCallback, aResult, false);
|
||||
}
|
||||
|
||||
void NetworkUtils::modifyRouteOnInterface(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult,
|
||||
bool aDoAdd)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
|
||||
// AOSP adds host route to its interface table but it doesn't work for
|
||||
// B2G because we cannot set fwmark per application. So, we add
|
||||
// all host routes to legacy_system table except scope link route.
|
||||
|
||||
nsCString ipOrSubnetIp = NS_ConvertUTF16toUTF8(GET_FIELD(mIp));
|
||||
nsCString gatewayOrEmpty;
|
||||
const char* legacyOrEmpty = "legacy 0 "; // Add to legacy by default.
|
||||
if (GET_FIELD(mGateway).IsEmpty()) {
|
||||
ipOrSubnetIp = getSubnetIp(ipOrSubnetIp, GET_FIELD(mPrefixLength));
|
||||
legacyOrEmpty = ""; // Add to interface table for scope link route.
|
||||
} else {
|
||||
gatewayOrEmpty = nsCString(" ") + NS_ConvertUTF16toUTF8(GET_FIELD(mGateway));
|
||||
}
|
||||
|
||||
const char* action = aDoAdd ? "add" : "remove";
|
||||
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1, "network route %s%s %d %s %s/%d%s",
|
||||
legacyOrEmpty, action,
|
||||
GET_FIELD(mNetId), GET_CHAR(mIfname), ipOrSubnetIp.get(),
|
||||
GET_FIELD(mPrefixLength), gatewayOrEmpty.get());
|
||||
|
||||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::addDefaultRouteToNetwork(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
|
||||
// FIXME: (Bug 1121795) We only add the first gateway to the default route.
|
||||
// For dual stack (ipv4/ipv6) device, one of the gateway would
|
||||
// not be added to the default route.
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1, "network route add %d %s 0.0.0.0/0 %s",
|
||||
GET_FIELD(mNetId), GET_CHAR(mIfname), GET_CHAR(mGateways[0]));
|
||||
|
||||
struct MyCallback {
|
||||
static void callback(CommandCallback::CallbackType aOriginalCallback,
|
||||
CommandChain* aChain,
|
||||
bool aError,
|
||||
mozilla::dom::NetworkResultOptions& aResult)
|
||||
{
|
||||
NS_ConvertUTF16toUTF8 reason(aResult.mResultReason);
|
||||
NU_DBG("addDefaultRouteToNetwork's reason: %s", reason.get());
|
||||
if (aError && reason.EqualsASCII("addRoute() failed (File exists)")) {
|
||||
NU_DBG("Ignore \"File exists\" error when adding host route.");
|
||||
return aOriginalCallback(aChain, false, aResult);
|
||||
}
|
||||
aOriginalCallback(aChain, aError, aResult);
|
||||
}
|
||||
};
|
||||
|
||||
CommandCallback wrappedCallback(MyCallback::callback, aCallback);
|
||||
doCommand(command, aChain, wrappedCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::setDefaultNetwork(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1, "network default set %d", GET_FIELD(mNetId));
|
||||
|
||||
doCommand(command, aChain, aCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::setIpv6Enabled(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult,
|
||||
bool aEnabled)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1, "interface ipv6 %s %s",
|
||||
GET_CHAR(mIfname), aEnabled ? "enable" : "disable");
|
||||
|
||||
struct MyCallback {
|
||||
static void callback(CommandCallback::CallbackType aOriginalCallback,
|
||||
CommandChain* aChain,
|
||||
bool aError,
|
||||
mozilla::dom::NetworkResultOptions& aResult)
|
||||
{
|
||||
aOriginalCallback(aChain, false, aResult);
|
||||
}
|
||||
};
|
||||
|
||||
CommandCallback wrappedCallback(MyCallback::callback, aCallback);
|
||||
doCommand(command, aChain, wrappedCallback);
|
||||
}
|
||||
|
||||
void NetworkUtils::enableIpv6(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
setIpv6Enabled(aChain, aCallback, aResult, true);
|
||||
}
|
||||
|
||||
void NetworkUtils::disableIpv6(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
setIpv6Enabled(aChain, aCallback, aResult, false);
|
||||
}
|
||||
|
||||
#undef GET_CHAR
|
||||
#undef GET_FIELD
|
||||
|
||||
|
@ -1053,14 +1250,6 @@ void NetworkUtils::networkInterfaceAlarmSuccess(CommandChain* aChain,
|
|||
finalizeSuccess(aChain, aResult);
|
||||
}
|
||||
|
||||
void NetworkUtils::setDnsSuccess(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
postMessage(aChain->getParams(), aResult);
|
||||
finalizeSuccess(aChain, aResult);
|
||||
}
|
||||
|
||||
void NetworkUtils::updateUpStreamFail(NetworkParams& aOptions, NetworkResultOptions& aResult)
|
||||
{
|
||||
postMessage(aOptions, aResult);
|
||||
|
@ -1107,6 +1296,22 @@ void NetworkUtils::setDnsFail(NetworkParams& aOptions, NetworkResultOptions& aRe
|
|||
postMessage(aOptions, aResult);
|
||||
}
|
||||
|
||||
void NetworkUtils::defaultAsyncSuccessHandler(CommandChain* aChain,
|
||||
CommandCallback aCallback,
|
||||
NetworkResultOptions& aResult)
|
||||
{
|
||||
NU_DBG("defaultAsyncSuccessHandler");
|
||||
aResult.mRet = true;
|
||||
postMessage(aChain->getParams(), aResult);
|
||||
finalizeSuccess(aChain, aResult);
|
||||
}
|
||||
|
||||
void NetworkUtils::defaultAsyncFailureHandler(NetworkParams& aOptions, NetworkResultOptions& aResult)
|
||||
{
|
||||
aResult.mRet = false;
|
||||
postMessage(aOptions, aResult);
|
||||
}
|
||||
|
||||
#undef ASSIGN_FIELD
|
||||
#undef ASSIGN_FIELD_VALUE
|
||||
|
||||
|
@ -1170,6 +1375,8 @@ void NetworkUtils::ExecuteCommand(NetworkParams aOptions)
|
|||
BUILD_ENTRY(enableInterface),
|
||||
BUILD_ENTRY(disableInterface),
|
||||
BUILD_ENTRY(resetConnections),
|
||||
BUILD_ENTRY(createNetwork),
|
||||
BUILD_ENTRY(destroyNetwork),
|
||||
|
||||
#undef BUILD_ENTRY
|
||||
};
|
||||
|
@ -1266,14 +1473,14 @@ void NetworkUtils::onNetdMessage(NetdCommand* aCommand)
|
|||
gPending = false;
|
||||
}
|
||||
|
||||
if (gCurrentCommand.callback) {
|
||||
{
|
||||
char buf[BUF_SIZE];
|
||||
join(gReason, INTERFACE_DELIMIT, BUF_SIZE, buf);
|
||||
|
||||
NetworkResultOptions result;
|
||||
result.mResultCode = code;
|
||||
result.mResultReason = NS_ConvertUTF8toUTF16(buf);
|
||||
(*gCurrentCommand.callback)(gCurrentCommand.chain, isError(code), result);
|
||||
(gCurrentCommand.callback)(gCurrentCommand.chain, isError(code), result);
|
||||
gReason.Clear();
|
||||
}
|
||||
|
||||
|
@ -1335,8 +1542,28 @@ CommandResult NetworkUtils::setDNS(NetworkParams& aOptions)
|
|||
property_set("net.dnschange", num);
|
||||
|
||||
// DNS needs to be set through netd since JellyBean (4.3).
|
||||
if (SDK_VERSION >= 20) {
|
||||
// Lollipop.
|
||||
static CommandFunc COMMAND_CHAIN[] = {
|
||||
setInterfaceDns,
|
||||
defaultAsyncSuccessHandler
|
||||
};
|
||||
NetIdManager::NetIdInfo netIdInfo;
|
||||
if (!mNetIdManager.lookup(aOptions.mIfname, &netIdInfo)) {
|
||||
return -1;
|
||||
}
|
||||
aOptions.mNetId = netIdInfo.mNetId;
|
||||
runChain(aOptions, COMMAND_CHAIN, setDnsFail);
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
if (SDK_VERSION >= 18) {
|
||||
runChain(aOptions, sSetDnsChain, setDnsFail);
|
||||
// JB, KK.
|
||||
static CommandFunc COMMAND_CHAIN[] = {
|
||||
setDefaultInterface,
|
||||
setInterfaceDns,
|
||||
defaultAsyncSuccessHandler
|
||||
};
|
||||
runChain(aOptions, COMMAND_CHAIN, setDnsFail);
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
|
@ -1439,6 +1666,33 @@ CommandResult NetworkUtils::resetConnections(NetworkParams& aOptions) {
|
|||
* Set default route and DNS servers for given network interface.
|
||||
*/
|
||||
CommandResult NetworkUtils::setDefaultRoute(NetworkParams& aOptions)
|
||||
{
|
||||
if (SDK_VERSION < 20) {
|
||||
return setDefaultRouteLegacy(aOptions);
|
||||
}
|
||||
|
||||
static CommandFunc COMMAND_CHAIN[] = {
|
||||
addDefaultRouteToNetwork,
|
||||
setDefaultNetwork,
|
||||
defaultAsyncSuccessHandler,
|
||||
};
|
||||
|
||||
NetIdManager::NetIdInfo netIdInfo;
|
||||
if (!mNetIdManager.lookup(GET_FIELD(mIfname), &netIdInfo)) {
|
||||
ERROR("No such interface");
|
||||
return -1;
|
||||
}
|
||||
|
||||
aOptions.mNetId = netIdInfo.mNetId;
|
||||
|
||||
runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler);
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default route and DNS servers for given network interface by obsoleted libnetutils.
|
||||
*/
|
||||
CommandResult NetworkUtils::setDefaultRouteLegacy(NetworkParams& aOptions)
|
||||
{
|
||||
NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
|
||||
|
||||
|
@ -1493,6 +1747,37 @@ CommandResult NetworkUtils::setDefaultRoute(NetworkParams& aOptions)
|
|||
*/
|
||||
CommandResult NetworkUtils::removeDefaultRoute(NetworkParams& aOptions)
|
||||
{
|
||||
NU_DBG("Calling NetworkUtils::removeDefaultRoute");
|
||||
|
||||
if (SDK_VERSION < 20) {
|
||||
return removeDefaultRouteLegacy(aOptions);
|
||||
}
|
||||
|
||||
static CommandFunc COMMAND_CHAIN[] = {
|
||||
removeDefaultRoute,
|
||||
defaultAsyncSuccessHandler,
|
||||
};
|
||||
|
||||
NetIdManager::NetIdInfo netIdInfo;
|
||||
if (!mNetIdManager.lookup(GET_FIELD(mIfname), &netIdInfo)) {
|
||||
ERROR("No such interface: %s", GET_CHAR(mIfname));
|
||||
return -1;
|
||||
}
|
||||
|
||||
NU_DBG("Obtained netid %d for interface %s", netIdInfo.mNetId, GET_CHAR(mIfname));
|
||||
|
||||
aOptions.mNetId = netIdInfo.mNetId;
|
||||
runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler);
|
||||
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove default route for given network interface by obsoleted libnetutils.
|
||||
*/
|
||||
CommandResult NetworkUtils::removeDefaultRouteLegacy(NetworkParams& aOptions)
|
||||
{
|
||||
// Legacy libnetutils calls before Lollipop.
|
||||
uint32_t length = aOptions.mGateways.Length();
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
NS_ConvertUTF16toUTF8 autoGateway(aOptions.mGateways[i]);
|
||||
|
@ -1514,6 +1799,34 @@ CommandResult NetworkUtils::removeDefaultRoute(NetworkParams& aOptions)
|
|||
* Add host route for given network interface.
|
||||
*/
|
||||
CommandResult NetworkUtils::addHostRoute(NetworkParams& aOptions)
|
||||
{
|
||||
if (SDK_VERSION < 20) {
|
||||
return addHostRouteLegacy(aOptions);
|
||||
}
|
||||
|
||||
static CommandFunc COMMAND_CHAIN[] = {
|
||||
addRouteToInterface,
|
||||
defaultAsyncSuccessHandler,
|
||||
};
|
||||
|
||||
NetIdManager::NetIdInfo netIdInfo;
|
||||
if (!mNetIdManager.lookup(GET_FIELD(mIfname), &netIdInfo)) {
|
||||
ERROR("No such interface: %s", GET_CHAR(mIfname));
|
||||
return -1;
|
||||
}
|
||||
|
||||
NU_DBG("Obtained netid %d for interface %s", netIdInfo.mNetId, GET_CHAR(mIfname));
|
||||
|
||||
aOptions.mNetId = netIdInfo.mNetId;
|
||||
runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler);
|
||||
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add host route for given network interface.
|
||||
*/
|
||||
CommandResult NetworkUtils::addHostRouteLegacy(NetworkParams& aOptions)
|
||||
{
|
||||
NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
|
||||
NS_ConvertUTF16toUTF8 autoHostname(aOptions.mIp);
|
||||
|
@ -1538,6 +1851,34 @@ CommandResult NetworkUtils::addHostRoute(NetworkParams& aOptions)
|
|||
* Remove host route for given network interface.
|
||||
*/
|
||||
CommandResult NetworkUtils::removeHostRoute(NetworkParams& aOptions)
|
||||
{
|
||||
if (SDK_VERSION < 20) {
|
||||
return removeHostRouteLegacy(aOptions);
|
||||
}
|
||||
|
||||
static CommandFunc COMMAND_CHAIN[] = {
|
||||
removeRouteFromInterface,
|
||||
defaultAsyncSuccessHandler,
|
||||
};
|
||||
|
||||
NetIdManager::NetIdInfo netIdInfo;
|
||||
if (!mNetIdManager.lookup(GET_FIELD(mIfname), &netIdInfo)) {
|
||||
ERROR("No such interface: %s", GET_CHAR(mIfname));
|
||||
return -1;
|
||||
}
|
||||
|
||||
NU_DBG("Obtained netid %d for interface %s", netIdInfo.mNetId, GET_CHAR(mIfname));
|
||||
|
||||
aOptions.mNetId = netIdInfo.mNetId;
|
||||
runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler);
|
||||
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove host route for given network interface.
|
||||
*/
|
||||
CommandResult NetworkUtils::removeHostRouteLegacy(NetworkParams& aOptions)
|
||||
{
|
||||
NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
|
||||
NS_ConvertUTF16toUTF8 autoHostname(aOptions.mIp);
|
||||
|
@ -1562,11 +1903,89 @@ CommandResult NetworkUtils::removeHostRoute(NetworkParams& aOptions)
|
|||
* Remove the routes associated with the named interface.
|
||||
*/
|
||||
CommandResult NetworkUtils::removeHostRoutes(NetworkParams& aOptions)
|
||||
{
|
||||
if (SDK_VERSION < 20) {
|
||||
return removeHostRoutesLegacy(aOptions);
|
||||
}
|
||||
|
||||
NU_DBG("Don't know how to remove host routes on a interface");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the routes associated with the named interface.
|
||||
*/
|
||||
CommandResult NetworkUtils::removeHostRoutesLegacy(NetworkParams& aOptions)
|
||||
{
|
||||
return mNetUtils->do_ifc_remove_host_routes(GET_CHAR(mIfname));
|
||||
}
|
||||
|
||||
CommandResult NetworkUtils::removeNetworkRoute(NetworkParams& aOptions)
|
||||
{
|
||||
if (SDK_VERSION < 20) {
|
||||
return removeNetworkRouteLegacy(aOptions);
|
||||
}
|
||||
|
||||
static CommandFunc COMMAND_CHAIN[] = {
|
||||
clearAddrForInterface,
|
||||
defaultAsyncSuccessHandler,
|
||||
};
|
||||
|
||||
NetIdManager::NetIdInfo netIdInfo;
|
||||
if (!mNetIdManager.lookup(GET_FIELD(mIfname), &netIdInfo)) {
|
||||
ERROR("interface %s is not present in any network", GET_CHAR(mIfname));
|
||||
return -1;
|
||||
}
|
||||
|
||||
NU_DBG("Obtained netid %d for interface %s", netIdInfo.mNetId, GET_CHAR(mIfname));
|
||||
|
||||
aOptions.mNetId = netIdInfo.mNetId;
|
||||
runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler);
|
||||
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
nsCString NetworkUtils::getSubnetIp(const nsCString& aIp, int aPrefixLength)
|
||||
{
|
||||
int type = getIpType(aIp.get());
|
||||
|
||||
if (AF_INET6 == type) {
|
||||
struct in6_addr in6;
|
||||
if (inet_pton(AF_INET6, aIp.get(), &in6) != 1) {
|
||||
return nsCString();
|
||||
}
|
||||
|
||||
uint32_t p, i, p1, mask;
|
||||
p = aPrefixLength;
|
||||
i = 0;
|
||||
while (i < 4) {
|
||||
p1 = p > 32 ? 32 : p;
|
||||
p -= p1;
|
||||
mask = p1 ? ~0x0 << (32 - p1) : 0;
|
||||
in6.s6_addr32[i++] &= htonl(mask);
|
||||
}
|
||||
|
||||
char subnetStr[INET6_ADDRSTRLEN];
|
||||
if (!inet_ntop(AF_INET6, &in6, subnetStr, sizeof subnetStr)) {
|
||||
return nsCString();
|
||||
}
|
||||
|
||||
return nsCString(subnetStr);
|
||||
}
|
||||
|
||||
if (AF_INET == type) {
|
||||
uint32_t ip = inet_addr(aIp.get());
|
||||
uint32_t netmask = makeMask(aPrefixLength);
|
||||
uint32_t subnet = ip & netmask;
|
||||
struct in_addr addr;
|
||||
addr.s_addr = subnet;
|
||||
return nsCString(inet_ntoa(addr));
|
||||
}
|
||||
|
||||
return nsCString();
|
||||
}
|
||||
|
||||
CommandResult NetworkUtils::removeNetworkRouteLegacy(NetworkParams& aOptions)
|
||||
{
|
||||
NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
|
||||
NS_ConvertUTF16toUTF8 autoIp(aOptions.mIp);
|
||||
|
@ -1857,6 +2276,74 @@ CommandResult NetworkUtils::updateUpStream(NetworkParams& aOptions)
|
|||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
/**
|
||||
* handling upstream interface change event.
|
||||
*/
|
||||
CommandResult NetworkUtils::createNetwork(NetworkParams& aOptions)
|
||||
{
|
||||
if (SDK_VERSION < 20) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static CommandFunc COMMAND_CHAIN[] = {
|
||||
createNetwork,
|
||||
enableIpv6,
|
||||
addInterfaceToNetwork,
|
||||
defaultAsyncSuccessHandler,
|
||||
};
|
||||
|
||||
NetIdManager::NetIdInfo netIdInfo;
|
||||
mNetIdManager.acquire(GET_FIELD(mIfname), &netIdInfo);
|
||||
if (netIdInfo.mRefCnt > 1) {
|
||||
// Already created. Just return.
|
||||
NU_DBG("Interface %s (%d) has been created.", GET_CHAR(mIfname),
|
||||
netIdInfo.mNetId);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
NU_DBG("Request netd to create a network with netid %d", netIdInfo.mNetId);
|
||||
// Newly created netid. Ask netd to create network.
|
||||
aOptions.mNetId = netIdInfo.mNetId;
|
||||
runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler);
|
||||
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
/**
|
||||
* handling upstream interface change event.
|
||||
*/
|
||||
CommandResult NetworkUtils::destroyNetwork(NetworkParams& aOptions)
|
||||
{
|
||||
if (SDK_VERSION < 20) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static CommandFunc COMMAND_CHAIN[] = {
|
||||
disableIpv6,
|
||||
destroyNetwork,
|
||||
defaultAsyncSuccessHandler,
|
||||
};
|
||||
|
||||
NetIdManager::NetIdInfo netIdInfo;
|
||||
if (!mNetIdManager.release(GET_FIELD(mIfname), &netIdInfo)) {
|
||||
ERROR("No existing netid for %s", GET_CHAR(mIfname));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (netIdInfo.mRefCnt > 0) {
|
||||
// Still be referenced. Just return.
|
||||
NU_DBG("Someone is still using this interface.");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
NU_DBG("Interface %s (%d) is no longer used. Tell netd to destroy.",
|
||||
GET_CHAR(mIfname), netIdInfo.mNetId);
|
||||
|
||||
aOptions.mNetId = netIdInfo.mNetId;
|
||||
runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler);
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
void NetworkUtils::sendBroadcastMessage(uint32_t code, char* reason)
|
||||
{
|
||||
NetworkResultOptions result;
|
||||
|
|
|
@ -10,12 +10,55 @@
|
|||
#include "mozilla/dom/network/NetUtils.h"
|
||||
#include "mozilla/ipc/Netd.h"
|
||||
#include "nsTArray.h"
|
||||
#include "NetIdManager.h"
|
||||
|
||||
class NetworkParams;
|
||||
class CommandChain;
|
||||
|
||||
typedef void (*CommandCallback)(CommandChain*, bool,
|
||||
mozilla::dom::NetworkResultOptions& aResult);
|
||||
class CommandCallback {
|
||||
public:
|
||||
typedef void (*CallbackType)(CommandChain*, bool,
|
||||
mozilla::dom::NetworkResultOptions& aResult);
|
||||
|
||||
typedef void (*CallbackWrapperType)(CallbackType aOriginalCallback,
|
||||
CommandChain*, bool,
|
||||
mozilla::dom::NetworkResultOptions& aResult);
|
||||
|
||||
CommandCallback()
|
||||
: mCallback(nullptr)
|
||||
, mCallbackWrapper(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
CommandCallback(CallbackType aCallback)
|
||||
: mCallback(aCallback)
|
||||
, mCallbackWrapper(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
CommandCallback(CallbackWrapperType aCallbackWrapper,
|
||||
CommandCallback aOriginalCallback)
|
||||
: mCallback(aOriginalCallback.mCallback)
|
||||
, mCallbackWrapper(aCallbackWrapper)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(CommandChain* aChain, bool aError,
|
||||
mozilla::dom::NetworkResultOptions& aResult)
|
||||
{
|
||||
if (mCallbackWrapper) {
|
||||
return mCallbackWrapper(mCallback, aChain, aError, aResult);
|
||||
}
|
||||
if (mCallback) {
|
||||
return mCallback(aChain, aError, aResult);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CallbackType mCallback;
|
||||
CallbackWrapperType mCallbackWrapper;
|
||||
};
|
||||
|
||||
typedef void (*CommandFunc)(CommandChain*, CommandCallback,
|
||||
mozilla::dom::NetworkResultOptions& aResult);
|
||||
typedef void (*MessageCallback)(mozilla::dom::NetworkResultOptions& aResult);
|
||||
|
@ -109,6 +152,7 @@ public:
|
|||
#undef COPY_FIELD
|
||||
}
|
||||
|
||||
// Followings attributes are 1-to-1 mapping to NetworkCommandOptions.
|
||||
int32_t mId;
|
||||
nsString mCmd;
|
||||
nsString mDomain;
|
||||
|
@ -152,6 +196,9 @@ public:
|
|||
long mGateway_long;
|
||||
long mDns1_long;
|
||||
long mDns2_long;
|
||||
|
||||
// Auxiliary information required to carry accros command chain.
|
||||
int mNetId; // A locally defined id per interface.
|
||||
};
|
||||
|
||||
// CommandChain store the necessary information to execute command one by one.
|
||||
|
@ -258,6 +305,16 @@ private:
|
|||
CommandResult setUSBTethering(NetworkParams& aOptions);
|
||||
CommandResult enableUsbRndis(NetworkParams& aOptions);
|
||||
CommandResult updateUpStream(NetworkParams& aOptions);
|
||||
CommandResult createNetwork(NetworkParams& aOptions);
|
||||
CommandResult destroyNetwork(NetworkParams& aOptions);
|
||||
|
||||
CommandResult addHostRouteLegacy(NetworkParams& aOptions);
|
||||
CommandResult removeHostRouteLegacy(NetworkParams& aOptions);
|
||||
CommandResult removeHostRoutesLegacy(NetworkParams& aOptions);
|
||||
CommandResult setDefaultRouteLegacy(NetworkParams& aOptions);
|
||||
CommandResult removeDefaultRouteLegacy(NetworkParams& aOptions);
|
||||
CommandResult removeNetworkRouteLegacy(NetworkParams& aOptions);
|
||||
|
||||
|
||||
/**
|
||||
* function pointer array holds all netd commands should be executed
|
||||
|
@ -277,7 +334,6 @@ private:
|
|||
static const CommandFunc sNetworkInterfaceEnableAlarmChain[];
|
||||
static const CommandFunc sNetworkInterfaceDisableAlarmChain[];
|
||||
static const CommandFunc sNetworkInterfaceSetAlarmChain[];
|
||||
static const CommandFunc sSetDnsChain[];
|
||||
|
||||
/**
|
||||
* Individual netd command stored in command chain.
|
||||
|
@ -318,7 +374,23 @@ private:
|
|||
static void updateUpStreamSuccess(PARAMS);
|
||||
static void setDhcpServerSuccess(PARAMS);
|
||||
static void wifiOperationModeSuccess(PARAMS);
|
||||
static void setDnsSuccess(PARAMS);
|
||||
static void clearAddrForInterface(PARAMS);
|
||||
static void createNetwork(PARAMS);
|
||||
static void destroyNetwork(PARAMS);
|
||||
static void addInterfaceToNetwork(PARAMS);
|
||||
static void addDefaultRouteToNetwork(PARAMS);
|
||||
static void setDefaultNetwork(PARAMS);
|
||||
static void removeDefaultRoute(PARAMS);
|
||||
static void removeNetworkRouteSuccess(PARAMS);
|
||||
static void removeNetworkRoute(PARAMS);
|
||||
static void addRouteToInterface(PARAMS);
|
||||
static void removeRouteFromInterface(PARAMS);
|
||||
static void modifyRouteOnInterface(PARAMS, bool aDoAdd);
|
||||
static void enableIpv6(PARAMS);
|
||||
static void disableIpv6(PARAMS);
|
||||
static void setIpv6Enabled(PARAMS, bool aEnabled);
|
||||
static void defaultAsyncSuccessHandler(PARAMS);
|
||||
|
||||
#undef PARAMS
|
||||
|
||||
/**
|
||||
|
@ -333,6 +405,7 @@ private:
|
|||
static void setDhcpServerFail(PARAMS);
|
||||
static void networkInterfaceAlarmFail(PARAMS);
|
||||
static void setDnsFail(PARAMS);
|
||||
static void defaultAsyncFailureHandler(PARAMS);
|
||||
#undef PARAMS
|
||||
|
||||
/**
|
||||
|
@ -370,6 +443,8 @@ private:
|
|||
const CommandFunc (&aCmds)[N],
|
||||
ErrorCallback aError);
|
||||
|
||||
static nsCString getSubnetIp(const nsCString& aIp, int aPrefixLength);
|
||||
|
||||
/**
|
||||
* Callback function to send netd result to main thread.
|
||||
*/
|
||||
|
@ -379,6 +454,8 @@ private:
|
|||
* Utility class to access libnetutils.
|
||||
*/
|
||||
nsAutoPtr<NetUtils> mNetUtils;
|
||||
|
||||
NetIdManager mNetIdManager;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -44,6 +44,7 @@ UNIFIED_SOURCES += [
|
|||
'MozMtpDatabase.cpp',
|
||||
'MozMtpServer.cpp',
|
||||
'MozMtpStorage.cpp',
|
||||
'NetIdManager.cpp',
|
||||
'NetworkUtils.cpp',
|
||||
'NetworkWorker.cpp',
|
||||
'nsVolume.cpp',
|
||||
|
|
|
@ -159,9 +159,12 @@ interface nsIDhcpRequestCallback : nsISupports
|
|||
/**
|
||||
* Provide network services.
|
||||
*/
|
||||
[scriptable, uuid(9f1d78e0-1314-11e4-9191-0800200c9a66)]
|
||||
[scriptable, uuid(f7e23599-176a-435b-9ecf-7e5274bdfa76)]
|
||||
interface nsINetworkService : nsISupports
|
||||
{
|
||||
const long MODIFY_ROUTE_ADD = 0;
|
||||
const long MODIFY_ROUTE_REMOVE = 1;
|
||||
|
||||
/**
|
||||
* Enable or disable Wifi Tethering
|
||||
*
|
||||
|
@ -314,36 +317,28 @@ interface nsINetworkService : nsISupports
|
|||
void removeDefaultRoute(in nsINetworkInterface networkInterface);
|
||||
|
||||
/**
|
||||
* Add host route.
|
||||
* Modify route.
|
||||
*
|
||||
* @param action
|
||||
* nsINetworkService.MODIFY_ROUTE_ADD to add route and
|
||||
* nsINetworkService.MODIFY_ROUTE_REMOVE to remove.
|
||||
* @param interfaceName
|
||||
* Network interface name for the output of the host route.
|
||||
* @param gateway
|
||||
* Gateway ip for the output of the host route.
|
||||
* @param host
|
||||
* Host ip we want to add route for.
|
||||
*
|
||||
* @return A deferred promise that resolves on success or rejects with a
|
||||
* specified reason otherwise.
|
||||
*/
|
||||
jsval addHostRoute(in DOMString interfaceName, in DOMString gateway,
|
||||
in DOMString host);
|
||||
|
||||
/**
|
||||
* Remove host route.
|
||||
*
|
||||
* @param interfaceName
|
||||
* Network interface name for the output of the host route.
|
||||
* @param gateway
|
||||
* Gateway ip for the output of the host route.
|
||||
* @param host
|
||||
* Host ip we want to remove route for.
|
||||
* @param prefixLength
|
||||
* The prefix length of the route we'd like to modify.
|
||||
* @param [optional] gateway
|
||||
* Gateway ip for the output of the host route.
|
||||
*
|
||||
* @return A deferred promise that resolves on success or rejects with a
|
||||
* specified reason otherwise.
|
||||
*/
|
||||
jsval removeHostRoute(in DOMString interfaceName, in DOMString gateway,
|
||||
in DOMString host);
|
||||
jsval modifyRoute(in long action,
|
||||
in DOMString interfaceName,
|
||||
in DOMString host,
|
||||
in long prefixLength,
|
||||
[optional] in DOMString gateway);
|
||||
|
||||
/**
|
||||
* Remove all host routes.
|
||||
|
@ -470,4 +465,28 @@ interface nsINetworkService : nsISupports
|
|||
*/
|
||||
void resetConnections(in DOMString interfaceName,
|
||||
in nsINativeCommandCallback callback);
|
||||
|
||||
/**
|
||||
* Create network (required to call prior to any networking operation)
|
||||
*
|
||||
* @param networkInterface
|
||||
* The network interface name which we want to reset.
|
||||
*
|
||||
* @param callback
|
||||
* Callback to notify the result of resetting connections.
|
||||
*/
|
||||
void createNetwork(in DOMString interfaceName,
|
||||
in nsINativeCommandCallback callback);
|
||||
|
||||
/**
|
||||
* Destroy network (required to call prior to any networking operation)
|
||||
*
|
||||
* @param networkInterface
|
||||
* The network interface name which we want to reset.
|
||||
*
|
||||
* @param callback
|
||||
* Callback to notify the result of resetting connections.
|
||||
*/
|
||||
void destroyNetwork(in DOMString interfaceName,
|
||||
in nsINativeCommandCallback callback);
|
||||
};
|
||||
|
|
|
@ -15503,7 +15503,7 @@ ICCContactHelperObject.prototype = {
|
|||
*/
|
||||
readAllPhonebookSets: function(pbrs, onsuccess, onerror) {
|
||||
let allContacts = [], pbrIndex = 0;
|
||||
let readPhonebook = function readPhonebook(contacts) {
|
||||
let readPhonebook = function(contacts) {
|
||||
if (contacts) {
|
||||
allContacts = allContacts.concat(contacts);
|
||||
}
|
||||
|
|
|
@ -125,15 +125,219 @@ add_test(function test_read_icc_contacts() {
|
|||
let record = context.ICCRecordHelper;
|
||||
let contactHelper = context.ICCContactHelper;
|
||||
let ril = context.RIL;
|
||||
let test_data = [
|
||||
//Record 1.
|
||||
{
|
||||
comment: "Test read SIM adn contact",
|
||||
rawData: {
|
||||
simType: CARD_APPTYPE_SIM,
|
||||
contactType: GECKO_CARDCONTACT_TYPE_ADN,
|
||||
adnLike: [{recordId: 1, alphaId: "name", number: "111111"}],
|
||||
},
|
||||
expectedContact: [{
|
||||
recordId: 1,
|
||||
alphaId: "name",
|
||||
number: "111111"
|
||||
}],
|
||||
},
|
||||
//Record 2.
|
||||
{
|
||||
comment: "Test read SIM fdn contact",
|
||||
rawData: {
|
||||
simType: CARD_APPTYPE_SIM,
|
||||
contactType: GECKO_CARDCONTACT_TYPE_FDN,
|
||||
adnLike: [{recordId: 1, alphaId: "name", number: "111111"}],
|
||||
},
|
||||
expectedContact: [{
|
||||
recordId: 1,
|
||||
alphaId: "name",
|
||||
number: "111111"
|
||||
}],
|
||||
},
|
||||
//Record 3.
|
||||
{
|
||||
comment: "Test read USIM adn contact",
|
||||
rawData: {
|
||||
simType: CARD_APPTYPE_USIM,
|
||||
contactType: GECKO_CARDCONTACT_TYPE_ADN,
|
||||
pbrs: [{adn:{fileId: 0x6f3a}, email: {}, anr0: {}}],
|
||||
adnLike: [{recordId: 1, alphaId: "name", number: "111111"}],
|
||||
email: "hello@mail.com",
|
||||
anr: "123456",
|
||||
},
|
||||
expectedContact: [{
|
||||
pbrIndex: 0,
|
||||
recordId: 1,
|
||||
alphaId: "name",
|
||||
number: "111111",
|
||||
email: "hello@mail.com",
|
||||
anr: ["123456"]
|
||||
}],
|
||||
},
|
||||
//Record 4.
|
||||
{
|
||||
comment: "Test read USIM adn contacts",
|
||||
rawData: {
|
||||
simType: CARD_APPTYPE_USIM,
|
||||
contactType: GECKO_CARDCONTACT_TYPE_ADN,
|
||||
pbrs: [{adn:{fileId: 0x6f3a}, email: {}, anr0: {}},
|
||||
{adn:{fileId: 0x6f3b}, email: {}, anr0: {}}],
|
||||
adnLike: [{recordId: 1, alphaId: "name1", number: "111111"},
|
||||
{recordId: 2, alphaId: "name2", number: "222222"}],
|
||||
email: "hello@mail.com",
|
||||
anr: "123456",
|
||||
},
|
||||
expectedContact: [
|
||||
{
|
||||
pbrIndex: 0,
|
||||
recordId: 1,
|
||||
alphaId: "name1",
|
||||
number: "111111",
|
||||
email: "hello@mail.com",
|
||||
anr: ["123456"]
|
||||
}, {
|
||||
pbrIndex: 0,
|
||||
recordId: 2,
|
||||
alphaId: "name2",
|
||||
number: "222222",
|
||||
email: "hello@mail.com",
|
||||
anr: ["123456"]
|
||||
}, {
|
||||
pbrIndex: 1,
|
||||
recordId: 1,
|
||||
alphaId: "name1",
|
||||
number: "111111",
|
||||
email: "hello@mail.com",
|
||||
anr: ["123456"]
|
||||
}, {
|
||||
pbrIndex: 1,
|
||||
recordId: 2,
|
||||
alphaId: "name2",
|
||||
number: "222222",
|
||||
email: "hello@mail.com",
|
||||
anr: ["123456"]
|
||||
}
|
||||
],
|
||||
},
|
||||
//Record 5.
|
||||
{
|
||||
comment: "Test read USIM fdn contact",
|
||||
rawData: {
|
||||
simType: CARD_APPTYPE_USIM,
|
||||
contactType: GECKO_CARDCONTACT_TYPE_FDN,
|
||||
adnLike: [{recordId: 1, alphaId: "name", number: "111111"}],
|
||||
},
|
||||
expectedContact: [{
|
||||
recordId: 1,
|
||||
alphaId: "name",
|
||||
number: "111111"
|
||||
}],
|
||||
},
|
||||
//Record 6.
|
||||
{
|
||||
comment: "Test read RUIM adn contact",
|
||||
rawData: {
|
||||
simType: CARD_APPTYPE_RUIM,
|
||||
contactType: GECKO_CARDCONTACT_TYPE_ADN,
|
||||
adnLike: [{recordId: 1, alphaId: "name", number: "111111"}],
|
||||
},
|
||||
expectedContact: [{
|
||||
recordId: 1,
|
||||
alphaId: "name",
|
||||
number: "111111"
|
||||
}],
|
||||
},
|
||||
//Record 7.
|
||||
{
|
||||
comment: "Test read RUIM fdn contact",
|
||||
rawData: {
|
||||
simType: CARD_APPTYPE_RUIM,
|
||||
contactType: GECKO_CARDCONTACT_TYPE_FDN,
|
||||
adnLike: [{recordId: 1, alphaId: "name", number: "111111"}],
|
||||
},
|
||||
expectedContact: [{
|
||||
recordId: 1,
|
||||
alphaId: "name",
|
||||
number: "111111"
|
||||
}],
|
||||
},
|
||||
//Record 8.
|
||||
{
|
||||
comment: "Test read RUIM adn contact with enhanced phone book",
|
||||
rawData: {
|
||||
simType: CARD_APPTYPE_RUIM,
|
||||
contactType: GECKO_CARDCONTACT_TYPE_ADN,
|
||||
pbrs: [{adn:{fileId: 0x6f3a}, email: {}, anr0: {}}],
|
||||
adnLike: [{recordId: 1, alphaId: "name", number: "111111"}],
|
||||
email: "hello@mail.com",
|
||||
anr: "123456",
|
||||
enhancedPhoneBook: true,
|
||||
},
|
||||
expectedContact: [{
|
||||
pbrIndex: 0,
|
||||
recordId: 1,
|
||||
alphaId: "name",
|
||||
number: "111111",
|
||||
email: "hello@mail.com",
|
||||
anr: ["123456"]
|
||||
}],
|
||||
},
|
||||
//Record 9.
|
||||
{
|
||||
comment: "Test read RUIM adn contacts with enhanced phone book",
|
||||
rawData: {
|
||||
simType: CARD_APPTYPE_RUIM,
|
||||
contactType: GECKO_CARDCONTACT_TYPE_ADN,
|
||||
pbrs: [{adn:{fileId: 0x6f3a}, email: {}, anr0: {}},
|
||||
{adn:{fileId: 0x6f3b}, email: {}, anr0: {}}],
|
||||
adnLike: [{recordId: 1, alphaId: "name1", number: "111111"},
|
||||
{recordId: 2, alphaId: "name2", number: "222222"}],
|
||||
email: "hello@mail.com",
|
||||
anr: "123456",
|
||||
enhancedPhoneBook: true,
|
||||
},
|
||||
expectedContact: [
|
||||
{
|
||||
pbrIndex: 0,
|
||||
recordId: 1,
|
||||
alphaId: "name1",
|
||||
number: "111111",
|
||||
email: "hello@mail.com",
|
||||
anr: ["123456"]
|
||||
}, {
|
||||
pbrIndex: 0,
|
||||
recordId: 2,
|
||||
alphaId: "name2",
|
||||
number: "222222",
|
||||
email: "hello@mail.com",
|
||||
anr: ["123456"]
|
||||
}, {
|
||||
pbrIndex: 1,
|
||||
recordId: 1,
|
||||
alphaId: "name1",
|
||||
number: "111111",
|
||||
email: "hello@mail.com",
|
||||
anr: ["123456"]
|
||||
}, {
|
||||
pbrIndex: 1,
|
||||
recordId: 2,
|
||||
alphaId: "name2",
|
||||
number: "222222",
|
||||
email: "hello@mail.com",
|
||||
anr: ["123456"]
|
||||
}
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function do_test(aSimType, aContactType, aExpectedContact, aEnhancedPhoneBook) {
|
||||
ril.appType = aSimType;
|
||||
ril._isCdma = (aSimType === CARD_APPTYPE_RUIM);
|
||||
ril.iccInfoPrivate.cst = (aEnhancedPhoneBook) ?
|
||||
function do_test(aTestData, aExpectedContact) {
|
||||
ril.appType = aTestData.simType;
|
||||
ril._isCdma = (aTestData.simType === CARD_APPTYPE_RUIM);
|
||||
ril.iccInfoPrivate.cst = (aTestData.enhancedPhoneBook) ?
|
||||
[0x20, 0x0C, 0x0, 0x0, 0x0]:
|
||||
[0x20, 0x00, 0x0, 0x0, 0x0];
|
||||
|
||||
ril.iccInfoPrivate.sst = (aSimType === CARD_APPTYPE_SIM)?
|
||||
ril.iccInfoPrivate.sst = (aTestData.simType === CARD_APPTYPE_SIM)?
|
||||
[0x20, 0x0, 0x0, 0x0, 0x0]:
|
||||
[0x2, 0x0, 0x0, 0x0, 0x0];
|
||||
|
||||
|
@ -143,30 +347,25 @@ add_test(function test_read_icc_contacts() {
|
|||
};
|
||||
|
||||
record.readPBR = function readPBR(onsuccess, onerror) {
|
||||
onsuccess([{adn:{fileId: 0x6f3a}, email: {}, anr0: {}}]);
|
||||
onsuccess(JSON.parse(JSON.stringify(aTestData.pbrs)));
|
||||
};
|
||||
|
||||
record.readADNLike = function readADNLike(fileId, onsuccess, onerror) {
|
||||
onsuccess([{recordId: 1, alphaId: "name", number: "111111"}])
|
||||
onsuccess(JSON.parse(JSON.stringify(aTestData.adnLike)));
|
||||
};
|
||||
|
||||
record.readEmail = function readEmail(fileId, fileType, recordNumber, onsuccess, onerror) {
|
||||
onsuccess("hello@mail.com");
|
||||
onsuccess(aTestData.email);
|
||||
};
|
||||
|
||||
record.readANR = function readANR(fileId, fileType, recordNumber, onsuccess, onerror) {
|
||||
onsuccess("123456");
|
||||
onsuccess(aTestData.anr);
|
||||
};
|
||||
|
||||
let onsuccess = function onsuccess(contacts) {
|
||||
let contact = contacts[0];
|
||||
for (let key in contact) {
|
||||
do_print("check " + key);
|
||||
if (Array.isArray(contact[key])) {
|
||||
do_check_eq(contact[key][0], aExpectedContact[key]);
|
||||
} else {
|
||||
do_check_eq(contact[key], aExpectedContact[key]);
|
||||
}
|
||||
for (let i = 0; i < contacts.length; i++) {
|
||||
do_print("check contacts[" + i + "]:" + JSON.stringify(contacts[i]));
|
||||
deepEqual(contacts[i], aExpectedContact[i]);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -175,52 +374,13 @@ add_test(function test_read_icc_contacts() {
|
|||
do_check_true(false);
|
||||
};
|
||||
|
||||
contactHelper.readICCContacts(aSimType, aContactType, onsuccess, onerror);
|
||||
contactHelper.readICCContacts(aTestData.simType, aTestData.contactType, onsuccess, onerror);
|
||||
}
|
||||
|
||||
let expectedContact1 = {
|
||||
pbrIndex: 0,
|
||||
recordId: 1,
|
||||
alphaId: "name",
|
||||
number: "111111"
|
||||
};
|
||||
|
||||
let expectedContact2 = {
|
||||
pbrIndex: 0,
|
||||
recordId: 1,
|
||||
alphaId: "name",
|
||||
number: "111111",
|
||||
email: "hello@mail.com",
|
||||
anr: "123456"
|
||||
};
|
||||
|
||||
// SIM
|
||||
do_print("Test read SIM adn contacts");
|
||||
do_test(CARD_APPTYPE_SIM, GECKO_CARDCONTACT_TYPE_ADN, expectedContact1);
|
||||
|
||||
do_print("Test read SIM fdn contacts");
|
||||
do_test(CARD_APPTYPE_SIM, GECKO_CARDCONTACT_TYPE_FDN, expectedContact1);
|
||||
|
||||
// USIM
|
||||
do_print("Test read USIM adn contacts");
|
||||
do_test(CARD_APPTYPE_USIM, GECKO_CARDCONTACT_TYPE_ADN, expectedContact2);
|
||||
|
||||
do_print("Test read USIM fdn contacts");
|
||||
do_test(CARD_APPTYPE_USIM, GECKO_CARDCONTACT_TYPE_FDN, expectedContact1);
|
||||
|
||||
// RUIM
|
||||
do_print("Test read RUIM adn contacts");
|
||||
do_test(CARD_APPTYPE_RUIM, GECKO_CARDCONTACT_TYPE_ADN, expectedContact1);
|
||||
|
||||
do_print("Test read RUIM fdn contacts");
|
||||
do_test(CARD_APPTYPE_RUIM, GECKO_CARDCONTACT_TYPE_FDN, expectedContact1);
|
||||
|
||||
// RUIM with enhanced phone book
|
||||
do_print("Test read RUIM adn contacts with enhanced phone book");
|
||||
do_test(CARD_APPTYPE_RUIM, GECKO_CARDCONTACT_TYPE_ADN, expectedContact2, true);
|
||||
|
||||
do_print("Test read RUIM fdn contacts with enhanced phone book");
|
||||
do_test(CARD_APPTYPE_RUIM, GECKO_CARDCONTACT_TYPE_FDN, expectedContact1, true);
|
||||
for (let i = 0; i < test_data.length; i++) {
|
||||
do_print(test_data[i].comment);
|
||||
do_test(test_data[i].rawData, test_data[i].expectedContact);
|
||||
}
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
|
|
@ -203,6 +203,39 @@ methods of other kinds of objects.
|
|||
|
||||
* and the fourth line begins at offset 10.
|
||||
|
||||
`getAllColumnOffsets()`:
|
||||
: Return an array describing the relationship between bytecode instruction
|
||||
offsets and source code positions in this script. Unlike getAllOffsets(),
|
||||
which returns all offsets that are entry points for each line,
|
||||
getAllColumnOffsets() returns all offsets that are entry points for each
|
||||
(line, column) pair.
|
||||
|
||||
The elements of the array are objects, each of which describes a single
|
||||
entry point, and contains the following properties:
|
||||
|
||||
* lineNumber: the line number for which offset is an entry point
|
||||
|
||||
* columnNumber: the column number for which offset is an entry point
|
||||
|
||||
* offset: the bytecode instruction offset of the entry point
|
||||
|
||||
For example, suppose we have a script for the following source code:
|
||||
|
||||
```language-js
|
||||
a=[]
|
||||
for (i=1; i < 10; i++)
|
||||
// It's hip to be square.
|
||||
a[i] = i*i;
|
||||
```
|
||||
|
||||
Calling `getAllColumnOffsets()` on that code might yield an array like this:
|
||||
|
||||
```language-js
|
||||
[{ lineNumber: 0, columnNumber: 0, offset: 0 },
|
||||
{ lineNumber: 1, columnNumber: 5, offset: 5 },
|
||||
{ lineNumber: 1, columnNumber: 10, offset: 20 },
|
||||
{ lineNumber: 3, columnNumber: 4, offset: 10 }]
|
||||
|
||||
<code>getLineOffsets(<i>line</i>)</code>
|
||||
: Return an array of bytecode instruction offsets representing the entry
|
||||
points to source line <i>line</i>. If the script contains no executable
|
||||
|
@ -269,4 +302,6 @@ methods of other kinds of objects.
|
|||
<i>offset</i> is not a valid bytecode offset in this script, throw an
|
||||
error.
|
||||
|
||||
|
||||
<code>isInCatchScope([<i>offset</i>])</code>
|
||||
: This is `true` if this offset falls within the scope of a try block, and
|
||||
`false` otherwise.
|
||||
|
|
|
@ -4,6 +4,13 @@
|
|||
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.util.GeckoRequest;
|
||||
import org.mozilla.gecko.util.NativeJSObject;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.os.SystemClock;
|
||||
import android.util.FloatMath;
|
||||
|
@ -160,4 +167,29 @@ class MotionEventHelper {
|
|||
tap(x, y);
|
||||
tap(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* dragSync() can accidentally trigger longpress events on slower devices.
|
||||
* Consumers can instruct Gecko to ignore them.
|
||||
*/
|
||||
public void disableGeckoLongpress() {
|
||||
final JSONObject json = new JSONObject();
|
||||
try {
|
||||
json.put("isLongPressEnabled", 0);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "JSON error - Error creating request to ignore longpress events.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
GeckoAppShell.sendRequestToGecko(new GeckoRequest("ContextMenu:SetIsLongpressEnabled", json) {
|
||||
@Override
|
||||
public void onResponse(NativeJSObject response) {
|
||||
Log.d(LOGTAG, "Gecko received request to ignore longpress events.");
|
||||
}
|
||||
@Override
|
||||
public void onError(NativeJSObject error) {
|
||||
Log.d(LOGTAG, "No response from Gecko on request to ignore longpress events.");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ public class testAxisLocking extends PixelTest {
|
|||
// load page and check we're at 0,0
|
||||
loadAndVerifyBoxes(url);
|
||||
|
||||
// Tell Gecko to ignore any longpress events triggered by MEH.
|
||||
meh.disableGeckoLongpress();
|
||||
|
||||
// drag page upwards by 100 pixels with a slight angle. verify that
|
||||
// axis locking prevents any horizontal scrolling
|
||||
Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint();
|
||||
|
|
|
@ -36,6 +36,9 @@ public class testCheck extends PixelTest {
|
|||
|
||||
mDriver.startCheckerboardRecording();
|
||||
|
||||
// Tell Gecko to ignore any longpress events triggered by MEH.
|
||||
meh.disableGeckoLongpress();
|
||||
|
||||
// Scroll repeatedly downwards, then upwards. On each iteration of i,
|
||||
// increase the scroll distance to test different scroll amounts.
|
||||
for (int i = 2; i < 7; i++) {
|
||||
|
|
|
@ -24,6 +24,9 @@ public class testFlingCorrectness extends PixelTest {
|
|||
// load page and check we're at 0,0
|
||||
loadAndVerifyBoxes(url);
|
||||
|
||||
// Tell Gecko to ignore any longpress events triggered by MEH.
|
||||
meh.disableGeckoLongpress();
|
||||
|
||||
// drag page upwards by 200 pixels (use two drags instead of one in case
|
||||
// the screen size is small)
|
||||
Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint();
|
||||
|
|
|
@ -24,6 +24,9 @@ public class testPanCorrectness extends PixelTest {
|
|||
// load page and check we're at 0,0
|
||||
loadAndVerifyBoxes(url);
|
||||
|
||||
// Tell Gecko to ignore any longpress events triggered by MEH.
|
||||
meh.disableGeckoLongpress();
|
||||
|
||||
// drag page upwards by 100 pixels
|
||||
Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint();
|
||||
meh.dragSync(10, 150, 10, 50);
|
||||
|
|
|
@ -30,6 +30,9 @@ public class testVkbOverlap extends PixelTest {
|
|||
// scroll to the bottom of the page and let it settle
|
||||
Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint();
|
||||
MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop());
|
||||
|
||||
// Tell Gecko to ignore any longpress events triggered by MEH.
|
||||
meh.disableGeckoLongpress();
|
||||
meh.dragSync(10, 150, 10, 50);
|
||||
|
||||
// the input field has a green background, so let's count the number of green pixels
|
||||
|
|
|
@ -2267,9 +2267,14 @@ var NativeWindow = {
|
|||
contextmenus: {
|
||||
items: {}, // a list of context menu items that we may show
|
||||
DEFAULT_HTML5_ORDER: -1, // Sort order for HTML5 context menu items
|
||||
_isLongPressEnabled: 1, // Android longpress events can be ignored during robocop tests.
|
||||
|
||||
init: function() {
|
||||
BrowserApp.deck.addEventListener("contextmenu", this.show.bind(this), false);
|
||||
|
||||
Messaging.addListener((data) => {
|
||||
return {result: (this._isLongPressEnabled = data.isLongPressEnabled)};
|
||||
}, "ContextMenu:SetIsLongpressEnabled");
|
||||
},
|
||||
|
||||
add: function() {
|
||||
|
@ -2562,6 +2567,11 @@ var NativeWindow = {
|
|||
* for chrome consumers to do lazy menuitem construction
|
||||
*/
|
||||
show: function(event) {
|
||||
if (!this._isLongPressEnabled) {
|
||||
dump("Longpress Event is ignored by request");
|
||||
return;
|
||||
}
|
||||
|
||||
// Android Long-press / contextmenu event provides clientX/Y data. This is not provided
|
||||
// by mochitest: test_browserElement_inproc_ContextmenuEvents.html.
|
||||
if (!event.clientX || !event.clientY) {
|
||||
|
|
|
@ -1752,8 +1752,8 @@ class Mochitest(MochitestUtilsMixin):
|
|||
|
||||
self.setTestRoot(options)
|
||||
|
||||
# Until we have all green, this only runs on bc* jobs (not dt* jobs)
|
||||
if options.browserChrome and not options.subsuite:
|
||||
# Until we have all green, this only runs on bc*/dt* jobs
|
||||
if options.browserChrome:
|
||||
options.runByDir = True
|
||||
|
||||
if not options.runByDir:
|
||||
|
|
|
@ -148,7 +148,7 @@ function ensureModuleIsOpen() {
|
|||
* @param args
|
||||
* array of arguments to pass to the notification.
|
||||
*/
|
||||
function notify(observers, notification, args) {
|
||||
function notify(observers, notification, args = []) {
|
||||
for (let observer of observers) {
|
||||
try {
|
||||
observer[notification](...args);
|
||||
|
@ -253,7 +253,7 @@ this.History = Object.freeze({
|
|||
* A callback invoked for each page found.
|
||||
*
|
||||
* @return (Promise)
|
||||
* A promise resoled once the operation is complete.
|
||||
* A promise resolved once the operation is complete.
|
||||
* @resolve (bool)
|
||||
* `true` if at least one page was removed, `false` otherwise.
|
||||
* @throws (TypeError)
|
||||
|
@ -328,7 +328,7 @@ this.History = Object.freeze({
|
|||
* The full URI of the page or the GUID of the page.
|
||||
*
|
||||
* @return (Promise)
|
||||
* A promise resoled once the operation is complete.
|
||||
* A promise resolved once the operation is complete.
|
||||
* @resolve (bool)
|
||||
* `true` if the page has been visited, `false` otherwise.
|
||||
* @throws (Error)
|
||||
|
@ -339,6 +339,28 @@ this.History = Object.freeze({
|
|||
throw new Error("Method not implemented");
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear all history.
|
||||
*
|
||||
* @return (Promise)
|
||||
* A promise resolved once the operation is complete.
|
||||
*/
|
||||
clear() {
|
||||
ensureModuleIsOpen();
|
||||
|
||||
return Task.spawn(function* () {
|
||||
let promise = clear();
|
||||
operationsBarrier.client.addBlocker("History.clear", promise);
|
||||
|
||||
try {
|
||||
return (yield promise);
|
||||
} finally {
|
||||
// Cleanup the barrier.
|
||||
operationsBarrier.client.removeBlocker(promise);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Possible values for the `transition` property of `VisitInfo`
|
||||
* objects.
|
||||
|
@ -453,6 +475,34 @@ let invalidateFrecencies = Task.async(function*(db, idList) {
|
|||
);
|
||||
});
|
||||
|
||||
// Inner implementation of History.clear().
|
||||
let clear = Task.async(function* () {
|
||||
let db = yield DBConnPromised;
|
||||
|
||||
// Remove all history.
|
||||
yield db.execute("DELETE FROM moz_historyvisits");
|
||||
|
||||
// Clear the registered embed visits.
|
||||
PlacesUtils.history.clearEmbedVisits();
|
||||
|
||||
// Expiration will take care of orphans.
|
||||
let observers = PlacesUtils.history.getObservers();
|
||||
notify(observers, "onClearHistory");
|
||||
|
||||
// Invalidate frecencies for the remaining places. This must happen
|
||||
// after the notification to ensure it runs enqueued to expiration.
|
||||
yield db.execute(
|
||||
`UPDATE moz_places SET frecency =
|
||||
(CASE
|
||||
WHEN url BETWEEN 'place:' AND 'place;'
|
||||
THEN 0
|
||||
ELSE -1
|
||||
END)
|
||||
WHERE frecency > 0`);
|
||||
|
||||
// Notify frecency change observers.
|
||||
notify(observers, "onManyFrecenciesChanged");
|
||||
});
|
||||
|
||||
// Inner implementation of History.remove.
|
||||
let remove = Task.async(function*({guids, urls}, onResult = null) {
|
||||
|
|
|
@ -29,7 +29,7 @@ XPCOMUtils.defineLazyGetter(this, "localFileCtor",
|
|||
"nsILocalFile", "initWithPath"));
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "filenamesRegex",
|
||||
() => new RegExp("^bookmarks-([0-9\-]+)(?:_([0-9]+)){0,1}(?:_([a-z0-9=\+\-]{24})){0,1}\.(json(lz4)?)$", "i")
|
||||
() => /^bookmarks-([0-9-]+)(?:_([0-9]+)){0,1}(?:_([a-z0-9=+-]{24})){0,1}\.(json(lz4)?)$/i
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -265,7 +265,7 @@ this.PlacesBackups = {
|
|||
"https://bugzilla.mozilla.org/show_bug.cgi?id=859695");
|
||||
|
||||
for (let i = 0; i < this._entries.length; i++) {
|
||||
let rx = new RegExp("\.json(lz4)?$");
|
||||
let rx = /\.json(lz4)?$/;
|
||||
if (this._entries[i].leafName.match(rx))
|
||||
return this._entries[i];
|
||||
}
|
||||
|
@ -282,7 +282,7 @@ this.PlacesBackups = {
|
|||
return Task.spawn(function* () {
|
||||
let entries = yield this.getBackupFiles();
|
||||
for (let entry of entries) {
|
||||
let rx = new RegExp("\.json(lz4)?$");
|
||||
let rx = /\.json(lz4)?$/;
|
||||
if (OS.Path.basename(entry).match(rx)) {
|
||||
return entry;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче