Bug 1368383 part 1 - Always show Send Tab to Device in the context menu. r=markh

MozReview-Commit-ID: 1C2aqQIpKAJ

--HG--
extra : rebase_source : 2b982b0f47224fd3764eb35a3a63c28a7e43ce56
This commit is contained in:
Edouard Oger 2017-06-01 13:02:14 -04:00
Родитель 7d3f078161
Коммит fc6b74fa58
11 изменённых файлов: 271 добавлений и 181 удалений

Просмотреть файл

@ -1211,8 +1211,6 @@ pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
// user's tabs and bookmarks. Note this pref is also synced.
pref("services.sync.syncedTabs.showRemoteIcons", true);
pref("services.sync.sendTabToDevice.enabled", true);
// Developer edition preferences
#ifdef MOZ_DEV_EDITION
sticky_pref("lightweightThemes.selectedThemeID", "firefox-compact-dark@mozilla.org");

Просмотреть файл

@ -40,8 +40,8 @@ var gSync = {
);
},
get sendTabToDeviceEnabled() {
return Services.prefs.getBoolPref("services.sync.sendTabToDevice.enabled");
get syncReady() {
return Cc["@mozilla.org/weave/service;1"].getService().wrappedJSObject.ready;
},
get remoteClients() {
@ -356,44 +356,42 @@ var gSync = {
}
},
// "Send Tab to Device" menu item
updateTabContextMenu(aPopupMenu, aTargetTab) {
if (!this.sendTabToDeviceEnabled || !this.weaveService.ready) {
return;
}
const targetURI = aTargetTab.linkedBrowser.currentURI.spec;
const showSendTab = this.remoteClients.length > 0 && this.isSendableURI(targetURI);
["context_sendTabToDevice", "context_sendTabToDevice_separator"]
.forEach(id => { document.getElementById(id).hidden = !showSendTab });
const enabled = this.syncReady &&
this.remoteClients.length > 0 &&
this.isSendableURI(aTargetTab.linkedBrowser.currentURI.spec);
document.getElementById("context_sendTabToDevice").disabled = !enabled;
},
// "Send Page to Device" and "Send Link to Device" menu items
initPageContextMenu(contextMenu) {
if (!this.sendTabToDeviceEnabled || !this.weaveService.ready) {
return;
}
const remoteClientPresent = this.remoteClients.length > 0;
// showSendLink and showSendPage are mutually exclusive
let showSendLink = remoteClientPresent
&& (contextMenu.onSaveableLink || contextMenu.onPlainTextLink);
const showSendPage = !showSendLink && remoteClientPresent
const showSendLink = contextMenu.onSaveableLink || contextMenu.onPlainTextLink;
const showSendPage = !showSendLink
&& !(contextMenu.isContentSelected ||
contextMenu.onImage || contextMenu.onCanvas ||
contextMenu.onVideo || contextMenu.onAudio ||
contextMenu.onLink || contextMenu.onTextInput)
&& this.isSendableURI(contextMenu.browser.currentURI.spec);
if (showSendLink) {
// This isn't part of the condition above since we don't want to try and
// send the page if a link is clicked on or selected but is not sendable.
showSendLink = this.isSendableURI(contextMenu.linkURL);
}
contextMenu.onLink ||
(contextMenu.target &&
["input", "textarea"].includes(contextMenu.target.tagName.toLowerCase())));
["context-sendpagetodevice", "context-sep-sendpagetodevice"]
.forEach(id => contextMenu.showItem(id, showSendPage));
["context-sendlinktodevice", "context-sep-sendlinktodevice"]
.forEach(id => contextMenu.showItem(id, showSendLink));
if (!showSendLink && !showSendPage) {
return;
}
const targetURI = showSendLink ? contextMenu.linkURL :
contextMenu.browser.currentURI.spec;
const enabled = this.syncReady && this.remoteClients.length > 0 &&
this.isSendableURI(targetURI);
contextMenu.setItemAttr(showSendPage ? "context-sendpagetodevice" :
"context-sendlinktodevice",
"disabled", !enabled || null);
},
// Functions called by observers
@ -567,9 +565,3 @@ var gSync = {
Ci.nsISupportsWeakReference
])
};
XPCOMUtils.defineLazyGetter(gSync, "weaveService", function() {
return Components.classes["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
.wrappedJSObject;
});

Просмотреть файл

@ -102,9 +102,9 @@
hidden="true"
oncommand="gBrowser.openNonRemoteWindow(TabContextMenu.contextTab);"/>
#endif
<menuseparator id="context_sendTabToDevice_separator" hidden="true"/>
<menuseparator id="context_sendTabToDevice_separator"/>
<menu id="context_sendTabToDevice" label="&sendTabToDevice.label;"
accesskey="&sendTabToDevice.accesskey;" hidden="true">
accesskey="&sendTabToDevice.accesskey;">
<menupopup id="context_sendTabToDevicePopupMenu"
onpopupshowing="gSync.populateSendTabToDevicesMenu(event.target, TabContextMenu.contextTab.linkedBrowser.currentURI.spec, TabContextMenu.contextTab.linkedBrowser.contentTitle);"/>
</menu>

Просмотреть файл

@ -44,7 +44,9 @@ add_task(async function test_link() {
"---", null,
"context-savelink", true,
"context-copylink", true,
"context-searchselect", true]);
"context-searchselect", true,
"---", null,
"context-sendlinktodevice", false, [], null]);
});
add_task(async function test_video() {

Просмотреть файл

@ -38,7 +38,9 @@ add_task(async function test_xul_text_link_label() {
"context-savelink", true,
...(hasPocket ? ["context-savelinktopocket", true] : []),
"context-copylink", true,
"context-searchselect", true
"context-searchselect", true,
"---", null,
"context-sendlinktodevice", false, [], null,
]
);
@ -89,6 +91,8 @@ add_task(async function test_plaintext() {
"---", null,
"context-savepage", true,
...(hasPocket ? ["context-pocket", true] : []),
"---", null,
"context-sendpagetodevice", false, [], null,
"---", null,
"context-viewbgimage", false,
"context-selectall", true,
@ -115,7 +119,9 @@ add_task(async function test_link() {
"context-savelink", true,
...(hasPocket ? ["context-savelinktopocket", true] : []),
"context-copylink", true,
"context-searchselect", true
"context-searchselect", true,
"---", null,
"context-sendlinktodevice", false, [], null,
]
);
});
@ -262,6 +268,8 @@ add_task(async function test_iframe() {
"---", null,
"context-savepage", true,
...(hasPocket ? ["context-pocket", true] : []),
"---", null,
"context-sendpagetodevice", false, [], null,
"---", null,
"context-viewbgimage", false,
"context-selectall", true,
@ -566,6 +574,8 @@ add_task(async function test_pagemenu() {
"---", null,
"context-savepage", true,
...(hasPocket ? ["context-pocket", true] : []),
"---", null,
"context-sendpagetodevice", false, [], null,
"---", null,
"context-viewbgimage", false,
"context-selectall", true,
@ -598,6 +608,8 @@ add_task(async function test_dom_full_screen() {
"---", null,
"context-savepage", true,
...(hasPocket ? ["context-pocket", true] : []),
"---", null,
"context-sendpagetodevice", false, [], null,
"---", null,
"context-viewbgimage", false,
"context-selectall", true,
@ -645,6 +657,8 @@ add_task(async function test_pagemenu2() {
"---", null,
"context-savepage", true,
...(hasPocket ? ["context-pocket", true] : []),
"---", null,
"context-sendpagetodevice", false, [], null,
"---", null,
"context-viewbgimage", false,
"context-selectall", true,
@ -692,6 +706,8 @@ add_task(async function test_select_text_link() {
"context-selectall", true,
"---", null,
"context-searchselect", true,
"---", null,
"context-sendlinktodevice", false, [], null,
"context-viewpartialsource-selection", true
],
{
@ -732,7 +748,9 @@ add_task(async function test_imagelink() {
"context-saveimage", true,
"context-sendimage", true,
"context-setDesktopBackground", true,
"context-viewimageinfo", true
"context-viewimageinfo", true,
"---", null,
"context-sendlinktodevice", false, [], null,
]
);
});
@ -824,6 +842,8 @@ add_task(async function test_click_to_play_blocked_plugin() {
"---", null,
"context-savepage", true,
...(hasPocket ? ["context-pocket", true] : []),
"---", null,
"context-sendpagetodevice", false, [], null,
"---", null,
"context-viewbgimage", false,
"context-selectall", true,
@ -869,6 +889,8 @@ add_task(async function test_srcdoc() {
"---", null,
"context-savepage", true,
...(hasPocket ? ["context-pocket", true] : []),
"---", null,
"context-sendpagetodevice", false, [], null,
"---", null,
"context-viewbgimage", false,
"context-selectall", true,
@ -906,84 +928,6 @@ add_task(async function test_input_spell_false() {
*/
});
const remoteClientsFixture = [ { id: 1, name: "Foo"}, { id: 2, name: "Bar"} ];
add_task(async function test_plaintext_sendpagetodevice() {
if (!gSync.sendTabToDeviceEnabled) {
return;
}
await ensureSyncReady();
const oldGetter = setupRemoteClientsFixture(remoteClientsFixture);
let plainTextItemsWithSendPage =
["context-navigation", null,
["context-back", false,
"context-forward", false,
"context-reload", true,
"context-bookmarkpage", true], null,
"---", null,
"context-savepage", true,
...(hasPocket ? ["context-pocket", true] : []),
"---", null,
"context-sendpagetodevice", true,
["*Foo", true,
"*Bar", true,
"---", null,
"*All Devices", true], null,
"---", null,
"context-viewbgimage", false,
"context-selectall", true,
"---", null,
"context-viewsource", true,
"context-viewinfo", true
];
await test_contextmenu("#test-text", plainTextItemsWithSendPage, {
maybeScreenshotsPresent: true,
async onContextMenuShown() {
await openMenuItemSubmenu("context-sendpagetodevice");
}
});
restoreRemoteClients(oldGetter);
});
add_task(async function test_link_sendlinktodevice() {
if (!gSync.sendTabToDeviceEnabled) {
return;
}
await ensureSyncReady();
const oldGetter = setupRemoteClientsFixture(remoteClientsFixture);
await test_contextmenu("#test-link",
["context-openlinkintab", true,
...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
// We need a blank entry here because the containers submenu is
// dynamically generated with no ids.
...(hasContainers ? ["", null] : []),
"context-openlink", true,
"context-openlinkprivate", true,
"---", null,
"context-bookmarklink", true,
"context-savelink", true,
...(hasPocket ? ["context-savelinktopocket", true] : []),
"context-copylink", true,
"context-searchselect", true,
"---", null,
"context-sendlinktodevice", true,
["*Foo", true,
"*Bar", true,
"---", null,
"*All Devices", true], null,
],
{
async onContextMenuShown() {
await openMenuItemSubmenu("context-sendlinktodevice");
}
});
restoreRemoteClients(oldGetter);
});
add_task(async function test_svg_link() {
await test_contextmenu("#svg-with-link > a",
["context-openlinkintab", true,
@ -998,7 +942,9 @@ add_task(async function test_svg_link() {
"context-savelink", true,
...(hasPocket ? ["context-savelinktopocket", true] : []),
"context-copylink", true,
"context-searchselect", true
"context-searchselect", true,
"---", null,
"context-sendlinktodevice", false, [], null,
]
);
@ -1015,7 +961,9 @@ add_task(async function test_svg_link() {
"context-savelink", true,
...(hasPocket ? ["context-savelinktopocket", true] : []),
"context-copylink", true,
"context-searchselect", true
"context-searchselect", true,
"---", null,
"context-sendlinktodevice", false, [], null,
]
);
@ -1032,7 +980,9 @@ add_task(async function test_svg_link() {
"context-savelink", true,
...(hasPocket ? ["context-savelinktopocket", true] : []),
"context-copylink", true,
"context-searchselect", true
"context-searchselect", true,
"---", null,
"context-sendlinktodevice", false, [], null,
]
);
});
@ -1062,10 +1012,3 @@ async function selectText(selector) {
win.getSelection().addRange(div);
});
}
function ensureSyncReady() {
let service = Cc["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
.wrappedJSObject;
return service.whenLoaded();
}

Просмотреть файл

@ -16,28 +16,6 @@ add_task(async function test() {
is(document.getElementById("context_closeTab").disabled, false, "Close Tab is enabled");
is(document.getElementById("context_reloadAllTabs").disabled, false, "Reload All Tabs is enabled");
if (gSync.sendTabToDeviceEnabled) {
const origIsSendableURI = gSync.isSendableURI;
gSync.isSendableURI = () => true;
// Check the send tab to device menu item
await ensureSyncReady();
const oldGetter = setupRemoteClientsFixture(remoteClientsFixture);
await updateTabContextMenu(origTab, async function() {
await openMenuItemSubmenu("context_sendTabToDevice");
});
is(document.getElementById("context_sendTabToDevice").hidden, false, "Send tab to device is shown");
let targets = document.getElementById("context_sendTabToDevicePopupMenu").childNodes;
is(targets[0].getAttribute("label"), "Foo", "Foo target is present");
is(targets[1].getAttribute("label"), "Bar", "Bar target is present");
is(targets[3].getAttribute("label"), "All Devices", "All Devices target is present");
gSync.isSendableURI = () => false;
updateTabContextMenu(origTab);
is(document.getElementById("context_sendTabToDevice").hidden, true, "Send tab to device is hidden");
restoreRemoteClients(oldGetter);
gSync.isSendableURI = origIsSendableURI;
}
// Hide the original tab.
gBrowser.selectedTab = testTab;
gBrowser.showOnlyTheseTabs([testTab]);
@ -77,10 +55,3 @@ add_task(async function test() {
gBrowser.removeTab(pinned);
});
function ensureSyncReady() {
let service = Cc["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
.wrappedJSObject;
return service.whenLoaded();
}

Просмотреть файл

@ -831,26 +831,3 @@ function getCertExceptionDialog(aLocation) {
}
return undefined;
}
function setupRemoteClientsFixture(fixture) {
let oldRemoteClientsGetter =
Object.getOwnPropertyDescriptor(gSync, "remoteClients").get;
Object.defineProperty(gSync, "remoteClients", {
get() { return fixture; }
});
return oldRemoteClientsGetter;
}
function restoreRemoteClients(getter) {
Object.defineProperty(gSync, "remoteClients", {
get: getter
});
}
async function openMenuItemSubmenu(id) {
let menuPopup = document.getElementById(id).menupopup;
let menuPopupPromise = BrowserTestUtils.waitForEvent(menuPopup, "popupshown");
menuPopup.showPopup();
await menuPopupPromise;
}

Просмотреть файл

@ -1,4 +1,10 @@
[DEFAULT]
support-files =
head.js
[browser_sync.js]
[browser_contextmenu_sendtab.js]
[browser_contextmenu_sendpage.js]
[browser_fxa_web_channel.js]
support-files=
browser_fxa_web_channel.html

Просмотреть файл

@ -0,0 +1,92 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const remoteClientsFixture = [ { id: 1, name: "Foo"}, { id: 2, name: "Bar"} ];
const origRemoteClients = mockReturn(gSync, "remoteClients", remoteClientsFixture);
const origSyncReady = mockReturn(gSync, "syncReady", true);
const origIsSendableURI = mockReturn(gSync, "isSendableURI", true);
add_task(async function setup() {
await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
});
add_task(async function test_page_contextmenu() {
await updateContentContextMenu("#moztext", "context-sendpagetodevice");
is(document.getElementById("context-sendpagetodevice").hidden, false, "Send tab to device is shown");
is(document.getElementById("context-sendpagetodevice").disabled, false, "Send tab to device is enabled");
let devices = document.getElementById("context-sendpagetodevice-popup").childNodes;
is(devices[0].getAttribute("label"), "Foo", "Foo target is present");
is(devices[1].getAttribute("label"), "Bar", "Bar target is present");
is(devices[3].getAttribute("label"), "All Devices", "All Devices target is present");
});
add_task(async function test_page_contextmenu_notsendable() {
const isSendableURIMock = mockReturn(gSync, "isSendableURI", false);
await updateContentContextMenu("#moztext");
is(document.getElementById("context-sendpagetodevice").hidden, false, "Send tab to device is shown");
is(document.getElementById("context-sendpagetodevice").disabled, true, "Send tab to device is disabled");
isSendableURIMock.restore();
});
add_task(async function test_page_contextmenu_sendtab_no_remote_clients() {
let remoteClientsMock = mockReturn(gSync, "remoteClients", []);
await updateContentContextMenu("#moztext");
is(document.getElementById("context-sendpagetodevice").hidden, false, "Send tab to device is shown");
is(document.getElementById("context-sendpagetodevice").disabled, true, "Send tab to device is disabled");
remoteClientsMock.restore();
});
add_task(async function test_page_contextmenu_sync_not_ready() {
const syncReadyMock = mockReturn(gSync, "syncReady", false);
await updateContentContextMenu("#moztext");
is(document.getElementById("context-sendpagetodevice").hidden, false, "Send tab to device is shown");
is(document.getElementById("context-sendpagetodevice").disabled, true, "Send tab to device is disabled");
syncReadyMock.restore();
});
// We are not going to bother testing the states of context-sendlinktodevice since they use
// the exact same code.
// However, browser_contextmenu.js contains tests that verify the menu item is present.
add_task(async function cleanup() {
gBrowser.removeCurrentTab();
origSyncReady.restore();
origRemoteClients.restore();
origIsSendableURI.restore();
});
async function updateContentContextMenu(selector, openSubmenuId = null) {
let contextMenu = document.getElementById("contentAreaContextMenu");
is(contextMenu.state, "closed", "checking if popup is closed");
let awaitPopupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
await BrowserTestUtils.synthesizeMouse(selector, 0, 0, {
type: "contextmenu",
button: 2,
shiftkey: false,
centered: true
},
gBrowser.selectedBrowser);
await awaitPopupShown;
if (openSubmenuId) {
let menuPopup = document.getElementById(openSubmenuId).menupopup;
let menuPopupPromise = BrowserTestUtils.waitForEvent(menuPopup, "popupshown");
menuPopup.showPopup();
await menuPopupPromise;
}
let awaitPopupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
contextMenu.hidePopup();
await awaitPopupHidden;
}

Просмотреть файл

@ -0,0 +1,85 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const chrome_base = "chrome://mochitests/content/browser/browser/base/content/test/general/";
Services.scriptloader.loadSubScript(chrome_base + "head.js", this);
/* import-globals-from ../general/head.js */
const remoteClientsFixture = [ { id: 1, name: "Foo"}, { id: 2, name: "Bar"} ];
const origRemoteClients = mockReturn(gSync, "remoteClients", remoteClientsFixture);
const origSyncReady = mockReturn(gSync, "syncReady", true);
const origIsSendableURI = mockReturn(gSync, "isSendableURI", true);
let [testTab] = gBrowser.visibleTabs;
add_task(async function setup() {
is(gBrowser.visibleTabs.length, 1, "there is one visible tab");
});
add_task(async function test_tab_contextmenu() {
await updateTabContextMenu(testTab, openSendTabTargetsSubmenu);
is(document.getElementById("context_sendTabToDevice").hidden, false, "Send tab to device is shown");
is(document.getElementById("context_sendTabToDevice").disabled, false, "Send tab to device is enabled");
let devices = document.getElementById("context_sendTabToDevicePopupMenu").childNodes;
is(devices[0].getAttribute("label"), "Foo", "Foo target is present");
is(devices[1].getAttribute("label"), "Bar", "Bar target is present");
is(devices[3].getAttribute("label"), "All Devices", "All Devices target is present");
});
add_task(async function test_tab_contextmenu_only_one_remote_device() {
const remoteClientsMock = mockReturn(gSync, "remoteClients", [{ id: 1, name: "Foo"}]);
await updateTabContextMenu(testTab, openSendTabTargetsSubmenu);
is(document.getElementById("context_sendTabToDevice").hidden, false, "Send tab to device is shown");
is(document.getElementById("context_sendTabToDevice").disabled, false, "Send tab to device is enabled");
let devices = document.getElementById("context_sendTabToDevicePopupMenu").childNodes;
is(devices.length, 1, "There should not be any separator or All Devices item");
is(devices[0].getAttribute("label"), "Foo", "Foo target is present");
remoteClientsMock.restore();
});
add_task(async function test_tab_contextmenu_not_sendable() {
const isSendableURIMock = mockReturn(gSync, "isSendableURI", false);
updateTabContextMenu(testTab);
is(document.getElementById("context_sendTabToDevice").hidden, false, "Send tab to device is shown");
is(document.getElementById("context_sendTabToDevice").disabled, true, "Send tab to device is disabled");
isSendableURIMock.restore();
});
add_task(async function test_tab_contextmenu_no_remote_clients() {
let remoteClientsMock = mockReturn(gSync, "remoteClients", []);
updateTabContextMenu(testTab);
is(document.getElementById("context_sendTabToDevice").hidden, false, "Send tab to device is shown");
is(document.getElementById("context_sendTabToDevice").disabled, true, "Send tab to device is disabled");
remoteClientsMock.restore();
});
add_task(async function test_tab_contextmenu_sync_not_ready() {
const syncReadyMock = mockReturn(gSync, "syncReady", false);
updateTabContextMenu(testTab);
is(document.getElementById("context_sendTabToDevice").hidden, false, "Send tab to device is shown");
is(document.getElementById("context_sendTabToDevice").disabled, true, "Send tab to device is disabled");
syncReadyMock.restore();
});
add_task(async function cleanup() {
origSyncReady.restore();
origRemoteClients.restore();
origIsSendableURI.restore();
});
async function openSendTabTargetsSubmenu() {
let menuPopup = document.getElementById("context_sendTabToDevice").menupopup;
let menuPopupPromise = BrowserTestUtils.waitForEvent(menuPopup, "popupshown");
menuPopup.showPopup();
await menuPopupPromise;
}

Просмотреть файл

@ -0,0 +1,24 @@
// Mocks a getter or a function
// This is basically sinon.js (our in-tree version doesn't do getters :/) (see bug 1369855)
function mockReturn(obj, symbol, fixture) {
let getter = Object.getOwnPropertyDescriptor(obj, symbol).get;
if (getter) {
Object.defineProperty(obj, symbol, {
get() { return fixture; }
});
return {
restore() {
Object.defineProperty(obj, symbol, {
get: getter
});
}
}
}
let func = obj[symbol];
obj[symbol] = () => fixture;
return {
restore() {
obj[symbol] = func;
}
}
}