Merge m-c to inbound. a=merge
2
CLOBBER
@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Merge day clobber
|
||||
Bug 1101553 - remove nsPIPlacesHistoryListenersNotifier
|
||||
|
@ -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="ce6c9b72d863c80c842c65acf3b040baf4901a97"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
|
||||
<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="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<!-- 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"/>
|
||||
|
@ -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="ce6c9b72d863c80c842c65acf3b040baf4901a97"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
|
||||
<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="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<!-- 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="ce6c9b72d863c80c842c65acf3b040baf4901a97"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
|
||||
<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="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<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="ce6c9b72d863c80c842c65acf3b040baf4901a97"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
|
||||
<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="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<!-- 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="ce6c9b72d863c80c842c65acf3b040baf4901a97"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
|
||||
<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="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<!-- 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="ce6c9b72d863c80c842c65acf3b040baf4901a97"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
|
||||
<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="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<!-- 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="ce6c9b72d863c80c842c65acf3b040baf4901a97"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
|
||||
<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="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<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": "3f1ebdadd4043e68d2f6ebf295651e0d4cc5c93f",
|
||||
"revision": "5e3f9375165bca64a0066f51fbd79bd32e380a6b",
|
||||
"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="ce6c9b72d863c80c842c65acf3b040baf4901a97"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
|
||||
<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="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<!-- 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="ce6c9b72d863c80c842c65acf3b040baf4901a97"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
|
||||
<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="ce6c9b72d863c80c842c65acf3b040baf4901a97"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
|
||||
<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="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<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="ce6c9b72d863c80c842c65acf3b040baf4901a97"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bcc76f93f5659ac1eb8a769167109fd2d7ca4fbd"/>
|
||||
<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="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3f4cd30032f7d9002421bdb78860c28c78760888"/>
|
||||
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
|
@ -6450,8 +6450,18 @@ function WindowIsClosing()
|
||||
|
||||
for (let browser of gBrowser.browsers) {
|
||||
let ds = browser.docShell;
|
||||
if (ds.contentViewer && !ds.contentViewer.permitUnload())
|
||||
// Passing true to permitUnload indicates we plan on closing the window.
|
||||
// This means that once unload is permitted, all further calls to
|
||||
// permitUnload will be ignored. This avoids getting multiple prompts
|
||||
// to unload the page.
|
||||
if (ds.contentViewer && !ds.contentViewer.permitUnload(true)) {
|
||||
// ... however, if the user aborts closing, we need to undo that,
|
||||
// to ensure they get prompted again when we next try to close the window.
|
||||
// We do this on the window's toplevel docshell instead of on the tab, so
|
||||
// that all tabs we iterated before will get this reset.
|
||||
window.getInterface(Ci.nsIDocShell).contentViewer.resetCloseWindow();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -2043,25 +2043,6 @@
|
||||
return false;
|
||||
|
||||
var browser = this.getBrowserForTab(aTab);
|
||||
if (!aTab._pendingPermitUnload && !aTabWillBeMoved) {
|
||||
let ds = browser.docShell;
|
||||
if (ds && ds.contentViewer) {
|
||||
// We need to block while calling permitUnload() because it
|
||||
// processes the event queue and may lead to another removeTab()
|
||||
// call before permitUnload() even returned.
|
||||
aTab._pendingPermitUnload = true;
|
||||
let permitUnload = ds.contentViewer.permitUnload();
|
||||
delete aTab._pendingPermitUnload;
|
||||
// If we were closed during onbeforeunload, we return false now
|
||||
// so we don't (try to) close the same tab again. Of course, we
|
||||
// also stop if the unload was cancelled by the user:
|
||||
if (aTab.closing || !permitUnload) {
|
||||
// NB: deliberately keep the _closedDuringPermitUnload set to
|
||||
// true so we keep exiting early in case of multiple calls.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var closeWindow = false;
|
||||
var newTab = false;
|
||||
@ -2085,6 +2066,26 @@
|
||||
newTab = true;
|
||||
}
|
||||
|
||||
if (!aTab._pendingPermitUnload && !aTabWillBeMoved) {
|
||||
let ds = browser.docShell;
|
||||
if (ds && ds.contentViewer) {
|
||||
// We need to block while calling permitUnload() because it
|
||||
// processes the event queue and may lead to another removeTab()
|
||||
// call before permitUnload() returns.
|
||||
aTab._pendingPermitUnload = true;
|
||||
let permitUnload = ds.contentViewer.permitUnload();
|
||||
delete aTab._pendingPermitUnload;
|
||||
// If we were closed during onbeforeunload, we return false now
|
||||
// so we don't (try to) close the same tab again. Of course, we
|
||||
// also stop if the unload was cancelled by the user:
|
||||
if (aTab.closing || !permitUnload) {
|
||||
// NB: deliberately keep the _closedDuringPermitUnload set to
|
||||
// true so we keep exiting early in case of multiple calls.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aTab.closing = true;
|
||||
this._removingTabs.push(aTab);
|
||||
this._visibleTabs = null; // invalidate cache
|
||||
|
@ -126,6 +126,8 @@ skip-if = e10s # Bug 1101993 - times out for unknown reasons when run in the dir
|
||||
[browser_autocomplete_tag_star_visibility.js]
|
||||
[browser_backButtonFitts.js]
|
||||
skip-if = os != "win" || e10s # The Fitts Law back button is only supported on Windows (bug 571454) / e10s - Bug 1099154: test touches content (attempts to add an event listener directly to the contentWindow)
|
||||
[browser_beforeunload_duplicate_dialogs.js]
|
||||
skip-if = e10s # bug 967873 means permitUnload doesn't work in e10s mode
|
||||
[browser_blob-channelname.js]
|
||||
[browser_bookmark_titles.js]
|
||||
skip-if = buildapp == 'mulet' || toolkit == "windows" || e10s # Disabled on Windows due to frequent failures (bugs 825739, 841341) / e10s - Bug 1094205 - places doesn't return the right thing in e10s mode, for some reason
|
||||
|
@ -0,0 +1,44 @@
|
||||
const TEST_PAGE = "http://mochi.test:8888/browser/browser/base/content/test/general/file_double_close_tab.html";
|
||||
|
||||
let expectingDialog = false;
|
||||
function onTabModalDialogLoaded(node) {
|
||||
ok(expectingDialog, "Should be expecting this dialog.");
|
||||
expectingDialog = false;
|
||||
// This accepts the dialog, closing it
|
||||
node.Dialog.ui.button0.click();
|
||||
}
|
||||
|
||||
|
||||
// Listen for the dialog being created
|
||||
Services.obs.addObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded", false);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("browser.tabs.warnOnClose");
|
||||
Services.obs.removeObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded");
|
||||
});
|
||||
|
||||
add_task(function* closeLastTabInWindow() {
|
||||
let newWin = yield promiseOpenAndLoadWindow({}, true);
|
||||
let firstTab = newWin.gBrowser.selectedTab;
|
||||
yield promiseTabLoadEvent(firstTab, TEST_PAGE);
|
||||
let windowClosedPromise = promiseWindowWillBeClosed(newWin);
|
||||
expectingDialog = true;
|
||||
// close tab:
|
||||
document.getAnonymousElementByAttribute(firstTab, "anonid", "close-button").click();
|
||||
yield windowClosedPromise;
|
||||
ok(!expectingDialog, "There should have been a dialog.");
|
||||
ok(newWin.closed, "Window should be closed.");
|
||||
});
|
||||
|
||||
add_task(function* closeWindowWithMultipleTabsIncludingOneBeforeUnload() {
|
||||
Services.prefs.setBoolPref("browser.tabs.warnOnClose", false);
|
||||
let newWin = yield promiseOpenAndLoadWindow({}, true);
|
||||
let firstTab = newWin.gBrowser.selectedTab;
|
||||
yield promiseTabLoadEvent(firstTab, TEST_PAGE);
|
||||
yield promiseTabLoadEvent(newWin.gBrowser.addTab(), "http://example.com/");
|
||||
let windowClosedPromise = promiseWindowWillBeClosed(newWin);
|
||||
expectingDialog = true;
|
||||
newWin.BrowserTryToCloseWindow();
|
||||
yield windowClosedPromise;
|
||||
ok(!expectingDialog, "There should have been a dialog.");
|
||||
ok(newWin.closed, "Window should be closed.");
|
||||
});
|
@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for bug 1050638 - clicking tab close button twice should close tab even in beforeunload case</title>
|
||||
<title>Test page that blocks beforeunload. Used in tests for bug 1050638 and bug 305085</title>
|
||||
</head>
|
||||
<body>
|
||||
This page will block beforeunload. It should still be user-closable at all times.
|
||||
|
@ -202,14 +202,21 @@ function whenNewWindowLoaded(aOptions, aCallback) {
|
||||
}, false);
|
||||
}
|
||||
|
||||
function promiseWindowClosed(win) {
|
||||
let deferred = Promise.defer();
|
||||
win.addEventListener("unload", function onunload() {
|
||||
win.removeEventListener("unload", onunload);
|
||||
deferred.resolve();
|
||||
function promiseWindowWillBeClosed(win) {
|
||||
return new Promise((resolve, reject) => {
|
||||
Services.obs.addObserver(function observe(subject, topic) {
|
||||
if (subject == win) {
|
||||
Services.obs.removeObserver(observe, topic);
|
||||
resolve();
|
||||
}
|
||||
}, "domwindowclosed", false);
|
||||
});
|
||||
}
|
||||
|
||||
function promiseWindowClosed(win) {
|
||||
let promise = promiseWindowWillBeClosed(win);
|
||||
win.close();
|
||||
return deferred.promise;
|
||||
return promise;
|
||||
}
|
||||
|
||||
function promiseOpenAndLoadWindow(aOptions, aWaitForDelayedStartup=false) {
|
||||
|
@ -611,12 +611,8 @@ XPCOMUtils.defineLazyGetter(DownloadsCommon, "isWinVistaOrHigher", function () {
|
||||
function DownloadsDataCtor(aPrivate) {
|
||||
this._isPrivate = aPrivate;
|
||||
|
||||
// This Object contains all the available DownloadsDataItem objects, indexed by
|
||||
// their globally unique identifier. The identifiers of downloads that have
|
||||
// been removed from the Download Manager data are still present, however the
|
||||
// associated objects are replaced with the value "null". This is required to
|
||||
// prevent race conditions when populating the list asynchronously.
|
||||
this.dataItems = {};
|
||||
// Contains all the available DownloadsDataItem objects.
|
||||
this.dataItems = new Set();
|
||||
|
||||
// Array of view objects that should be notified when the available download
|
||||
// data changes.
|
||||
@ -644,8 +640,8 @@ DownloadsDataCtor.prototype = {
|
||||
* True if there are finished downloads that can be removed from the list.
|
||||
*/
|
||||
get canRemoveFinished() {
|
||||
for (let [, dataItem] of Iterator(this.dataItems)) {
|
||||
if (dataItem && !dataItem.inProgress) {
|
||||
for (let dataItem of this.dataItems) {
|
||||
if (!dataItem.inProgress) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -668,7 +664,7 @@ DownloadsDataCtor.prototype = {
|
||||
onDownloadAdded(aDownload) {
|
||||
let dataItem = new DownloadsDataItem(aDownload);
|
||||
this._downloadToDataItemMap.set(aDownload, dataItem);
|
||||
this.dataItems[dataItem.downloadGuid] = dataItem;
|
||||
this.dataItems.add(dataItem);
|
||||
|
||||
for (let view of this._views) {
|
||||
view.onDataItemAdded(dataItem, true);
|
||||
@ -695,7 +691,7 @@ DownloadsDataCtor.prototype = {
|
||||
}
|
||||
|
||||
this._downloadToDataItemMap.delete(aDownload);
|
||||
this.dataItems[dataItem.downloadGuid] = null;
|
||||
this.dataItems.delete(dataItem);
|
||||
for (let view of this._views) {
|
||||
view.onDataItemRemoved(dataItem);
|
||||
}
|
||||
@ -718,7 +714,7 @@ DownloadsDataCtor.prototype = {
|
||||
if (oldState != aDataItem.state) {
|
||||
for (let view of this._views) {
|
||||
try {
|
||||
view.getViewItem(aDataItem).onStateChange(oldState);
|
||||
view.onDataItemStateChanged(aDataItem, oldState);
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
@ -756,7 +752,7 @@ DownloadsDataCtor.prototype = {
|
||||
}
|
||||
|
||||
for (let view of this._views) {
|
||||
view.getViewItem(aDataItem).onProgressChange();
|
||||
view.onDataItemChanged(aDataItem);
|
||||
}
|
||||
},
|
||||
|
||||
@ -801,9 +797,7 @@ DownloadsDataCtor.prototype = {
|
||||
|
||||
// Sort backwards by start time, ensuring that the most recent
|
||||
// downloads are added first regardless of their state.
|
||||
let loadedItemsArray = [dataItem
|
||||
for each (dataItem in this.dataItems)
|
||||
if (dataItem)];
|
||||
let loadedItemsArray = [...this.dataItems];
|
||||
loadedItemsArray.sort((a, b) => b.startTime - a.startTime);
|
||||
loadedItemsArray.forEach(dataItem => aView.onDataItemAdded(dataItem, false));
|
||||
|
||||
@ -881,7 +875,6 @@ XPCOMUtils.defineLazyGetter(this, "DownloadsData", function() {
|
||||
function DownloadsDataItem(aDownload) {
|
||||
this._download = aDownload;
|
||||
|
||||
this.downloadGuid = "id:" + this._autoIncrementId;
|
||||
this.file = aDownload.target.path;
|
||||
this.target = OS.Path.basename(aDownload.target.path);
|
||||
this.uri = aDownload.source.url;
|
||||
@ -891,13 +884,6 @@ function DownloadsDataItem(aDownload) {
|
||||
}
|
||||
|
||||
DownloadsDataItem.prototype = {
|
||||
/**
|
||||
* The JavaScript API does not need identifiers for Download objects, so they
|
||||
* are generated sequentially for the corresponding DownloadDataItem.
|
||||
*/
|
||||
get _autoIncrementId() ++DownloadsDataItem.prototype.__lastId,
|
||||
__lastId: 0,
|
||||
|
||||
/**
|
||||
* Updates this object from the underlying Download object.
|
||||
*/
|
||||
@ -1250,16 +1236,26 @@ const DownloadsViewPrototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the view item associated with the provided data item for this view.
|
||||
* Called when the "state" property of a DownloadsDataItem has changed.
|
||||
*
|
||||
* @param aDataItem
|
||||
* DownloadsDataItem object for which the view item is requested.
|
||||
*
|
||||
* @return Object that can be used to notify item status events.
|
||||
* The onDataItemChanged notification will be sent afterwards.
|
||||
*
|
||||
* @note Subclasses should override this.
|
||||
*/
|
||||
getViewItem(aDataItem) {
|
||||
onDataItemStateChanged(aDataItem) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called every time any state property of a DownloadsDataItem may have
|
||||
* changed, including progress properties and the "state" property.
|
||||
*
|
||||
* Note that progress notification changes are throttled at the Downloads.jsm
|
||||
* API level, and there is no throttling mechanism in the front-end.
|
||||
*
|
||||
* @note Subclasses should override this.
|
||||
*/
|
||||
onDataItemChanged(aDataItem) {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
@ -1358,34 +1354,21 @@ DownloadsIndicatorDataCtor.prototype = {
|
||||
this._updateViews();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the view item associated with the provided data item for this view.
|
||||
*
|
||||
* @param aDataItem
|
||||
* DownloadsDataItem object for which the view item is requested.
|
||||
*
|
||||
* @return Object that can be used to notify item status events.
|
||||
*/
|
||||
getViewItem(aDataItem) {
|
||||
let data = this._isPrivate ? PrivateDownloadsIndicatorData
|
||||
: DownloadsIndicatorData;
|
||||
return Object.freeze({
|
||||
onStateChange(aOldState) {
|
||||
if (aDataItem.state == nsIDM.DOWNLOAD_FINISHED ||
|
||||
aDataItem.state == nsIDM.DOWNLOAD_FAILED) {
|
||||
data.attention = true;
|
||||
}
|
||||
// DownloadsView
|
||||
onDataItemStateChanged(aDataItem, aOldState) {
|
||||
if (aDataItem.state == nsIDM.DOWNLOAD_FINISHED ||
|
||||
aDataItem.state == nsIDM.DOWNLOAD_FAILED) {
|
||||
this.attention = true;
|
||||
}
|
||||
|
||||
// Since the state of a download changed, reset the estimated time left.
|
||||
data._lastRawTimeLeft = -1;
|
||||
data._lastTimeLeft = -1;
|
||||
// Since the state of a download changed, reset the estimated time left.
|
||||
this._lastRawTimeLeft = -1;
|
||||
this._lastTimeLeft = -1;
|
||||
},
|
||||
|
||||
data._updateViews();
|
||||
},
|
||||
onProgressChange() {
|
||||
data._updateViews();
|
||||
}
|
||||
});
|
||||
// DownloadsView
|
||||
onDataItemChanged() {
|
||||
this._updateViews();
|
||||
},
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@ -1480,7 +1463,7 @@ DownloadsIndicatorDataCtor.prototype = {
|
||||
_activeDataItems() {
|
||||
let dataItems = this._isPrivate ? PrivateDownloadsData.dataItems
|
||||
: DownloadsData.dataItems;
|
||||
for each (let dataItem in dataItems) {
|
||||
for (let dataItem of dataItems) {
|
||||
if (dataItem && dataItem.inProgress) {
|
||||
yield dataItem;
|
||||
}
|
||||
@ -1624,19 +1607,16 @@ DownloadsSummaryData.prototype = {
|
||||
this._updateViews();
|
||||
},
|
||||
|
||||
getViewItem(aDataItem) {
|
||||
let self = this;
|
||||
return Object.freeze({
|
||||
onStateChange(aOldState) {
|
||||
// Since the state of a download changed, reset the estimated time left.
|
||||
self._lastRawTimeLeft = -1;
|
||||
self._lastTimeLeft = -1;
|
||||
self._updateViews();
|
||||
},
|
||||
onProgressChange() {
|
||||
self._updateViews();
|
||||
}
|
||||
});
|
||||
// DownloadsView
|
||||
onDataItemStateChanged(aOldState) {
|
||||
// Since the state of a download changed, reset the estimated time left.
|
||||
this._lastRawTimeLeft = -1;
|
||||
this._lastTimeLeft = -1;
|
||||
},
|
||||
|
||||
// DownloadsView
|
||||
onDataItemChanged() {
|
||||
this._updateViews();
|
||||
},
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -53,14 +53,10 @@ const NOT_AVAILABLE = Number.MAX_VALUE;
|
||||
* The shell doesn't take care of inserting the item, or removing it when it's no longer
|
||||
* valid. That's the caller (a DownloadsPlacesView object) responsibility.
|
||||
*
|
||||
* The caller is also responsible for "passing over" notification from both the
|
||||
* download-view and the places-result-observer, in the following manner:
|
||||
* - The DownloadsPlacesView object implements getViewItem of the download-view
|
||||
* pseudo interface. It returns this object (therefore we implement
|
||||
* onStateChangea and onProgressChange here).
|
||||
* - The DownloadsPlacesView object adds itself as a places result observer and
|
||||
* calls this object's placesNodeIconChanged, placesNodeTitleChanged and
|
||||
* placeNodeAnnotationChanged from its callbacks.
|
||||
* The caller is also responsible for "passing over" notifications. The
|
||||
* DownloadsPlacesView object implements onDataItemStateChanged and
|
||||
* onDataItemChanged of the DownloadsView pseudo interface, and registers as a
|
||||
* Places result observer.
|
||||
*
|
||||
* @param [optional] aDataItem
|
||||
* The data item of a the session download. Required if aPlacesNode is not set
|
||||
@ -176,13 +172,10 @@ DownloadElementShell.prototype = {
|
||||
}
|
||||
|
||||
if (this._placesNode) {
|
||||
// Try to extract an extension from the uri.
|
||||
let ext = this._downloadURIObj.QueryInterface(Ci.nsIURL).fileExtension;
|
||||
if (ext) {
|
||||
return "moz-icon://." + ext + "?size=32";
|
||||
}
|
||||
return this._placesNode.icon || "moz-icon://.unknown?size=32";
|
||||
return "moz-icon://.unknown?size=32";
|
||||
}
|
||||
|
||||
// Assert unreachable.
|
||||
if (this._dataItem) {
|
||||
throw new Error("Session-download items should always have a target file uri");
|
||||
}
|
||||
@ -304,9 +297,9 @@ DownloadElementShell.prototype = {
|
||||
* - fileName: the downloaded file name on the file system. Set if filePath
|
||||
* is set.
|
||||
* - displayName: the user-facing label for the download. This is always
|
||||
* set. If available, it's set to the downloaded file name. If not,
|
||||
* the places title for the download uri is used it's set. As a last
|
||||
* resort, we fallback to the download uri.
|
||||
* set. If available, it's set to the downloaded file name. If not, this
|
||||
* means the download does not have Places metadata because it is very old,
|
||||
* and in this rare case the download uri is used.
|
||||
* - fileSize (only set for downloads which completed successfully):
|
||||
* the downloaded file size. For downloads done after the landing of
|
||||
* bug 826991, this value is "static" - that is, it does not necessarily
|
||||
@ -348,7 +341,7 @@ DownloadElementShell.prototype = {
|
||||
this._extractFilePathAndNameFromFileURI(targetFileURI);
|
||||
this._metaData.displayName = this._metaData.fileName;
|
||||
} catch (ex) {
|
||||
this._metaData.displayName = this._placesNode.title || this.downloadURI;
|
||||
this._metaData.displayName = this.downloadURI;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -489,12 +482,6 @@ DownloadElementShell.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_updateDisplayNameAndIcon() {
|
||||
let metaData = this.getDownloadMetaData();
|
||||
this._element.setAttribute("displayName", metaData.displayName);
|
||||
this._element.setAttribute("image", this._getIcon());
|
||||
},
|
||||
|
||||
_updateUI() {
|
||||
if (!this.active) {
|
||||
throw new Error("Trying to _updateUI on an inactive download shell");
|
||||
@ -503,7 +490,9 @@ DownloadElementShell.prototype = {
|
||||
this._metaData = null;
|
||||
this._targetFileInfoFetched = false;
|
||||
|
||||
this._updateDisplayNameAndIcon();
|
||||
let metaData = this.getDownloadMetaData();
|
||||
this._element.setAttribute("displayName", metaData.displayName);
|
||||
this._element.setAttribute("image", this._getIcon());
|
||||
|
||||
// For history downloads done in past releases, the downloads/metaData
|
||||
// annotation is not set, and therefore we cannot tell the download
|
||||
@ -515,61 +504,7 @@ DownloadElementShell.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
placesNodeIconChanged() {
|
||||
if (!this._dataItem) {
|
||||
this._element.setAttribute("image", this._getIcon());
|
||||
}
|
||||
},
|
||||
|
||||
placesNodeTitleChanged() {
|
||||
// If there's a file path, we use the leaf name for the title.
|
||||
if (!this._dataItem && this.active &&
|
||||
!this.getDownloadMetaData().filePath) {
|
||||
this._metaData = null;
|
||||
this._updateDisplayNameAndIcon();
|
||||
}
|
||||
},
|
||||
|
||||
placesNodeAnnotationChanged(aAnnoName) {
|
||||
this._annotations.delete(aAnnoName);
|
||||
if (!this._dataItem && this.active) {
|
||||
if (aAnnoName == DOWNLOAD_META_DATA_ANNO) {
|
||||
let metaData = this.getDownloadMetaData();
|
||||
let annotatedMetaData = this._getAnnotatedMetaData();
|
||||
metaData.endTime = annotatedMetaData.endTime;
|
||||
if ("fileSize" in annotatedMetaData) {
|
||||
metaData.fileSize = annotatedMetaData.fileSize;
|
||||
} else {
|
||||
delete metaData.fileSize;
|
||||
}
|
||||
|
||||
if (metaData.state != annotatedMetaData.state) {
|
||||
metaData.state = annotatedMetaData.state;
|
||||
if (this._element.selected) {
|
||||
goUpdateDownloadCommands();
|
||||
}
|
||||
}
|
||||
|
||||
this._updateDownloadStatusUI();
|
||||
} else if (aAnnoName == DESTINATION_FILE_URI_ANNO) {
|
||||
let metaData = this.getDownloadMetaData();
|
||||
let targetFileURI = this._getAnnotation(DESTINATION_FILE_URI_ANNO);
|
||||
[metaData.filePath, metaData.fileName] =
|
||||
this._extractFilePathAndNameFromFileURI(targetFileURI);
|
||||
metaData.displayName = metaData.fileName;
|
||||
this._updateDisplayNameAndIcon();
|
||||
|
||||
if (this._targetFileInfoFetched) {
|
||||
// This will also update the download commands if necessary.
|
||||
this._targetFileInfoFetched = false;
|
||||
this._fetchTargetFileInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/* DownloadView */
|
||||
onStateChange(aOldState) {
|
||||
onStateChanged(aOldState) {
|
||||
let metaData = this.getDownloadMetaData();
|
||||
metaData.state = this.dataItem.state;
|
||||
if (aOldState != nsIDM.DOWNLOAD_FINISHED && aOldState != metaData.state) {
|
||||
@ -583,6 +518,7 @@ DownloadElementShell.prototype = {
|
||||
}
|
||||
|
||||
this._updateDownloadStatusUI();
|
||||
|
||||
if (this._element.selected) {
|
||||
goUpdateDownloadCommands();
|
||||
} else {
|
||||
@ -590,8 +526,7 @@ DownloadElementShell.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/* DownloadView */
|
||||
onProgressChange() {
|
||||
onChanged() {
|
||||
this._updateDownloadStatusUI();
|
||||
},
|
||||
|
||||
@ -837,15 +772,6 @@ DownloadsPlacesView.prototype = {
|
||||
return this._active;
|
||||
},
|
||||
|
||||
_forEachDownloadElementShellForURI(aURI, aCallback) {
|
||||
if (this._downloadElementsShellsForURI.has(aURI)) {
|
||||
let downloadElementShells = this._downloadElementsShellsForURI.get(aURI);
|
||||
for (let des of downloadElementShells) {
|
||||
aCallback(des);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_getAnnotationsFor(aURI) {
|
||||
if (!this._cachedAnnotations) {
|
||||
this._cachedAnnotations = new Map();
|
||||
@ -934,7 +860,7 @@ DownloadsPlacesView.prototype = {
|
||||
// data item. Thus, we also check that we make sure we don't have a view item
|
||||
// already.
|
||||
if (!shouldCreateShell &&
|
||||
aDataItem && this.getViewItem(aDataItem) == null) {
|
||||
aDataItem && !this._viewItemsForDataItems.has(aDataItem)) {
|
||||
// If there's a past-download-only shell for this download-uri with no
|
||||
// associated data item, use it for the new data item. Otherwise, go ahead
|
||||
// and create another shell.
|
||||
@ -1051,7 +977,7 @@ DownloadsPlacesView.prototype = {
|
||||
throw new Error("Should have had at leaat one shell for this uri");
|
||||
}
|
||||
|
||||
let shell = this.getViewItem(aDataItem);
|
||||
let shell = this._viewItemsForDataItems.get(aDataItem);
|
||||
if (!shells.has(shell)) {
|
||||
throw new Error("Missing download element shell in shells list for url");
|
||||
}
|
||||
@ -1277,21 +1203,9 @@ DownloadsPlacesView.prototype = {
|
||||
this._removeHistoryDownloadFromView(aPlacesNode);
|
||||
},
|
||||
|
||||
nodeIconChanged(aNode) {
|
||||
this._forEachDownloadElementShellForURI(aNode.uri,
|
||||
des => des.placesNodeIconChanged());
|
||||
},
|
||||
|
||||
nodeAnnotationChanged(aNode, aAnnoName) {
|
||||
this._forEachDownloadElementShellForURI(aNode.uri,
|
||||
des => des.placesNodeAnnotationChanged(aAnnoName));
|
||||
},
|
||||
|
||||
nodeTitleChanged(aNode, aNewTitle) {
|
||||
this._forEachDownloadElementShellForURI(aNode.uri,
|
||||
des => des.placesNodeTitleChanged());
|
||||
},
|
||||
|
||||
nodeAnnotationChanged() {},
|
||||
nodeIconChanged() {},
|
||||
nodeTitleChanged() {},
|
||||
nodeKeywordChanged() {},
|
||||
nodeDateAddedChanged() {},
|
||||
nodeLastModifiedChanged() {},
|
||||
@ -1362,8 +1276,14 @@ DownloadsPlacesView.prototype = {
|
||||
this._removeSessionDownloadFromView(aDataItem);
|
||||
},
|
||||
|
||||
getViewItem(aDataItem) {
|
||||
return this._viewItemsForDataItems.get(aDataItem, null);
|
||||
// DownloadsView
|
||||
onDataItemStateChanged(aDataItem, aOldState) {
|
||||
this._viewItemsForDataItems.get(aDataItem).onStateChanged(aOldState);
|
||||
},
|
||||
|
||||
// DownloadsView
|
||||
onDataItemChanged(aDataItem) {
|
||||
this._viewItemsForDataItems.get(aDataItem).onChanged();
|
||||
},
|
||||
|
||||
supportsCommand(aCommand) {
|
||||
|
@ -554,7 +554,7 @@ const DownloadsPanel = {
|
||||
// still exist, and update the allowed items interactions accordingly. We
|
||||
// do these checks on a background thread, and don't prevent the panel to
|
||||
// be displayed while these checks are being performed.
|
||||
for each (let viewItem in DownloadsView._viewItems) {
|
||||
for (let viewItem of DownloadsView._visibleViewItems.values()) {
|
||||
viewItem.verifyTargetExists();
|
||||
}
|
||||
|
||||
@ -673,11 +673,11 @@ const DownloadsView = {
|
||||
_dataItems: [],
|
||||
|
||||
/**
|
||||
* Object containing the available DownloadsViewItem objects, indexed by their
|
||||
* numeric download identifier. There is a limited number of view items in
|
||||
* the panel at any given time.
|
||||
* Associates the visible DownloadsDataItem objects with their corresponding
|
||||
* DownloadsViewItem object. There is a limited number of view items in the
|
||||
* panel at any given time.
|
||||
*/
|
||||
_viewItems: {},
|
||||
_visibleViewItems: new Map(),
|
||||
|
||||
/**
|
||||
* Called when the number of items in the list changes.
|
||||
@ -814,30 +814,31 @@ const DownloadsView = {
|
||||
this._itemCountChanged();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the view item associated with the provided data item for this view.
|
||||
*
|
||||
* @param aDataItem
|
||||
* DownloadsDataItem object for which the view item is requested.
|
||||
*
|
||||
* @return Object that can be used to notify item status events.
|
||||
*/
|
||||
getViewItem(aDataItem) {
|
||||
// If the item is visible, just return it, otherwise return a mock object
|
||||
// that doesn't react to notifications.
|
||||
if (aDataItem.downloadGuid in this._viewItems) {
|
||||
return this._viewItems[aDataItem.downloadGuid];
|
||||
// DownloadsView
|
||||
onDataItemStateChanged(aDataItem, aOldState) {
|
||||
let viewItem = this._visibleViewItems.get(aDataItem);
|
||||
if (viewItem) {
|
||||
viewItem.onStateChanged(aOldState);
|
||||
}
|
||||
},
|
||||
|
||||
// DownloadsView
|
||||
onDataItemChanged(aDataItem) {
|
||||
let viewItem = this._visibleViewItems.get(aDataItem);
|
||||
if (viewItem) {
|
||||
viewItem.onChanged();
|
||||
}
|
||||
return this._invisibleViewItem;
|
||||
},
|
||||
|
||||
/**
|
||||
* Mock DownloadsDataItem object that doesn't react to notifications.
|
||||
* Associates each richlistitem for a download with its corresponding
|
||||
* DownloadsViewItemController object.
|
||||
*/
|
||||
_invisibleViewItem: Object.freeze({
|
||||
onStateChange() {},
|
||||
onProgressChange() {},
|
||||
}),
|
||||
_controllersForElements: new Map(),
|
||||
|
||||
controllerForElement(element) {
|
||||
return this._controllersForElements.get(element);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a new view item associated with the specified data item, and adds
|
||||
@ -850,7 +851,9 @@ const DownloadsView = {
|
||||
|
||||
let element = document.createElement("richlistitem");
|
||||
let viewItem = new DownloadsViewItem(aDataItem, element);
|
||||
this._viewItems[aDataItem.downloadGuid] = viewItem;
|
||||
this._visibleViewItems.set(aDataItem, viewItem);
|
||||
let viewItemController = new DownloadsViewItemController(aDataItem);
|
||||
this._controllersForElements.set(element, viewItemController);
|
||||
if (aNewest) {
|
||||
this.richListBox.insertBefore(element, this.richListBox.firstChild);
|
||||
} else {
|
||||
@ -863,14 +866,15 @@ const DownloadsView = {
|
||||
*/
|
||||
_removeViewItem(aDataItem) {
|
||||
DownloadsCommon.log("Removing a DownloadsViewItem from the downloads list.");
|
||||
let element = this.getViewItem(aDataItem)._element;
|
||||
let element = this._visibleViewItems.get(aDataItem)._element;
|
||||
let previousSelectedIndex = this.richListBox.selectedIndex;
|
||||
this.richListBox.removeChild(element);
|
||||
if (previousSelectedIndex != -1) {
|
||||
this.richListBox.selectedIndex = Math.min(previousSelectedIndex,
|
||||
this.richListBox.itemCount - 1);
|
||||
}
|
||||
delete this._viewItems[aDataItem.downloadGuid];
|
||||
this._visibleViewItems.delete(aDataItem);
|
||||
this._controllersForElements.delete(element);
|
||||
},
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@ -891,7 +895,7 @@ const DownloadsView = {
|
||||
while (target.nodeName != "richlistitem") {
|
||||
target = target.parentNode;
|
||||
}
|
||||
new DownloadsViewItemController(target).doCommand(aCommand);
|
||||
DownloadsView.controllerForElement(target).doCommand(aCommand);
|
||||
},
|
||||
|
||||
onDownloadClick(aEvent) {
|
||||
@ -965,8 +969,8 @@ const DownloadsView = {
|
||||
return;
|
||||
}
|
||||
|
||||
let controller = new DownloadsViewItemController(element);
|
||||
let localFile = controller.dataItem.localFile;
|
||||
let localFile = DownloadsView.controllerForElement(element)
|
||||
.dataItem.localFile;
|
||||
if (!localFile.exists()) {
|
||||
return;
|
||||
}
|
||||
@ -1010,8 +1014,6 @@ function DownloadsViewItem(aDataItem, aElement) {
|
||||
let attributes = {
|
||||
"type": "download",
|
||||
"class": "download-state",
|
||||
"id": "downloadsItem_" + this.dataItem.downloadGuid,
|
||||
"downloadGuid": this.dataItem.downloadGuid,
|
||||
"state": this.dataItem.state,
|
||||
"progress": this.dataItem.inProgress ? this.dataItem.percentComplete : 100,
|
||||
"target": this.dataItem.target,
|
||||
@ -1052,7 +1054,7 @@ DownloadsViewItem.prototype = {
|
||||
* the download might be the same as before, if the data layer received
|
||||
* multiple events for the same download.
|
||||
*/
|
||||
onStateChange(aOldState) {
|
||||
onStateChanged(aOldState) {
|
||||
// If a download just finished successfully, it means that the target file
|
||||
// now exists and we can extract its specific icon. To ensure that the icon
|
||||
// is reloaded, we must change the URI used by the XUL image element, for
|
||||
@ -1072,14 +1074,12 @@ DownloadsViewItem.prototype = {
|
||||
|
||||
// Update the user interface after switching states.
|
||||
this._element.setAttribute("state", this.dataItem.state);
|
||||
this._updateProgress();
|
||||
this._updateStatusLine();
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the download progress has changed.
|
||||
*/
|
||||
onProgressChange() {
|
||||
onChanged() {
|
||||
this._updateProgress();
|
||||
this._updateStatusLine();
|
||||
},
|
||||
@ -1283,8 +1283,8 @@ const DownloadsViewController = {
|
||||
|
||||
// Other commands are selection-specific.
|
||||
let element = DownloadsView.richListBox.selectedItem;
|
||||
return element &&
|
||||
new DownloadsViewItemController(element).isCommandEnabled(aCommand);
|
||||
return element && DownloadsView.controllerForElement(element)
|
||||
.isCommandEnabled(aCommand);
|
||||
},
|
||||
|
||||
doCommand(aCommand) {
|
||||
@ -1298,7 +1298,7 @@ const DownloadsViewController = {
|
||||
let element = DownloadsView.richListBox.selectedItem;
|
||||
if (element) {
|
||||
// The doCommand function also checks if the command is enabled.
|
||||
new DownloadsViewItemController(element).doCommand(aCommand);
|
||||
DownloadsView.controllerForElement(element).doCommand(aCommand);
|
||||
}
|
||||
},
|
||||
|
||||
@ -1334,9 +1334,8 @@ const DownloadsViewController = {
|
||||
* Handles all the user interaction events, in particular the "commands",
|
||||
* related to a single item in the downloads list widgets.
|
||||
*/
|
||||
function DownloadsViewItemController(aElement) {
|
||||
let downloadGuid = aElement.getAttribute("downloadGuid");
|
||||
this.dataItem = DownloadsCommon.getData(window).dataItems[downloadGuid];
|
||||
function DownloadsViewItemController(aDataItem) {
|
||||
this.dataItem = aDataItem;
|
||||
}
|
||||
|
||||
DownloadsViewItemController.prototype = {
|
||||
|
@ -49,7 +49,7 @@ add_task(function* test_basic_functionality() {
|
||||
let itemCount = richlistbox.children.length;
|
||||
for (let i = 0; i < itemCount; i++) {
|
||||
let element = richlistbox.children[itemCount - i - 1];
|
||||
let dataItem = new DownloadsViewItemController(element).dataItem;
|
||||
let dataItem = DownloadsView.controllerForElement(element).dataItem;
|
||||
is(dataItem.state, DownloadData[i].state, "Download states match up");
|
||||
}
|
||||
});
|
||||
|
@ -771,7 +771,7 @@ loop.conversationViews = (function(mozL10n) {
|
||||
this.props.store.getStoreState("callStateReason");
|
||||
|
||||
if (callStateReason === "reject" || callStateReason === "busy" ||
|
||||
callStateReason === "setup") {
|
||||
callStateReason === "user-unknown") {
|
||||
var contactDisplayName = _getContactDisplayName(this.props.contact);
|
||||
if (contactDisplayName.length) {
|
||||
return mozL10n.get(
|
||||
|
@ -771,7 +771,7 @@ loop.conversationViews = (function(mozL10n) {
|
||||
this.props.store.getStoreState("callStateReason");
|
||||
|
||||
if (callStateReason === "reject" || callStateReason === "busy" ||
|
||||
callStateReason === "setup") {
|
||||
callStateReason === "user-unknown") {
|
||||
var contactDisplayName = _getContactDisplayName(this.props.contact);
|
||||
if (contactDisplayName.length) {
|
||||
return mozL10n.get(
|
||||
|
@ -374,8 +374,12 @@ loop.store = loop.store || {};
|
||||
function(err, result) {
|
||||
if (err) {
|
||||
console.error("Failed to get outgoing call data", err);
|
||||
var failureReason = "setup";
|
||||
if (err.errno == 122) {
|
||||
failureReason = "user-unknown";
|
||||
}
|
||||
this.dispatcher.dispatch(
|
||||
new sharedActions.ConnectionFailure({reason: "setup"}));
|
||||
new sharedActions.ConnectionFailure({reason: failureReason}));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -444,12 +444,22 @@ describe("loop.conversationViews", function () {
|
||||
{contactName: loop.conversationViews._getContactDisplayName(contact)});
|
||||
});
|
||||
|
||||
it("should show 'contact unavailable' when the reason is 'setup'",
|
||||
it("should show 'something went wrong' when the reason is 'setup'",
|
||||
function () {
|
||||
store.setStoreState({callStateReason: "setup"});
|
||||
|
||||
view = mountTestComponent({contact: contact});
|
||||
|
||||
sinon.assert.calledWithExactly(document.mozL10n.get,
|
||||
"generic_failure_title");
|
||||
});
|
||||
|
||||
it("should show 'contact unavailable' when the reason is 'user-unknown'",
|
||||
function () {
|
||||
store.setStoreState({callStateReason: "user-unknown"});
|
||||
|
||||
view = mountTestComponent({contact: contact});
|
||||
|
||||
sinon.assert.calledWithExactly(document.mozL10n.get,
|
||||
"contact_unavailable_title",
|
||||
{contactName: loop.conversationViews._getContactDisplayName(contact)});
|
||||
|
@ -98,6 +98,15 @@ NfcCallback.prototype = {
|
||||
Ci.nsINfcRequestCallback]),
|
||||
};
|
||||
|
||||
// Should be mapped to the NFCTagType defined in MozNFCTag.webidl.
|
||||
let TagType = {
|
||||
TYPE1: "Type1",
|
||||
TYPE2: "Type2",
|
||||
TYPE3: "Type3",
|
||||
TYPE4: "Type4",
|
||||
MIFARE_CLASSIC: "MIFARE-Classic"
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of NFCTag.
|
||||
*
|
||||
@ -120,8 +129,9 @@ function MozNFCTagImpl(window, sessionToken, tagInfo, ndefInfo) {
|
||||
this.maxNDEFSize = ndefInfo.maxNDEFSize;
|
||||
this.isReadOnly = ndefInfo.isReadOnly;
|
||||
this.isFormatable = ndefInfo.isFormatable;
|
||||
this.canBeMadeReadOnly = this.type == "type1" || this.type == "type2" ||
|
||||
this.type == "mifare_classic";
|
||||
this.canBeMadeReadOnly = this.type == TagType.TYPE1 ||
|
||||
this.type == TagType.TYPE2 ||
|
||||
this.type == TagType.MIFARE_CLASSIC;
|
||||
}
|
||||
}
|
||||
MozNFCTagImpl.prototype = {
|
||||
|
@ -584,28 +584,16 @@ SelectionCarets::SelectWord()
|
||||
nsPoint ptInFrame = mDownPoint;
|
||||
nsLayoutUtils::TransformPoint(rootFrame, ptFrame, ptInFrame);
|
||||
|
||||
nsIFrame* currFrame = ptFrame;
|
||||
nsIContent* newFocusContent = nullptr;
|
||||
while (currFrame) {
|
||||
int32_t tabIndexUnused = 0;
|
||||
if (currFrame->IsFocusable(&tabIndexUnused, true)) {
|
||||
newFocusContent = currFrame->GetContent();
|
||||
nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocusContent));
|
||||
if (domElement)
|
||||
break;
|
||||
}
|
||||
currFrame = currFrame->GetParent();
|
||||
}
|
||||
|
||||
|
||||
// If target frame is focusable, we should move focus to it. If target frame
|
||||
// isn't focusable, and our previous focused content is editable, we should
|
||||
// If target frame is editable, we should move focus to targe frame. If
|
||||
// target frame isn't editable and our focus content is editable, we should
|
||||
// clear focus.
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
nsIContent* editingHost = ptFrame->GetContent()->GetEditingHost();
|
||||
if (newFocusContent && currFrame) {
|
||||
nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocusContent));
|
||||
fm->SetFocus(domElement,0);
|
||||
if (editingHost) {
|
||||
nsCOMPtr<nsIDOMElement> elt = do_QueryInterface(editingHost->GetParent());
|
||||
if (elt) {
|
||||
fm->SetFocus(elt, 0);
|
||||
}
|
||||
|
||||
if (!nsContentUtils::HasNonEmptyTextContent(
|
||||
editingHost, nsContentUtils::eRecurseIntoChildren)) {
|
||||
|
@ -39,32 +39,17 @@ class SelectionCaretsTest(MarionetteTestCase):
|
||||
self._contenteditable = self.marionette.find_element(*self._contenteditable_selector)
|
||||
self._content = self.marionette.find_element(*self._content_selector)
|
||||
|
||||
def _first_word_location(self, el):
|
||||
'''Get the location (x, y) of the first word in el.
|
||||
|
||||
Note: this function has a side effect which changes focus to the
|
||||
target element el.
|
||||
|
||||
'''
|
||||
sel = SelectionManager(el)
|
||||
|
||||
# Move caret behind the first character to get the location of the first
|
||||
# word.
|
||||
def _long_press_to_select_first_word(self, el, sel):
|
||||
# Move caret inside the first word.
|
||||
el.tap()
|
||||
sel.move_caret_to_front()
|
||||
sel.move_caret_by_offset(1)
|
||||
x, y = sel.caret_location()
|
||||
|
||||
return sel.caret_location()
|
||||
|
||||
def _long_press_to_select(self, el, x, y):
|
||||
'''Long press the location (x, y) to select a word.
|
||||
|
||||
SelectionCarets should appear. On Windows, those spaces after the
|
||||
word will also be selected.
|
||||
|
||||
'''
|
||||
long_press_without_contextmenu(self.marionette, el, self._long_press_time,
|
||||
x, y)
|
||||
# Long press the caret position. Selection carets should appear, and the
|
||||
# first word will be selected. On Windows, those spaces after the word
|
||||
# will also be selected.
|
||||
long_press_without_contextmenu(self.marionette, el, self._long_press_time, x, y)
|
||||
|
||||
def _test_long_press_to_select_a_word(self, el, assertFunc):
|
||||
sel = SelectionManager(el)
|
||||
@ -74,8 +59,7 @@ class SelectionCaretsTest(MarionetteTestCase):
|
||||
target_content = words[0]
|
||||
|
||||
# Goal: Select the first word.
|
||||
x, y = self._first_word_location(el)
|
||||
self._long_press_to_select(el, x, y)
|
||||
self._long_press_to_select_first_word(el, sel)
|
||||
|
||||
# Ignore extra spaces selected after the word.
|
||||
assertFunc(target_content, sel.selected_content.rstrip())
|
||||
@ -95,8 +79,7 @@ class SelectionCaretsTest(MarionetteTestCase):
|
||||
sel.select_all()
|
||||
(_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location()
|
||||
|
||||
x, y = self._first_word_location(el)
|
||||
self._long_press_to_select(el, x, y)
|
||||
self._long_press_to_select_first_word(el, sel)
|
||||
|
||||
# Move the right caret to the end of the content.
|
||||
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location()
|
||||
@ -108,8 +91,7 @@ class SelectionCaretsTest(MarionetteTestCase):
|
||||
# Ignore extra spaces at the beginning of the content in comparison.
|
||||
assertFunc(target_content.lstrip(), sel.selected_content.lstrip())
|
||||
|
||||
def _test_minimum_select_one_character(self, el, assertFunc,
|
||||
x=None, y=None):
|
||||
def _test_minimum_select_one_character(self, el, assertFunc):
|
||||
sel = SelectionManager(el)
|
||||
original_content = sel.content
|
||||
words = original_content.split()
|
||||
@ -118,13 +100,7 @@ class SelectionCaretsTest(MarionetteTestCase):
|
||||
# Goal: Select the first character.
|
||||
target_content = original_content[0]
|
||||
|
||||
if x and y:
|
||||
# If we got x and y from the arguments, use it as a hint of the
|
||||
# location of the first word
|
||||
pass
|
||||
else:
|
||||
x, y = self._first_word_location(el)
|
||||
self._long_press_to_select(el, x, y)
|
||||
self._long_press_to_select_first_word(el, sel)
|
||||
|
||||
# Move the right caret to the position of the left caret.
|
||||
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location()
|
||||
@ -132,24 +108,6 @@ class SelectionCaretsTest(MarionetteTestCase):
|
||||
|
||||
assertFunc(target_content, sel.selected_content)
|
||||
|
||||
def _test_focus_obtained_by_long_press(self, el1, el2):
|
||||
'''Test the focus could be changed from el1 to el2 by long press.
|
||||
|
||||
If the focus is changed to e2 successfully, SelectionCarets should
|
||||
appear and could be dragged.
|
||||
|
||||
'''
|
||||
# Goal: Tap to focus el1, and then select the first character on
|
||||
# el2.
|
||||
|
||||
# We want to collect the location of the first word in el2 here
|
||||
# since self._first_word_location() has the side effect which would
|
||||
# change the focus.
|
||||
x, y = self._first_word_location(el2)
|
||||
el1.tap()
|
||||
self._test_minimum_select_one_character(el2, self.assertEqual,
|
||||
x=x, y=y)
|
||||
|
||||
########################################################################
|
||||
# <input> test cases with selection carets enabled
|
||||
########################################################################
|
||||
@ -165,18 +123,6 @@ class SelectionCaretsTest(MarionetteTestCase):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_minimum_select_one_character(self._input, self.assertEqual)
|
||||
|
||||
def test_input_focus_obtained_by_long_press_from_textarea(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._textarea, self._input)
|
||||
|
||||
def test_input_focus_obtained_by_long_press_from_contenteditable(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._contenteditable, self._input)
|
||||
|
||||
def test_input_focus_obtained_by_long_press_from_content_non_editable(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._content, self._input)
|
||||
|
||||
########################################################################
|
||||
# <input> test cases with selection carets disabled
|
||||
########################################################################
|
||||
@ -203,18 +149,6 @@ class SelectionCaretsTest(MarionetteTestCase):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_minimum_select_one_character(self._textarea, self.assertEqual)
|
||||
|
||||
def test_textarea_focus_obtained_by_long_press_from_input(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._input, self._textarea)
|
||||
|
||||
def test_textarea_focus_obtained_by_long_press_from_contenteditable(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._contenteditable, self._textarea)
|
||||
|
||||
def test_textarea_focus_obtained_by_long_press_from_content_non_editable(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._content, self._textarea)
|
||||
|
||||
########################################################################
|
||||
# <textarea> test cases with selection carets disabled
|
||||
########################################################################
|
||||
@ -267,18 +201,6 @@ class SelectionCaretsTest(MarionetteTestCase):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_minimum_select_one_character(self._contenteditable, self.assertEqual)
|
||||
|
||||
def test_contenteditable_focus_obtained_by_long_press_from_input(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._input, self._contenteditable)
|
||||
|
||||
def test_contenteditable_focus_obtained_by_long_press_from_textarea(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._textarea, self._contenteditable)
|
||||
|
||||
def test_contenteditable_focus_obtained_by_long_press_from_content_non_editable(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._content, self._contenteditable)
|
||||
|
||||
########################################################################
|
||||
# <div> contenteditable test cases with selection carets disabled
|
||||
########################################################################
|
||||
@ -296,16 +218,3 @@ class SelectionCaretsTest(MarionetteTestCase):
|
||||
def test_content_non_editable_minimum_select_one_character_by_selection(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_minimum_select_one_character(self._content, self.assertEqual)
|
||||
|
||||
def test_content_non_editable_focus_obtained_by_long_press_from_input(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._input, self._content)
|
||||
|
||||
def test_content_non_editable_focus_obtained_by_long_press_from_textarea(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._textarea, self._content)
|
||||
|
||||
def test_content_non_editable_focus_obtained_by_long_press_from_contenteditable(self):
|
||||
self.openTestHtml(enabled=True)
|
||||
self._test_focus_obtained_by_long_press(self._contenteditable, self._content)
|
||||
|
||||
|
@ -233,12 +233,12 @@ public class ImmutableViewportMetrics {
|
||||
public ImmutableViewportMetrics offsetViewportByAndClamp(float dx, float dy) {
|
||||
if (isRTL) {
|
||||
return setViewportOrigin(
|
||||
Math.min(pageRectRight - getWidth(), Math.max(viewportRectLeft + dx, pageRectLeft)),
|
||||
Math.max(pageRectTop, Math.min(viewportRectTop + dy, pageRectBottom - getHeight())));
|
||||
Math.min(pageRectRight - getWidthWithoutMargins(), Math.max(viewportRectLeft + dx, pageRectLeft)),
|
||||
Math.max(pageRectTop, Math.min(viewportRectTop + dy, pageRectBottom - getHeightWithoutMargins())));
|
||||
}
|
||||
return setViewportOrigin(
|
||||
Math.max(pageRectLeft, Math.min(viewportRectLeft + dx, pageRectRight - getWidth())),
|
||||
Math.max(pageRectTop, Math.min(viewportRectTop + dy, pageRectBottom - getHeight())));
|
||||
Math.max(pageRectLeft, Math.min(viewportRectLeft + dx, pageRectRight - getWidthWithoutMargins())),
|
||||
Math.max(pageRectTop, Math.min(viewportRectTop + dy, pageRectBottom - getHeightWithoutMargins())));
|
||||
}
|
||||
|
||||
public ImmutableViewportMetrics setPageRect(RectF pageRect, RectF cssPageRect) {
|
||||
|
Before Width: | Height: | Size: 365 B |
Before Width: | Height: | Size: 350 B |
Before Width: | Height: | Size: 665 B |
Before Width: | Height: | Size: 116 B |
Before Width: | Height: | Size: 271 B |
Before Width: | Height: | Size: 278 B |
Before Width: | Height: | Size: 557 B |
Before Width: | Height: | Size: 110 B |
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!-- Bug 1106935: Temporary replacement for old tablet resources -->
|
||||
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:src="@null"/>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!-- Bug 1106935: Temporary replacement for old tablet resources -->
|
||||
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:src="@null"/>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!-- Bug 1106935: Temporary replacement for old tablet resources -->
|
||||
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:src="@null"/>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!-- Bug 1106935: Temporary replacement for old tablet resources -->
|
||||
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:src="@null"/>
|
Before Width: | Height: | Size: 511 B |
Before Width: | Height: | Size: 501 B |
Before Width: | Height: | Size: 774 B |
Before Width: | Height: | Size: 122 B |
Before Width: | Height: | Size: 798 B |
Before Width: | Height: | Size: 546 B |
Before Width: | Height: | Size: 643 B |
Before Width: | Height: | Size: 440 B |
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!-- Bug 1106935: Temporary replacement for old tablet resources -->
|
||||
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:src="@null"/>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!-- Bug 1106935: Temporary replacement for old tablet resources -->
|
||||
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:src="@null"/>
|
Before Width: | Height: | Size: 998 B |
Before Width: | Height: | Size: 645 B |
@ -5,7 +5,7 @@
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/restore_defaults"
|
||||
android:drawable="@drawable/menu"
|
||||
android:drawable="@drawable/new_tablet_menu"
|
||||
android:showAsAction="never"
|
||||
android:title="@string/pref_search_restore_defaults" />
|
||||
</menu>
|
||||
|
@ -118,11 +118,6 @@ public class UpdateService extends IntentService {
|
||||
} else if (UpdateServiceHelper.ACTION_CANCEL_DOWNLOAD.equals(intent.getAction())) {
|
||||
mCancelDownload = true;
|
||||
} else {
|
||||
if (!UpdateServiceHelper.ACTION_APPLY_UPDATE.equals(intent.getAction())) {
|
||||
// Delete the update package used to install the current version.
|
||||
deleteUpdatePackage(getLastFileName());
|
||||
}
|
||||
|
||||
super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
@ -465,6 +460,11 @@ public class UpdateService extends IntentService {
|
||||
}
|
||||
}
|
||||
|
||||
if (!info.buildID.equals(getLastBuildID())) {
|
||||
// Delete the previous package when a new version becomes available.
|
||||
deleteUpdatePackage(getLastFileName());
|
||||
}
|
||||
|
||||
Log.i(LOGTAG, "downloading update package");
|
||||
sendCheckUpdateResult(UpdateServiceHelper.CheckUpdateResult.DOWNLOADING);
|
||||
|
||||
|
@ -103,6 +103,14 @@ var HelperApps = {
|
||||
},
|
||||
|
||||
getAppsForUri: function getAppsForUri(uri, flags = { }, callback) {
|
||||
// Return early for well-known internal schemes
|
||||
if (!uri || uri.schemeIs("about") || uri.schemeIs("chrome")) {
|
||||
if (callback) {
|
||||
callback([]);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
flags.filterBrowsers = "filterBrowsers" in flags ? flags.filterBrowsers : true;
|
||||
flags.filterHtml = "filterHtml" in flags ? flags.filterHtml : true;
|
||||
|
||||
@ -110,9 +118,9 @@ var HelperApps = {
|
||||
let msg = this._getMessage("Intent:GetHandlers", uri, flags);
|
||||
let parseData = (d) => {
|
||||
let apps = []
|
||||
|
||||
if (!d)
|
||||
if (!d) {
|
||||
return apps;
|
||||
}
|
||||
|
||||
apps = this._parseApps(d.apps);
|
||||
|
||||
@ -140,8 +148,6 @@ var HelperApps = {
|
||||
|
||||
if (!callback) {
|
||||
let data = this._sendMessageSync(msg);
|
||||
if (!data)
|
||||
return [];
|
||||
return parseData(data);
|
||||
} else {
|
||||
Messaging.sendRequestForResult(msg).then(function(data) {
|
||||
@ -173,9 +179,10 @@ var HelperApps = {
|
||||
|
||||
_getMessage: function(type, uri, options = {}) {
|
||||
let mimeType = options.mimeType;
|
||||
if (uri && mimeType == undefined)
|
||||
if (uri && mimeType == undefined) {
|
||||
mimeType = ContentAreaUtils.getMIMETypeForURI(uri) || "";
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
type: type,
|
||||
mime: mimeType,
|
||||
@ -211,8 +218,9 @@ var HelperApps = {
|
||||
});
|
||||
|
||||
let thread = Services.tm.currentThread;
|
||||
while (res == null)
|
||||
while (res == null) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
@ -105,7 +105,7 @@ class SelectionManager(object):
|
||||
last_rect_list = self.selection_rect_list(range_count - 1)
|
||||
last_list_length = last_rect_list['length']
|
||||
first_rect, last_rect = first_rect_list['0'], last_rect_list[str(last_list_length - 1)]
|
||||
origin_x, origin_y = self.element.rect['x'], self.element.rect['y']
|
||||
origin_x, origin_y = self.element.location['x'], self.element.location['y']
|
||||
|
||||
if self.element.get_attribute('dir') == 'rtl': # such as Arabic
|
||||
start_pos, end_pos = 'right', 'left'
|
||||
|
@ -77,9 +77,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
|
||||
"resource://gre/modules/Sqlite.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gNotifier",
|
||||
"@mozilla.org/browser/nav-history-service;1",
|
||||
Ci.nsPIPlacesHistoryListenersNotifier);
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
Cu.importGlobalProperties(["URL"]);
|
||||
@ -529,15 +526,14 @@ let remove = Task.async(function*({guids, urls}, onResult = null) {
|
||||
}
|
||||
|
||||
// 5. Notify observers.
|
||||
let observers = PlacesUtils.history.getObservers();
|
||||
let reason = Ci.nsINavHistoryObserver.REASON_DELETED;
|
||||
for (let {guid, uri, toRemove} of pages) {
|
||||
gNotifier.notifyOnPageExpired(
|
||||
uri, // uri
|
||||
0, // visitTime - There are no more visits
|
||||
toRemove, // wholeEntry
|
||||
guid, // guid
|
||||
Ci.nsINavHistoryObserver.REASON_DELETED, // reason
|
||||
-1 // transition
|
||||
);
|
||||
if (toRemove) {
|
||||
notify(observers, "onDeleteURI", [uri, guid, reason]);
|
||||
} else {
|
||||
notify(observers, "onDeleteVisits", [uri, 0, guid, reason, 0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -28,7 +28,6 @@ if CONFIG['MOZ_PLACES']:
|
||||
'nsINavBookmarksService.idl',
|
||||
'nsITaggingService.idl',
|
||||
'nsPIPlacesDatabase.idl',
|
||||
'nsPIPlacesHistoryListenersNotifier.idl',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.places = [
|
||||
|
@ -168,7 +168,6 @@ NS_INTERFACE_MAP_BEGIN(nsNavHistory)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_ENTRY(nsPIPlacesDatabase)
|
||||
NS_INTERFACE_MAP_ENTRY(nsPIPlacesHistoryListenersNotifier)
|
||||
NS_INTERFACE_MAP_ENTRY(mozIStorageVacuumParticipant)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsINavHistoryService)
|
||||
NS_IMPL_QUERY_CLASSINFO(nsNavHistory)
|
||||
@ -3051,9 +3050,7 @@ nsNavHistory::AsyncExecuteLegacyQueries(nsINavHistoryQuery** aQueries,
|
||||
}
|
||||
|
||||
|
||||
// nsPIPlacesHistoryListenersNotifier ******************************************
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsresult
|
||||
nsNavHistory::NotifyOnPageExpired(nsIURI *aURI, PRTime aVisitTime,
|
||||
bool aWholeEntry, const nsACString& aGUID,
|
||||
uint16_t aReason, uint32_t aTransitionType)
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
#include "nsINavHistoryService.h"
|
||||
#include "nsPIPlacesDatabase.h"
|
||||
#include "nsPIPlacesHistoryListenersNotifier.h"
|
||||
#include "nsIBrowserHistory.h"
|
||||
#include "nsINavBookmarksService.h"
|
||||
#include "nsIFaviconService.h"
|
||||
@ -67,7 +66,6 @@ class nsNavHistory MOZ_FINAL : public nsSupportsWeakReference
|
||||
, public nsIObserver
|
||||
, public nsIBrowserHistory
|
||||
, public nsPIPlacesDatabase
|
||||
, public nsPIPlacesHistoryListenersNotifier
|
||||
, public mozIStorageVacuumParticipant
|
||||
{
|
||||
friend class PlacesSQLQueryBuilder;
|
||||
@ -80,7 +78,6 @@ public:
|
||||
NS_DECL_NSIBROWSERHISTORY
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSPIPLACESDATABASE
|
||||
NS_DECL_NSPIPLACESHISTORYLISTENERSNOTIFIER
|
||||
NS_DECL_MOZISTORAGEVACUUMPARTICIPANT
|
||||
|
||||
/**
|
||||
@ -183,6 +180,29 @@ public:
|
||||
*/
|
||||
nsresult invalidateFrecencies(const nsCString& aPlaceIdsQueryString);
|
||||
|
||||
/**
|
||||
* Calls onDeleteVisits and onDeleteURI notifications on registered listeners
|
||||
* with the history service.
|
||||
*
|
||||
* @param aURI
|
||||
* The nsIURI object representing the URI of the page being expired.
|
||||
* @param aVisitTime
|
||||
* The time, in microseconds, that the page being expired was visited.
|
||||
* @param aWholeEntry
|
||||
* Indicates if this is the last visit for this URI.
|
||||
* @param aGUID
|
||||
* The unique ID associated with the page.
|
||||
* @param aReason
|
||||
* Indicates the reason for the removal.
|
||||
* See nsINavHistoryObserver::REASON_* constants.
|
||||
* @param aTransitionType
|
||||
* If it's a valid TRANSITION_* value, all visits of the specified type
|
||||
* have been removed.
|
||||
*/
|
||||
nsresult NotifyOnPageExpired(nsIURI *aURI, PRTime aVisitTime,
|
||||
bool aWholeEntry, const nsACString& aGUID,
|
||||
uint16_t aReason, uint32_t aTransitionType);
|
||||
|
||||
/**
|
||||
* These functions return non-owning references to the locale-specific
|
||||
* objects for places components.
|
||||
|
@ -1,46 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 sts=2 expandtab
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIURI;
|
||||
|
||||
/**
|
||||
* This is a private interface used by Places components to notify history
|
||||
* listeners about important notifications. These should not be used by any
|
||||
* code that is not part of core.
|
||||
*
|
||||
* @note See also: nsINavHistoryObserver
|
||||
*/
|
||||
[scriptable, uuid(808cf36c-4c9a-4bdb-91a4-d60a6fc25add)]
|
||||
interface nsPIPlacesHistoryListenersNotifier : nsISupports
|
||||
{
|
||||
/**
|
||||
* Calls onDeleteVisits and onDeleteURI notifications on registered listeners
|
||||
* with the history service.
|
||||
*
|
||||
* @param aURI
|
||||
* The nsIURI object representing the URI of the page being expired.
|
||||
* @param aVisitTime
|
||||
* The time, in microseconds, that the page being expired was visited.
|
||||
* @param aWholeEntry
|
||||
* Indicates if this is the last visit for this URI.
|
||||
* @param aGUID
|
||||
* The unique ID associated with the page.
|
||||
* @param aReason
|
||||
* Indicates the reason for the removal.
|
||||
* See nsINavHistoryObserver::REASON_* constants.
|
||||
* @param aTransitionType
|
||||
* If it's a valid TRANSITION_* value, all visits of the specified type
|
||||
* have been removed.
|
||||
*/
|
||||
void notifyOnPageExpired(in nsIURI aURI,
|
||||
in PRTime aVisitTime,
|
||||
in boolean aWholeEntry,
|
||||
in ACString aGUID,
|
||||
in unsigned short aReason,
|
||||
in unsigned long aTransitionType);
|
||||
};
|
@ -27,6 +27,9 @@ const Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Constants
|
||||
|
||||
@ -400,6 +403,24 @@ const EXPIRATION_QUERIES = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends a bookmarks notification through the given observers.
|
||||
*
|
||||
* @param observers
|
||||
* array of nsINavBookmarkObserver objects.
|
||||
* @param notification
|
||||
* the notification name.
|
||||
* @param args
|
||||
* array of arguments to pass to the notification.
|
||||
*/
|
||||
function notify(observers, notification, args = []) {
|
||||
for (let observer of observers) {
|
||||
try {
|
||||
observer[notification](...args);
|
||||
} catch (ex) {}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// nsPlacesExpiration definition
|
||||
|
||||
@ -430,9 +451,6 @@ function nsPlacesExpiration()
|
||||
return db;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "_hsn",
|
||||
"@mozilla.org/browser/nav-history-service;1",
|
||||
"nsPIPlacesHistoryListenersNotifier");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "_sys",
|
||||
"@mozilla.org/system-info;1",
|
||||
"nsIPropertyBag2");
|
||||
@ -626,9 +644,15 @@ nsPlacesExpiration.prototype = {
|
||||
let guid = row.getResultByName("guid");
|
||||
let visitDate = row.getResultByName("visit_date");
|
||||
let wholeEntry = row.getResultByName("whole_entry");
|
||||
let reason = Ci.nsINavHistoryObserver.REASON_EXPIRED;
|
||||
let observers = PlacesUtils.history.getObservers();
|
||||
|
||||
// Dispatch expiration notifications to history.
|
||||
this._hsn.notifyOnPageExpired(uri, visitDate, wholeEntry, guid,
|
||||
Ci.nsINavHistoryObserver.REASON_EXPIRED, 0);
|
||||
if (wholeEntry) {
|
||||
notify(observers, "onDeleteURI", [uri, guid, reason]);
|
||||
} else {
|
||||
notify(observers, "onDeleteVisits", [uri, visitDate, guid, reason, 0]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -110,6 +110,30 @@ let AnimationPlayerActor = ActorClass({
|
||||
return parseFloat(durationText) * 1000;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the animation delay from this player, in milliseconds.
|
||||
* Note that the Web Animations API doesn't yet offer a way to retrieve this
|
||||
* directly from the AnimationPlayer object, so for now, a delay is only
|
||||
* returned if found in the node's computed styles.
|
||||
* @return {Number}
|
||||
*/
|
||||
getDelay: function() {
|
||||
let delayText;
|
||||
if (this.styles.animationDelay !== "0s") {
|
||||
delayText = this.styles.animationDelay;
|
||||
} else if (this.styles.transitionDelay !== "0s") {
|
||||
delayText = this.styles.transitionDelay;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (delayText.indexOf(",") !== -1) {
|
||||
delayText = delayText.split(",")[this.playerIndex];
|
||||
}
|
||||
|
||||
return parseFloat(delayText) * 1000;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the animation iteration count for this player. That is, how many times
|
||||
* is the animation scheduled to run.
|
||||
@ -145,6 +169,7 @@ let AnimationPlayerActor = ActorClass({
|
||||
playState: this.player.playState,
|
||||
name: this.player.source.effect.name,
|
||||
duration: this.getDuration(),
|
||||
delay: this.getDelay(),
|
||||
iterationCount: this.getIterationCount(),
|
||||
/**
|
||||
* Is the animation currently running on the compositor. This is important for
|
||||
@ -239,6 +264,7 @@ let AnimationPlayerFront = FrontClass(AnimationPlayerActor, {
|
||||
playState: this._form.playState,
|
||||
name: this._form.name,
|
||||
duration: this._form.duration,
|
||||
delay: this._form.delay,
|
||||
iterationCount: this._form.iterationCount,
|
||||
isRunningOnCompositor: this._form.isRunningOnCompositor
|
||||
}
|
||||
|
@ -3,8 +3,8 @@
|
||||
.simple-animation {
|
||||
display: inline-block;
|
||||
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background: red;
|
||||
|
||||
@ -14,8 +14,8 @@
|
||||
.multiple-animations {
|
||||
display: inline-block;
|
||||
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background: #eee;
|
||||
|
||||
@ -25,8 +25,8 @@
|
||||
.transition {
|
||||
display: inline-block;
|
||||
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background: #f06;
|
||||
|
||||
@ -39,14 +39,50 @@
|
||||
.short-animation {
|
||||
display: inline-block;
|
||||
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background: purple;
|
||||
|
||||
animation: move 1s;
|
||||
}
|
||||
|
||||
.delayed-animation {
|
||||
display: inline-block;
|
||||
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background: rebeccapurple;
|
||||
|
||||
animation: move 2s 5s infinite;
|
||||
}
|
||||
|
||||
.delayed-transition {
|
||||
display: inline-block;
|
||||
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background: black;
|
||||
|
||||
transition: width 5s 3s;
|
||||
}
|
||||
.delayed-transition.get-round {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.delayed-multiple-animations {
|
||||
display: inline-block;
|
||||
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background: green;
|
||||
|
||||
animation: move .5s 1s 10, glow 1s .75s 30;
|
||||
}
|
||||
|
||||
@keyframes move {
|
||||
100% {
|
||||
transform: translateY(100px);
|
||||
@ -64,10 +100,14 @@
|
||||
<div class="multiple-animations"></div>
|
||||
<div class="transition"></div>
|
||||
<div class="short-animation"></div>
|
||||
<div class="delayed-animation"></div>
|
||||
<div class="delayed-transition"></div>
|
||||
<div class="delayed-multiple-animations"></div>
|
||||
<script type="text/javascript">
|
||||
// Get the transition started when the page loads
|
||||
// Get the transitions started when the page loads
|
||||
var players;
|
||||
addEventListener("load", function() {
|
||||
document.querySelector(".transition").classList.add("get-round");
|
||||
document.querySelector(".delayed-transition").classList.add("get-round");
|
||||
});
|
||||
</script>
|
||||
|
@ -19,6 +19,7 @@ support-files =
|
||||
[browser_animation_actors_03.js]
|
||||
[browser_animation_actors_04.js]
|
||||
[browser_animation_actors_05.js]
|
||||
[browser_animation_actors_06.js]
|
||||
[browser_navigateEvents.js]
|
||||
[browser_storage_dynamic_windows.js]
|
||||
[browser_storage_listings.js]
|
||||
|
@ -36,6 +36,7 @@ function* playerHasAnInitialState(walker, front) {
|
||||
ok("playState" in player.initialState, "Player's state has playState");
|
||||
ok("name" in player.initialState, "Player's state has name");
|
||||
ok("duration" in player.initialState, "Player's state has duration");
|
||||
ok("delay" in player.initialState, "Player's state has delay");
|
||||
ok("iterationCount" in player.initialState, "Player's state has iterationCount");
|
||||
ok("isRunningOnCompositor" in player.initialState, "Player's state has isRunningOnCompositor");
|
||||
}
|
||||
@ -67,6 +68,16 @@ function* playerStateIsCorrect(walker, front) {
|
||||
is(state.duration, 1000, "The 2nd animation's duration is correct");
|
||||
is(state.iterationCount, 5, "The 2nd animation's iteration count is correct");
|
||||
is(state.playState, "running", "The 2nd animation's playState is correct");
|
||||
|
||||
info("Checking the state of an animation with delay");
|
||||
|
||||
state = yield getAnimationStateForNode(walker, front, ".delayed-animation", 0);
|
||||
is(state.delay, 5000, "The animation delay is correct");
|
||||
|
||||
info("Checking the state of an transition with delay");
|
||||
|
||||
state = yield getAnimationStateForNode(walker, front, ".delayed-transition", 0);
|
||||
is(state.delay, 3000, "The transition delay is correct");
|
||||
}
|
||||
|
||||
function* getAnimationStateForNode(walker, front, nodeSelector, playerIndex) {
|
||||
|
@ -0,0 +1,50 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Check that the duration, iterationCount and delay are retrieved correctly for
|
||||
// multiple animations.
|
||||
|
||||
const {AnimationsFront} = require("devtools/server/actors/animation");
|
||||
const {InspectorFront} = require("devtools/server/actors/inspector");
|
||||
|
||||
add_task(function*() {
|
||||
let doc = yield addTab(MAIN_DOMAIN + "animation.html");
|
||||
|
||||
initDebuggerServer();
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
let form = yield connectDebuggerClient(client);
|
||||
let inspector = InspectorFront(client, form);
|
||||
let walker = yield inspector.getWalker();
|
||||
let front = AnimationsFront(client, form);
|
||||
|
||||
yield playerHasAnInitialState(walker, front);
|
||||
|
||||
yield closeDebuggerClient(client);
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
function* playerHasAnInitialState(walker, front) {
|
||||
let state = yield getAnimationStateForNode(walker, front,
|
||||
".delayed-multiple-animations", 0);
|
||||
ok(state.duration, 500, "The duration of the first animation is correct");
|
||||
ok(state.iterationCount, 10, "The iterationCount of the first animation is correct");
|
||||
ok(state.delay, 1000, "The delay of the first animation is correct");
|
||||
|
||||
state = yield getAnimationStateForNode(walker, front,
|
||||
".delayed-multiple-animations", 1);
|
||||
ok(state.duration, 1000, "The duration of the secon animation is correct");
|
||||
ok(state.iterationCount, 30, "The iterationCount of the secon animation is correct");
|
||||
ok(state.delay, 750, "The delay of the secon animation is correct");
|
||||
}
|
||||
|
||||
function* getAnimationStateForNode(walker, front, nodeSelector, playerIndex) {
|
||||
let node = yield walker.querySelector(walker.rootNode, nodeSelector);
|
||||
let players = yield front.getAnimationPlayersForNode(node);
|
||||
let player = players[playerIndex];
|
||||
yield player.ready();
|
||||
let state = yield player.getCurrentState();
|
||||
return state;
|
||||
}
|