mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Merge m-c to inbound, a=merge
This commit is contained in:
commit
6feb6f6051
@ -15,12 +15,12 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f345f6a015709beeb2ca3955cab077fcaa959d3b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1bc0b19527777ffee494962b48db4be857b07d64"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="828317e64d28138f24d578ab340c2a0ff8552df0"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
@ -15,12 +15,12 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f345f6a015709beeb2ca3955cab077fcaa959d3b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1bc0b19527777ffee494962b48db4be857b07d64"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="828317e64d28138f24d578ab340c2a0ff8552df0"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
@ -19,12 +19,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="f345f6a015709beeb2ca3955cab077fcaa959d3b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1bc0b19527777ffee494962b48db4be857b07d64"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="12ff7481566587aa4198cf1287598acb3a999973"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="828317e64d28138f24d578ab340c2a0ff8552df0"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
|
@ -17,9 +17,9 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f345f6a015709beeb2ca3955cab077fcaa959d3b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1bc0b19527777ffee494962b48db4be857b07d64"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="828317e64d28138f24d578ab340c2a0ff8552df0"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f004530b30a63c08a16d82536858600446b2abf5"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
@ -15,11 +15,11 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f345f6a015709beeb2ca3955cab077fcaa959d3b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1bc0b19527777ffee494962b48db4be857b07d64"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="828317e64d28138f24d578ab340c2a0ff8552df0"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
@ -15,11 +15,11 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f345f6a015709beeb2ca3955cab077fcaa959d3b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1bc0b19527777ffee494962b48db4be857b07d64"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="828317e64d28138f24d578ab340c2a0ff8552df0"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
@ -19,12 +19,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="f345f6a015709beeb2ca3955cab077fcaa959d3b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1bc0b19527777ffee494962b48db4be857b07d64"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="12ff7481566587aa4198cf1287598acb3a999973"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="828317e64d28138f24d578ab340c2a0ff8552df0"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
|
@ -15,12 +15,12 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f345f6a015709beeb2ca3955cab077fcaa959d3b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1bc0b19527777ffee494962b48db4be857b07d64"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="828317e64d28138f24d578ab340c2a0ff8552df0"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"git": {
|
||||
"git_revision": "f345f6a015709beeb2ca3955cab077fcaa959d3b",
|
||||
"git_revision": "1bc0b19527777ffee494962b48db4be857b07d64",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "83e7fd1f9ab848025ef06f1283ad95fe5005f26c",
|
||||
"revision": "d8e220a9a7bc3083b1cd9bc94f5a524e5b114384",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
@ -15,12 +15,12 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f345f6a015709beeb2ca3955cab077fcaa959d3b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1bc0b19527777ffee494962b48db4be857b07d64"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="828317e64d28138f24d578ab340c2a0ff8552df0"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
@ -18,9 +18,9 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f345f6a015709beeb2ca3955cab077fcaa959d3b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1bc0b19527777ffee494962b48db4be857b07d64"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="828317e64d28138f24d578ab340c2a0ff8552df0"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f004530b30a63c08a16d82536858600446b2abf5"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
@ -15,12 +15,12 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f345f6a015709beeb2ca3955cab077fcaa959d3b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1bc0b19527777ffee494962b48db4be857b07d64"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9465d16f95ab87636b2ae07538ee88e5aeff2d7d"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="828317e64d28138f24d578ab340c2a0ff8552df0"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
@ -1,3 +1,7 @@
|
||||
user_pref("devtools.debugger.prompt-connection", false);
|
||||
user_pref("devtools.debugger.forbid-certified-apps", false);
|
||||
user_pref("devtools.apps.forbidden-permissions", "");
|
||||
|
||||
// Required for Mulet in order to run the debugger server from the command line
|
||||
user_pref("devtools.debugger.remote-enabled", true);
|
||||
user_pref("devtools.chrome.enabled", true);
|
||||
|
@ -1344,7 +1344,7 @@ pref("devtools.toolbox.sidebar.width", 500);
|
||||
pref("devtools.toolbox.host", "bottom");
|
||||
pref("devtools.toolbox.previousHost", "side");
|
||||
pref("devtools.toolbox.selectedTool", "webconsole");
|
||||
pref("devtools.toolbox.toolbarSpec", '["splitconsole", "paintflashing toggle","tilt toggle","scratchpad","resize toggle","eyedropper","screenshot --fullpage", "rulers"]');
|
||||
pref("devtools.toolbox.toolbarSpec", '["splitconsole", "paintflashing toggle","tilt toggle","scratchpad","resize toggle","eyedropper","screenshot --fullpage", "rulers", "measure"]');
|
||||
pref("devtools.toolbox.sideEnabled", true);
|
||||
pref("devtools.toolbox.zoomValue", "1");
|
||||
pref("devtools.toolbox.splitconsoleEnabled", false);
|
||||
@ -1361,6 +1361,7 @@ pref("devtools.command-button-responsive.enabled", true);
|
||||
pref("devtools.command-button-eyedropper.enabled", false);
|
||||
pref("devtools.command-button-screenshot.enabled", false);
|
||||
pref("devtools.command-button-rulers.enabled", false);
|
||||
pref("devtools.command-button-measure.enabled", false);
|
||||
|
||||
// Inspector preferences
|
||||
// Enable the Inspector
|
||||
|
@ -37,6 +37,7 @@ loop.store.ActiveRoomStore = (function() {
|
||||
|
||||
var sharedActions = loop.shared.actions;
|
||||
var crypto = loop.crypto;
|
||||
var CHAT_CONTENT_TYPES = loop.shared.utils.CHAT_CONTENT_TYPES;
|
||||
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
||||
|
||||
@ -107,6 +108,7 @@ loop.store.ActiveRoomStore = (function() {
|
||||
*/
|
||||
_statesToResetOnLeave: [
|
||||
"audioMuted",
|
||||
"chatMessageExchanged",
|
||||
"localSrcMediaElement",
|
||||
"localVideoDimensions",
|
||||
"mediaConnected",
|
||||
@ -155,7 +157,10 @@ loop.store.ActiveRoomStore = (function() {
|
||||
// Social API state.
|
||||
socialShareProviders: null,
|
||||
// True if media has been connected both-ways.
|
||||
mediaConnected: false
|
||||
mediaConnected: false,
|
||||
// True if a chat message was sent or received during a session.
|
||||
// Read more at https://wiki.mozilla.org/Loop/Session.
|
||||
chatMessageExchanged: false
|
||||
};
|
||||
},
|
||||
|
||||
@ -236,7 +241,7 @@ loop.store.ActiveRoomStore = (function() {
|
||||
|
||||
this._registeredActions = true;
|
||||
|
||||
this.dispatcher.register(this, [
|
||||
var actions = [
|
||||
"roomFailure",
|
||||
"retryAfterRoomFailure",
|
||||
"updateRoomInfo",
|
||||
@ -263,7 +268,14 @@ loop.store.ActiveRoomStore = (function() {
|
||||
"updateSocialShareInfo",
|
||||
"connectionStatus",
|
||||
"mediaConnected"
|
||||
]);
|
||||
];
|
||||
// Register actions that are only used on Desktop.
|
||||
if (this._isDesktop) {
|
||||
// 'receivedTextChatMessage' and 'sendTextChatMessage' actions are only
|
||||
// registered for Telemetry. Once measured, they're unregistered.
|
||||
actions.push("receivedTextChatMessage", "sendTextChatMessage");
|
||||
}
|
||||
this.dispatcher.register(this, actions);
|
||||
|
||||
this._onUpdateListener = this._handleRoomUpdate.bind(this);
|
||||
this._onDeleteListener = this._handleRoomDelete.bind(this);
|
||||
@ -611,12 +623,18 @@ loop.store.ActiveRoomStore = (function() {
|
||||
// previously. We should add better user feedback here.
|
||||
console.error("Firefox didn't handle room it said it could.");
|
||||
} else {
|
||||
this.dispatcher.dispatch(new sharedActions.JoinedRoom({
|
||||
apiKey: "",
|
||||
sessionToken: "",
|
||||
sessionId: "",
|
||||
expires: 0
|
||||
}));
|
||||
if (e.detail.message.alreadyOpen) {
|
||||
this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
|
||||
reason: FAILURE_DETAILS.ROOM_ALREADY_OPEN
|
||||
}));
|
||||
} else {
|
||||
this.dispatcher.dispatch(new sharedActions.JoinedRoom({
|
||||
apiKey: "",
|
||||
sessionToken: "",
|
||||
sessionId: "",
|
||||
expires: 0
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1052,6 +1070,15 @@ loop.store.ActiveRoomStore = (function() {
|
||||
* will skip the leave message.
|
||||
*/
|
||||
_leaveRoom: function(nextState, failedJoinRequest) {
|
||||
if (this._storeState.standalone && this._storeState.userAgentHandlesRoom) {
|
||||
// If the user agent is handling the room, all we need to do is advance
|
||||
// to the next state.
|
||||
this.setStoreState({
|
||||
roomState: nextState
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (loop.standaloneMedia) {
|
||||
loop.standaloneMedia.multiplexGum.reset();
|
||||
}
|
||||
@ -1123,6 +1150,50 @@ loop.store.ActiveRoomStore = (function() {
|
||||
nextState[storeProp] = this.getStoreState()[storeProp];
|
||||
nextState[storeProp][actionData.videoType] = actionData.dimensions;
|
||||
this.setStoreState(nextState);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles chat messages received and/ or about to send. If this is the first
|
||||
* chat message for the current session, register a count with telemetry.
|
||||
* It will unhook the listeners when the telemetry criteria have been
|
||||
* fulfilled to make sure we remain lean.
|
||||
* Note: the 'receivedTextChatMessage' and 'sendTextChatMessage' actions are
|
||||
* only registered on Desktop.
|
||||
*
|
||||
* @param {sharedActions.ReceivedTextChatMessage|SendTextChatMessage} actionData
|
||||
*/
|
||||
_handleTextChatMessage: function(actionData) {
|
||||
if (!this._isDesktop || this.getStoreState().chatMessageExchanged ||
|
||||
actionData.contentType !== CHAT_CONTENT_TYPES.TEXT) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setStoreState({ chatMessageExchanged: true });
|
||||
// There's no need to listen to these actions anymore.
|
||||
this.dispatcher.unregister(this, [
|
||||
"receivedTextChatMessage",
|
||||
"sendTextChatMessage"
|
||||
]);
|
||||
// Ping telemetry of this session with successful message(s) exchange.
|
||||
this._mozLoop.telemetryAddValue("LOOP_ROOM_SESSION_WITHCHAT", 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles received text chat messages. For telemetry purposes only.
|
||||
*
|
||||
* @param {sharedActions.ReceivedTextChatMessage} actionData
|
||||
*/
|
||||
receivedTextChatMessage: function(actionData) {
|
||||
this._handleTextChatMessage(actionData);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles sending of a chat message. For telemetry purposes only.
|
||||
*
|
||||
* @param {sharedActions.SendTextChatMessage} actionData
|
||||
*/
|
||||
sendTextChatMessage: function(actionData) {
|
||||
this._handleTextChatMessage(actionData);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -38,6 +38,28 @@ loop.Dispatcher = (function() {
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Unregister a store from receiving notifications of specific actions.
|
||||
*
|
||||
* @param {Object} store The store object to unregister
|
||||
* @param {Array} eventTypes An array of action names
|
||||
*/
|
||||
unregister: function(store, eventTypes) {
|
||||
eventTypes.forEach(function(type) {
|
||||
if (!this._eventData.hasOwnProperty(type)) {
|
||||
return;
|
||||
}
|
||||
var idx = this._eventData[type].indexOf(store);
|
||||
if (idx === -1) {
|
||||
return;
|
||||
}
|
||||
this._eventData[type].splice(idx, 1);
|
||||
if (!this._eventData[type].length) {
|
||||
delete this._eventData[type];
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Dispatches an action to all registered stores.
|
||||
*/
|
||||
|
@ -16,11 +16,7 @@ loop.store.TextChatStore = (function() {
|
||||
SPECIAL: "special"
|
||||
};
|
||||
|
||||
var CHAT_CONTENT_TYPES = loop.store.CHAT_CONTENT_TYPES = {
|
||||
CONTEXT: "chat-context",
|
||||
TEXT: "chat-text",
|
||||
ROOM_NAME: "room-name"
|
||||
};
|
||||
var CHAT_CONTENT_TYPES = loop.shared.utils.CHAT_CONTENT_TYPES;
|
||||
|
||||
/**
|
||||
* A store to handle text chats. The store has a message list that may
|
||||
|
@ -12,7 +12,7 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
var sharedViews = loop.shared.views;
|
||||
var CHAT_MESSAGE_TYPES = loop.store.CHAT_MESSAGE_TYPES;
|
||||
var CHAT_CONTENT_TYPES = loop.store.CHAT_CONTENT_TYPES;
|
||||
var CHAT_CONTENT_TYPES = loop.shared.utils.CHAT_CONTENT_TYPES;
|
||||
|
||||
/**
|
||||
* Renders an individual entry for the text chat entries view.
|
||||
|
@ -12,7 +12,7 @@ loop.shared.views.chat = (function(mozL10n) {
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
var sharedViews = loop.shared.views;
|
||||
var CHAT_MESSAGE_TYPES = loop.store.CHAT_MESSAGE_TYPES;
|
||||
var CHAT_CONTENT_TYPES = loop.store.CHAT_CONTENT_TYPES;
|
||||
var CHAT_CONTENT_TYPES = loop.shared.utils.CHAT_CONTENT_TYPES;
|
||||
|
||||
/**
|
||||
* Renders an individual entry for the text chat entries view.
|
||||
|
@ -75,6 +75,7 @@ var inChrome = typeof Components != "undefined" && "utils" in Components;
|
||||
var FAILURE_DETAILS = {
|
||||
MEDIA_DENIED: "reason-media-denied",
|
||||
NO_MEDIA: "reason-no-media",
|
||||
ROOM_ALREADY_OPEN: "reason-room-already-open",
|
||||
UNABLE_TO_PUBLISH_MEDIA: "unable-to-publish-media",
|
||||
USER_UNAVAILABLE: "reason-user-unavailable",
|
||||
COULD_NOT_CONNECT: "reason-could-not-connect",
|
||||
@ -110,6 +111,12 @@ var inChrome = typeof Components != "undefined" && "utils" in Components;
|
||||
ACTIVE: "ss-active"
|
||||
};
|
||||
|
||||
var CHAT_CONTENT_TYPES = {
|
||||
CONTEXT: "chat-context",
|
||||
TEXT: "chat-text",
|
||||
ROOM_NAME: "room-name"
|
||||
};
|
||||
|
||||
/**
|
||||
* Format a given date into an l10n-friendly string.
|
||||
*
|
||||
@ -775,6 +782,7 @@ var inChrome = typeof Components != "undefined" && "utils" in Components;
|
||||
|
||||
this.utils = {
|
||||
CALL_TYPES: CALL_TYPES,
|
||||
CHAT_CONTENT_TYPES: CHAT_CONTENT_TYPES,
|
||||
FAILURE_DETAILS: FAILURE_DETAILS,
|
||||
REST_ERRNOS: REST_ERRNOS,
|
||||
WEBSOCKET_REASONS: WEBSOCKET_REASONS,
|
||||
|
@ -961,9 +961,10 @@ var LoopRoomsInternal = {
|
||||
return;
|
||||
}
|
||||
|
||||
let sendResponse = response => {
|
||||
let sendResponse = (response, alreadyOpen) => {
|
||||
gLinkClickerChannel.send({
|
||||
response: response
|
||||
response: response,
|
||||
alreadyOpen: alreadyOpen
|
||||
}, sendingContext);
|
||||
};
|
||||
|
||||
@ -971,16 +972,22 @@ var LoopRoomsInternal = {
|
||||
|
||||
switch (message.command) {
|
||||
case "checkWillOpenRoom":
|
||||
sendResponse(hasRoom);
|
||||
sendResponse(hasRoom, false);
|
||||
break;
|
||||
case "openRoom":
|
||||
if (hasRoom) {
|
||||
this.open(message.roomToken);
|
||||
if (MozLoopService.isChatWindowOpen(message.roomToken)) {
|
||||
sendResponse(hasRoom, true);
|
||||
} else {
|
||||
this.open(message.roomToken);
|
||||
sendResponse(hasRoom, false);
|
||||
}
|
||||
} else {
|
||||
sendResponse(hasRoom, false);
|
||||
}
|
||||
sendResponse(hasRoom);
|
||||
break;
|
||||
default:
|
||||
sendResponse(false);
|
||||
sendResponse(false, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -203,7 +203,8 @@ var MozLoopServiceInternal = {
|
||||
pushURLs: new Map(),
|
||||
|
||||
mocks: {
|
||||
pushHandler: undefined
|
||||
pushHandler: undefined,
|
||||
isChatWindowOpen: undefined
|
||||
},
|
||||
|
||||
/**
|
||||
@ -871,6 +872,22 @@ var MozLoopServiceInternal = {
|
||||
return "about:loopconversation#" + chatWindowId;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if a chat window is already open for a given window id.
|
||||
*
|
||||
* @param {String} chatWindowId The window id.
|
||||
* @return {Boolean} True if the window is opened.
|
||||
*/
|
||||
isChatWindowOpen: function(chatWindowId) {
|
||||
if (this.mocks.isChatWindowOpen !== undefined) {
|
||||
return this.mocks.isChatWindowOpen;
|
||||
}
|
||||
|
||||
let chatUrl = this.getChatURL(chatWindowId);
|
||||
|
||||
return [...Chat.chatboxes].some(chatbox => chatbox.src == chatUrl);
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens the chat window
|
||||
*
|
||||
@ -1377,6 +1394,16 @@ this.MozLoopService = {
|
||||
return MozLoopServiceInternal.openChatWindow(conversationWindowData);
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if a chat window is already open for a given window id.
|
||||
*
|
||||
* @param {String} chatWindowId The window id.
|
||||
* @return {Boolean} True if the window is opened.
|
||||
*/
|
||||
isChatWindowOpen: function(chatWindowId) {
|
||||
return MozLoopServiceInternal.isChatWindowOpen(chatWindowId);
|
||||
},
|
||||
|
||||
/**
|
||||
* @see MozLoopServiceInternal.promiseRegisteredWithServers
|
||||
*/
|
||||
|
@ -184,6 +184,14 @@ html[dir="rtl"] .rooms-footer .footer-logo {
|
||||
margin: 2rem auto;
|
||||
}
|
||||
|
||||
.handle-user-agent-view > .info-panel > .failure {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
/* Add padding to match the height of the button. */
|
||||
padding: 1.15rem 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.handle-user-agent-view > .info-panel > button {
|
||||
width: 80%;
|
||||
height: 4rem;
|
||||
|
@ -125,12 +125,19 @@ loop.store.StandaloneMetricsStore = (function() {
|
||||
* @param {sharedActions.ConnectionFailure} actionData
|
||||
*/
|
||||
connectionFailure: function(actionData) {
|
||||
if (actionData.reason === FAILURE_DETAILS.MEDIA_DENIED) {
|
||||
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.failed,
|
||||
"Media denied");
|
||||
} else if (actionData.reason === FAILURE_DETAILS.NO_MEDIA) {
|
||||
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.failed,
|
||||
"No media");
|
||||
switch(actionData.reason) {
|
||||
case FAILURE_DETAILS.MEDIA_DENIED:
|
||||
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.failed,
|
||||
"Media denied");
|
||||
break;
|
||||
case FAILURE_DETAILS.NO_MEDIA:
|
||||
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.failed,
|
||||
"No media");
|
||||
break;
|
||||
case FAILURE_DETAILS.ROOM_ALREADY_OPEN:
|
||||
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.failed,
|
||||
"Room already open");
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -75,7 +75,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
this.props.dispatcher.dispatch(new sharedActions.JoinRoom());
|
||||
},
|
||||
|
||||
render: function() {
|
||||
_renderJoinButton: function() {
|
||||
var buttonMessage = this.state.roomState === ROOM_STATES.JOINED ?
|
||||
mozL10n.get("rooms_room_joined_own_conversation_label") :
|
||||
mozL10n.get("rooms_room_join_label");
|
||||
@ -86,6 +86,22 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
disabled: this.state.roomState === ROOM_STATES.JOINED
|
||||
});
|
||||
|
||||
return (
|
||||
React.createElement("button", {
|
||||
className: buttonClasses,
|
||||
onClick: this.handleJoinButton},
|
||||
buttonMessage
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
_renderFailureText: function() {
|
||||
return (
|
||||
React.createElement("p", {className: "failure"}, mozL10n.get("rooms_already_joined") )
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// The extra scroller div here is for providing a scroll view for shorter
|
||||
// screens, as the common.css specifies overflow:hidden for the body which
|
||||
// we need in some places.
|
||||
@ -96,11 +112,11 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
React.createElement("p", {className: "loop-logo-text", title: mozL10n.get("clientShortname2") }),
|
||||
React.createElement("p", {className: "roomName"}, this.state.roomName),
|
||||
React.createElement("p", {className: "loop-logo"}),
|
||||
React.createElement("button", {
|
||||
className: buttonClasses,
|
||||
onClick: this.handleJoinButton},
|
||||
buttonMessage
|
||||
)
|
||||
|
||||
this.state.failureReason ?
|
||||
this._renderFailureText() :
|
||||
this._renderJoinButton()
|
||||
|
||||
),
|
||||
React.createElement(ToSView, {
|
||||
dispatcher: this.props.dispatcher}),
|
||||
|
@ -75,7 +75,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
this.props.dispatcher.dispatch(new sharedActions.JoinRoom());
|
||||
},
|
||||
|
||||
render: function() {
|
||||
_renderJoinButton: function() {
|
||||
var buttonMessage = this.state.roomState === ROOM_STATES.JOINED ?
|
||||
mozL10n.get("rooms_room_joined_own_conversation_label") :
|
||||
mozL10n.get("rooms_room_join_label");
|
||||
@ -86,6 +86,22 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
disabled: this.state.roomState === ROOM_STATES.JOINED
|
||||
});
|
||||
|
||||
return (
|
||||
<button
|
||||
className={buttonClasses}
|
||||
onClick={this.handleJoinButton}>
|
||||
{buttonMessage}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
|
||||
_renderFailureText: function() {
|
||||
return (
|
||||
<p className="failure">{ mozL10n.get("rooms_already_joined") }</p>
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// The extra scroller div here is for providing a scroll view for shorter
|
||||
// screens, as the common.css specifies overflow:hidden for the body which
|
||||
// we need in some places.
|
||||
@ -96,11 +112,11 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
<p className="loop-logo-text" title={ mozL10n.get("clientShortname2") }></p>
|
||||
<p className="roomName">{ this.state.roomName }</p>
|
||||
<p className="loop-logo" />
|
||||
<button
|
||||
className={buttonClasses}
|
||||
onClick={this.handleJoinButton}>
|
||||
{buttonMessage}
|
||||
</button>
|
||||
{
|
||||
this.state.failureReason ?
|
||||
this._renderFailureText() :
|
||||
this._renderJoinButton()
|
||||
}
|
||||
</div>
|
||||
<ToSView
|
||||
dispatcher={this.props.dispatcher} />
|
||||
|
@ -69,6 +69,7 @@ rooms_room_full_call_to_action_label=Learn more about {{clientShortname}} »
|
||||
rooms_room_joined_label=Someone has joined the conversation!
|
||||
rooms_room_join_label=Join the conversation
|
||||
rooms_room_joined_own_conversation_label=Enjoy your conversation
|
||||
rooms_already_joined=You're already in this conversation.
|
||||
rooms_display_name_guest=Guest
|
||||
rooms_unavailable_notification_message=Sorry, you cannot join this conversation. The link may be expired or invalid.
|
||||
rooms_media_denied_message=We could not get access to your microphone or camera. Please reload the page to try again.
|
||||
|
@ -89,7 +89,7 @@ class Test1BrowserCall(MarionetteTestCase):
|
||||
self.marionette.set_context("content")
|
||||
|
||||
def local_start_a_conversation(self):
|
||||
button = self.marionette.find_element(By.CSS_SELECTOR, ".new-room-view .btn-info")
|
||||
button = self.wait_for_element_displayed(By.CSS_SELECTOR, ".new-room-view .btn-info")
|
||||
|
||||
self.wait_for_element_enabled(button, 120)
|
||||
|
||||
|
@ -28,7 +28,9 @@ function BackChannel(uri) {
|
||||
|
||||
this.channel.listen((id, data) => {
|
||||
if (this.pendingResolve) {
|
||||
this.pendingResolve(data);
|
||||
let resolve = this.pendingResolve;
|
||||
this.pendingResolve = null;
|
||||
resolve(data);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -54,7 +56,9 @@ var gBadBackChannel;
|
||||
function promiseNewChannelResponse(uri, channel, hash) {
|
||||
let waitForChannelPromise = new Promise((resolve, reject) => {
|
||||
if (channel.receivedData) {
|
||||
resolve(channel.receivedData);
|
||||
let data = channel.receivedData;
|
||||
channel.receivedData = null;
|
||||
resolve(data);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -113,12 +117,16 @@ add_task(function* test_loopRooms_webchannel_checkWillOpenRoom() {
|
||||
});
|
||||
|
||||
add_task(function* test_loopRooms_webchannel_openRoom() {
|
||||
let openedUrl;
|
||||
let openedUrl;
|
||||
Chat.open = function(contentWindow, origin, title, url) {
|
||||
openedUrl = url;
|
||||
};
|
||||
|
||||
MozLoopServiceInternal.mocks.isChatWindowOpen = false;
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
Chat.open = openChatOrig;
|
||||
MozLoopServiceInternal.mocks.isChatWindowOpen = undefined;
|
||||
});
|
||||
|
||||
// Test when the room doesn't exist
|
||||
@ -128,9 +136,13 @@ add_task(function* test_loopRooms_webchannel_openRoom() {
|
||||
|
||||
Assert.ok(!openedUrl, "should not open a chat window");
|
||||
Assert.equal(got.message.response, false, "should have got a response of false");
|
||||
Assert.equal(got.message.alreadyOpen, false, "should not indicate that its already open");
|
||||
|
||||
// Now add a room & check it.
|
||||
LoopRooms._setRoomsCache(fakeRoomList);
|
||||
registerCleanupFunction(() => {
|
||||
LoopRooms._setRoomsCache();
|
||||
});
|
||||
|
||||
got = yield promiseNewChannelResponse(TEST_URI_GOOD, gGoodBackChannel, "openRoom");
|
||||
|
||||
@ -144,4 +156,13 @@ add_task(function* test_loopRooms_webchannel_openRoom() {
|
||||
Assert.equal(windowData.roomToken, ROOM_TOKEN, "window data should have the roomToken");
|
||||
|
||||
Assert.equal(got.message.response, true, "should have got a response of true");
|
||||
Assert.equal(got.message.alreadyOpen, false, "should not indicate that its already open");
|
||||
|
||||
// Simulate a window already being open.
|
||||
MozLoopServiceInternal.mocks.isChatWindowOpen = true;
|
||||
|
||||
got = yield promiseNewChannelResponse(TEST_URI_GOOD, gGoodBackChannel, "openRoom");
|
||||
|
||||
Assert.equal(got.message.response, true, "should have got a response of true");
|
||||
Assert.equal(got.message.alreadyOpen, true, "should indicate the room is already open");
|
||||
});
|
||||
|
@ -172,7 +172,21 @@ add_task(function* test_mozLoop_telemetryAdd_roomContextClick() {
|
||||
|
||||
let snapshot;
|
||||
for (let i = 1; i < 4; ++i) {
|
||||
gMozLoopAPI.telemetryAddValue("LOOP_ROOM_CONTEXT_CLICK", 1);
|
||||
gMozLoopAPI.telemetryAddValue(histogramId, 1);
|
||||
snapshot = histogram.snapshot();
|
||||
Assert.strictEqual(snapshot.counts[0], i);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_mozLoop_telemetryAdd_roomSessionWithChat() {
|
||||
let histogramId = "LOOP_ROOM_SESSION_WITHCHAT";
|
||||
let histogram = Services.telemetry.getHistogramById(histogramId);
|
||||
|
||||
histogram.clear();
|
||||
|
||||
let snapshot;
|
||||
for (let i = 1; i < 4; ++i) {
|
||||
gMozLoopAPI.telemetryAddValue(histogramId, 1);
|
||||
snapshot = histogram.snapshot();
|
||||
Assert.strictEqual(snapshot.counts[0], i);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
var sharedUtils = loop.shared.utils;
|
||||
var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
|
||||
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||
var CHAT_CONTENT_TYPES = loop.shared.utils.CHAT_CONTENT_TYPES;
|
||||
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
||||
var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
|
||||
@ -39,7 +40,8 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
},
|
||||
setScreenShareState: sinon.stub(),
|
||||
getActiveTabWindowId: sandbox.stub().callsArgWith(0, null, 42),
|
||||
getSocialShareProviders: sinon.stub().returns([])
|
||||
getSocialShareProviders: sinon.stub().returns([]),
|
||||
telemetryAddValue: sinon.stub()
|
||||
};
|
||||
|
||||
fakeSdkDriver = {
|
||||
@ -871,7 +873,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Firefox Handles Room", function() {
|
||||
describe("User Agent Handles Room", function() {
|
||||
var channelListener;
|
||||
|
||||
beforeEach(function() {
|
||||
@ -949,7 +951,8 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
detail: {
|
||||
id: "loop-link-clicker",
|
||||
message: {
|
||||
response: true
|
||||
response: true,
|
||||
alreadyOpen: false
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -963,6 +966,28 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
expires: 0
|
||||
}));
|
||||
});
|
||||
|
||||
it("should dispatch a ConnectionFailure action if the room was already opened", function() {
|
||||
// Start the join.
|
||||
store.joinRoom();
|
||||
|
||||
// Pretend Firefox calls back.
|
||||
channelListener({
|
||||
detail: {
|
||||
id: "loop-link-clicker",
|
||||
message: {
|
||||
response: true,
|
||||
alreadyOpen: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
sinon.assert.called(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionFailure({
|
||||
reason: FAILURE_DETAILS.ROOM_ALREADY_OPEN
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1255,6 +1280,29 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
|
||||
expect(store.getStoreState().roomState).eql(ROOM_STATES.FAILED);
|
||||
});
|
||||
|
||||
it("should set the state to `FAILED` if the user agent is handling the room", function() {
|
||||
store.setStoreState({
|
||||
standalone: true,
|
||||
userAgentHandlesRoom: true
|
||||
});
|
||||
|
||||
store.connectionFailure(connectionFailureAction);
|
||||
|
||||
expect(store.getStoreState().roomState).eql(ROOM_STATES.FAILED);
|
||||
});
|
||||
|
||||
it("should not do any other cleanup if the user agent is handling the room", function() {
|
||||
store.setStoreState({
|
||||
standalone: true,
|
||||
userAgentHandlesRoom: true
|
||||
});
|
||||
|
||||
store.connectionFailure(connectionFailureAction);
|
||||
|
||||
sinon.assert.notCalled(fakeMultiplexGum.reset);
|
||||
sinon.assert.notCalled(fakeSdkDriver.disconnectSession);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#setMute", function() {
|
||||
@ -1842,7 +1890,8 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
receivingScreenShare: true,
|
||||
remoteVideoDimensions: { y: 10 },
|
||||
screenSharingState: true,
|
||||
videoMuted: true
|
||||
videoMuted: true,
|
||||
chatMessageExchanged: false
|
||||
});
|
||||
|
||||
store.leaveRoom();
|
||||
@ -1853,6 +1902,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
expect(store._storeState.remoteVideoDimensions).eql({});
|
||||
expect(store._storeState.screenSharingState).eql(SCREEN_SHARE_STATES.INACTIVE);
|
||||
expect(store._storeState.videoMuted).eql(false);
|
||||
expect(store._storeState.chatMessageExchanged).eql(false);
|
||||
});
|
||||
|
||||
it("should not reset the room context", function() {
|
||||
@ -1886,6 +1936,73 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("#_handleTextChatMessage", function() {
|
||||
beforeEach(function() {
|
||||
store._isDesktop = true;
|
||||
store.setupWindowData(new sharedActions.SetupWindowData({
|
||||
windowId: "42",
|
||||
type: "room",
|
||||
roomToken: "fakeToken"
|
||||
}));
|
||||
});
|
||||
|
||||
function assertWeDidNothing() {
|
||||
expect(dispatcher._eventData.receivedTextChatMessage.length).eql(1);
|
||||
expect(dispatcher._eventData.sendTextChatMessage.length).eql(1);
|
||||
expect(store.getStoreState().chatMessageExchanged).eql(false);
|
||||
sinon.assert.notCalled(fakeMozLoop.telemetryAddValue);
|
||||
}
|
||||
|
||||
it("should not do anything for the link clicker side", function() {
|
||||
store._isDesktop = false;
|
||||
|
||||
store._handleTextChatMessage(new sharedActions.SendTextChatMessage({
|
||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Hello!",
|
||||
sentTimestamp: "1970-01-01T00:00:00.000Z"
|
||||
}));
|
||||
|
||||
assertWeDidNothing();
|
||||
});
|
||||
|
||||
it("should not do anything when a chat message has arrived before", function() {
|
||||
store.setStoreState({ chatMessageExchanged: true });
|
||||
|
||||
store._handleTextChatMessage(new sharedActions.ReceivedTextChatMessage({
|
||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Hello!",
|
||||
receivedTimestamp: "1970-01-01T00:00:00.000Z"
|
||||
}));
|
||||
|
||||
sinon.assert.notCalled(fakeMozLoop.telemetryAddValue);
|
||||
});
|
||||
|
||||
it("should not do anything for non-chat messages", function() {
|
||||
store._handleTextChatMessage(new sharedActions.SendTextChatMessage({
|
||||
contentType: CHAT_CONTENT_TYPES.CONTEXT,
|
||||
message: "Hello!",
|
||||
sentTimestamp: "1970-01-01T00:00:00.000Z"
|
||||
}));
|
||||
|
||||
assertWeDidNothing();
|
||||
});
|
||||
|
||||
it("should ping telemetry when a chat message arrived or is to be sent", function() {
|
||||
store._handleTextChatMessage(new sharedActions.ReceivedTextChatMessage({
|
||||
contentType: CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Hello!",
|
||||
receivedTimestamp: "1970-01-01T00:00:00.000Z"
|
||||
}));
|
||||
|
||||
sinon.assert.calledOnce(fakeMozLoop.telemetryAddValue);
|
||||
sinon.assert.calledWithExactly(fakeMozLoop.telemetryAddValue,
|
||||
"LOOP_ROOM_SESSION_WITHCHAT", 1);
|
||||
expect(store.getStoreState().chatMessageExchanged).eql(true);
|
||||
expect(dispatcher._eventData.hasOwnProperty("receivedTextChatMessage")).eql(false);
|
||||
expect(dispatcher._eventData.hasOwnProperty("sendTextChatMessage")).eql(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Events", function() {
|
||||
describe("update:{roomToken}", function() {
|
||||
beforeEach(function() {
|
||||
|
@ -23,6 +23,8 @@ describe("loop.Dispatcher", function () {
|
||||
|
||||
dispatcher.register(object, ["getWindowData"]);
|
||||
|
||||
// XXXmikedeboer: Consider changing these tests to not access private
|
||||
// properties anymore (`_eventData`).
|
||||
expect(dispatcher._eventData.getWindowData[0]).eql(object);
|
||||
});
|
||||
|
||||
@ -38,6 +40,31 @@ describe("loop.Dispatcher", function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("#unregister", function() {
|
||||
it("should unregister a store against an action name", function() {
|
||||
var object = { fake: true };
|
||||
|
||||
dispatcher.register(object, ["getWindowData"]);
|
||||
dispatcher.unregister(object, ["getWindowData"]);
|
||||
|
||||
expect(dispatcher._eventData.hasOwnProperty("getWindowData")).eql(false);
|
||||
});
|
||||
|
||||
it("should unregister multiple stores against an action name", function() {
|
||||
var object1 = { fake: true };
|
||||
var object2 = { fake2: true };
|
||||
|
||||
dispatcher.register(object1, ["getWindowData"]);
|
||||
dispatcher.register(object2, ["getWindowData"]);
|
||||
|
||||
dispatcher.unregister(object1, ["getWindowData"]);
|
||||
expect(dispatcher._eventData.getWindowData.length).eql(1);
|
||||
|
||||
dispatcher.unregister(object2, ["getWindowData"]);
|
||||
expect(dispatcher._eventData.hasOwnProperty("getWindowData")).eql(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#dispatch", function() {
|
||||
var getDataStore1, getDataStore2, cancelStore1, connectStore1;
|
||||
var getDataAction, cancelAction, connectAction, resolveCancelStore1;
|
||||
|
@ -9,7 +9,7 @@ describe("loop.OTSdkDriver", function () {
|
||||
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
||||
var STREAM_PROPERTIES = loop.shared.utils.STREAM_PROPERTIES;
|
||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
||||
var CHAT_CONTENT_TYPES = loop.store.CHAT_CONTENT_TYPES;
|
||||
var CHAT_CONTENT_TYPES = loop.shared.utils.CHAT_CONTENT_TYPES;
|
||||
|
||||
var sandbox;
|
||||
var dispatcher, driver, mozLoop, publisher, sdk, session, sessionData, subscriber;
|
||||
|
@ -7,7 +7,7 @@ describe("loop.store.TextChatStore", function () {
|
||||
var expect = chai.expect;
|
||||
var sharedActions = loop.shared.actions;
|
||||
var CHAT_MESSAGE_TYPES = loop.store.CHAT_MESSAGE_TYPES;
|
||||
var CHAT_CONTENT_TYPES = loop.store.CHAT_CONTENT_TYPES;
|
||||
var CHAT_CONTENT_TYPES = loop.shared.utils.CHAT_CONTENT_TYPES;
|
||||
|
||||
var dispatcher, fakeSdkDriver, sandbox, store;
|
||||
|
||||
|
@ -9,7 +9,7 @@ describe("loop.shared.views.TextChatView", function () {
|
||||
var sharedViews = loop.shared.views;
|
||||
var TestUtils = React.addons.TestUtils;
|
||||
var CHAT_MESSAGE_TYPES = loop.store.CHAT_MESSAGE_TYPES;
|
||||
var CHAT_CONTENT_TYPES = loop.store.CHAT_CONTENT_TYPES;
|
||||
var CHAT_CONTENT_TYPES = loop.shared.utils.CHAT_CONTENT_TYPES;
|
||||
var fixtures = document.querySelector("#fixtures");
|
||||
|
||||
var dispatcher, fakeSdkDriver, sandbox, store, fakeClock;
|
||||
|
@ -77,6 +77,17 @@ describe("loop.store.StandaloneMetricsStore", function() {
|
||||
"No media");
|
||||
});
|
||||
|
||||
it("should log an event on connection failure if the room was already open", function() {
|
||||
store.connectionFailure(new sharedActions.ConnectionFailure({
|
||||
reason: FAILURE_DETAILS.ROOM_ALREADY_OPEN
|
||||
}));
|
||||
|
||||
sinon.assert.calledOnce(window.ga);
|
||||
sinon.assert.calledWithExactly(window.ga,
|
||||
"send", "event", METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.failed,
|
||||
"Room already open");
|
||||
});
|
||||
|
||||
it("should log an event on GotMediaPermission", function() {
|
||||
store.gotMediaPermission();
|
||||
|
||||
|
@ -183,6 +183,28 @@ describe("loop.standaloneRoomViews", function() {
|
||||
|
||||
expect(button.classList.contains("disabled")).eql(true);
|
||||
});
|
||||
|
||||
it("should not display a join button if there is a failure reason", function() {
|
||||
activeRoomStore.setStoreState({
|
||||
failureReason: FAILURE_DETAILS.ROOM_ALREADY_OPEN
|
||||
});
|
||||
|
||||
view = mountTestComponent();
|
||||
var button = view.getDOMNode().querySelector(".info-panel > button");
|
||||
|
||||
expect(button).eql(null);
|
||||
});
|
||||
|
||||
it("should display a room already joined message if opening failed", function() {
|
||||
activeRoomStore.setStoreState({
|
||||
failureReason: FAILURE_DETAILS.ROOM_ALREADY_OPEN
|
||||
});
|
||||
|
||||
view = mountTestComponent();
|
||||
var text = view.getDOMNode().querySelector(".failure");
|
||||
|
||||
expect(text.textContent).eql("rooms_already_joined");
|
||||
});
|
||||
});
|
||||
|
||||
describe("StandaloneRoomHeader", function() {
|
||||
|
@ -98,7 +98,7 @@
|
||||
|
||||
sendTextChatMessage: function(actionData) {
|
||||
dispatcher.dispatch(new loop.shared.actions.ReceivedTextChatMessage({
|
||||
contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
|
||||
contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
|
||||
message: actionData.message,
|
||||
receivedTimestamp: actionData.sentTimestamp
|
||||
}));
|
||||
@ -409,40 +409,40 @@
|
||||
textChatStore.setStoreState({textChatEnabled: true});
|
||||
|
||||
dispatcher.dispatch(new sharedActions.SendTextChatMessage({
|
||||
contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
|
||||
contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Rheet!",
|
||||
sentTimestamp: "2015-06-23T22:21:45.590Z"
|
||||
}));
|
||||
dispatcher.dispatch(new sharedActions.ReceivedTextChatMessage({
|
||||
contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
|
||||
contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Hello",
|
||||
receivedTimestamp: "2015-06-23T23:24:45.590Z"
|
||||
}));
|
||||
dispatcher.dispatch(new sharedActions.SendTextChatMessage({
|
||||
contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
|
||||
contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Nowforareallylongwordwithoutspacesorpunctuationwhichshouldcause" +
|
||||
"linewrappingissuesifthecssiswrong",
|
||||
sentTimestamp: "2015-06-23T22:23:45.590Z"
|
||||
}));
|
||||
dispatcher.dispatch(new sharedActions.SendTextChatMessage({
|
||||
contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
|
||||
contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Check out this menu from DNA Pizza:" +
|
||||
" http://example.com/DNA/pizza/menu/lots-of-different-kinds-of-pizza/" +
|
||||
"%8D%E0%B8%88%E0%B8%A1%E0%B8%A3%E0%8D%E0%B8%88%E0%B8%A1%E0%B8%A3%E0%",
|
||||
sentTimestamp: "2015-06-23T22:23:45.590Z"
|
||||
}));
|
||||
dispatcher.dispatch(new sharedActions.ReceivedTextChatMessage({
|
||||
contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
|
||||
contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "That avocado monkey-brains pie sounds tasty!",
|
||||
receivedTimestamp: "2015-06-23T22:25:45.590Z"
|
||||
}));
|
||||
dispatcher.dispatch(new sharedActions.SendTextChatMessage({
|
||||
contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
|
||||
contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "What time should we meet?",
|
||||
sentTimestamp: "2015-06-23T22:27:45.590Z"
|
||||
}));
|
||||
dispatcher.dispatch(new sharedActions.ReceivedTextChatMessage({
|
||||
contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
|
||||
contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "8:00 PM",
|
||||
receivedTimestamp: "2015-06-23T22:27:45.590Z"
|
||||
}));
|
||||
|
@ -98,7 +98,7 @@
|
||||
|
||||
sendTextChatMessage: function(actionData) {
|
||||
dispatcher.dispatch(new loop.shared.actions.ReceivedTextChatMessage({
|
||||
contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
|
||||
contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
|
||||
message: actionData.message,
|
||||
receivedTimestamp: actionData.sentTimestamp
|
||||
}));
|
||||
@ -409,40 +409,40 @@
|
||||
textChatStore.setStoreState({textChatEnabled: true});
|
||||
|
||||
dispatcher.dispatch(new sharedActions.SendTextChatMessage({
|
||||
contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
|
||||
contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Rheet!",
|
||||
sentTimestamp: "2015-06-23T22:21:45.590Z"
|
||||
}));
|
||||
dispatcher.dispatch(new sharedActions.ReceivedTextChatMessage({
|
||||
contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
|
||||
contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Hello",
|
||||
receivedTimestamp: "2015-06-23T23:24:45.590Z"
|
||||
}));
|
||||
dispatcher.dispatch(new sharedActions.SendTextChatMessage({
|
||||
contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
|
||||
contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Nowforareallylongwordwithoutspacesorpunctuationwhichshouldcause" +
|
||||
"linewrappingissuesifthecssiswrong",
|
||||
sentTimestamp: "2015-06-23T22:23:45.590Z"
|
||||
}));
|
||||
dispatcher.dispatch(new sharedActions.SendTextChatMessage({
|
||||
contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
|
||||
contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "Check out this menu from DNA Pizza:" +
|
||||
" http://example.com/DNA/pizza/menu/lots-of-different-kinds-of-pizza/" +
|
||||
"%8D%E0%B8%88%E0%B8%A1%E0%B8%A3%E0%8D%E0%B8%88%E0%B8%A1%E0%B8%A3%E0%",
|
||||
sentTimestamp: "2015-06-23T22:23:45.590Z"
|
||||
}));
|
||||
dispatcher.dispatch(new sharedActions.ReceivedTextChatMessage({
|
||||
contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
|
||||
contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "That avocado monkey-brains pie sounds tasty!",
|
||||
receivedTimestamp: "2015-06-23T22:25:45.590Z"
|
||||
}));
|
||||
dispatcher.dispatch(new sharedActions.SendTextChatMessage({
|
||||
contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
|
||||
contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "What time should we meet?",
|
||||
sentTimestamp: "2015-06-23T22:27:45.590Z"
|
||||
}));
|
||||
dispatcher.dispatch(new sharedActions.ReceivedTextChatMessage({
|
||||
contentType: loop.store.CHAT_CONTENT_TYPES.TEXT,
|
||||
contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
|
||||
message: "8:00 PM",
|
||||
receivedTimestamp: "2015-06-23T22:27:45.590Z"
|
||||
}));
|
||||
|
@ -7,16 +7,21 @@ support-files =
|
||||
[browser_advanced_update.js]
|
||||
[browser_basic_rebuild_fonts_test.js]
|
||||
[browser_bug410900.js]
|
||||
[browser_bug705422.js]
|
||||
[browser_bug731866.js]
|
||||
[browser_bug795764_cachedisabled.js]
|
||||
[browser_bug1018066_resetScrollPosition.js]
|
||||
[browser_bug1020245_openPreferences_to_paneContent.js]
|
||||
[browser_change_app_handler.js]
|
||||
skip-if = os != "win" # This test tests the windows-specific app selection dialog, so can't run on non-Windows
|
||||
[browser_chunk_permissions.js]
|
||||
[browser_connection.js]
|
||||
[browser_connection_bug388287.js]
|
||||
[browser_cookies_exceptions.js]
|
||||
skip-if = os == "linux" # See bug 1209521 for re-enabling on Linux
|
||||
[browser_healthreport.js]
|
||||
skip-if = !healthreport || (os == 'linux' && debug)
|
||||
[browser_permissions.js]
|
||||
[browser_proxy_backup.js]
|
||||
[browser_privacypane_1.js]
|
||||
[browser_privacypane_3.js]
|
||||
|
@ -0,0 +1,144 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
// Allow all cookies, then actually set up the test
|
||||
SpecialPowers.pushPrefEnv({"set": [["network.cookie.cookieBehavior", 0]]}, initTest);
|
||||
}
|
||||
|
||||
function initTest() {
|
||||
const searchTerm = "example";
|
||||
const dummyTerm = "elpmaxe";
|
||||
|
||||
var cm = Components.classes["@mozilla.org/cookiemanager;1"]
|
||||
.getService(Components.interfaces.nsICookieManager);
|
||||
|
||||
// delete all cookies (might be left over from other tests)
|
||||
cm.removeAll();
|
||||
|
||||
// data for cookies
|
||||
var vals = [[searchTerm+".com", dummyTerm, dummyTerm], // match
|
||||
[searchTerm+".org", dummyTerm, dummyTerm], // match
|
||||
[dummyTerm+".com", searchTerm, dummyTerm], // match
|
||||
[dummyTerm+".edu", searchTerm+dummyTerm, dummyTerm],// match
|
||||
[dummyTerm+".net", dummyTerm, searchTerm], // match
|
||||
[dummyTerm+".org", dummyTerm, searchTerm+dummyTerm],// match
|
||||
[dummyTerm+".int", dummyTerm, dummyTerm]]; // no match
|
||||
|
||||
// matches must correspond to above data
|
||||
const matches = 6;
|
||||
|
||||
var ios = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService);
|
||||
var cookieSvc = Components.classes["@mozilla.org/cookieService;1"]
|
||||
.getService(Components.interfaces.nsICookieService);
|
||||
var v;
|
||||
// inject cookies
|
||||
for (v in vals) {
|
||||
let [host, name, value] = vals[v];
|
||||
var cookieUri = ios.newURI("http://"+host, null, null);
|
||||
cookieSvc.setCookieString(cookieUri, null, name+"="+value+";", null);
|
||||
}
|
||||
|
||||
// open cookie manager
|
||||
var cmd = window.openDialog("chrome://browser/content/preferences/cookies.xul",
|
||||
"Browser:Cookies", "", {});
|
||||
|
||||
// when it has loaded, run actual tests
|
||||
cmd.addEventListener("load", function() {executeSoon(function() {runTest(cmd, searchTerm, vals.length, matches);});}, false);
|
||||
}
|
||||
|
||||
function isDisabled(win, expectation) {
|
||||
var disabled = win.document.getElementById("removeAllCookies").disabled;
|
||||
is(disabled, expectation, "Remove all cookies button has correct state: "+(expectation?"disabled":"enabled"));
|
||||
}
|
||||
|
||||
function runTest(win, searchTerm, cookies, matches) {
|
||||
var cm = Components.classes["@mozilla.org/cookiemanager;1"]
|
||||
.getService(Components.interfaces.nsICookieManager);
|
||||
|
||||
|
||||
// number of cookies should match injected cookies
|
||||
var cnt = 0,
|
||||
enumerator = cm.enumerator;
|
||||
while (enumerator.hasMoreElements()) {
|
||||
cnt++;
|
||||
enumerator.getNext();
|
||||
}
|
||||
is(cnt, cookies, "Number of cookies match injected cookies");
|
||||
|
||||
// "delete all cookies" should be enabled
|
||||
isDisabled(win, false);
|
||||
|
||||
// filter cookies and count matches
|
||||
win.gCookiesWindow.setFilter(searchTerm);
|
||||
is(win.gCookiesWindow._view.rowCount, matches, "Correct number of cookies shown after filter is applied");
|
||||
|
||||
// "delete all cookies" should be enabled
|
||||
isDisabled(win, false);
|
||||
|
||||
|
||||
// select first cookie and delete
|
||||
var tree = win.document.getElementById("cookiesList");
|
||||
var deleteButton = win.document.getElementById("removeSelectedCookies");
|
||||
var rect = tree.treeBoxObject.getCoordsForCellItem(0, tree.columns[0], "cell");
|
||||
EventUtils.synthesizeMouse(tree.body, rect.x + rect.width / 2, rect.y + rect.height / 2, {}, win);
|
||||
EventUtils.synthesizeMouseAtCenter(deleteButton, {}, win);
|
||||
|
||||
// count cookies should be matches-1
|
||||
is(win.gCookiesWindow._view.rowCount, matches-1, "Deleted selected cookie");
|
||||
|
||||
// select two adjacent cells and delete
|
||||
EventUtils.synthesizeMouse(tree.body, rect.x + rect.width / 2, rect.y + rect.height / 2, {}, win);
|
||||
var eventObj = {};
|
||||
if (navigator.platform.indexOf("Mac") >= 0)
|
||||
eventObj.metaKey = true;
|
||||
else
|
||||
eventObj.ctrlKey = true;
|
||||
rect = tree.treeBoxObject.getCoordsForCellItem(1, tree.columns[0], "cell");
|
||||
EventUtils.synthesizeMouse(tree.body, rect.x + rect.width / 2, rect.y + rect.height / 2, eventObj, win);
|
||||
EventUtils.synthesizeMouseAtCenter(deleteButton, {}, win);
|
||||
|
||||
// count cookies should be matches-3
|
||||
is(win.gCookiesWindow._view.rowCount, matches-3, "Deleted selected two adjacent cookies");
|
||||
|
||||
// "delete all cookies" should be enabled
|
||||
isDisabled(win, false);
|
||||
|
||||
// delete all cookies and count
|
||||
var deleteAllButton = win.document.getElementById("removeAllCookies");
|
||||
EventUtils.synthesizeMouseAtCenter(deleteAllButton, {}, win);
|
||||
is(win.gCookiesWindow._view.rowCount, 0, "Deleted all matching cookies");
|
||||
|
||||
// "delete all cookies" should be disabled
|
||||
isDisabled(win, true);
|
||||
|
||||
// clear filter and count should be cookies-matches
|
||||
win.gCookiesWindow.setFilter("");
|
||||
is(win.gCookiesWindow._view.rowCount, cookies-matches, "Unmatched cookies remain");
|
||||
|
||||
// "delete all cookies" should be enabled
|
||||
isDisabled(win, false);
|
||||
|
||||
// delete all cookies and count should be 0
|
||||
EventUtils.synthesizeMouseAtCenter(deleteAllButton, {}, win);
|
||||
is(win.gCookiesWindow._view.rowCount, 0, "Deleted all cookies");
|
||||
|
||||
// check that datastore is also at 0
|
||||
var cnt = 0,
|
||||
enumerator = cm.enumerator;
|
||||
while (enumerator.hasMoreElements()) {
|
||||
cnt++;
|
||||
enumerator.getNext();
|
||||
}
|
||||
is(cnt, 0, "Zero cookies remain");
|
||||
|
||||
// "delete all cookies" should be disabled
|
||||
isDisabled(win, true);
|
||||
|
||||
// clean up
|
||||
win.close();
|
||||
finish();
|
||||
}
|
||||
|
@ -0,0 +1,140 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
||||
Components.utils.import("resource://gre/modules/ForgetAboutSite.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
|
||||
"resource://testing-common/PlacesTestUtils.jsm");
|
||||
|
||||
const ABOUT_PERMISSIONS_SPEC = "about:permissions";
|
||||
|
||||
const TEST_URI_1 = NetUtil.newURI("http://mozilla.com/");
|
||||
const TEST_URI_2 = NetUtil.newURI("http://mozilla.org/");
|
||||
const TEST_URI_3 = NetUtil.newURI("http://wikipedia.org/");
|
||||
|
||||
// values from DefaultPermissions object
|
||||
const PERM_UNKNOWN = 0;
|
||||
const PERM_ALLOW = 1;
|
||||
const PERM_DENY = 2;
|
||||
|
||||
// used to set permissions on test sites
|
||||
const TEST_PERMS = {
|
||||
"password": PERM_ALLOW,
|
||||
"cookie": PERM_ALLOW,
|
||||
"geo": PERM_UNKNOWN,
|
||||
"indexedDB": PERM_UNKNOWN,
|
||||
"popup": PERM_DENY
|
||||
};
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
registerCleanupFunction(cleanUp);
|
||||
setup(function() {
|
||||
runNextTest();
|
||||
});
|
||||
}
|
||||
|
||||
function setup(aCallback) {
|
||||
// add test history visit
|
||||
PlacesTestUtils.addVisits(TEST_URI_1).then(() => {
|
||||
// set permissions ourselves to avoid problems with different defaults
|
||||
// from test harness configuration
|
||||
for (let type in TEST_PERMS) {
|
||||
if (type == "password") {
|
||||
Services.logins.setLoginSavingEnabled(TEST_URI_2.prePath, true);
|
||||
} else {
|
||||
// set permissions on a site without history visits to test enumerateServices
|
||||
Services.perms.add(TEST_URI_2, type, TEST_PERMS[type]);
|
||||
}
|
||||
}
|
||||
|
||||
Services.perms.add(TEST_URI_3, "popup", TEST_PERMS["popup"]);
|
||||
aCallback();
|
||||
});
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
for (let type in TEST_PERMS) {
|
||||
if (type != "password") {
|
||||
Services.perms.remove(TEST_URI_1, type);
|
||||
Services.perms.remove(TEST_URI_2, type);
|
||||
Services.perms.remove(TEST_URI_3, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
if (gTestIndex == tests.length) {
|
||||
PlacesTestUtils.clearHistory().then(finish);
|
||||
return;
|
||||
}
|
||||
|
||||
let nextTest = tests[gTestIndex++];
|
||||
info(nextTest.desc);
|
||||
|
||||
function preinit_observer() {
|
||||
Services.obs.removeObserver(preinit_observer, "browser-permissions-preinit");
|
||||
nextTest.preInit();
|
||||
}
|
||||
Services.obs.addObserver(preinit_observer, "browser-permissions-preinit", false);
|
||||
|
||||
function init_observer() {
|
||||
Services.obs.removeObserver(init_observer, "browser-permissions-initialized");
|
||||
nextTest.run();
|
||||
}
|
||||
Services.obs.addObserver(init_observer, "browser-permissions-initialized", false);
|
||||
|
||||
// open about:permissions
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab("about:permissions");
|
||||
registerCleanupFunction(function() {
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
}
|
||||
|
||||
var gSitesList;
|
||||
|
||||
var gTestIndex = 0;
|
||||
var tests = [
|
||||
// 'preInit' occurs after opening about:permissions, before sites-list is populated
|
||||
// 'run' occurs after sites-list is populated
|
||||
{
|
||||
desc: "test filtering before sites-list is fully constructed.",
|
||||
preInit: function() {
|
||||
let sitesFilter = gBrowser.contentDocument.getElementById("sites-filter");
|
||||
sitesFilter.value = TEST_URI_2.host;
|
||||
sitesFilter.doCommand();
|
||||
},
|
||||
run: function() {
|
||||
let testSite1 = getSiteItem(TEST_URI_1.prePath);
|
||||
ok(testSite1.collapsed, "test site 1 is collapsed after early filtering");
|
||||
let testSite2 = getSiteItem(TEST_URI_2.prePath);
|
||||
ok(!testSite2.collapsed, "test site 2 is not collapsed after early filtering");
|
||||
let testSite3 = getSiteItem(TEST_URI_3.prePath);
|
||||
ok(testSite3.collapsed, "test site 3 is collapsed after early filtering");
|
||||
|
||||
runNextTest();
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: "test removing from sites-list before it is fully constructed.",
|
||||
preInit: function() {
|
||||
ForgetAboutSite.removeDataFromDomain(TEST_URI_2.host);
|
||||
},
|
||||
run: function() {
|
||||
let testSite1 = getSiteItem(TEST_URI_1.prePath);
|
||||
ok(testSite1, "test site 1 was not removed from sites list");
|
||||
let testSite2 = getSiteItem(TEST_URI_2.prePath);
|
||||
ok(!testSite2, "test site 2 was pre-removed from sites list");
|
||||
let testSite3 = getSiteItem(TEST_URI_3.prePath);
|
||||
ok(testSite3, "test site 3 was not removed from sites list");
|
||||
|
||||
runNextTest();
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
function getSiteItem(aPrePath) {
|
||||
return gBrowser.contentDocument.
|
||||
querySelector(".site[value='" + aPrePath + "']");
|
||||
}
|
@ -0,0 +1,304 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
testRunner.runTests();
|
||||
}
|
||||
|
||||
var testRunner = {
|
||||
|
||||
tests:
|
||||
[
|
||||
{
|
||||
test: function(params) {
|
||||
params.url.value = "test.com";
|
||||
params.btnAllow.doCommand();
|
||||
is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
|
||||
is(params.tree.view.getCellText(0, params.nameCol), "http://test.com",
|
||||
"origin name should be set correctly");
|
||||
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
|
||||
"permission text should be set correctly");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "cookie", origin: "http://test.com", data: "added",
|
||||
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
|
||||
},
|
||||
{
|
||||
test: function(params) {
|
||||
params.url.value = "test.com";
|
||||
params.btnBlock.doCommand();
|
||||
is(params.tree.view.getCellText(0, params.nameCol), "http://test.com",
|
||||
"origin name should be set correctly");
|
||||
is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
|
||||
"permission should change to deny in UI");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "cookie", origin: "http://test.com", data: "changed",
|
||||
capability: Ci.nsIPermissionManager.DENY_ACTION }],
|
||||
},
|
||||
{
|
||||
test: function(params) {
|
||||
params.url.value = "test.com";
|
||||
params.btnAllow.doCommand();
|
||||
is(params.tree.view.getCellText(0, params.nameCol), "http://test.com",
|
||||
"origin name should be set correctly");
|
||||
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
|
||||
"permission should revert back to allow");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "cookie", origin: "http://test.com", data: "changed",
|
||||
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
|
||||
},
|
||||
{
|
||||
test: function(params) {
|
||||
params.url.value = "test.com";
|
||||
params.btnRemove.doCommand();
|
||||
is(params.tree.view.rowCount, 0, "exception should be removed");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "cookie", origin: "http://test.com", data: "deleted" }],
|
||||
},
|
||||
{
|
||||
expectPermObservancesDuringTestFunction: true,
|
||||
test: function(params) {
|
||||
let uri = params.ioService.newURI("http://test.com", null, null);
|
||||
params.pm.add(uri, "popup", Ci.nsIPermissionManager.DENY_ACTION);
|
||||
is(params.tree.view.rowCount, 0, "adding unrelated permission should not change display");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "popup", origin: "http://test.com", data: "added",
|
||||
capability: Ci.nsIPermissionManager.DENY_ACTION }],
|
||||
cleanUp: function(params) {
|
||||
let uri = params.ioService.newURI("http://test.com", null, null);
|
||||
params.pm.remove(uri, "popup");
|
||||
},
|
||||
},
|
||||
{
|
||||
test: function(params) {
|
||||
params.url.value = "https://test.com:12345";
|
||||
params.btnAllow.doCommand();
|
||||
is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
|
||||
is(params.tree.view.getCellText(0, params.nameCol), "https://test.com:12345",
|
||||
"origin name should be set correctly");
|
||||
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
|
||||
"permission text should be set correctly");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "cookie", origin: "https://test.com:12345", data: "added",
|
||||
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
|
||||
},
|
||||
{
|
||||
test: function(params) {
|
||||
params.url.value = "https://test.com:12345";
|
||||
params.btnBlock.doCommand();
|
||||
is(params.tree.view.getCellText(0, params.nameCol), "https://test.com:12345",
|
||||
"origin name should be set correctly");
|
||||
is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
|
||||
"permission should change to deny in UI");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "cookie", origin: "https://test.com:12345", data: "changed",
|
||||
capability: Ci.nsIPermissionManager.DENY_ACTION }],
|
||||
},
|
||||
{
|
||||
test: function(params) {
|
||||
params.url.value = "https://test.com:12345";
|
||||
params.btnAllow.doCommand();
|
||||
is(params.tree.view.getCellText(0, params.nameCol), "https://test.com:12345",
|
||||
"origin name should be set correctly");
|
||||
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
|
||||
"permission should revert back to allow");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "cookie", origin: "https://test.com:12345", data: "changed",
|
||||
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
|
||||
},
|
||||
{
|
||||
test: function(params) {
|
||||
params.url.value = "https://test.com:12345";
|
||||
params.btnRemove.doCommand();
|
||||
is(params.tree.view.rowCount, 0, "exception should be removed");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "cookie", origin: "https://test.com:12345", data: "deleted" }],
|
||||
},
|
||||
{
|
||||
test: function(params) {
|
||||
params.url.value = "localhost:12345";
|
||||
params.btnAllow.doCommand();
|
||||
is(params.tree.view.rowCount, 1, "added exception shows up in treeview");
|
||||
is(params.tree.view.getCellText(0, params.nameCol), "http://localhost:12345",
|
||||
"origin name should be set correctly");
|
||||
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
|
||||
"permission text should be set correctly");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "cookie", origin: "http://localhost:12345", data: "added",
|
||||
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
|
||||
},
|
||||
{
|
||||
test: function(params) {
|
||||
params.url.value = "localhost:12345";
|
||||
params.btnBlock.doCommand();
|
||||
is(params.tree.view.getCellText(0, params.nameCol), "http://localhost:12345",
|
||||
"origin name should be set correctly");
|
||||
is(params.tree.view.getCellText(0, params.statusCol), params.denyText,
|
||||
"permission should change to deny in UI");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "cookie", origin: "http://localhost:12345", data: "changed",
|
||||
capability: Ci.nsIPermissionManager.DENY_ACTION }],
|
||||
},
|
||||
{
|
||||
test: function(params) {
|
||||
params.url.value = "localhost:12345";
|
||||
params.btnAllow.doCommand();
|
||||
is(params.tree.view.getCellText(0, params.nameCol), "http://localhost:12345",
|
||||
"origin name should be set correctly");
|
||||
is(params.tree.view.getCellText(0, params.statusCol), params.allowText,
|
||||
"permission should revert back to allow");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "cookie", origin: "http://localhost:12345", data: "changed",
|
||||
capability: Ci.nsIPermissionManager.ALLOW_ACTION }],
|
||||
},
|
||||
{
|
||||
test: function(params) {
|
||||
params.url.value = "localhost:12345";
|
||||
params.btnRemove.doCommand();
|
||||
is(params.tree.view.rowCount, 0, "exception should be removed");
|
||||
params.btnApplyChanges.doCommand();
|
||||
},
|
||||
observances: [{ type: "cookie", origin: "http://localhost:12345", data: "deleted" }],
|
||||
},
|
||||
],
|
||||
|
||||
_currentTest: -1,
|
||||
|
||||
runTests: function() {
|
||||
this._currentTest++;
|
||||
|
||||
info("Running test #" + (this._currentTest + 1) + "\n");
|
||||
let that = this;
|
||||
let p = this.runCurrentTest(this._currentTest + 1);
|
||||
p.then(function() {
|
||||
if (that._currentTest == that.tests.length - 1) {
|
||||
finish();
|
||||
}
|
||||
else {
|
||||
that.runTests();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
runCurrentTest: function(testNumber) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
let helperFunctions = {
|
||||
windowLoad: function(win) {
|
||||
let doc = win.document;
|
||||
let params = {
|
||||
doc,
|
||||
tree: doc.getElementById("permissionsTree"),
|
||||
nameCol: doc.getElementById("permissionsTree").treeBoxObject.columns.getColumnAt(0),
|
||||
statusCol: doc.getElementById("permissionsTree").treeBoxObject.columns.getColumnAt(1),
|
||||
url: doc.getElementById("url"),
|
||||
btnAllow: doc.getElementById("btnAllow"),
|
||||
btnBlock: doc.getElementById("btnBlock"),
|
||||
btnApplyChanges: doc.getElementById("btnApplyChanges"),
|
||||
btnRemove: doc.getElementById("removePermission"),
|
||||
pm: Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager),
|
||||
ioService: Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService),
|
||||
allowText: win.gPermissionManager._getCapabilityString(
|
||||
Ci.nsIPermissionManager.ALLOW_ACTION),
|
||||
denyText: win.gPermissionManager._getCapabilityString(
|
||||
Ci.nsIPermissionManager.DENY_ACTION),
|
||||
allow: Ci.nsIPermissionManager.ALLOW_ACTION,
|
||||
deny: Ci.nsIPermissionManager.DENY_ACTION,
|
||||
};
|
||||
|
||||
let permObserver = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic != "perm-changed")
|
||||
return;
|
||||
|
||||
if (testRunner.tests[testRunner._currentTest].observances.length == 0) {
|
||||
// Should fail here as we are not expecting a notification, but we don't.
|
||||
// See bug 1063410.
|
||||
return;
|
||||
}
|
||||
|
||||
let permission = aSubject.QueryInterface(Ci.nsIPermission);
|
||||
let expected = testRunner.tests[testRunner._currentTest].observances.shift();
|
||||
|
||||
is(aData, expected.data, "type of message should be the same");
|
||||
for each (let prop in ["type", "capability"]) {
|
||||
if (expected[prop])
|
||||
is(permission[prop], expected[prop],
|
||||
"property: \"" + prop + "\" should be equal");
|
||||
}
|
||||
|
||||
if (expected.origin) {
|
||||
is(permission.principal.origin, expected.origin,
|
||||
"property: \"origin\" should be equal");
|
||||
}
|
||||
|
||||
os.removeObserver(permObserver, "perm-changed");
|
||||
|
||||
let test = testRunner.tests[testRunner._currentTest];
|
||||
if (!test.expectPermObservancesDuringTestFunction) {
|
||||
if (test.cleanUp) {
|
||||
test.cleanUp(params);
|
||||
}
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let os = Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService);
|
||||
|
||||
os.addObserver(permObserver, "perm-changed", false);
|
||||
|
||||
if (testRunner._currentTest == 0) {
|
||||
is(params.tree.view.rowCount, 0, "no cookie exceptions");
|
||||
}
|
||||
|
||||
try {
|
||||
let test = testRunner.tests[testRunner._currentTest];
|
||||
test.test(params);
|
||||
if (test.expectPermObservancesDuringTestFunction) {
|
||||
if (test.cleanUp) {
|
||||
test.cleanUp(params);
|
||||
}
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
resolve();
|
||||
}
|
||||
} catch (ex) {
|
||||
ok(false, "exception while running test #" +
|
||||
testNumber + ": " + ex);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
openPreferencesViaOpenPreferencesAPI("panePrivacy", null, {leaveOpen: true}).then(function() {
|
||||
let doc = gBrowser.contentDocument;
|
||||
let historyMode = doc.getElementById("historyMode");
|
||||
historyMode.value = "custom";
|
||||
historyMode.doCommand();
|
||||
doc.getElementById("cookieExceptions").doCommand();
|
||||
|
||||
let subDialogURL = "chrome://browser/content/preferences/permissions.xul";
|
||||
promiseLoadSubDialog(subDialogURL).then(function(win) {
|
||||
helperFunctions.windowLoad(win);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
@ -0,0 +1,334 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
|
||||
"resource://testing-common/PlacesTestUtils.jsm");
|
||||
|
||||
const ABOUT_PERMISSIONS_SPEC = "about:permissions";
|
||||
|
||||
const TEST_URI_1 = NetUtil.newURI("http://mozilla.com/");
|
||||
const TEST_URI_2 = NetUtil.newURI("http://mozilla.org/");
|
||||
|
||||
const TEST_PRINCIPAL_1 =
|
||||
Services.scriptSecurityManager.createCodebasePrincipal(TEST_URI_1, {});
|
||||
const TEST_PRINCIPAL_2 =
|
||||
Services.scriptSecurityManager.createCodebasePrincipal(TEST_URI_2, {});
|
||||
|
||||
// values from DefaultPermissions object
|
||||
const PERM_UNKNOWN = 0;
|
||||
const PERM_ALLOW = 1;
|
||||
const PERM_DENY = 2;
|
||||
// cookie specific permissions
|
||||
const PERM_FIRST_PARTY_ONLY = 9;
|
||||
|
||||
// used to set permissions on test sites
|
||||
const TEST_PERMS = {
|
||||
"password": PERM_ALLOW,
|
||||
"cookie": PERM_ALLOW,
|
||||
"geo": PERM_UNKNOWN,
|
||||
"push": PERM_DENY,
|
||||
"indexedDB": PERM_UNKNOWN,
|
||||
"popup": PERM_DENY,
|
||||
"camera": PERM_UNKNOWN,
|
||||
"microphone": PERM_UNKNOWN
|
||||
};
|
||||
|
||||
const NO_GLOBAL_ALLOW = [
|
||||
"geo",
|
||||
"indexedDB",
|
||||
];
|
||||
|
||||
// number of managed permissions in the interface
|
||||
const TEST_PERMS_COUNT = 8;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
registerCleanupFunction(cleanUp);
|
||||
|
||||
// add test history visit
|
||||
PlacesTestUtils.addVisits(TEST_URI_1).then(() => {
|
||||
// set permissions ourselves to avoid problems with different defaults
|
||||
// from test harness configuration
|
||||
for (let type in TEST_PERMS) {
|
||||
if (type == "password") {
|
||||
Services.logins.setLoginSavingEnabled(TEST_URI_2.prePath, true);
|
||||
} else {
|
||||
// set permissions on a site without history visits to test enumerateServices
|
||||
Services.perms.addFromPrincipal(TEST_PRINCIPAL_2, type, TEST_PERMS[type]);
|
||||
}
|
||||
}
|
||||
|
||||
// open about:permissions
|
||||
gBrowser.selectedTab = gBrowser.addTab("about:permissions");
|
||||
});
|
||||
|
||||
function observer() {
|
||||
Services.obs.removeObserver(observer, "browser-permissions-initialized");
|
||||
runNextTest();
|
||||
}
|
||||
Services.obs.addObserver(observer, "browser-permissions-initialized", false);
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
for (let type in TEST_PERMS) {
|
||||
if (type != "password") {
|
||||
Services.perms.removeFromPrincipal(TEST_PRINCIPAL_1, type);
|
||||
Services.perms.removeFromPrincipal(TEST_PRINCIPAL_2, type);
|
||||
}
|
||||
}
|
||||
|
||||
gBrowser.removeTab(gBrowser.selectedTab);
|
||||
}
|
||||
|
||||
function runNextTest() {
|
||||
if (gTestIndex == tests.length) {
|
||||
PlacesTestUtils.clearHistory().then(finish);
|
||||
return;
|
||||
}
|
||||
|
||||
let nextTest = tests[gTestIndex++];
|
||||
info("[" + nextTest.name + "] running test");
|
||||
nextTest();
|
||||
}
|
||||
|
||||
var gSitesList;
|
||||
var gHeaderDeck;
|
||||
var gSiteLabel;
|
||||
|
||||
var gTestIndex = 0;
|
||||
var tests = [
|
||||
function test_page_load() {
|
||||
is(gBrowser.currentURI.spec, ABOUT_PERMISSIONS_SPEC, "about:permissions loaded");
|
||||
|
||||
gSitesList = gBrowser.contentDocument.getElementById("sites-list");
|
||||
ok(gSitesList, "got sites list");
|
||||
|
||||
gHeaderDeck = gBrowser.contentDocument.getElementById("header-deck");
|
||||
ok(gHeaderDeck, "got header deck");
|
||||
|
||||
gSiteLabel = gBrowser.contentDocument.getElementById("site-label");
|
||||
ok(gSiteLabel, "got site label");
|
||||
|
||||
runNextTest();
|
||||
},
|
||||
|
||||
function test_sites_list() {
|
||||
is(gSitesList.firstChild.id, "all-sites-item",
|
||||
"all sites is the first item in the sites list");
|
||||
|
||||
ok(getSiteItem(TEST_URI_1.prePath), "site item from places db exists");
|
||||
ok(getSiteItem(TEST_URI_2.prePath), "site item from enumerating services exists");
|
||||
|
||||
runNextTest();
|
||||
},
|
||||
|
||||
function test_filter_sites_list() {
|
||||
// set filter to test host
|
||||
let sitesFilter = gBrowser.contentDocument.getElementById("sites-filter");
|
||||
sitesFilter.value = TEST_URI_1.host;
|
||||
sitesFilter.doCommand();
|
||||
|
||||
// make sure correct sites are collapsed/showing
|
||||
let testSite1 = getSiteItem(TEST_URI_1.prePath);
|
||||
ok(!testSite1.collapsed, "test site 1 is not collapsed");
|
||||
let testSite2 = getSiteItem(TEST_URI_2.prePath);
|
||||
ok(testSite2.collapsed, "test site 2 is collapsed");
|
||||
|
||||
// clear filter
|
||||
sitesFilter.value = "";
|
||||
sitesFilter.doCommand();
|
||||
|
||||
runNextTest();
|
||||
},
|
||||
|
||||
function test_all_sites() {
|
||||
// "All Sites" item should be selected when the page is first loaded
|
||||
is(gSitesList.selectedItem, gBrowser.contentDocument.getElementById("all-sites-item"),
|
||||
"all sites item is selected");
|
||||
|
||||
let defaultsHeader = gBrowser.contentDocument.getElementById("defaults-header");
|
||||
is(defaultsHeader, gHeaderDeck.selectedPanel,
|
||||
"correct header shown for all sites");
|
||||
|
||||
ok(gBrowser.contentDocument.getElementById("passwords-count").hidden,
|
||||
"passwords count is hidden");
|
||||
ok(gBrowser.contentDocument.getElementById("cookies-count").hidden,
|
||||
"cookies count is hidden");
|
||||
|
||||
// Test to make sure "Allow" items hidden for certain permission types
|
||||
NO_GLOBAL_ALLOW.forEach(function(aType) {
|
||||
let menuitem = gBrowser.contentDocument.getElementById(aType + "-" + PERM_ALLOW);
|
||||
ok(menuitem.hidden, aType + " allow menuitem hidden for all sites");
|
||||
});
|
||||
|
||||
runNextTest();
|
||||
},
|
||||
|
||||
function test_all_sites_permission() {
|
||||
// apply the old default of allowing all cookies
|
||||
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
|
||||
|
||||
// there should be no user-set pref for cookie behavior
|
||||
is(Services.prefs.getIntPref("network.cookie.cookieBehavior"), PERM_UNKNOWN,
|
||||
"network.cookie.cookieBehavior is expected default");
|
||||
|
||||
// the default behavior is to allow cookies
|
||||
let cookieMenulist = getPermissionMenulist("cookie");
|
||||
is(cookieMenulist.value, PERM_ALLOW,
|
||||
"menulist correctly shows that cookies are allowed");
|
||||
|
||||
// set the pref to block cookies
|
||||
Services.prefs.setIntPref("network.cookie.cookieBehavior", PERM_DENY);
|
||||
// check to make sure this change is reflected in the UI
|
||||
is(cookieMenulist.value, PERM_DENY, "menulist correctly shows that cookies are blocked");
|
||||
|
||||
// clear the pref
|
||||
Services.prefs.clearUserPref("network.cookie.cookieBehavior");
|
||||
|
||||
runNextTest();
|
||||
},
|
||||
|
||||
function test_manage_all_passwords() {
|
||||
// make sure "Manage All Passwords..." button opens the correct dialog
|
||||
addWindowListener("chrome://passwordmgr/content/passwordManager.xul", runNextTest);
|
||||
gBrowser.contentDocument.getElementById("passwords-manage-all-button").doCommand();
|
||||
|
||||
},
|
||||
|
||||
function test_manage_all_cookies() {
|
||||
// make sure "Manage All Cookies..." button opens the correct dialog
|
||||
addWindowListener("chrome://browser/content/preferences/cookies.xul", runNextTest);
|
||||
gBrowser.contentDocument.getElementById("cookies-manage-all-button").doCommand();
|
||||
},
|
||||
|
||||
function test_select_site() {
|
||||
// select the site that has the permissions we set at the beginning of the test
|
||||
let testSiteItem = getSiteItem(TEST_URI_2.prePath);
|
||||
gSitesList.selectedItem = testSiteItem;
|
||||
|
||||
let siteHeader = gBrowser.contentDocument.getElementById("site-header");
|
||||
is(siteHeader, gHeaderDeck.selectedPanel,
|
||||
"correct header shown for a specific site");
|
||||
is(gSiteLabel.value, TEST_URI_2.prePath, "header updated for selected site");
|
||||
|
||||
ok(!gBrowser.contentDocument.getElementById("passwords-count").hidden,
|
||||
"passwords count is not hidden");
|
||||
ok(!gBrowser.contentDocument.getElementById("cookies-count").hidden,
|
||||
"cookies count is not hidden");
|
||||
|
||||
// Test to make sure "Allow" items are *not* hidden for certain permission types
|
||||
NO_GLOBAL_ALLOW.forEach(function(aType) {
|
||||
let menuitem = gBrowser.contentDocument.getElementById(aType + "-" + PERM_ALLOW);
|
||||
ok(!menuitem.hidden, aType + " allow menuitem not hidden for single site");
|
||||
});
|
||||
|
||||
runNextTest();
|
||||
},
|
||||
|
||||
function test_permissions() {
|
||||
let menulists = gBrowser.contentDocument.getElementsByClassName("pref-menulist");
|
||||
is(menulists.length, TEST_PERMS_COUNT, "got expected number of managed permissions");
|
||||
|
||||
for (let i = 0; i < menulists.length; i++) {
|
||||
let permissionMenulist = menulists.item(i);
|
||||
let permissionType = permissionMenulist.getAttribute("type");
|
||||
|
||||
// permissions should reflect what we set at the beginning of the test
|
||||
is(permissionMenulist.value, TEST_PERMS[permissionType],
|
||||
"got expected value for " + permissionType + " permission");
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
},
|
||||
|
||||
function test_permission_change() {
|
||||
let geoMenulist = getPermissionMenulist("geo");
|
||||
is(geoMenulist.value, PERM_UNKNOWN, "menulist correctly shows that geolocation permission is unspecified");
|
||||
|
||||
// change a permission programatically
|
||||
Services.perms.addFromPrincipal(TEST_PRINCIPAL_2, "geo", PERM_DENY);
|
||||
// check to make sure this change is reflected in the UI
|
||||
is(geoMenulist.value, PERM_DENY, "menulist shows that geolocation is blocked");
|
||||
|
||||
// change a permisssion in the UI
|
||||
let geoAllowItem = gBrowser.contentDocument.getElementById("geo-" + PERM_ALLOW);
|
||||
geoMenulist.selectedItem = geoAllowItem;
|
||||
geoMenulist.doCommand();
|
||||
// check to make sure this change is reflected in the permission manager
|
||||
is(Services.perms.testPermissionFromPrincipal(TEST_PRINCIPAL_2, "geo"), PERM_ALLOW,
|
||||
"permission manager shows that geolocation is allowed");
|
||||
|
||||
|
||||
// change a site-specific cookie permission, just for fun
|
||||
let cookieMenuList = getPermissionMenulist("cookie");
|
||||
let cookieItem = gBrowser.contentDocument.getElementById("cookie-" + PERM_FIRST_PARTY_ONLY);
|
||||
cookieMenuList.selectedItem = cookieItem;
|
||||
cookieMenuList.doCommand();
|
||||
is(cookieMenuList.value, PERM_FIRST_PARTY_ONLY, "menulist correctly shows that " +
|
||||
"first party only cookies are allowed");
|
||||
is(Services.perms.testPermissionFromPrincipal(TEST_PRINCIPAL_2, "cookie"),
|
||||
PERM_FIRST_PARTY_ONLY, "permission manager shows that first party cookies " +
|
||||
"are allowed");
|
||||
|
||||
runNextTest();
|
||||
},
|
||||
|
||||
function test_forget_site() {
|
||||
// click "Forget About This Site" button
|
||||
gBrowser.contentDocument.getElementById("forget-site-button").doCommand();
|
||||
PlacesTestUtils.clearHistory().then(() => {
|
||||
is(gSiteLabel.value, "", "site label cleared");
|
||||
|
||||
let allSitesItem = gBrowser.contentDocument.getElementById("all-sites-item");
|
||||
is(gSitesList.selectedItem, allSitesItem,
|
||||
"all sites item selected after forgetting selected site");
|
||||
|
||||
// check to make sure site is gone from sites list
|
||||
let testSiteItem = getSiteItem(TEST_URI_2.prePath);
|
||||
ok(!testSiteItem, "site removed from sites list");
|
||||
|
||||
// check to make sure we forgot all permissions corresponding to site
|
||||
for (let type in TEST_PERMS) {
|
||||
if (type == "password") {
|
||||
ok(Services.logins.getLoginSavingEnabled(TEST_URI_2.prePath),
|
||||
"password saving should be enabled by default");
|
||||
} else {
|
||||
is(Services.perms.testPermissionFromPrincipal(TEST_PRINCIPAL_2, type), PERM_UNKNOWN,
|
||||
type + " permission should not be set for test site 2");
|
||||
}
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
});
|
||||
}
|
||||
];
|
||||
|
||||
function getPermissionMenulist(aType) {
|
||||
return gBrowser.contentDocument.getElementById(aType + "-menulist");
|
||||
}
|
||||
|
||||
function getSiteItem(aHost) {
|
||||
return gBrowser.contentDocument.
|
||||
querySelector(".site[value='" + aHost + "']");
|
||||
}
|
||||
|
||||
function addWindowListener(aURL, aCallback) {
|
||||
Services.wm.addListener({
|
||||
onOpenWindow: function(aXULWindow) {
|
||||
info("window opened, waiting for focus");
|
||||
Services.wm.removeListener(this);
|
||||
|
||||
var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
waitForFocus(function() {
|
||||
is(domwindow.document.location.href, aURL, "should have seen the right window open");
|
||||
domwindow.close();
|
||||
aCallback();
|
||||
}, domwindow);
|
||||
},
|
||||
onCloseWindow: function(aXULWindow) { },
|
||||
onWindowTitleChange: function(aXULWindow, aNewTitle) { }
|
||||
});
|
||||
}
|
@ -297,7 +297,6 @@ if test -n "$MOZ_NATIVE_DEVICES" ; then
|
||||
|
||||
MOZ_ANDROID_AAR(play-services-base, 7.8.0, google, com/google/android/gms)
|
||||
MOZ_ANDROID_AAR(play-services-cast, 7.8.0, google, com/google/android/gms)
|
||||
MOZ_ANDROID_AAR(appcompat-v7, 22.2.1, android, com/android/support)
|
||||
MOZ_ANDROID_AAR(mediarouter-v7, 22.2.1, android, com/android/support, REQUIRED_INTERNAL_IMPL)
|
||||
fi
|
||||
|
||||
@ -398,8 +397,9 @@ case "$target" in
|
||||
AC_SUBST(ANDROID_SDK)
|
||||
AC_SUBST(ANDROID_TOOLS)
|
||||
|
||||
MOZ_ANDROID_AAR(support-v4, 22.2.1, android, com/android/support, REQUIRED_INTERNAL_IMPL)
|
||||
MOZ_ANDROID_AAR(appcompat-v7, 22.2.1, android, com/android/support)
|
||||
MOZ_ANDROID_AAR(recyclerview-v7, 22.2.1, android, com/android/support)
|
||||
MOZ_ANDROID_AAR(support-v4, 22.2.1, android, com/android/support, REQUIRED_INTERNAL_IMPL)
|
||||
|
||||
ANDROID_SUPPORT_ANNOTATIONS_JAR="$ANDROID_SDK_ROOT/extras/android/m2repository/com/android/support/support-annotations/22.2.1/support-annotations-22.2.1.jar"
|
||||
AC_MSG_CHECKING([for support-annotations JAR])
|
||||
|
@ -395,7 +395,7 @@ BasePrincipal::GetUnknownAppId(bool* aUnknownAppId)
|
||||
}
|
||||
|
||||
already_AddRefed<BasePrincipal>
|
||||
BasePrincipal::CreateCodebasePrincipal(nsIURI* aURI, const OriginAttributes& aAttrs)
|
||||
BasePrincipal::CreateCodebasePrincipal(nsIURI* aURI, OriginAttributes& aAttrs)
|
||||
{
|
||||
// If the URI is supposed to inherit the security context of whoever loads it,
|
||||
// we shouldn't make a codebase principal for it.
|
||||
|
@ -159,7 +159,7 @@ public:
|
||||
virtual bool IsCodebasePrincipal() const { return false; };
|
||||
|
||||
static BasePrincipal* Cast(nsIPrincipal* aPrin) { return static_cast<BasePrincipal*>(aPrin); }
|
||||
static already_AddRefed<BasePrincipal> CreateCodebasePrincipal(nsIURI* aURI, const OriginAttributes& aAttrs);
|
||||
static already_AddRefed<BasePrincipal> CreateCodebasePrincipal(nsIURI* aURI, OriginAttributes& aAttrs);
|
||||
|
||||
const OriginAttributes& OriginAttributesRef() { return mOriginAttributes; }
|
||||
uint32_t AppId() const { return mOriginAttributes.mAppId; }
|
||||
|
@ -64,6 +64,7 @@ support-files =
|
||||
support-files =
|
||||
browser_cmd_jsb_script.jsi
|
||||
[browser_cmd_listen.js]
|
||||
[browser_cmd_measure.js]
|
||||
[browser_cmd_media.js]
|
||||
support-files =
|
||||
browser_cmd_media.html
|
||||
|
53
devtools/client/commandline/test/browser_cmd_measure.js
Normal file
53
devtools/client/commandline/test/browser_cmd_measure.js
Normal file
@ -0,0 +1,53 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests the highlight command, ensure no invalid arguments are given
|
||||
|
||||
const TEST_PAGE = "data:text/html;charset=utf-8,foo";
|
||||
|
||||
function test() {
|
||||
return Task.spawn(spawnTest).then(finish, helpers.handleError);
|
||||
}
|
||||
|
||||
function* spawnTest() {
|
||||
let options = yield helpers.openTab(TEST_PAGE);
|
||||
yield helpers.openToolbar(options);
|
||||
|
||||
yield helpers.audit(options, [
|
||||
{
|
||||
setup: "measure",
|
||||
check: {
|
||||
input: "measure",
|
||||
markup: "VVVVVVV",
|
||||
status: "VALID"
|
||||
}
|
||||
},
|
||||
{
|
||||
setup: "measure on",
|
||||
check: {
|
||||
input: "measure on",
|
||||
markup: "VVVVVVVVEE",
|
||||
status: "ERROR"
|
||||
},
|
||||
exec: {
|
||||
output: "Error: Too many arguments"
|
||||
}
|
||||
},
|
||||
{
|
||||
setup: "measure --visible",
|
||||
check: {
|
||||
input: "measure --visible",
|
||||
markup: "VVVVVVVVEEEEEEEEE",
|
||||
status: "ERROR"
|
||||
},
|
||||
exec: {
|
||||
output: "Error: Too many arguments"
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
yield helpers.closeToolbar(options);
|
||||
yield helpers.closeTab(options);
|
||||
}
|
@ -92,7 +92,8 @@ const ToolboxButtons = exports.ToolboxButtons = [
|
||||
{ id: "command-button-scratchpad" },
|
||||
{ id: "command-button-eyedropper" },
|
||||
{ id: "command-button-screenshot" },
|
||||
{ id: "command-button-rulers"}
|
||||
{ id: "command-button-rulers" },
|
||||
{ id: "command-button-measure" }
|
||||
];
|
||||
|
||||
/**
|
||||
@ -512,6 +513,11 @@ Toolbox.prototype = {
|
||||
let toggleKey = this.doc.getElementById("toolbox-toggle-host-key");
|
||||
toggleKey.addEventListener("command", this.switchToPreviousHost.bind(this), true);
|
||||
|
||||
if (Services.prefs.prefHasUserValue("devtools.loader.srcdir")) {
|
||||
let reloadKey = this.doc.getElementById("tools-reload-key");
|
||||
reloadKey.addEventListener("command", this.reload.bind(this), true);
|
||||
}
|
||||
|
||||
// Split console uses keypress instead of command so the event can be
|
||||
// cancelled with stopPropagation on the keypress, and not preventDefault.
|
||||
this.doc.addEventListener("keypress", this._splitConsoleOnKeypress, false);
|
||||
@ -1612,6 +1618,11 @@ Toolbox.prototype = {
|
||||
return newHost;
|
||||
},
|
||||
|
||||
reload: function () {
|
||||
const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
devtools.reload(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Switch to the last used host for the toolbox UI.
|
||||
* This is determined by the devtools.toolbox.previousHost pref.
|
||||
|
@ -95,6 +95,10 @@
|
||||
key="&toolboxToggle.key;"
|
||||
oncommand="void(0);"
|
||||
modifiers="accel shift"/>
|
||||
<key id="tools-reload-key"
|
||||
key="&toolboxReload.key;"
|
||||
oncommand="void(0);"
|
||||
modifiers="accel alt"/>
|
||||
</keyset>
|
||||
|
||||
<popupset>
|
||||
|
@ -63,6 +63,8 @@ skip-if = e10s # GCLI isn't e10s compatible. See bug 1128988.
|
||||
[browser_inspector_highlighter-keybinding_02.js]
|
||||
[browser_inspector_highlighter-keybinding_03.js]
|
||||
[browser_inspector_highlighter-keybinding_04.js]
|
||||
[browser_inspector_highlighter-measure_01.js]
|
||||
[browser_inspector_highlighter-measure_02.js]
|
||||
[browser_inspector_highlighter-options.js]
|
||||
[browser_inspector_highlighter-rect_01.js]
|
||||
[browser_inspector_highlighter-rect_02.js]
|
||||
|
@ -0,0 +1,88 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URL = `data:text/html;charset=utf-8,
|
||||
<div style='
|
||||
position:absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 40000px;
|
||||
height: 8000px'>
|
||||
</div>`;
|
||||
|
||||
const PREFIX = "measuring-tool-highlighter-";
|
||||
const HIGHLIGHTER_TYPE = "MeasuringToolHighlighter";
|
||||
|
||||
const X = 32;
|
||||
const Y = 20;
|
||||
|
||||
add_task(function*() {
|
||||
let helper = yield openInspectorForURL(TEST_URL)
|
||||
.then(getHighlighterHelperFor(HIGHLIGHTER_TYPE));
|
||||
|
||||
let { finalize } = helper;
|
||||
|
||||
helper.prefix = PREFIX;
|
||||
|
||||
yield isHiddenByDefault(helper);
|
||||
yield areLabelsHiddenByDefaultWhenShows(helper);
|
||||
yield areLabelsProperlyDisplayedWhenMouseMoved(helper);
|
||||
|
||||
yield finalize();
|
||||
});
|
||||
|
||||
function* isHiddenByDefault({isElementHidden}) {
|
||||
info("Checking the highlighter is hidden by default");
|
||||
|
||||
let hidden = yield isElementHidden("elements");
|
||||
ok(hidden, "highlighter's root is hidden by default");
|
||||
|
||||
hidden = yield isElementHidden("label-size");
|
||||
ok(hidden, "highlighter's label size is hidden by default");
|
||||
|
||||
hidden = yield isElementHidden("label-position");
|
||||
ok(hidden, "highlighter's label position is hidden by default");
|
||||
}
|
||||
|
||||
function* areLabelsHiddenByDefaultWhenShows({isElementHidden, show}) {
|
||||
info("Checking the highlighter is displayed when asked");
|
||||
|
||||
yield show();
|
||||
|
||||
let hidden = yield isElementHidden("elements");
|
||||
is(hidden, false, "highlighter is visible after show");
|
||||
|
||||
hidden = yield isElementHidden("label-size");
|
||||
ok(hidden, "label's size still hidden");
|
||||
|
||||
hidden = yield isElementHidden("label-position");
|
||||
ok(hidden, "label's position still hidden");
|
||||
}
|
||||
|
||||
function* areLabelsProperlyDisplayedWhenMouseMoved({isElementHidden,
|
||||
synthesizeMouse, getElementTextContent}) {
|
||||
info("Checking labels are properly displayed when mouse moved");
|
||||
|
||||
yield synthesizeMouse({
|
||||
selector: ":root",
|
||||
options: {type: "mousemove"},
|
||||
x: X,
|
||||
y: Y
|
||||
});
|
||||
|
||||
let hidden = yield isElementHidden("label-position");
|
||||
is(hidden, false, "label's position is displayed after the mouse is moved");
|
||||
|
||||
hidden = yield isElementHidden("label-size");
|
||||
ok(hidden, "label's size still hidden");
|
||||
|
||||
let text = yield getElementTextContent("label-position");
|
||||
|
||||
let [x, y] = text.replace(/ /g, "").split(/\n/);
|
||||
|
||||
is(+x, X, "label's position shows the proper X coord");
|
||||
is(+y, Y, "label's position shows the proper Y coord");
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URL = `data:text/html;charset=utf-8,
|
||||
<div style='
|
||||
position:absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 40000px;
|
||||
height: 8000px'>
|
||||
</div>`;
|
||||
|
||||
const PREFIX = "measuring-tool-highlighter-";
|
||||
const HIGHLIGHTER_TYPE = "MeasuringToolHighlighter";
|
||||
|
||||
const SIDES = ["top", "right", "bottom", "left"];
|
||||
|
||||
const X = 32;
|
||||
const Y = 20;
|
||||
const WIDTH = 160;
|
||||
const HEIGHT = 100;
|
||||
const HYPOTENUSE = Math.hypot(WIDTH, HEIGHT).toFixed(2);
|
||||
|
||||
add_task(function*() {
|
||||
let helper = yield openInspectorForURL(TEST_URL)
|
||||
.then(getHighlighterHelperFor(HIGHLIGHTER_TYPE));
|
||||
|
||||
let { show, finalize } = helper;
|
||||
|
||||
helper.prefix = PREFIX;
|
||||
|
||||
yield show();
|
||||
|
||||
yield hasNoLabelsWhenStarts(helper);
|
||||
yield hasSizeLabelWhenMoved(helper);
|
||||
yield hasCorrectSizeLabelValue(helper);
|
||||
yield hasSizeLabelAndGuidesWhenStops(helper);
|
||||
yield hasCorrectSizeLabelValue(helper);
|
||||
|
||||
yield finalize();
|
||||
});
|
||||
|
||||
function* hasNoLabelsWhenStarts({isElementHidden, synthesizeMouse}) {
|
||||
info("Checking highlighter has no labels when we start to select");
|
||||
|
||||
yield synthesizeMouse({
|
||||
selector: ":root",
|
||||
options: {type: "mousedown"},
|
||||
x: X,
|
||||
y: Y
|
||||
});
|
||||
|
||||
let hidden = yield isElementHidden("label-size");
|
||||
ok(hidden, "label's size still hidden");
|
||||
|
||||
hidden = yield isElementHidden("label-position");
|
||||
ok(hidden, "label's position still hidden");
|
||||
|
||||
info("Checking highlighter has no guides when we start to select");
|
||||
|
||||
let guidesHidden = true;
|
||||
for (let side of SIDES) {
|
||||
guidesHidden = guidesHidden && (yield isElementHidden("guide-" + side));
|
||||
}
|
||||
|
||||
ok(guidesHidden, "guides are hidden during dragging");
|
||||
}
|
||||
|
||||
function* hasSizeLabelWhenMoved({isElementHidden, synthesizeMouse}) {
|
||||
info("Checking highlighter has size label when we select the area");
|
||||
|
||||
yield synthesizeMouse({
|
||||
selector: ":root",
|
||||
options: {type: "mousemove"},
|
||||
x: X + WIDTH,
|
||||
y: Y + HEIGHT
|
||||
});
|
||||
|
||||
let hidden = yield isElementHidden("label-size");
|
||||
is(hidden, false, "label's size is visible during selection");
|
||||
|
||||
hidden = yield isElementHidden("label-position");
|
||||
ok(hidden, "label's position still hidden");
|
||||
|
||||
info("Checking highlighter has no guides when we select the area");
|
||||
|
||||
let guidesHidden = true;
|
||||
for (let side of SIDES) {
|
||||
guidesHidden = guidesHidden && (yield isElementHidden("guide-" + side));
|
||||
}
|
||||
|
||||
ok(guidesHidden, "guides are hidden during selection");
|
||||
}
|
||||
|
||||
function* hasSizeLabelAndGuidesWhenStops({isElementHidden, synthesizeMouse}) {
|
||||
info("Checking highlighter has size label and guides when we stop");
|
||||
|
||||
yield synthesizeMouse({
|
||||
selector: ":root",
|
||||
options: {type: "mouseup"},
|
||||
x: X + WIDTH,
|
||||
y: Y + HEIGHT
|
||||
});
|
||||
|
||||
let hidden = yield isElementHidden("label-size");
|
||||
is(hidden, false, "label's size is visible when the selection is done");
|
||||
|
||||
hidden = yield isElementHidden("label-position");
|
||||
ok(hidden, "label's position still hidden");
|
||||
|
||||
let guidesVisible = true;
|
||||
for (let side of SIDES) {
|
||||
guidesVisible = guidesVisible && !(yield isElementHidden("guide-" + side));
|
||||
}
|
||||
|
||||
ok(guidesVisible, "guides are visible when the selection is done");
|
||||
}
|
||||
|
||||
function* hasCorrectSizeLabelValue({getElementTextContent}) {
|
||||
let text = yield getElementTextContent("label-size");
|
||||
|
||||
let [width, height, hypot] = text.match(/\d.*px/g);
|
||||
|
||||
is(parseFloat(width), WIDTH, "width on label's size is correct");
|
||||
is(parseFloat(height), HEIGHT, "height on label's size is correct");
|
||||
is(parseFloat(hypot), HYPOTENUSE, "hypotenuse on label's size is correct");
|
||||
}
|
@ -480,3 +480,57 @@ function dispatchCommandEvent(node) {
|
||||
false, false, null);
|
||||
node.dispatchEvent(commandEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulate some common operations for highlighter's tests, to have
|
||||
* the tests cleaner, without exposing directly `inspector`, `highlighter`, and
|
||||
* `testActor` if not needed.
|
||||
*
|
||||
* @param {String}
|
||||
* The highlighter's type
|
||||
* @return
|
||||
* A generator function that takes an object with `inspector` and `testActor`
|
||||
* properties. (see `openInspector`)
|
||||
*/
|
||||
const getHighlighterHelperFor = (type) => Task.async(
|
||||
function*({inspector, testActor}) {
|
||||
let front = inspector.inspector;
|
||||
let highlighter = yield front.getHighlighterByType(type);
|
||||
|
||||
let prefix = "";
|
||||
|
||||
return {
|
||||
set prefix(value) {
|
||||
prefix = value;
|
||||
},
|
||||
|
||||
show: function*(selector = ":root") {
|
||||
let node = yield getNodeFront(selector, inspector);
|
||||
yield highlighter.show(node);
|
||||
},
|
||||
|
||||
isElementHidden: function*(id) {
|
||||
return (yield testActor.getHighlighterNodeAttribute(
|
||||
prefix + id, "hidden", highlighter)) === "true";
|
||||
},
|
||||
|
||||
getElementTextContent: function*(id) {
|
||||
return yield testActor.getHighlighterNodeTextContent(
|
||||
prefix + id, highlighter);
|
||||
},
|
||||
|
||||
getElementAttribute: function*(id, name) {
|
||||
return yield testActor.getHighlighterNodeAttribute(
|
||||
prefix + id, name, highlighter);
|
||||
},
|
||||
|
||||
synthesizeMouse: function*(options) {
|
||||
yield testActor.synthesizeMouse(options);
|
||||
},
|
||||
|
||||
finalize: function*() {
|
||||
yield highlighter.finalize();
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
@ -205,6 +205,8 @@ devtools.jar:
|
||||
skin/themes/images/command-eyedropper@2x.png (themes/images/command-eyedropper@2x.png)
|
||||
skin/themes/images/command-rulers.png (themes/images/command-rulers.png)
|
||||
skin/themes/images/command-rulers@2x.png (themes/images/command-rulers@2x.png)
|
||||
skin/themes/images/command-measure.png (themes/images/command-measure.png)
|
||||
skin/themes/images/command-measure@2x.png (themes/images/command-measure@2x.png)
|
||||
skin/themes/markup-view.css (themes/markup-view.css)
|
||||
skin/themes/images/editor-error.png (themes/images/editor-error.png)
|
||||
skin/themes/images/editor-breakpoint.png (themes/images/editor-breakpoint.png)
|
||||
|
@ -70,6 +70,9 @@ var CommandUtils = {
|
||||
* Utility to ensure that things are loaded in the correct order
|
||||
*/
|
||||
createRequisition: function(target, options) {
|
||||
if (!gcliInit) {
|
||||
return promise.reject("Unable to load gcli");
|
||||
}
|
||||
return gcliInit.getSystem(target).then(system => {
|
||||
var Requisition = require("gcli/cli").Requisition;
|
||||
return new Requisition(system, options);
|
||||
|
BIN
devtools/client/themes/images/command-measure.png
Normal file
BIN
devtools/client/themes/images/command-measure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 207 B |
BIN
devtools/client/themes/images/command-measure@2x.png
Normal file
BIN
devtools/client/themes/images/command-measure@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 179 B |
@ -768,6 +768,10 @@
|
||||
background-image: url("chrome://devtools/skin/themes/images/command-rulers.png");
|
||||
}
|
||||
|
||||
#command-button-measure > image {
|
||||
background-image: url("chrome://devtools/skin/themes/images/command-measure.png");
|
||||
}
|
||||
|
||||
@media (min-resolution: 1.1dppx) {
|
||||
#command-button-paintflashing > image {
|
||||
background-image: url("chrome://devtools/skin/themes/images/command-paintflashing@2x.png");
|
||||
@ -808,6 +812,10 @@
|
||||
#command-button-rulers > image {
|
||||
background-image: url("chrome://devtools/skin/themes/images/command-rulers@2x.png");
|
||||
}
|
||||
|
||||
#command-button-measure > image {
|
||||
background-image: url("chrome://devtools/skin/themes/images/command-measure@2x.png");
|
||||
}
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
|
@ -273,3 +273,62 @@
|
||||
transform: rotate(-90deg);
|
||||
text-anchor: end;
|
||||
}
|
||||
|
||||
/* Measuring Tool highlighter */
|
||||
|
||||
:-moz-native-anonymous .measuring-tool-highlighter-root {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: auto;
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
:-moz-native-anonymous .measuring-tool-highlighter-root path {
|
||||
shape-rendering: crispEdges;
|
||||
fill: rgba(135, 206, 235, 0.6);
|
||||
stroke: #08c;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
:-moz-native-anonymous .dragging path {
|
||||
fill: rgba(135, 206, 235, 0.6);
|
||||
stroke: #08c;
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
:-moz-native-anonymous .measuring-tool-highlighter-label-size,
|
||||
:-moz-native-anonymous .measuring-tool-highlighter-label-position {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: inline-block;
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
white-space: pre-line;
|
||||
font: message-box;
|
||||
font-size: 10px;
|
||||
pointer-events: none;
|
||||
-moz-user-select: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:-moz-native-anonymous .measuring-tool-highlighter-label-position {
|
||||
color: #fff;
|
||||
background: hsla(214, 13%, 24%, 0.8);
|
||||
}
|
||||
|
||||
:-moz-native-anonymous .measuring-tool-highlighter-label-size {
|
||||
color: hsl(216, 33%, 97%);
|
||||
background: hsl(214, 13%, 24%);
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
:-moz-native-anonymous .measuring-tool-highlighter-guide-top,
|
||||
:-moz-native-anonymous .measuring-tool-highlighter-guide-right,
|
||||
:-moz-native-anonymous .measuring-tool-highlighter-guide-bottom,
|
||||
:-moz-native-anonymous .measuring-tool-highlighter-guide-left {
|
||||
stroke: #08c;
|
||||
stroke-dasharray: 5 3;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
@ -703,3 +703,7 @@ exports.GeometryEditorHighlighter = GeometryEditorHighlighter;
|
||||
const { RulersHighlighter } = require("./highlighters/rulers");
|
||||
register(RulersHighlighter);
|
||||
exports.RulersHighlighter = RulersHighlighter;
|
||||
|
||||
const { MeasuringToolHighlighter } = require("./highlighters/measuring-tool");
|
||||
register(MeasuringToolHighlighter);
|
||||
exports.MeasuringToolHighlighter = MeasuringToolHighlighter;
|
||||
|
562
devtools/server/actors/highlighters/measuring-tool.js
Normal file
562
devtools/server/actors/highlighters/measuring-tool.js
Normal file
@ -0,0 +1,562 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const events = require("sdk/event/core");
|
||||
const { getCurrentZoom,
|
||||
setIgnoreLayoutChanges } = require("devtools/shared/layout/utils");
|
||||
const {
|
||||
CanvasFrameAnonymousContentHelper,
|
||||
createSVGNode, createNode } = require("./utils/markup");
|
||||
|
||||
// Hard coded value about the size of measuring tool label, in order to
|
||||
// position and flip it when is needed.
|
||||
const LABEL_SIZE_MARGIN = 8;
|
||||
const LABEL_SIZE_WIDTH = 80;
|
||||
const LABEL_SIZE_HEIGHT = 52;
|
||||
const LABEL_POS_MARGIN = 4;
|
||||
const LABEL_POS_WIDTH = 40;
|
||||
const LABEL_POS_HEIGHT = 34;
|
||||
|
||||
const SIDES = ["top", "right", "bottom", "left"];
|
||||
|
||||
/**
|
||||
* The MeasuringToolHighlighter is used to measure distances in a content page.
|
||||
* It allows users to click and drag with their mouse to draw an area whose
|
||||
* dimensions will be displayed in a tooltip next to it.
|
||||
* This allows users to measure distances between elements on a page.
|
||||
*/
|
||||
function MeasuringToolHighlighter(highlighterEnv) {
|
||||
this.env = highlighterEnv;
|
||||
this.markup = new CanvasFrameAnonymousContentHelper(highlighterEnv,
|
||||
this._buildMarkup.bind(this));
|
||||
|
||||
this.coords = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
|
||||
let { pageListenerTarget } = highlighterEnv;
|
||||
|
||||
pageListenerTarget.addEventListener("mousedown", this);
|
||||
pageListenerTarget.addEventListener("mousemove", this);
|
||||
pageListenerTarget.addEventListener("mouseleave", this);
|
||||
pageListenerTarget.addEventListener("scroll", this);
|
||||
pageListenerTarget.addEventListener("pagehide", this);
|
||||
}
|
||||
|
||||
MeasuringToolHighlighter.prototype = {
|
||||
typeName: "MeasuringToolHighlighter",
|
||||
|
||||
ID_CLASS_PREFIX: "measuring-tool-highlighter-",
|
||||
|
||||
_buildMarkup() {
|
||||
let prefix = this.ID_CLASS_PREFIX;
|
||||
let { window } = this.env;
|
||||
|
||||
let container = createNode(window, {
|
||||
attributes: {"class": "highlighter-container"}
|
||||
});
|
||||
|
||||
let root = createNode(window, {
|
||||
parent: container,
|
||||
attributes: {
|
||||
"id": "root",
|
||||
"class": "root",
|
||||
},
|
||||
prefix
|
||||
});
|
||||
|
||||
let svg = createSVGNode(window, {
|
||||
nodeType: "svg",
|
||||
parent: root,
|
||||
attributes: {
|
||||
id: "elements",
|
||||
"class": "elements",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
hidden: "true"
|
||||
},
|
||||
prefix
|
||||
});
|
||||
|
||||
createNode(window, {
|
||||
nodeType: "label",
|
||||
attributes: {
|
||||
id: "label-size",
|
||||
"class": "label-size",
|
||||
"hidden": "true"
|
||||
},
|
||||
parent: root,
|
||||
prefix
|
||||
});
|
||||
|
||||
createNode(window, {
|
||||
nodeType: "label",
|
||||
attributes: {
|
||||
id: "label-position",
|
||||
"class": "label-position",
|
||||
"hidden": "true"
|
||||
},
|
||||
parent: root,
|
||||
prefix
|
||||
});
|
||||
|
||||
// Creating a <g> element in order to group all the paths below, that
|
||||
// together represent the measuring tool; so that would be easier move them
|
||||
// around
|
||||
let g = createSVGNode(window, {
|
||||
nodeType: "g",
|
||||
attributes: {
|
||||
id: "tool",
|
||||
},
|
||||
parent: svg,
|
||||
prefix
|
||||
});
|
||||
|
||||
createSVGNode(window, {
|
||||
nodeType: "path",
|
||||
attributes: {
|
||||
id: "box-path"
|
||||
},
|
||||
parent: g,
|
||||
prefix
|
||||
});
|
||||
|
||||
createSVGNode(window, {
|
||||
nodeType: "path",
|
||||
attributes: {
|
||||
id: "diagonal-path"
|
||||
},
|
||||
parent: g,
|
||||
prefix
|
||||
});
|
||||
|
||||
for (let side of SIDES) {
|
||||
createSVGNode(window, {
|
||||
nodeType: "line",
|
||||
parent: svg,
|
||||
attributes: {
|
||||
"class": `guide-${side}`,
|
||||
id: `guide-${side}`,
|
||||
hidden: "true"
|
||||
},
|
||||
prefix
|
||||
});
|
||||
}
|
||||
|
||||
return container;
|
||||
},
|
||||
|
||||
_update() {
|
||||
let { window } = this.env;
|
||||
|
||||
setIgnoreLayoutChanges(true);
|
||||
|
||||
let zoom = getCurrentZoom(window);
|
||||
|
||||
let { documentElement } = window.document;
|
||||
|
||||
let width = Math.max(documentElement.clientWidth,
|
||||
documentElement.scrollWidth,
|
||||
documentElement.offsetWidth);
|
||||
|
||||
let height = Math.max(documentElement.clientHeight,
|
||||
documentElement.scrollHeight,
|
||||
documentElement.offsetHeight);
|
||||
|
||||
let { body } = window.document;
|
||||
|
||||
// get the size of the content document despite the compatMode
|
||||
if (body) {
|
||||
width = Math.max(width, body.scrollWidth, body.offsetWidth);
|
||||
height = Math.max(height, body.scrollHeight, body.offsetHeight);
|
||||
}
|
||||
|
||||
let { coords } = this;
|
||||
|
||||
let isZoomChanged = zoom !== coords.zoom;
|
||||
|
||||
if (isZoomChanged) {
|
||||
coords.zoom = zoom;
|
||||
this.updateLabel();
|
||||
}
|
||||
|
||||
let isDocumentSizeChanged = width !== coords.documentWidth ||
|
||||
height !== coords.documentHeight;
|
||||
|
||||
if (isDocumentSizeChanged) {
|
||||
coords.documentWidth = width;
|
||||
coords.documentHeight = height;
|
||||
}
|
||||
|
||||
// If either the document's size or the zoom is changed since the last
|
||||
// repaint, we update the tool's size as well.
|
||||
if (isZoomChanged || isDocumentSizeChanged) {
|
||||
this.updateViewport();
|
||||
}
|
||||
|
||||
setIgnoreLayoutChanges(false, documentElement);
|
||||
|
||||
this._rafID = window.requestAnimationFrame(() => this._update());
|
||||
},
|
||||
|
||||
_cancelUpdate() {
|
||||
if (this._rafID) {
|
||||
this.env.window.cancelAnimationFrame(this._rafID);
|
||||
this._rafID = 0;
|
||||
}
|
||||
},
|
||||
|
||||
destroy() {
|
||||
this.hide();
|
||||
|
||||
this._cancelUpdate();
|
||||
|
||||
let { pageListenerTarget } = this.env;
|
||||
|
||||
pageListenerTarget.removeEventListener("mousedown", this);
|
||||
pageListenerTarget.removeEventListener("mousemove", this);
|
||||
pageListenerTarget.removeEventListener("mouseup", this);
|
||||
pageListenerTarget.removeEventListener("scroll", this);
|
||||
pageListenerTarget.removeEventListener("pagehide", this);
|
||||
|
||||
this.markup.destroy();
|
||||
|
||||
events.emit(this, "destroy");
|
||||
},
|
||||
|
||||
show() {
|
||||
setIgnoreLayoutChanges(true);
|
||||
|
||||
this.getElement("elements").removeAttribute("hidden");
|
||||
|
||||
this._update();
|
||||
|
||||
setIgnoreLayoutChanges(false, this.env.window.document.documentElement);
|
||||
},
|
||||
|
||||
hide() {
|
||||
setIgnoreLayoutChanges(true);
|
||||
|
||||
this.hideLabel("size");
|
||||
this.hideLabel("position");
|
||||
|
||||
this.getElement("elements").setAttribute("hidden", "true");
|
||||
|
||||
this._cancelUpdate();
|
||||
|
||||
setIgnoreLayoutChanges(false, this.env.window.document.documentElement);
|
||||
},
|
||||
|
||||
getElement(id) {
|
||||
return this.markup.getElement(this.ID_CLASS_PREFIX + id);
|
||||
},
|
||||
|
||||
setSize(w, h) {
|
||||
this.setCoords(undefined, undefined, w, h);
|
||||
},
|
||||
|
||||
setCoords(x, y, w, h) {
|
||||
let { coords } = this;
|
||||
|
||||
if (typeof x !== "undefined") {
|
||||
coords.x = x;
|
||||
}
|
||||
|
||||
if (typeof y !== "undefined") {
|
||||
coords.y = y;
|
||||
}
|
||||
|
||||
if (typeof w !== "undefined") {
|
||||
coords.w = w;
|
||||
}
|
||||
|
||||
if (typeof h !== "undefined") {
|
||||
coords.h = h;
|
||||
}
|
||||
|
||||
setIgnoreLayoutChanges(true);
|
||||
|
||||
if (this._isDragging) {
|
||||
this.updatePaths();
|
||||
}
|
||||
|
||||
this.updateLabel();
|
||||
|
||||
setIgnoreLayoutChanges(false, this.env.window.document.documentElement);
|
||||
},
|
||||
|
||||
updatePaths() {
|
||||
let { x, y, w, h } = this.coords;
|
||||
let dir = `M0 0 L${w} 0 L${w} ${h} L0 ${h}z`;
|
||||
|
||||
// Adding correction to the line path, otherwise some pixels are drawn
|
||||
// outside the main rectangle area.
|
||||
let x1 = w > 0 ? 0.5 : 0;
|
||||
let y1 = w < 0 && h < 0 ? -0.5 : 0;
|
||||
let w1 = w + (h < 0 && w < 0 ? 0.5 : 0);
|
||||
let h1 = h + (h > 0 && w > 0 ? -0.5 : 0);
|
||||
|
||||
let linedir = `M${x1} ${y1} L${w1} ${h1}`;
|
||||
|
||||
this.getElement("box-path").setAttribute("d", dir);
|
||||
this.getElement("diagonal-path").setAttribute("d", linedir);
|
||||
this.getElement("tool").setAttribute("transform", `translate(${x},${y})`);
|
||||
},
|
||||
|
||||
updateLabel(type) {
|
||||
type = type || this._isDragging ? "size" : "position";
|
||||
|
||||
let isSizeLabel = type === "size";
|
||||
|
||||
let label = this.getElement(`label-${type}`);
|
||||
|
||||
let origin = "top left";
|
||||
|
||||
let { innerWidth, innerHeight, scrollX, scrollY } = this.env.window;
|
||||
let { x, y, w, h, zoom } = this.coords;
|
||||
let scale = 1 / zoom;
|
||||
|
||||
w = w || 0;
|
||||
h = h || 0;
|
||||
x = (x || 0) + w;
|
||||
y = (y || 0) + h;
|
||||
|
||||
let labelMargin, labelHeight, labelWidth;
|
||||
|
||||
if (isSizeLabel) {
|
||||
labelMargin = LABEL_SIZE_MARGIN;
|
||||
labelWidth = LABEL_SIZE_WIDTH;
|
||||
labelHeight = LABEL_SIZE_HEIGHT;
|
||||
|
||||
let d = Math.hypot(w, h).toFixed(2);
|
||||
|
||||
label.setTextContent(`W: ${Math.abs(w)} px
|
||||
H: ${Math.abs(h)} px
|
||||
↘: ${d}px`);
|
||||
} else {
|
||||
labelMargin = LABEL_POS_MARGIN;
|
||||
labelWidth = LABEL_POS_WIDTH;
|
||||
labelHeight = LABEL_POS_HEIGHT;
|
||||
|
||||
label.setTextContent(`${x}
|
||||
${y}`);
|
||||
}
|
||||
|
||||
// Size used to position properly the label
|
||||
let labelBoxWidth = (labelWidth + labelMargin) * scale;
|
||||
let labelBoxHeight = (labelHeight + labelMargin) * scale;
|
||||
|
||||
let isGoingLeft = w < scrollX;
|
||||
let isSizeGoingLeft = isSizeLabel && isGoingLeft;
|
||||
let isExceedingLeftMargin = x - labelBoxWidth < scrollX;
|
||||
let isExceedingRightMargin = x + labelBoxWidth > innerWidth + scrollX;
|
||||
let isExceedingTopMargin = y - labelBoxHeight < scrollY;
|
||||
let isExceedingBottomMargin = y + labelBoxHeight > innerHeight + scrollY;
|
||||
|
||||
if ((isSizeGoingLeft && !isExceedingLeftMargin) || isExceedingRightMargin) {
|
||||
x -= labelBoxWidth;
|
||||
origin = "top right";
|
||||
} else {
|
||||
x += labelMargin * scale;
|
||||
}
|
||||
|
||||
if (isSizeLabel) {
|
||||
y += isExceedingTopMargin ? labelMargin * scale : -labelBoxHeight;
|
||||
} else {
|
||||
y += isExceedingBottomMargin ? -labelBoxHeight : labelMargin * scale;
|
||||
}
|
||||
|
||||
label.setAttribute("style", `
|
||||
width: ${labelWidth}px;
|
||||
height: ${labelHeight}px;
|
||||
transform-origin: ${origin};
|
||||
transform: translate(${x}px,${y}px) scale(${scale})
|
||||
`);
|
||||
|
||||
if (!isSizeLabel) {
|
||||
let labelSize = this.getElement("label-size");
|
||||
let style = labelSize.getAttribute("style");
|
||||
|
||||
if (style) {
|
||||
labelSize.setAttribute("style",
|
||||
style.replace(/scale[^)]+\)/, `scale(${scale})`));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
updateViewport() {
|
||||
let { scrollX, scrollY, devicePixelRatio } = this.env.window;
|
||||
let { documentWidth, documentHeight, zoom } = this.coords;
|
||||
|
||||
// Because `devicePixelRatio` is affected by zoom (see bug 809788),
|
||||
// in order to get the "real" device pixel ratio, we need divide by `zoom`
|
||||
let pixelRatio = devicePixelRatio / zoom;
|
||||
|
||||
// The "real" device pixel ratio is used to calculate the max stroke
|
||||
// width we can actually assign: on retina, for instance, it would be 0.5,
|
||||
// where on non high dpi monitor would be 1.
|
||||
let minWidth = 1 / pixelRatio;
|
||||
let strokeWidth = Math.min(minWidth, minWidth / zoom);
|
||||
|
||||
this.getElement("root").setAttribute("style",
|
||||
`stroke-width:${strokeWidth};
|
||||
width:${documentWidth}px;
|
||||
height:${documentHeight}px;
|
||||
transform: translate(${-scrollX}px,${-scrollY}px)`);
|
||||
},
|
||||
|
||||
updateGuides() {
|
||||
let { x, y, w, h } = this.coords;
|
||||
|
||||
let guide = this.getElement("guide-top");
|
||||
|
||||
guide.setAttribute("x1", "0");
|
||||
guide.setAttribute("y1", y);
|
||||
guide.setAttribute("x2", "100%");
|
||||
guide.setAttribute("y2", y);
|
||||
|
||||
guide = this.getElement("guide-right");
|
||||
|
||||
guide.setAttribute("x1", x + w);
|
||||
guide.setAttribute("y1", 0);
|
||||
guide.setAttribute("x2", x + w);
|
||||
guide.setAttribute("y2", "100%");
|
||||
|
||||
guide = this.getElement("guide-bottom");
|
||||
|
||||
guide.setAttribute("x1", "0");
|
||||
guide.setAttribute("y1", y + h);
|
||||
guide.setAttribute("x2", "100%");
|
||||
guide.setAttribute("y2", y + h);
|
||||
|
||||
guide = this.getElement("guide-left");
|
||||
|
||||
guide.setAttribute("x1", x);
|
||||
guide.setAttribute("y1", 0);
|
||||
guide.setAttribute("x2", x);
|
||||
guide.setAttribute("y2", "100%");
|
||||
},
|
||||
|
||||
showLabel(type) {
|
||||
setIgnoreLayoutChanges(true);
|
||||
|
||||
this.getElement(`label-${type}`).removeAttribute("hidden");
|
||||
|
||||
setIgnoreLayoutChanges(false, this.env.window.document.documentElement);
|
||||
},
|
||||
|
||||
hideLabel(type) {
|
||||
setIgnoreLayoutChanges(true);
|
||||
|
||||
this.getElement(`label-${type}`).setAttribute("hidden", "true");
|
||||
|
||||
setIgnoreLayoutChanges(false, this.env.window.document.documentElement);
|
||||
},
|
||||
|
||||
showGuides() {
|
||||
let prefix = this.ID_CLASS_PREFIX + "guide-";
|
||||
|
||||
for (let side of SIDES) {
|
||||
this.markup.removeAttributeForElement(`${prefix + side}`, "hidden");
|
||||
}
|
||||
},
|
||||
|
||||
hideGuides() {
|
||||
let prefix = this.ID_CLASS_PREFIX + "guide-";
|
||||
|
||||
for (let side of SIDES) {
|
||||
this.markup.setAttributeForElement(`${prefix + side}`, "hidden", "true");
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent(event) {
|
||||
let scrollX, scrollY, innerWidth, innerHeight;
|
||||
let x, y;
|
||||
|
||||
let { pageListenerTarget } = this.env;
|
||||
|
||||
switch (event.type) {
|
||||
case "mousedown":
|
||||
if (event.button) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._isDragging = true;
|
||||
|
||||
let { window } = this.env;
|
||||
|
||||
({ scrollX, scrollY } = window);
|
||||
x = event.clientX + scrollX;
|
||||
y = event.clientY + scrollY;
|
||||
|
||||
pageListenerTarget.addEventListener("mouseup", this);
|
||||
|
||||
setIgnoreLayoutChanges(true);
|
||||
|
||||
this.getElement("tool").setAttribute("class", "dragging");
|
||||
|
||||
this.hideLabel("size");
|
||||
this.hideLabel("position");
|
||||
|
||||
this.hideGuides();
|
||||
this.setCoords(x, y, 0, 0);
|
||||
|
||||
setIgnoreLayoutChanges(false, window.document.documentElement);
|
||||
|
||||
break;
|
||||
case "mouseup":
|
||||
this._isDragging = false;
|
||||
|
||||
pageListenerTarget.removeEventListener("mouseup", this);
|
||||
|
||||
setIgnoreLayoutChanges(true);
|
||||
|
||||
this.getElement("tool").removeAttribute("class", "");
|
||||
|
||||
// Shows the guides only if an actual area is selected
|
||||
if (this.coords.w !== 0 && this.coords.h !== 0) {
|
||||
this.updateGuides();
|
||||
this.showGuides();
|
||||
}
|
||||
|
||||
setIgnoreLayoutChanges(false, this.env.window.document.documentElement);
|
||||
|
||||
break;
|
||||
case "mousemove":
|
||||
({ scrollX, scrollY, innerWidth, innerHeight } = this.env.window);
|
||||
x = event.clientX + scrollX;
|
||||
y = event.clientY + scrollY;
|
||||
|
||||
let { coords } = this;
|
||||
|
||||
x = Math.min(innerWidth + scrollX - 1, Math.max(0 + scrollX, x));
|
||||
y = Math.min(innerHeight + scrollY, Math.max(1 + scrollY, y));
|
||||
|
||||
this.setSize(x - coords.x, y - coords.y);
|
||||
|
||||
let type = this._isDragging ? "size" : "position";
|
||||
|
||||
this.showLabel(type);
|
||||
break;
|
||||
case "mouseleave":
|
||||
if (!this._isDragging) {
|
||||
this.hideLabel("position");
|
||||
}
|
||||
break;
|
||||
case "scroll":
|
||||
setIgnoreLayoutChanges(true);
|
||||
this.updateViewport();
|
||||
setIgnoreLayoutChanges(false, this.env.window.document.documentElement);
|
||||
|
||||
break;
|
||||
case "pagehide":
|
||||
this.destroy();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.MeasuringToolHighlighter = MeasuringToolHighlighter;
|
@ -13,6 +13,7 @@ DevToolsModules(
|
||||
'box-model.js',
|
||||
'css-transform.js',
|
||||
'geometry-editor.js',
|
||||
'measuring-tool.js',
|
||||
'rect.js',
|
||||
'rulers.js',
|
||||
'selector.js',
|
||||
|
@ -842,8 +842,9 @@ var NodeFront = protocol.FrontClass(NodeActor, {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// This is a new attribute.
|
||||
if (!found) {
|
||||
// This is a new attribute. The null check is because of Bug 1192270,
|
||||
// in the case of a newly added then removed attribute
|
||||
if (!found && change.newValue !== null) {
|
||||
this.attributes.push({
|
||||
name: change.attributeName,
|
||||
namespace: change.attributeNamespace,
|
||||
|
@ -126,13 +126,19 @@ function testQueuedMutations() {
|
||||
attrNode.removeAttribute("data-newattr3");
|
||||
attrNode.setAttribute("data-newattr3", "3");
|
||||
|
||||
// This shouldn't be added in the attribute set, since it's a new
|
||||
// attribute that's been added and removed.
|
||||
attrNode.setAttribute("data-newattr4", "4");
|
||||
attrNode.removeAttribute("data-newattr4");
|
||||
|
||||
gWalker.once("mutations", mutations => {
|
||||
is(mutations.length, 3, "Only one mutation each is sent for multiple queued attribute changes");
|
||||
is(mutations.length, 4, "Only one mutation each is sent for multiple queued attribute changes");
|
||||
is(attrFront.attributes.length, 3, "Should have id, data-newattr2, and data-newattr3.");
|
||||
|
||||
is(attrFront.getAttribute("data-newattr2"), "1000", "Node front should still have the correct value");
|
||||
is(attrFront.getAttribute("data-newattr3"), "3", "Node front should still have the correct value");
|
||||
ok(!attrFront.hasAttribute("data-newattr"), "Attribute value should be removed.");
|
||||
ok(!attrFront.hasAttribute("data-newattr4"), "Attribute value should be removed.");
|
||||
|
||||
runNextTest();
|
||||
})
|
||||
|
@ -423,7 +423,7 @@ DevToolsLoader.prototype = {
|
||||
/**
|
||||
* Reload the current provider.
|
||||
*/
|
||||
reload: function() {
|
||||
reload: function(showToolbox) {
|
||||
var events = this.require("sdk/system/events");
|
||||
events.emit("startupcache-invalidate", {});
|
||||
events.emit("devtools-unloaded", {});
|
||||
@ -433,6 +433,14 @@ DevToolsLoader.prototype = {
|
||||
delete this._mainid;
|
||||
this._chooseProvider();
|
||||
this.main("devtools/client/main");
|
||||
|
||||
// Reopen the toolbox automatically if requested
|
||||
if (showToolbox) {
|
||||
let { gBrowser } = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let target = this.TargetFactory.forTab(gBrowser.selectedTab);
|
||||
const { gDevTools } = this.require("resource:///modules/devtools/client/framework/gDevTools.jsm");
|
||||
gDevTools.showToolbox(target);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -65,6 +65,7 @@ exports.devtoolsModules = [
|
||||
"devtools/shared/gcli/commands/inject",
|
||||
"devtools/shared/gcli/commands/jsb",
|
||||
"devtools/shared/gcli/commands/listen",
|
||||
"devtools/shared/gcli/commands/measure",
|
||||
"devtools/shared/gcli/commands/media",
|
||||
"devtools/shared/gcli/commands/pagemod",
|
||||
"devtools/shared/gcli/commands/paintflashing",
|
||||
|
112
devtools/shared/gcli/commands/measure.js
Normal file
112
devtools/shared/gcli/commands/measure.js
Normal file
@ -0,0 +1,112 @@
|
||||
/* 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/. */
|
||||
/* globals getOuterId, getBrowserForTab */
|
||||
|
||||
"use strict";
|
||||
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const eventEmitter = new EventEmitter();
|
||||
const events = require("sdk/event/core");
|
||||
|
||||
loader.lazyRequireGetter(this, "getOuterId", "sdk/window/utils", true);
|
||||
loader.lazyRequireGetter(this, "getBrowserForTab", "sdk/tabs/utils", true);
|
||||
|
||||
const l10n = require("gcli/l10n");
|
||||
require("devtools/server/actors/inspector");
|
||||
const { MeasuringToolHighlighter, HighlighterEnvironment } =
|
||||
require("devtools/server/actors/highlighters");
|
||||
|
||||
const highlighters = new WeakMap();
|
||||
const visibleHighlighters = new Set();
|
||||
|
||||
const isCheckedFor = (tab) =>
|
||||
tab ? visibleHighlighters.has(getBrowserForTab(tab).outerWindowID) : false;
|
||||
|
||||
exports.items = [
|
||||
// The client measure command is used to maintain the toolbar button state
|
||||
// only and redirects to the server command to actually toggle the measuring
|
||||
// tool (see `measure_server` below).
|
||||
{
|
||||
name: "measure",
|
||||
runAt: "client",
|
||||
description: l10n.lookup("measureDesc"),
|
||||
manual: l10n.lookup("measureManual"),
|
||||
buttonId: "command-button-measure",
|
||||
buttonClass: "command-button command-button-invertable",
|
||||
tooltipText: l10n.lookup("measureTooltip"),
|
||||
state: {
|
||||
isChecked: ({_tab}) => isCheckedFor(_tab),
|
||||
onChange: (target, handler) => eventEmitter.on("changed", handler),
|
||||
offChange: (target, handler) => eventEmitter.off("changed", handler)
|
||||
},
|
||||
exec: function*(args, context) {
|
||||
let { target } = context.environment;
|
||||
|
||||
// Pipe the call to the server command.
|
||||
let response = yield context.updateExec("measure_server");
|
||||
let { visible, id } = response.data;
|
||||
|
||||
if (visible) {
|
||||
visibleHighlighters.add(id);
|
||||
} else {
|
||||
visibleHighlighters.delete(id);
|
||||
}
|
||||
|
||||
eventEmitter.emit("changed", { target });
|
||||
|
||||
// Toggle off the button when the page navigates because the measuring
|
||||
// tool is removed automatically by the MeasuringToolHighlighter on the
|
||||
// server then.
|
||||
let onNavigate = () => {
|
||||
visibleHighlighters.delete(id);
|
||||
eventEmitter.emit("changed", { target });
|
||||
};
|
||||
target.off("will-navigate", onNavigate);
|
||||
target.once("will-navigate", onNavigate);
|
||||
}
|
||||
},
|
||||
// The server measure command is hidden by default, it's just used by the
|
||||
// client command.
|
||||
{
|
||||
name: "measure_server",
|
||||
runAt: "server",
|
||||
hidden: true,
|
||||
returnType: "highlighterVisibility",
|
||||
exec: function(args, context) {
|
||||
let env = context.environment;
|
||||
let { document } = env;
|
||||
let id = getOuterId(env.window);
|
||||
|
||||
// Calling the command again after the measuring tool has been shown once,
|
||||
// hides it.
|
||||
if (highlighters.has(document)) {
|
||||
let { highlighter } = highlighters.get(document);
|
||||
highlighter.destroy();
|
||||
return {visible: false, id};
|
||||
}
|
||||
|
||||
// Otherwise, display the measuring tool.
|
||||
let environment = new HighlighterEnvironment();
|
||||
environment.initFromWindow(env.window);
|
||||
let highlighter = new MeasuringToolHighlighter(environment);
|
||||
|
||||
// Store the instance of the measuring tool highlighter for this document
|
||||
// so we can hide it later.
|
||||
highlighters.set(document, { highlighter, environment });
|
||||
|
||||
// Listen to the highlighter's destroy event which may happen if the
|
||||
// window is refreshed or closed with the measuring tool shown.
|
||||
events.once(highlighter, "destroy", () => {
|
||||
if (highlighters.has(document)) {
|
||||
let { environment } = highlighters.get(document);
|
||||
environment.destroy();
|
||||
highlighters.delete(document);
|
||||
}
|
||||
});
|
||||
|
||||
highlighter.show();
|
||||
return {visible: true, id};
|
||||
}
|
||||
}
|
||||
];
|
@ -17,6 +17,7 @@ DevToolsModules(
|
||||
'inject.js',
|
||||
'jsb.js',
|
||||
'listen.js',
|
||||
'measure.js',
|
||||
'media.js',
|
||||
'pagemod.js',
|
||||
'paintflashing.js',
|
||||
|
@ -425,14 +425,15 @@ StructuredCloneHelper::ReadFullySerializableObjects(JSContext* aCx,
|
||||
} else if (aTag == SCTAG_DOM_NULL_PRINCIPAL) {
|
||||
info = mozilla::ipc::NullPrincipalInfo();
|
||||
} else {
|
||||
uint32_t suffixLength, specLength;
|
||||
if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) {
|
||||
uint32_t appId = aIndex;
|
||||
|
||||
uint32_t isInBrowserElement, specLength;
|
||||
if (!JS_ReadUint32Pair(aReader, &isInBrowserElement, &specLength)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoCString suffix;
|
||||
suffix.SetLength(suffixLength);
|
||||
if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) {
|
||||
uint32_t signedPkgLength, dummy;
|
||||
if (!JS_ReadUint32Pair(aReader, &signedPkgLength, &dummy)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -442,9 +443,14 @@ StructuredCloneHelper::ReadFullySerializableObjects(JSContext* aCx,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OriginAttributes attrs;
|
||||
attrs.PopulateFromSuffix(suffix);
|
||||
info = mozilla::ipc::ContentPrincipalInfo(attrs, spec);
|
||||
nsAutoCString signedPkg;
|
||||
signedPkg.SetLength(signedPkgLength);
|
||||
if (!JS_ReadBytes(aReader, signedPkg.BeginWriting(), signedPkgLength)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
info = mozilla::ipc::ContentPrincipalInfo(appId, isInBrowserElement,
|
||||
spec, signedPkg);
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
@ -571,12 +577,13 @@ StructuredCloneHelper::WriteFullySerializableObjects(JSContext* aCx,
|
||||
|
||||
MOZ_ASSERT(info.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
|
||||
const mozilla::ipc::ContentPrincipalInfo& cInfo = info;
|
||||
nsAutoCString suffix;
|
||||
cInfo.attrs().CreateSuffix(suffix);
|
||||
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) &&
|
||||
JS_WriteUint32Pair(aWriter, suffix.Length(), cInfo.spec().Length()) &&
|
||||
JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
|
||||
JS_WriteBytes(aWriter, cInfo.spec().get(), cInfo.spec().Length());
|
||||
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL,
|
||||
cInfo.appId()) &&
|
||||
JS_WriteUint32Pair(aWriter, cInfo.isInBrowserElement(),
|
||||
cInfo.spec().Length()) &&
|
||||
JS_WriteUint32Pair(aWriter, cInfo.signedPkg().Length(), 0) &&
|
||||
JS_WriteBytes(aWriter, cInfo.spec().get(), cInfo.spec().Length()) &&
|
||||
JS_WriteBytes(aWriter, cInfo.signedPkg().get(), cInfo.signedPkg().Length());
|
||||
}
|
||||
}
|
||||
|
||||
|
2
dom/cache/CacheStorage.cpp
vendored
2
dom/cache/CacheStorage.cpp
vendored
@ -81,7 +81,7 @@ IsTrusted(const PrincipalInfo& aPrincipalInfo, bool aTestingPrefEnabled)
|
||||
// worker. We require exact knowledge of this information before allowing
|
||||
// the caller to touch the disk using the Cache API.
|
||||
if (NS_WARN_IF(aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo ||
|
||||
aPrincipalInfo.get_ContentPrincipalInfo().attrs().mAppId ==
|
||||
aPrincipalInfo.get_ContentPrincipalInfo().appId() ==
|
||||
nsIScriptSecurityManager::UNKNOWN_APP_ID)) {
|
||||
return false;
|
||||
}
|
||||
|
7
dom/cache/DBSchema.cpp
vendored
7
dom/cache/DBSchema.cpp
vendored
@ -1740,8 +1740,10 @@ InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
|
||||
|
||||
serializedInfo.Append(cInfo.spec());
|
||||
|
||||
MOZ_ASSERT(cInfo.appId() != nsIScriptSecurityManager::UNKNOWN_APP_ID);
|
||||
OriginAttributes attrs(cInfo.appId(), cInfo.isInBrowserElement());
|
||||
nsAutoCString suffix;
|
||||
cInfo.attrs().CreateSuffix(suffix);
|
||||
attrs.CreateSuffix(suffix);
|
||||
serializedInfo.Append(suffix);
|
||||
}
|
||||
|
||||
@ -1911,8 +1913,9 @@ ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCString signedPkg = NS_ConvertUTF16toUTF8(attrs.mSignedPkg);
|
||||
aSavedResponseOut->mValue.principalInfo() =
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, originNoSuffix);
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs.mAppId, attrs.mInBrowser, originNoSuffix, signedPkg);
|
||||
}
|
||||
|
||||
int32_t redirected;
|
||||
|
@ -332,8 +332,9 @@ ServiceWorkerRegistrar::ReadData()
|
||||
}
|
||||
|
||||
GET_LINE(line);
|
||||
nsCString signedPkg = NS_ConvertUTF16toUTF8(attrs.mSignedPkg);
|
||||
entry->principal() =
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, line);
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs.mAppId, attrs.mInBrowser, line, signedPkg);
|
||||
|
||||
GET_LINE(entry->scope());
|
||||
GET_LINE(entry->scriptSpec());
|
||||
@ -548,8 +549,9 @@ ServiceWorkerRegistrar::WriteData()
|
||||
const mozilla::ipc::ContentPrincipalInfo& cInfo =
|
||||
info.get_ContentPrincipalInfo();
|
||||
|
||||
OriginAttributes attrs(cInfo.appId(), cInfo.isInBrowserElement());
|
||||
nsAutoCString suffix;
|
||||
cInfo.attrs().CreateSuffix(suffix);
|
||||
attrs.CreateSuffix(suffix);
|
||||
|
||||
buffer.Truncate();
|
||||
buffer.Append(suffix.get());
|
||||
|
@ -163,8 +163,9 @@ TEST(ServiceWorkerRegistrar, TestReadData)
|
||||
ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
|
||||
const mozilla::ipc::ContentPrincipalInfo& cInfo0 = data[0].principal();
|
||||
|
||||
mozilla::OriginAttributes attrs0(cInfo0.appId(), cInfo0.isInBrowserElement());
|
||||
nsAutoCString suffix0;
|
||||
cInfo0.attrs().CreateSuffix(suffix0);
|
||||
attrs0.CreateSuffix(suffix0);
|
||||
|
||||
ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get());
|
||||
ASSERT_STREQ("spec 0", cInfo0.spec().get());
|
||||
@ -178,8 +179,9 @@ TEST(ServiceWorkerRegistrar, TestReadData)
|
||||
ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
|
||||
const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal();
|
||||
|
||||
mozilla::OriginAttributes attrs1(cInfo1.appId(), cInfo1.isInBrowserElement());
|
||||
nsAutoCString suffix1;
|
||||
cInfo1.attrs().CreateSuffix(suffix1);
|
||||
attrs1.CreateSuffix(suffix1);
|
||||
|
||||
ASSERT_STREQ("", suffix1.get());
|
||||
ASSERT_STREQ("spec 1", cInfo1.spec().get());
|
||||
@ -219,7 +221,7 @@ TEST(ServiceWorkerRegistrar, TestWriteData)
|
||||
|
||||
nsAutoCString spec;
|
||||
spec.AppendPrintf("spec write %d", i);
|
||||
d->principal() = mozilla::ipc::ContentPrincipalInfo(mozilla::OriginAttributes(i, i % 2), spec);
|
||||
d->principal() = mozilla::ipc::ContentPrincipalInfo(i, i % 2, spec, EmptyCString());
|
||||
d->scope().AppendPrintf("scope write %d", i);
|
||||
d->scriptSpec().AppendPrintf("scriptSpec write %d", i);
|
||||
d->currentWorkerURL().AppendPrintf("currentWorkerURL write %d", i);
|
||||
@ -245,12 +247,8 @@ TEST(ServiceWorkerRegistrar, TestWriteData)
|
||||
ASSERT_EQ(data[i].principal().type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
|
||||
const mozilla::ipc::ContentPrincipalInfo& cInfo = data[i].principal();
|
||||
|
||||
mozilla::OriginAttributes attrs(i, i % 2);
|
||||
nsAutoCString suffix, expectSuffix;
|
||||
attrs.CreateSuffix(expectSuffix);
|
||||
cInfo.attrs().CreateSuffix(suffix);
|
||||
|
||||
ASSERT_STREQ(expectSuffix.get(), suffix.get());
|
||||
ASSERT_EQ((uint32_t)i, cInfo.appId());
|
||||
ASSERT_EQ((uint32_t)(i % 2), (uint32_t)cInfo.isInBrowserElement());
|
||||
|
||||
test.AppendPrintf("spec write %d", i);
|
||||
ASSERT_STREQ(test.get(), cInfo.spec().get());
|
||||
|
@ -25,6 +25,7 @@ class OptionalLoadInfoArgs;
|
||||
}
|
||||
|
||||
using mozilla::BasePrincipal;
|
||||
using mozilla::OriginAttributes;
|
||||
using namespace mozilla::net;
|
||||
|
||||
namespace ipc {
|
||||
@ -76,10 +77,13 @@ PrincipalInfoToPrincipal(const PrincipalInfo& aPrincipalInfo,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (info.attrs().mAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
|
||||
if (info.appId() == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
|
||||
rv = secMan->GetSimpleCodebasePrincipal(uri, getter_AddRefs(principal));
|
||||
} else {
|
||||
principal = BasePrincipal::CreateCodebasePrincipal(uri, info.attrs());
|
||||
// TODO: Bug 1167100 - User nsIPrincipal.originAttribute in ContentPrincipalInfo
|
||||
OriginAttributes attrs(info.appId(), info.isInBrowserElement());
|
||||
attrs.mSignedPkg = NS_ConvertUTF8toUTF16(info.signedPkg());
|
||||
principal = BasePrincipal::CreateCodebasePrincipal(uri, attrs);
|
||||
rv = principal ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
@ -199,14 +203,33 @@ PrincipalToPrincipalInfo(nsIPrincipal* aPrincipal,
|
||||
return rv;
|
||||
}
|
||||
|
||||
const mozilla::OriginAttributes& attr =
|
||||
mozilla::BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
|
||||
nsCString signedPkg = NS_ConvertUTF16toUTF8(attr.mSignedPkg);
|
||||
|
||||
bool isUnknownAppId;
|
||||
rv = aPrincipal->GetUnknownAppId(&isUnknownAppId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
*aPrincipalInfo = ContentPrincipalInfo(BasePrincipal::Cast(aPrincipal)->OriginAttributesRef(),
|
||||
spec);
|
||||
uint32_t appId;
|
||||
if (isUnknownAppId) {
|
||||
appId = nsIScriptSecurityManager::UNKNOWN_APP_ID;
|
||||
} else {
|
||||
rv = aPrincipal->GetAppId(&appId);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
bool isInBrowserElement;
|
||||
rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
*aPrincipalInfo = ContentPrincipalInfo(appId, isInBrowserElement, spec, signedPkg);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
* 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/. */
|
||||
|
||||
using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
|
||||
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
@ -10,8 +9,10 @@ namespace ipc {
|
||||
|
||||
struct ContentPrincipalInfo
|
||||
{
|
||||
OriginAttributes attrs;
|
||||
uint32_t appId;
|
||||
bool isInBrowserElement;
|
||||
nsCString spec;
|
||||
nsCString signedPkg;
|
||||
};
|
||||
|
||||
struct SystemPrincipalInfo
|
||||
|
@ -57,6 +57,7 @@ JAVA_CLASSPATH += \
|
||||
$(ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB) \
|
||||
$(ANDROID_SUPPORT_V4_AAR_LIB) \
|
||||
$(ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB) \
|
||||
$(ANDROID_APPCOMPAT_V7_AAR_LIB) \
|
||||
$(ANDROID_RECYCLERVIEW_V7_AAR_LIB) \
|
||||
$(NULL)
|
||||
|
||||
@ -68,7 +69,6 @@ ifdef MOZ_NATIVE_DEVICES
|
||||
$(ANDROID_PLAY_SERVICES_CAST_AAR_LIB) \
|
||||
$(ANDROID_MEDIAROUTER_V7_AAR_LIB) \
|
||||
$(ANDROID_MEDIAROUTER_V7_AAR_INTERNAL_LIB) \
|
||||
$(ANDROID_APPCOMPAT_V7_AAR_LIB) \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
@ -79,6 +79,7 @@ JAVA_CLASSPATH := $(subst $(NULL) ,:,$(strip $(JAVA_CLASSPATH)))
|
||||
java_bundled_libs := \
|
||||
$(ANDROID_SUPPORT_V4_AAR_LIB) \
|
||||
$(ANDROID_SUPPORT_V4_AAR_INTERNAL_LIB) \
|
||||
$(ANDROID_APPCOMPAT_V7_AAR_LIB) \
|
||||
$(ANDROID_RECYCLERVIEW_V7_AAR_LIB) \
|
||||
$(NULL)
|
||||
|
||||
@ -88,7 +89,6 @@ ifdef MOZ_NATIVE_DEVICES
|
||||
$(ANDROID_PLAY_SERVICES_CAST_AAR_LIB) \
|
||||
$(ANDROID_MEDIAROUTER_V7_AAR_LIB) \
|
||||
$(ANDROID_MEDIAROUTER_V7_AAR_INTERNAL_LIB) \
|
||||
$(ANDROID_APPCOMPAT_V7_AAR_LIB) \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
|
@ -319,6 +319,10 @@ public class HistoryPanel extends HomeFragment {
|
||||
emptyHint.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (!RestrictedProfiles.isAllowed(getActivity(), Restriction.DISALLOW_PRIVATE_BROWSING)) {
|
||||
emptyHint.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
mList.setEmptyView(mEmptyView);
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,10 @@ if CONFIG['ANDROID_SUPPORT_V4_AAR']:
|
||||
ANDROID_EXTRA_PACKAGES += ['android.support.v4']
|
||||
ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_SUPPORT_V4_AAR_RES']]
|
||||
resjar.generated_sources += ['android/support/v4/R.java']
|
||||
if CONFIG['ANDROID_APPCOMPAT_V7_AAR']:
|
||||
ANDROID_EXTRA_PACKAGES += ['android.support.v7.appcompat']
|
||||
ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_APPCOMPAT_V7_AAR_RES']]
|
||||
resjar.generated_sources += ['android/support/v7/appcompat/R.java']
|
||||
if CONFIG['ANDROID_RECYCLERVIEW_V7_AAR']:
|
||||
ANDROID_EXTRA_PACKAGES += ['android.support.v7.recyclerview']
|
||||
ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_RECYCLERVIEW_V7_AAR_RES']]
|
||||
@ -647,7 +651,6 @@ gbjar.extra_jars += [
|
||||
]
|
||||
|
||||
moz_native_devices_jars = [
|
||||
CONFIG['ANDROID_APPCOMPAT_V7_AAR_LIB'],
|
||||
CONFIG['ANDROID_MEDIAROUTER_V7_AAR_LIB'],
|
||||
CONFIG['ANDROID_MEDIAROUTER_V7_AAR_INTERNAL_LIB'],
|
||||
CONFIG['ANDROID_PLAY_SERVICES_BASE_AAR_LIB'],
|
||||
@ -663,11 +666,6 @@ if CONFIG['MOZ_NATIVE_DEVICES']:
|
||||
gbjar.extra_jars += moz_native_devices_jars
|
||||
gbjar.sources += moz_native_devices_sources
|
||||
|
||||
if CONFIG['ANDROID_APPCOMPAT_V7_AAR']:
|
||||
ANDROID_EXTRA_PACKAGES += ['android.support.v7.appcompat']
|
||||
ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_APPCOMPAT_V7_AAR_RES']]
|
||||
resjar.generated_sources += ['android/support/v7/appcompat/R.java']
|
||||
|
||||
if CONFIG['ANDROID_MEDIAROUTER_V7_AAR']:
|
||||
ANDROID_EXTRA_PACKAGES += ['android.support.v7.mediarouter']
|
||||
ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_MEDIAROUTER_V7_AAR_RES']]
|
||||
@ -683,6 +681,7 @@ if CONFIG['MOZ_NATIVE_DEVICES']:
|
||||
ANDROID_EXTRA_RES_DIRS += ['%' + CONFIG['ANDROID_PLAY_SERVICES_CAST_AAR_RES']]
|
||||
resjar.generated_sources += ['com/google/android/gms/cast/R.java']
|
||||
|
||||
gbjar.extra_jars += [CONFIG['ANDROID_APPCOMPAT_V7_AAR_LIB']]
|
||||
gbjar.extra_jars += [CONFIG['ANDROID_RECYCLERVIEW_V7_AAR_LIB']]
|
||||
|
||||
gbjar.javac_flags += ['-Xlint:all,-deprecation,-fallthrough', '-J-Xmx512m', '-J-Xms128m']
|
||||
|
@ -70,12 +70,12 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:support-v4:22.2.0'
|
||||
compile 'com.android.support:recyclerview-v7:22.2.0'
|
||||
compile 'com.android.support:support-v4:22.2.1'
|
||||
compile 'com.android.support:appcompat-v7:22.2.1'
|
||||
compile 'com.android.support:recyclerview-v7:22.2.1'
|
||||
|
||||
if (mozconfig.substs.MOZ_NATIVE_DEVICES) {
|
||||
compile 'com.android.support:appcompat-v7:22.2.0'
|
||||
compile 'com.android.support:mediarouter-v7:22.2.0'
|
||||
compile 'com.android.support:mediarouter-v7:22.2.1'
|
||||
compile 'com.google.android.gms:play-services-base:7.8.0'
|
||||
compile 'com.google.android.gms:play-services-cast:7.8.0'
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ ADD bin /home/worker/bin
|
||||
ADD config /home/worker/.aws/config
|
||||
ADD socorro.token /home/worker/socorro.token
|
||||
|
||||
RUN yum install -y bc lzop
|
||||
RUN yum install -y bc lzop java-1.7.0-openjdk
|
||||
RUN pip install awscli
|
||||
RUN npm install -g bower gulp apm grunt-cli
|
||||
|
||||
|
@ -1 +1 @@
|
||||
0.0.20
|
||||
0.0.22
|
||||
|
@ -6,10 +6,10 @@ while getopts "t:i:k:s:" arg; do
|
||||
TAG=$OPTARG
|
||||
;;
|
||||
i)
|
||||
KEY_ID=$OPTARG
|
||||
AWS_ACCESS_KEY_ID=$OPTARG
|
||||
;;
|
||||
k)
|
||||
SECRET_KEY=$OPTARG
|
||||
AWS_SECRET_ACCESS_KEY=$OPTARG
|
||||
;;
|
||||
s)
|
||||
SOCORRO_TOKEN=$OPTARG
|
||||
@ -20,13 +20,13 @@ done
|
||||
pushd $(dirname $0)
|
||||
|
||||
test $TAG
|
||||
test $KEY_ID
|
||||
test $SECRET_KEY
|
||||
test $AWS_ACCESS_KEY_ID
|
||||
test $AWS_SECRET_ACCESS_KEY
|
||||
test $SOCORRO_TOKEN
|
||||
|
||||
(echo '[default]'
|
||||
echo "aws_access_key_id = $KEY_ID"
|
||||
echo "aws_secret_access_key = $SECRET_KEY") > config
|
||||
echo "aws_access_key_id = $AWS_ACCESS_KEY_ID"
|
||||
echo "aws_secret_access_key = $AWS_SECRET_ACCESS_KEY") > config
|
||||
|
||||
echo $SOCORRO_TOKEN > socorro.token
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import urllib
|
||||
import os
|
||||
|
||||
from marionette_driver import By, errors
|
||||
from marionette.marionette_test import MarionetteTestCase, skip_if_b2g
|
||||
@ -16,7 +17,18 @@ elements = inline("<p>foo</p> <p>bar</p>")
|
||||
|
||||
|
||||
class TestExecuteContent(MarionetteTestCase):
|
||||
def test_stack_trace(self):
|
||||
def test_execute_js_script_stack_trace(self):
|
||||
try:
|
||||
self.marionette.execute_js_script("""
|
||||
let a = 1;
|
||||
throwHere();
|
||||
""", filename="file.js")
|
||||
self.assertFalse(True)
|
||||
except errors.JavascriptException as inst:
|
||||
self.assertIn('throwHere is not defined', inst.msg)
|
||||
self.assertIn('@file.js:2', inst.stacktrace)
|
||||
|
||||
def test_execute_script_stack_trace(self):
|
||||
try:
|
||||
self.marionette.execute_script("""
|
||||
let a = 1;
|
||||
@ -24,7 +36,10 @@ class TestExecuteContent(MarionetteTestCase):
|
||||
""")
|
||||
self.assertFalse(True)
|
||||
except errors.JavascriptException as inst:
|
||||
self.assertTrue('return b' in inst.stacktrace)
|
||||
# By default execute_script pass the name of the python file
|
||||
self.assertIn(os.path.basename(__file__.replace(".pyc", ".py")), inst.stacktrace)
|
||||
self.assertIn('b is not defined', inst.msg)
|
||||
self.assertIn('return b', inst.stacktrace)
|
||||
|
||||
def test_execute_simple(self):
|
||||
self.assertEqual(1, self.marionette.execute_script("return 1;"))
|
||||
|
@ -816,6 +816,9 @@ GeckoDriver.prototype.applyArgumentsToSandbox = function(win, sb, args) {
|
||||
* True if the script is asynchronous.
|
||||
* @param {number} timeout
|
||||
* When to interrupt script in milliseconds.
|
||||
* @param {string} filename
|
||||
* Optional. URI or name of the file we are executing.
|
||||
* (Used to improve stack trace readability)
|
||||
*/
|
||||
GeckoDriver.prototype.executeScriptInSandbox = function(
|
||||
resp,
|
||||
@ -823,7 +826,8 @@ GeckoDriver.prototype.executeScriptInSandbox = function(
|
||||
script,
|
||||
directInject,
|
||||
async,
|
||||
timeout) {
|
||||
timeout,
|
||||
filename) {
|
||||
if (directInject && async && (timeout === null || timeout === 0)) {
|
||||
throw new TimeoutError("Please set a timeout");
|
||||
}
|
||||
@ -837,7 +841,7 @@ GeckoDriver.prototype.executeScriptInSandbox = function(
|
||||
script = data + script;
|
||||
}
|
||||
|
||||
let res = Cu.evalInSandbox(script, sandbox, "1.8", "dummy file", 0);
|
||||
let res = Cu.evalInSandbox(script, sandbox, "1.8", filename ? filename : "dummy file", 0);
|
||||
|
||||
if (directInject && !async &&
|
||||
(typeof res == "undefined" || typeof res.passed == "undefined")) {
|
||||
@ -945,7 +949,8 @@ GeckoDriver.prototype.execute = function(cmd, resp, directInject) {
|
||||
script,
|
||||
directInject,
|
||||
false /* async */,
|
||||
scriptTimeout);
|
||||
scriptTimeout,
|
||||
filename);
|
||||
} catch (e) {
|
||||
throw new JavaScriptError(e, "execute_script", filename, line, script);
|
||||
}
|
||||
@ -1180,7 +1185,8 @@ GeckoDriver.prototype.executeWithCallback = function(cmd, resp, directInject) {
|
||||
script,
|
||||
directInject,
|
||||
true /* async */,
|
||||
scriptTimeout);
|
||||
scriptTimeout,
|
||||
filename);
|
||||
} catch (e) {
|
||||
chromeAsyncError(e, "execute_async_script", filename, line, script);
|
||||
}
|
||||
|
@ -207,6 +207,7 @@ this.JavaScriptError = function(err, fnName, file, line, script) {
|
||||
"inline javascript, line " + jsLine + "\n" +
|
||||
"src: \"" + src + "\"";
|
||||
}
|
||||
trace += "\nStack:\n" + String(err.stack);
|
||||
}
|
||||
|
||||
WebDriverError.call(this, msg);
|
||||
|
@ -576,6 +576,7 @@ function executeScript(msg, directInject) {
|
||||
|
||||
asyncTestCommandId = msg.json.command_id;
|
||||
let script = msg.json.script;
|
||||
let filename = msg.json.filename;
|
||||
sandboxName = msg.json.sandboxName;
|
||||
|
||||
if (msg.json.newSandbox ||
|
||||
@ -602,7 +603,7 @@ function executeScript(msg, directInject) {
|
||||
stream.close();
|
||||
script = data + script;
|
||||
}
|
||||
let res = Cu.evalInSandbox(script, sandbox, "1.8", "dummy file" ,0);
|
||||
let res = Cu.evalInSandbox(script, sandbox, "1.8", filename ? filename : "dummy file" ,0);
|
||||
sendSyncMessage("Marionette:shareData",
|
||||
{log: elementManager.wrapValue(marionetteLogObj.getLogs())});
|
||||
marionetteLogObj.clearLogs();
|
||||
@ -633,7 +634,7 @@ function executeScript(msg, directInject) {
|
||||
stream.close();
|
||||
script = data + script;
|
||||
}
|
||||
let res = Cu.evalInSandbox(script, sandbox, "1.8", "dummy file", 0);
|
||||
let res = Cu.evalInSandbox(script, sandbox, "1.8", filename ? filename : "dummy file", 0);
|
||||
sendSyncMessage("Marionette:shareData",
|
||||
{log: elementManager.wrapValue(marionetteLogObj.getLogs())});
|
||||
marionetteLogObj.clearLogs();
|
||||
@ -724,6 +725,7 @@ function executeWithCallback(msg, useFinish) {
|
||||
}
|
||||
|
||||
let script = msg.json.script;
|
||||
let filename = msg.json.filename;
|
||||
asyncTestCommandId = msg.json.command_id;
|
||||
sandboxName = msg.json.sandboxName;
|
||||
|
||||
@ -788,7 +790,7 @@ function executeWithCallback(msg, useFinish) {
|
||||
stream.close();
|
||||
scriptSrc = data + scriptSrc;
|
||||
}
|
||||
Cu.evalInSandbox(scriptSrc, sandbox, "1.8", "dummy file", 0);
|
||||
Cu.evalInSandbox(scriptSrc, sandbox, "1.8", filename ? filename : "dummy file", 0);
|
||||
} catch (e) {
|
||||
let err = new JavaScriptError(
|
||||
e,
|
||||
|
@ -412,12 +412,14 @@ class Graph(object):
|
||||
build_parameters['build_slugid'],
|
||||
build_task['task']['extra']['locations']['build']
|
||||
)
|
||||
build_parameters['build_url'] = build_url
|
||||
|
||||
# img_url is only necessary for device builds
|
||||
img_url = ARTIFACT_URL.format(
|
||||
build_parameters['build_slugid'],
|
||||
build_task['task']['extra']['locations'].get('img', '')
|
||||
)
|
||||
build_parameters['img_url'] = img_url
|
||||
|
||||
define_task = DEFINE_TASK.format(build_task['task']['workerType'])
|
||||
|
||||
@ -459,8 +461,6 @@ class Graph(object):
|
||||
for test in build['dependents']:
|
||||
test = test['allowed_build_tasks'][build['task']]
|
||||
test_parameters = copy.copy(build_parameters)
|
||||
test_parameters['build_url'] = build_url
|
||||
test_parameters['img_url'] = img_url
|
||||
if tests_url:
|
||||
test_parameters['tests_url'] = tests_url
|
||||
if test_packages_url:
|
||||
|
113
testing/taskcluster/scripts/builder/build-simulator.sh
Executable file
113
testing/taskcluster/scripts/builder/build-simulator.sh
Executable file
@ -0,0 +1,113 @@
|
||||
#!/bin/bash
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 GECKO_DIR GAIA_DIR MULET_TARBALL_URL"
|
||||
}
|
||||
|
||||
if [ "$#" -ne 3 ]; then
|
||||
usage
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ -z $1 ] || [ ! -d $1 ]; then
|
||||
usage
|
||||
echo "First argument should be path to a gecko checkout"
|
||||
exit
|
||||
fi
|
||||
GECKO_DIR=$1
|
||||
|
||||
if [ -z $2 ] || [ ! -d $2 ]; then
|
||||
usage
|
||||
echo "Second argument should be path to a gaia checkout"
|
||||
exit
|
||||
fi
|
||||
GAIA_DIR=$2
|
||||
|
||||
url_re='^(http|https|ftp)://.*tar.bz2'
|
||||
if [ -z $3 ] || ! [[ $3 =~ $url_re ]] ; then
|
||||
usage
|
||||
echo "Third argument should be URL to mulet tarball"
|
||||
exit
|
||||
fi
|
||||
BUILD_URL=$3
|
||||
|
||||
set -ex
|
||||
|
||||
WORKDIR=simulator
|
||||
SIM_DIR=$GECKO_DIR/b2g/simulator
|
||||
|
||||
if [[ $BUILD_URL =~ 'linux-x86_64' ]] ; then
|
||||
PLATFORM=linux64
|
||||
elif [[ $BUILD_URL =~ 'linux' ]] ; then
|
||||
PLATFORM=linux
|
||||
elif [[ $BUILD_URL =~ 'win32' ]] ; then
|
||||
PLATFORM=win32
|
||||
elif [[ $BUILD_URL =~ 'mac64' ]] ; then
|
||||
PLATFORM=mac64
|
||||
fi
|
||||
if [ -z $PLATFORM ]; then
|
||||
echo "Unable to compute platform out of mulet URL: $BUILD_URL"
|
||||
exit
|
||||
fi
|
||||
echo "PLATFORM: $PLATFORM"
|
||||
|
||||
VERSION=$(sed -nE "s/MOZ_B2G_VERSION=([0-9]+\.[0-9]+).*prerelease/\1/p" $GECKO_DIR/b2g/confvars.sh)
|
||||
re='^[0-9]\.[0-9]$'
|
||||
if ! [[ $VERSION =~ $re ]] ; then
|
||||
echo "Invalid b2g version: $VERSION"
|
||||
exit
|
||||
fi
|
||||
echo "B2G VERSION: $VERSION"
|
||||
|
||||
mkdir -p $WORKDIR
|
||||
|
||||
if [ ! -f mulet.tar.bz2 ]; then
|
||||
wget -O mulet.tar.bz2 $BUILD_URL
|
||||
fi
|
||||
(cd $WORKDIR/ && tar jxvf ../mulet.tar.bz2)
|
||||
|
||||
# Not sure it is still useful with mulet...
|
||||
# remove useless stuff we don't want to ship in simulators
|
||||
rm -rf $WORKDIR/firefox/gaia $WORKDIR/firefox/B2G.app/Contents/MacOS/gaia $WORKDIR/firefox/B2G.app/Contents/Resources/gaia
|
||||
|
||||
# Build a gaia profile specific to the simulator in order to:
|
||||
# - disable the FTU
|
||||
# - set custom prefs to enable devtools debugger server
|
||||
# - set custom settings to disable lockscreen and screen timeout
|
||||
# - only ship production apps
|
||||
NOFTU=1 GAIA_APP_TARGET=production SETTINGS_PATH=$SIM_DIR/custom-settings.json make -j1 -C $GAIA_DIR profile
|
||||
mv $GAIA_DIR/profile $WORKDIR/
|
||||
cat $SIM_DIR/custom-prefs.js >> $WORKDIR/profile/user.js
|
||||
|
||||
APP_BUILDID=$(sed -n "s/BuildID=\([0-9]\{8\}\)/\1/p" $WORKDIR/firefox/application.ini)
|
||||
echo "BUILDID $APP_BUILDID -- VERSION $VERSION"
|
||||
|
||||
XPI_NAME=fxos-simulator-$VERSION.$APP_BUILDID-$PLATFORM.xpi
|
||||
ADDON_ID=fxos_$(echo $VERSION | sed "s/\./_/")_simulator@mozilla.org
|
||||
ADDON_VERSION=$VERSION.$APP_BUILDID
|
||||
ADDON_NAME="Firefox OS $VERSION Simulator"
|
||||
ADDON_DESCRIPTION="a Firefox OS $VERSION Simulator"
|
||||
|
||||
echo "ADDON_ID: $ADDON_ID"
|
||||
echo "ADDON_VERSION: $ADDON_VERSION"
|
||||
|
||||
FTP_ROOT_PATH=/pub/mozilla.org/labs/fxos-simulator
|
||||
UPDATE_PATH=$VERSION/$PLATFORM
|
||||
UPDATE_LINK=https://ftp.mozilla.org$FTP_ROOT_PATH/$UPDATE_PATH/$XPI_NAME
|
||||
UPDATE_URL=https://ftp.mozilla.org$FTP_ROOT_PATH/$UPDATE_PATH/update.rdf
|
||||
|
||||
# Replace all template string in install.rdf
|
||||
sed -e "s/@ADDON_VERSION@/$ADDON_VERSION/" \
|
||||
-e "s/@ADDON_ID@/$ADDON_ID/" \
|
||||
-e "s#@ADDON_UPDATE_URL@#$UPDATE_URL#" \
|
||||
-e "s/@ADDON_NAME@/$ADDON_NAME/" \
|
||||
-e "s/@ADDON_DESCRIPTION@/$ADDON_DESCRIPTION/" \
|
||||
$SIM_DIR/install.rdf.in > $WORKDIR/install.rdf
|
||||
|
||||
# copy all useful addon file
|
||||
cp $SIM_DIR/icon.png $WORKDIR/
|
||||
cp $SIM_DIR/icon64.png $WORKDIR/
|
||||
|
||||
mkdir -p artifacts
|
||||
(cd $WORKDIR && zip -r - .) > artifacts/$XPI_NAME
|
||||
|
@ -1,5 +1,9 @@
|
||||
#! /bin/bash -e
|
||||
|
||||
# Ensure all the scripts in this dir are on the path....
|
||||
DIRNAME=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
PATH=$DIRNAME:$PATH
|
||||
|
||||
gecko_dir=$1
|
||||
target=$2
|
||||
|
||||
|
@ -9,6 +9,7 @@ $inherits:
|
||||
flags:
|
||||
post-build:
|
||||
- upload-symbols
|
||||
- simulator
|
||||
|
||||
builds:
|
||||
linux64_gecko:
|
||||
@ -212,6 +213,10 @@ post-build:
|
||||
- tasks/builds/dbg_linux64.yml
|
||||
- tasks/builds/android_api_11.yml
|
||||
task: tasks/post-builds/upload_symbols.yml
|
||||
simulator:
|
||||
allowed_build_tasks:
|
||||
- tasks/builds/mulet_linux.yml
|
||||
task: tasks/post-builds/mulet_simulator.yml
|
||||
|
||||
tests:
|
||||
cppunit:
|
||||
|
58
testing/taskcluster/tasks/post-builds/mulet_simulator.yml
Normal file
58
testing/taskcluster/tasks/post-builds/mulet_simulator.yml
Normal file
@ -0,0 +1,58 @@
|
||||
# This tasks takes a mulet build, pull gaia and craft a xpi file for FxOS simulator addon
|
||||
---
|
||||
taskId: {{taskId}}
|
||||
|
||||
task:
|
||||
created: '{{now}}'
|
||||
deadline: '{{#from_now}}24 hours{{/from_now}}'
|
||||
metadata:
|
||||
source: https://hg.mozilla.org/mozilla-central/file/tip/b2g/simulator
|
||||
owner: apoirot@mozilla.com
|
||||
name: '[TC] FxOS Simulator'
|
||||
description: 'Firefox OS Simulator addon'
|
||||
tags:
|
||||
createdForUser: {{owner}}
|
||||
|
||||
workerType: b2gbuild
|
||||
provisionerId: aws-provisioner-v1
|
||||
schedulerId: task-graph-scheduler
|
||||
|
||||
scopes:
|
||||
- 'docker-worker:cache:tc-vcs'
|
||||
- 'docker-worker:image:{{#docker_image}}builder{{/docker_image}}'
|
||||
- 'queue:define-task:aws-provisioner-v1/build-c4-2xlarge'
|
||||
- 'queue:create-task:aws-provisioner-v1/build-c4-2xlarge'
|
||||
|
||||
payload:
|
||||
image: '{{#docker_image}}builder{{/docker_image}}'
|
||||
|
||||
maxRunTime: 600
|
||||
|
||||
command:
|
||||
- /bin/bash
|
||||
- -exc
|
||||
- >
|
||||
tc-vcs checkout ./gecko {{base_repository}} {{head_repository}} {{head_rev}} {{head_ref}} &&
|
||||
./gecko/testing/taskcluster/scripts/builder/pull-gaia.sh ./gecko ./gaia &&
|
||||
./gecko/testing/taskcluster/scripts/builder/build-simulator.sh ./gecko ./gaia {{build_url}}
|
||||
|
||||
artifacts:
|
||||
'public/build':
|
||||
type: directory
|
||||
path: '/home/worker/artifacts/'
|
||||
expires: '{{#from_now}}1 year{{/from_now}}'
|
||||
|
||||
env:
|
||||
GECKO_BASE_REPOSITORY: '{{base_repository}}'
|
||||
GECKO_HEAD_REPOSITORY: '{{head_repository}}'
|
||||
GECKO_HEAD_REV: '{{head_rev}}'
|
||||
GECKO_HEAD_REF: '{{head_ref}}'
|
||||
|
||||
extra:
|
||||
treeherderEnv:
|
||||
- production
|
||||
- staging
|
||||
treeherder:
|
||||
groupSymbol: tc-Sim
|
||||
groupName: Simulator build
|
||||
symbol: 'S'
|
@ -8319,6 +8319,13 @@
|
||||
"releaseChannelCollection": "opt-out",
|
||||
"description": "Number times room context is clicked to visit the attached URL"
|
||||
},
|
||||
"LOOP_ROOM_SESSION_WITHCHAT": {
|
||||
"alert_emails": ["firefox-dev@mozilla.org", "mdeboer@mozilla.com"],
|
||||
"expires_in_version": "45",
|
||||
"kind": "count",
|
||||
"releaseChannelCollection": "opt-out",
|
||||
"description": "Number of sessions where at least one chat message was exchanged"
|
||||
},
|
||||
"E10S_AUTOSTART": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "boolean",
|
||||
|
@ -1618,3 +1618,17 @@ rulersManual=Toggle the horizontal and vertical rulers for the current page
|
||||
# LOCALIZATION NOTE (rulersTooltip) A string displayed as the
|
||||
# tooltip of button in devtools toolbox which toggles the rulers.
|
||||
rulersTooltip=Toggle rulers for the page
|
||||
|
||||
# LOCALIZATION NOTE (measureDesc) A very short description of the
|
||||
# 'measure' command. See measureManual for a fuller description of what
|
||||
# it does. This string is designed to be shown in a menu alongside the
|
||||
# command name, which is why it should be as short as possible.
|
||||
measureDesc=Measure a portion of the page
|
||||
|
||||
# LOCALIZATION NOTE (measureManual) A fuller description of the 'measure'
|
||||
# command, displayed when the user asks for help on what it does.
|
||||
measureManual=Activate the measuring tool to measure an arbitrary area of the page
|
||||
|
||||
# LOCALIZATION NOTE (measureTooltip) A string displayed as the
|
||||
# tooltip of button in devtools toolbox which toggles the measuring tool.
|
||||
measureTooltip=Measure a portion of the page
|
||||
|
Loading…
Reference in New Issue
Block a user