Bug 1269277 - Add Manage Device button in Synced Tabs menu/sidebar. r=markh

MozReview-Commit-ID: 2BMgLeI0DNF

--HG--
extra : rebase_source : d8e1bb4f52723071d395db3ac3c896096ca6df2a
This commit is contained in:
Edouard Oger 2017-03-10 20:14:51 -05:00
Родитель e4c43ec7d3
Коммит a014194719
10 изменённых файлов: 98 добавлений и 75 удалений

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

@ -323,6 +323,13 @@ var gFxAccounts = {
this.openAccountsPage("reauth", { entrypoint: entryPoint });
},
async openDevicesManagementPage(entryPoint) {
let url = await fxAccounts.promiseAccountsManageDevicesURI(entryPoint);
switchToTabHavingURI(url, true, {
replaceQueryString: true
});
},
sendTabToDevice(url, clientId, title) {
Weave.Service.clientsEngine.sendURIToClientForDisplay(url, clientId, title);
},

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

@ -478,6 +478,10 @@
<menuitem label="&syncedTabs.context.openAllInTabs.label;"
accesskey="&syncedTabs.context.openAllInTabs.accesskey;"
id="syncedTabsOpenAllInTabs"/>
<menuitem label="&syncedTabs.context.managedevices.label;"
accesskey="&syncedTabs.context.managedevices.accesskey;"
id="syncedTabsManageDevices"
oncommand="gFxAccounts.openDevicesManagementPage('syncedtabs-sidebar');"/>
<menuitem label="&syncSyncNowItem.label;"
accesskey="&syncSyncNowItem.accesskey;"
id="syncedTabsRefresh"/>

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

@ -200,13 +200,13 @@ var gTests = [
yield signOut();
},
*run() {
const expected_url = "https://example.com/?is_force_auth";
const expected_url = "https://example.com/force_auth";
setPref("identity.fxaccounts.remote.force_auth.uri", expected_url);
yield setSignedInUser();
let [, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=reauth");
// The current user will be appended to the url
let expected = expected_url + "&email=foo%40example.com";
let expected = expected_url + "?uid=1234%40lcip.org&email=foo%40example.com";
is(url, expected, "action=reauth got the expected URL");
},
},

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

@ -114,6 +114,10 @@
class="subviewbutton"
observes="viewTabsSidebar"
label="&appMenuRemoteTabs.sidebar.label;"/>
<toolbarbutton id="PanelUI-remotetabs-view-managedevices"
class="subviewbutton"
label="&appMenuRemoteTabs.managedevices.label;"
oncommand="gFxAccounts.openDevicesManagementPage('syncedtabs-menupanel');"/>
<toolbarbutton id="PanelUI-remotetabs-syncnow"
observes="sync-status"
class="subviewbutton"

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

@ -148,6 +148,8 @@ function BrowserGlue() {
return sanitizerScope.Sanitizer;
});
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts", "resource://gre/modules/FxAccounts.jsm");
this._init();
}
@ -305,6 +307,10 @@ BrowserGlue.prototype = {
this.ensurePlacesDefaultQueriesInitialized().then(() => {
Services.obs.notifyObservers(null, "test-smart-bookmarks-done", null);
});
} else if (data == "mock-fxaccounts") {
Object.defineProperty(this, "fxAccounts", {
value: subject.wrappedJSObject
});
}
break;
case "initial-migration-will-import-default-bookmarks":
@ -2302,21 +2308,21 @@ BrowserGlue.prototype = {
let body = accountsBundle.formatStringFromName("deviceConnectedBody" +
(deviceName ? "" : ".noDeviceName"),
[deviceName], 1);
let url = Services.urlFormatter.formatURLPref("identity.fxaccounts.settings.devices.uri");
function clickCallback(subject, topic, data) {
let clickCallback = async (subject, topic, data) => {
if (topic != "alertclickcallback")
return;
let url = await this.fxAccounts.promiseAccountsManageDevicesURI("device-connected-notification");
let win = RecentWindow.getMostRecentBrowserWindow({private: false});
if (!win) {
Services.appShell.hiddenDOMWindow.open(url);
} else {
win.gBrowser.addTab(url);
}
}
};
try {
AlertsService.showAlertNotification(null, title, body, true, url, clickCallback);
AlertsService.showAlertNotification(null, title, body, true, null, clickCallback);
} catch (ex) {
Cu.reportError("Error notifying of a new Sync device: " + ex);
}

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

@ -524,7 +524,8 @@ TabListView.prototype = {
while (el) {
let show = false;
if (showTabOptions) {
if (el.getAttribute("id") != "syncedTabsOpenAllInTabs") {
if (el.getAttribute("id") != "syncedTabsOpenAllInTabs" &&
el.getAttribute("id") != "syncedTabsManageDevices") {
show = true;
}
} else if (el.getAttribute("id") == "syncedTabsOpenAllInTabs") {
@ -532,6 +533,8 @@ TabListView.prototype = {
show = tabs.length > 0;
} else if (el.getAttribute("id") == "syncedTabsRefresh") {
show = true;
} else if (el.getAttribute("id") == "syncedTabsManageDevices") {
show = true;
}
el.hidden = !show;

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

@ -310,6 +310,7 @@ add_task(function* testSyncedTabsSidebarContextMenu() {
["menuitem#syncedTabsCopySelected", { hidden: false }],
["menuseparator", { hidden: false }],
["menuitem#syncedTabsOpenAllInTabs", { hidden: true }],
["menuitem#syncedTabsManageDevices", { hidden: true }],
["menuitem#syncedTabsRefresh", { hidden: false }],
];
yield* testContextMenu(syncedTabsDeckComponent,
@ -317,7 +318,7 @@ add_task(function* testSyncedTabsSidebarContextMenu() {
"#tab-7cqCr77ptzX3-0",
tabMenuItems);
info("Right-clicking a client should show the Open All in Tabs action");
info("Right-clicking a client should show the Open All in Tabs and Manage devices actions");
let sidebarMenuItems = [
["menuitem#syncedTabsOpenSelected", { hidden: true }],
["menuitem#syncedTabsOpenSelectedInTab", { hidden: true }],
@ -328,6 +329,7 @@ add_task(function* testSyncedTabsSidebarContextMenu() {
["menuitem#syncedTabsCopySelected", { hidden: true }],
["menuseparator", { hidden: true }],
["menuitem#syncedTabsOpenAllInTabs", { hidden: false }],
["menuitem#syncedTabsManageDevices", { hidden: false }],
["menuitem#syncedTabsRefresh", { hidden: false }],
];
yield* testContextMenu(syncedTabsDeckComponent,
@ -346,6 +348,7 @@ add_task(function* testSyncedTabsSidebarContextMenu() {
["menuitem#syncedTabsCopySelected", { hidden: true }],
["menuseparator", { hidden: true }],
["menuitem#syncedTabsOpenAllInTabs", { hidden: true }],
["menuitem#syncedTabsManageDevices", { hidden: false }],
["menuitem#syncedTabsRefresh", { hidden: false }],
];
yield* testContextMenu(syncedTabsDeckComponent,

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

@ -377,6 +377,7 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY appMenuRemoteTabs.openprefs.label "Sync Preferences">
<!ENTITY appMenuRemoteTabs.notsignedin.label "Sign in to view a list of tabs from your other devices.">
<!ENTITY appMenuRemoteTabs.signin.label "Sign in to Sync">
<!ENTITY appMenuRemoteTabs.managedevices.label "Manage Devices…">
<!ENTITY appMenuRemoteTabs.sidebar.label "View Synced Tabs Sidebar">
<!ENTITY customizeMenu.addToToolbar.label "Add to Toolbar">
@ -785,6 +786,8 @@ you can use these alternative items. Otherwise, their values should be empty. -
<!ENTITY syncedTabs.context.openAllInTabs.label "Open All in Tabs">
<!ENTITY syncedTabs.context.openAllInTabs.accesskey "O">
<!ENTITY syncedTabs.context.managedevices.label "Manage Devices…">
<!ENTITY syncedTabs.context.managedevices.accesskey "D">
<!ENTITY syncBrand.shortName.label "Sync">

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

@ -7,6 +7,8 @@ this.EXPORTED_SYMBOLS = ["fxAccounts", "FxAccounts"];
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.importGlobalProperties(["URL"]);
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://services-common/utils.js");
@ -59,6 +61,7 @@ var publicProperties = [
"promiseAccountsChangeProfileURI",
"promiseAccountsForceSigninURI",
"promiseAccountsManageURI",
"promiseAccountsManageDevicesURI",
"promiseAccountsSignUpURI",
"promiseAccountsSignInURI",
"removeCachedOAuthToken",
@ -1263,80 +1266,66 @@ FxAccountsInternal.prototype = {
return FxAccountsConfig.promiseAccountsSignInURI();
},
// Returns a promise that resolves with the URL to use to force a re-signin
// of the current account.
promiseAccountsForceSigninURI: Task.async(function *() {
yield FxAccountsConfig.ensureConfigured();
let url = Services.urlFormatter.formatURLPref("identity.fxaccounts.remote.force_auth.uri");
if (this.requiresHttps() && !/^https:/.test(url)) { // Comment to un-break emacs js-mode highlighting
/**
* Pull an URL defined in the user preferences, add the current UID and email
* to the query string, add entrypoint and extra params to the query string if
* requested.
* @param {string} prefName The preference name from where to pull the URL to format.
* @param {string} [entrypoint] "entrypoint" searchParam value.
* @param {Object.<string, string>} [extraParams] Additionnal searchParam key and values.
* @returns {Promise.<string>} A promise that resolves to the formatted URL
*/
async _formatPrefURL(prefName, entrypoint, extraParams) {
let url = new URL(Services.urlFormatter.formatURLPref(prefName));
if (this.requiresHttps() && url.protocol != "https:") {
throw new Error("Firefox Accounts server must use HTTPS");
}
let currentState = this.currentAccountState;
// but we need to append the email address onto a query string.
return this.getSignedInUser().then(accountData => {
if (!accountData) {
return null;
let accountData = await this.getSignedInUser();
if (!accountData) {
return Promise.resolve(null);
}
url.searchParams.append("uid", accountData.uid);
url.searchParams.append("email", accountData.email);
if (entrypoint) {
url.searchParams.append("entrypoint", entrypoint);
}
if (extraParams) {
for (let [k, v] of Object.entries(extraParams)) {
url.searchParams.append(k, v);
}
let newQueryPortion = url.indexOf("?") == -1 ? "?" : "&";
newQueryPortion += "email=" + encodeURIComponent(accountData.email);
return url + newQueryPortion;
}).then(result => currentState.resolve(result));
}),
}
return this.currentAccountState.resolve(url.href);
},
// Returns a promise that resolves with the URL to use to force a re-signin
// of the current account.
async promiseAccountsForceSigninURI() {
await FxAccountsConfig.ensureConfigured();
return this._formatPrefURL("identity.fxaccounts.remote.force_auth.uri");
},
// Returns a promise that resolves with the URL to use to change
// the current account's profile image.
// if settingToEdit is set, the profile page should hightlight that setting
// for the user to edit.
promiseAccountsChangeProfileURI(entrypoint, settingToEdit = null) {
let url = Services.urlFormatter.formatURLPref("identity.fxaccounts.settings.uri");
async promiseAccountsChangeProfileURI(entrypoint, settingToEdit = null) {
let extraParams;
if (settingToEdit) {
url += (url.indexOf("?") == -1 ? "?" : "&") +
"setting=" + encodeURIComponent(settingToEdit);
extraParams = { setting: settingToEdit };
}
if (this.requiresHttps() && !/^https:/.test(url)) { // Comment to un-break emacs js-mode highlighting
throw new Error("Firefox Accounts server must use HTTPS");
}
let currentState = this.currentAccountState;
// but we need to append the email address onto a query string.
return this.getSignedInUser().then(accountData => {
if (!accountData) {
return null;
}
let newQueryPortion = url.indexOf("?") == -1 ? "?" : "&";
newQueryPortion += "email=" + encodeURIComponent(accountData.email);
newQueryPortion += "&uid=" + encodeURIComponent(accountData.uid);
if (entrypoint) {
newQueryPortion += "&entrypoint=" + encodeURIComponent(entrypoint);
}
return url + newQueryPortion;
}).then(result => currentState.resolve(result));
return this._formatPrefURL("identity.fxaccounts.settings.uri", entrypoint, extraParams);
},
// Returns a promise that resolves with the URL to use to manage the current
// user's FxA acct.
promiseAccountsManageURI(entrypoint) {
let url = Services.urlFormatter.formatURLPref("identity.fxaccounts.settings.uri");
if (this.requiresHttps() && !/^https:/.test(url)) { // Comment to un-break emacs js-mode highlighting
throw new Error("Firefox Accounts server must use HTTPS");
}
let currentState = this.currentAccountState;
// but we need to append the uid and email address onto a query string
// (if the server has no matching uid it will offer to sign in with the
// email address)
return this.getSignedInUser().then(accountData => {
if (!accountData) {
return null;
}
let newQueryPortion = url.indexOf("?") == -1 ? "?" : "&";
newQueryPortion += "uid=" + encodeURIComponent(accountData.uid) +
"&email=" + encodeURIComponent(accountData.email);
if (entrypoint) {
newQueryPortion += "&entrypoint=" + encodeURIComponent(entrypoint);
}
return url + newQueryPortion;
}).then(result => currentState.resolve(result));
async promiseAccountsManageURI(entrypoint) {
return this._formatPrefURL("identity.fxaccounts.settings.uri", entrypoint);
},
// Returns a promise that resolves with the URL to use to manage the devices in
// the current user's FxA acct.
async promiseAccountsManageDevicesURI(entrypoint) {
return this._formatPrefURL("identity.fxaccounts.settings.devices.uri", entrypoint);
},
/**

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

@ -5,14 +5,22 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
const { MockRegistrar } =
Cu.import("resource://testing-common/MockRegistrar.jsm", {});
let accountsBundle = Services.strings.createBundle(
const gBrowserGlue = Cc["@mozilla.org/browser/browserglue;1"]
.getService(Ci.nsIObserver);
const accountsBundle = Services.strings.createBundle(
"chrome://browser/locale/accounts.properties"
);
const DEVICES_URL = "http://localhost/devices";
let expectedBody;
add_task(async function setup() {
let fxAccounts = {
promiseAccountsManageDevicesURI() {
return Promise.resolve(DEVICES_URL);
}
};
gBrowserGlue.observe({wrappedJSObject: fxAccounts}, "browser-glue-test", "mock-fxaccounts");
const alertsService = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAlertsService, Ci.nsISupports]),
showAlertNotification: (image, title, text, clickable, cookie, clickCallback) => {
@ -33,8 +41,6 @@ async function testDeviceConnected(deviceName) {
gBrowser.selectedBrowser.loadURI("about:robots");
await waitForDocLoadComplete();
Preferences.set("identity.fxaccounts.settings.devices.uri", "http://localhost/devices");
let waitForTabPromise = BrowserTestUtils.waitForNewTab(gBrowser);
Services.obs.notifyObservers(null, "fxaccounts:device_connected", deviceName);
@ -42,9 +48,7 @@ async function testDeviceConnected(deviceName) {
let tab = await waitForTabPromise;
Assert.ok("Tab successfully opened");
let expectedURI = Preferences.get("identity.fxaccounts.settings.devices.uri",
"prefundefined");
Assert.equal(tab.linkedBrowser.currentURI.spec, expectedURI);
Assert.equal(tab.linkedBrowser.currentURI.spec, DEVICES_URL);
await BrowserTestUtils.removeTab(tab);
}