Bug 1434706 - Add identity.fxaccounts.enabled pref to disable Sync and FxA. r=markh

MozReview-Commit-ID: 4UuppJyOi5s

--HG--
extra : rebase_source : 706d6a9a1187e9b666074d0dd68d03eda3fb448b
This commit is contained in:
Edouard Oger 2018-02-15 11:24:44 +08:00
parent c65e310932
commit 25db2f7ed6
18 changed files with 401 additions and 312 deletions

View File

@ -1416,6 +1416,10 @@ pref("browser.uiCustomization.debug", false);
// CustomizableUI state of the browser's user interface
pref("browser.uiCustomization.state", "");
// If set to false, FxAccounts and Sync will be unavailable.
// A restart is mandatory after flipping that preference.
pref("identity.fxaccounts.enabled", true);
// The remote FxA root content URL. Must use HTTPS.
pref("identity.fxaccounts.remote.root", "https://accounts.firefox.com/");

View File

@ -263,11 +263,13 @@
label="&savePageCmd.label;"
accesskey="&savePageCmd.accesskey2;"
oncommand="gContextMenu.savePageAs();"/>
<menuseparator id="context-sep-sendpagetodevice" hidden="true"/>
<menuseparator id="context-sep-sendpagetodevice" class="sync-ui-item"
hidden="true"/>
<menu id="context-sendpagetodevice"
label="&sendPageToDevice.label;"
accesskey="&sendPageToDevice.accesskey;"
hidden="true">
class="sync-ui-item"
label="&sendPageToDevice.label;"
accesskey="&sendPageToDevice.accesskey;"
hidden="true">
<menupopup id="context-sendpagetodevice-popup"
onpopupshowing="(() => { let browser = gBrowser || getPanelBrowser(); gSync.populateSendTabToDevicesMenu(event.target, browser.currentURI.spec, browser.contentTitle); })()"/>
</menu>
@ -310,11 +312,13 @@
oncommand="AddKeywordForSearchField();"/>
<menuitem id="context-searchselect"
oncommand="BrowserSearch.loadSearchFromContext(this.searchTerms);"/>
<menuseparator id="context-sep-sendlinktodevice" hidden="true"/>
<menuseparator id="context-sep-sendlinktodevice" class="sync-ui-item"
hidden="true"/>
<menu id="context-sendlinktodevice"
label="&sendLinkToDevice.label;"
accesskey="&sendLinkToDevice.accesskey;"
hidden="true">
class="sync-ui-item"
label="&sendLinkToDevice.label;"
accesskey="&sendLinkToDevice.accesskey;"
hidden="true">
<menupopup id="context-sendlinktodevice-popup"
onpopupshowing="gSync.populateSendTabToDevicesMenu(event.target, gContextMenu.linkURL, gContextMenu.linkTextStr);"/>
</menu>

View File

@ -486,21 +486,25 @@
<!-- only one of sync-setup, sync-unverifieditem, sync-syncnowitem or sync-reauthitem will be showing at once -->
<menuitem id="sync-setup"
class="sync-ui-item"
label="&syncSignIn.label;"
accesskey="&syncSignIn.accesskey;"
observes="sync-setup-state"
oncommand="gSync.openPrefs('menubar')"/>
<menuitem id="sync-unverifieditem"
class="sync-ui-item"
label="&syncSignIn.label;"
accesskey="&syncSignIn.accesskey;"
observes="sync-unverified-state"
oncommand="gSync.openPrefs('menubar')"/>
<menuitem id="sync-syncnowitem"
class="sync-ui-item"
label="&syncSyncNowItem.label;"
accesskey="&syncSyncNowItem.accesskey;"
observes="sync-syncnow-state"
oncommand="gSync.doSync(event);"/>
<menuitem id="sync-reauthitem"
class="sync-ui-item"
label="&syncReAuthItem.label;"
accesskey="&syncReAuthItem.accesskey;"
observes="sync-reauth-state"

View File

@ -95,6 +95,8 @@ var gSync = {
});
XPCOMUtils.defineLazyPreferenceGetter(this, "PRODUCT_INFO_BASE_URL",
"app.productInfo.baseURL");
XPCOMUtils.defineLazyPreferenceGetter(this, "SYNC_ENABLED",
"identity.fxaccounts.enabled");
},
_maybeUpdateUIState() {
@ -114,6 +116,13 @@ var gSync = {
return;
}
this._definePrefGetters();
if (!this.SYNC_ENABLED) {
this.onSyncDisabled();
return;
}
// initial label for the sync buttons.
let statusBroadcaster = document.getElementById("sync-status");
if (!statusBroadcaster) {
@ -132,7 +141,6 @@ var gSync = {
}
this._generateNodeGetters();
this._definePrefGetters();
this._maybeUpdateUIState();
@ -470,6 +478,10 @@ var gSync = {
// "Send Tab to Device" menu item
updateTabContextMenu(aPopupMenu, aTargetTab) {
if (!this.SYNC_ENABLED) {
// These items are hidden in onSyncDisabled(). No need to do anything.
return;
}
const enabled = !this.syncConfiguredAndLoading &&
this.isSendableURI(aTargetTab.linkedBrowser.currentURI.spec);
@ -478,6 +490,10 @@ var gSync = {
// "Send Page to Device" and "Send Link to Device" menu items
updateContentContextMenu(contextMenu) {
if (!this.SYNC_ENABLED) {
// These items are hidden by default. No need to do anything.
return;
}
// showSendLink and showSendPage are mutually exclusive
const showSendLink = contextMenu.onSaveableLink || contextMenu.onPlainTextLink;
const showSendPage = !showSendLink
@ -651,6 +667,13 @@ var gSync = {
}
},
onSyncDisabled() {
const toHide = [...document.querySelectorAll(".sync-ui-item")];
for (const item of toHide) {
item.hidden = true;
}
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIObserver,
Ci.nsISupportsWeakReference

View File

@ -97,8 +97,9 @@
accesskey="&moveToNewWindow.accesskey;"
tbattr="tabbrowser-multiple"
oncommand="gBrowser.replaceTabWithWindow(TabContextMenu.contextTab);"/>
<menuseparator id="context_sendTabToDevice_separator"/>
<menuseparator id="context_sendTabToDevice_separator" class="sync-ui-item"/>
<menu id="context_sendTabToDevice" label="&sendTabToDevice.label;"
class="sync-ui-item"
accesskey="&sendTabToDevice.accesskey;">
<menupopup id="context_sendTabToDevicePopupMenu"
onpopupshowing="gSync.populateSendTabToDevicesMenu(event.target, TabContextMenu.contextTab.linkedBrowser.currentURI.spec, TabContextMenu.contextTab.linkedBrowser.contentTitle);"/>

View File

@ -7,6 +7,8 @@ const remoteClientsFixture = [ { id: 1, name: "Foo"}, { id: 2, name: "Bar"} ];
add_task(async function setup() {
await promiseSyncReady();
// gSync.init() is called in a requestIdleCallback. Force its initialization.
gSync.init();
await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
});
@ -28,7 +30,7 @@ add_task(async function test_page_contextmenu() {
sandbox.restore();
});
add_task(async function test_page_contextmenu_sendtab_no_remote_clients() {
add_task(async function test_page_contextmenu_no_remote_clients() {
const sandbox = setupSendTabMocks({ syncReady: true, clientsSynced: true, remoteClients: [],
state: UIState.STATUS_SIGNED_IN, isSendableURI: true });
@ -46,7 +48,7 @@ add_task(async function test_page_contextmenu_sendtab_no_remote_clients() {
sandbox.restore();
});
add_task(async function test_page_contextmenu_sendtab_one_remote_client() {
add_task(async function test_page_contextmenu_one_remote_client() {
const sandbox = setupSendTabMocks({ syncReady: true, clientsSynced: true, remoteClients: [{ id: 1, name: "Foo"}],
state: UIState.STATUS_SIGNED_IN, isSendableURI: true });
@ -175,6 +177,17 @@ add_task(async function test_page_contextmenu_login_failed() {
isSendableURI.restore();
});
add_task(async function test_page_contextmenu_fxa_disabled() {
const getter = sinon.stub(gSync, "SYNC_ENABLED").get(() => false);
gSync.onSyncDisabled(); // Would have been called on gSync initialization if SYNC_ENABLED had been set.
await openContentContextMenu("#moztext");
is(document.getElementById("context-sendpagetodevice").hidden, true, "Send tab to device is hidden");
is(document.getElementById("context-sep-sendpagetodevice").hidden, true, "Separator is also hidden");
await hideContentContextMenu();
getter.restore();
[...document.querySelectorAll(".sync-ui-item")].forEach(e => e.hidden = false);
});
// We are not going to bother testing the visibility of context-sendlinktodevice
// since it uses the exact same code.
// However, browser_contextmenu.js contains tests that verify its presence.

View File

@ -24,6 +24,8 @@ function updateTabContextMenu(tab) {
add_task(async function setup() {
await promiseSyncReady();
// gSync.init() is called in a requestIdleCallback. Force its initialization.
gSync.init();
is(gBrowser.visibleTabs.length, 1, "there is one visible tab");
});
@ -95,3 +97,16 @@ add_task(async function test_tab_contextmenu_sync_not_ready_other_state() {
sandbox.restore();
});
add_task(async function test_tab_contextmenu_fxa_disabled() {
const getter = sinon.stub(gSync, "SYNC_ENABLED").get(() => false);
// Simulate onSyncDisabled() being called on window open.
gSync.onSyncDisabled();
updateTabContextMenu(testTab);
is(document.getElementById("context_sendTabToDevice").hidden, true, "Send tab to device is hidden");
is(document.getElementById("context_sendTabToDevice_separator").hidden, true, "Separator is also hidden");
getter.restore();
[...document.querySelectorAll(".sync-ui-item")].forEach(e => e.hidden = false);
});

View File

@ -238,252 +238,6 @@ const CustomizableWidgets = [
panelview.appendChild(body);
panelview.appendChild(footer);
}
}, {
id: "sync-button",
label: "remotetabs-panelmenu.label",
tooltiptext: "remotetabs-panelmenu.tooltiptext2",
type: "view",
viewId: "PanelUI-remotetabs",
deckIndices: {
DECKINDEX_TABS: 0,
DECKINDEX_TABSDISABLED: 1,
DECKINDEX_FETCHING: 2,
DECKINDEX_NOCLIENTS: 3,
},
TABS_PER_PAGE: 25,
NEXT_PAGE_MIN_TABS: 5, // Minimum number of tabs displayed when we click "Show All"
onCreated(aNode) {
this._initialize(aNode);
},
_initialize(aNode) {
if (this._initialized) {
return;
}
// Add an observer to the button so we get the animation during sync.
// (Note the observer sets many attributes, including label and
// tooltiptext, but we only want the 'syncstatus' attribute for the
// animation)
let doc = aNode.ownerDocument;
let obnode = doc.createElementNS(kNSXUL, "observes");
obnode.setAttribute("element", "sync-status");
obnode.setAttribute("attribute", "syncstatus");
aNode.appendChild(obnode);
this._initialized = true;
},
onViewShowing(aEvent) {
this._initialize(aEvent.target);
let doc = aEvent.target.ownerDocument;
this._tabsList = doc.getElementById("PanelUI-remotetabs-tabslist");
Services.obs.addObserver(this, SyncedTabs.TOPIC_TABS_CHANGED);
if (SyncedTabs.isConfiguredToSyncTabs) {
if (SyncedTabs.hasSyncedThisSession) {
this.setDeckIndex(this.deckIndices.DECKINDEX_TABS);
} else {
// Sync hasn't synced tabs yet, so show the "fetching" panel.
this.setDeckIndex(this.deckIndices.DECKINDEX_FETCHING);
}
// force a background sync.
SyncedTabs.syncTabs().catch(ex => {
Cu.reportError(ex);
});
// show the current list - it will be updated by our observer.
this._showTabs();
} else {
// not configured to sync tabs, so no point updating the list.
this.setDeckIndex(this.deckIndices.DECKINDEX_TABSDISABLED);
}
},
onViewHiding() {
Services.obs.removeObserver(this, SyncedTabs.TOPIC_TABS_CHANGED);
this._tabsList = null;
},
_tabsList: null,
observe(subject, topic, data) {
switch (topic) {
case SyncedTabs.TOPIC_TABS_CHANGED:
this._showTabs();
break;
default:
break;
}
},
setDeckIndex(index) {
let deck = this._tabsList.ownerDocument.getElementById("PanelUI-remotetabs-deck");
// We call setAttribute instead of relying on the XBL property setter due
// to things going wrong when we try and set the index before the XBL
// binding has been created - see bug 1241851 for the gory details.
deck.setAttribute("selectedIndex", index);
},
_showTabsPromise: Promise.resolve(),
// Update the tab list after any existing in-flight updates are complete.
_showTabs(paginationInfo) {
this._showTabsPromise = this._showTabsPromise.then(() => {
return this.__showTabs(paginationInfo);
}, e => {
Cu.reportError(e);
});
},
// Return a new promise to update the tab list.
__showTabs(paginationInfo) {
if (!this._tabsList) {
// Closed between the previous `this._showTabsPromise`
// resolving and now.
return undefined;
}
let doc = this._tabsList.ownerDocument;
return SyncedTabs.getTabClients().then(clients => {
// The view may have been hidden while the promise was resolving.
if (!this._tabsList) {
return;
}
if (clients.length === 0 && !SyncedTabs.hasSyncedThisSession) {
// the "fetching tabs" deck is being shown - let's leave it there.
// When that first sync completes we'll be notified and update.
return;
}
if (clients.length === 0) {
this.setDeckIndex(this.deckIndices.DECKINDEX_NOCLIENTS);
return;
}
this.setDeckIndex(this.deckIndices.DECKINDEX_TABS);
this._clearTabList();
SyncedTabs.sortTabClientsByLastUsed(clients);
let fragment = doc.createDocumentFragment();
for (let client of clients) {
// add a menu separator for all clients other than the first.
if (fragment.lastChild) {
let separator = doc.createElementNS(kNSXUL, "menuseparator");
fragment.appendChild(separator);
}
if (paginationInfo && paginationInfo.clientId == client.id) {
this._appendClient(client, fragment, paginationInfo.maxTabs);
} else {
this._appendClient(client, fragment);
}
}
this._tabsList.appendChild(fragment);
PanelView.forNode(this._tabsList.closest("panelview"))
.descriptionHeightWorkaround();
}).catch(err => {
Cu.reportError(err);
}).then(() => {
// an observer for tests.
Services.obs.notifyObservers(null, "synced-tabs-menu:test:tabs-updated");
});
},
_clearTabList() {
let list = this._tabsList;
while (list.lastChild) {
list.lastChild.remove();
}
},
_showNoClientMessage() {
this._appendMessageLabel("notabslabel");
},
_appendMessageLabel(messageAttr, appendTo = null) {
if (!appendTo) {
appendTo = this._tabsList;
}
let message = this._tabsList.getAttribute(messageAttr);
let doc = this._tabsList.ownerDocument;
let messageLabel = doc.createElementNS(kNSXUL, "label");
messageLabel.textContent = message;
appendTo.appendChild(messageLabel);
return messageLabel;
},
_appendClient(client, attachFragment, maxTabs = this.TABS_PER_PAGE) {
let doc = attachFragment.ownerDocument;
// Create the element for the remote client.
let clientItem = doc.createElementNS(kNSXUL, "label");
clientItem.setAttribute("itemtype", "client");
let window = doc.defaultView;
clientItem.setAttribute("tooltiptext",
window.gSync.formatLastSyncDate(new Date(client.lastModified)));
clientItem.textContent = client.name;
attachFragment.appendChild(clientItem);
if (client.tabs.length == 0) {
let label = this._appendMessageLabel("notabsforclientlabel", attachFragment);
label.setAttribute("class", "PanelUI-remotetabs-notabsforclient-label");
} else {
// If this page will display all tabs, show no additional buttons.
// If the next page will display all the remaining tabs, show a "Show All" button
// Otherwise, show a "Shore More" button
let hasNextPage = client.tabs.length > maxTabs;
let nextPageIsLastPage = hasNextPage && maxTabs + this.TABS_PER_PAGE >= client.tabs.length;
if (nextPageIsLastPage) {
// When the user clicks "Show All", try to have at least NEXT_PAGE_MIN_TABS more tabs
// to display in order to avoid user frustration
maxTabs = Math.min(client.tabs.length - this.NEXT_PAGE_MIN_TABS, maxTabs);
}
if (hasNextPage) {
client.tabs = client.tabs.slice(0, maxTabs);
}
for (let tab of client.tabs) {
let tabEnt = this._createTabElement(doc, tab);
attachFragment.appendChild(tabEnt);
}
if (hasNextPage) {
let showAllEnt = this._createShowMoreElement(doc, client.id,
nextPageIsLastPage ?
Infinity :
maxTabs + this.TABS_PER_PAGE);
attachFragment.appendChild(showAllEnt);
}
}
},
_createTabElement(doc, tabInfo) {
let item = doc.createElementNS(kNSXUL, "toolbarbutton");
let tooltipText = (tabInfo.title ? tabInfo.title + "\n" : "") + tabInfo.url;
item.setAttribute("itemtype", "tab");
item.setAttribute("class", "subviewbutton");
item.setAttribute("targetURI", tabInfo.url);
item.setAttribute("label", tabInfo.title != "" ? tabInfo.title : tabInfo.url);
item.setAttribute("image", tabInfo.icon);
item.setAttribute("tooltiptext", tooltipText);
// We need to use "click" instead of "command" here so openUILink
// respects different buttons (eg, to open in a new tab).
item.addEventListener("click", e => {
doc.defaultView.openUILink(tabInfo.url, e);
if (doc.defaultView.whereToOpenLink(e) != "current") {
e.preventDefault();
e.stopPropagation();
} else {
CustomizableUI.hidePanelForNode(item);
}
BrowserUITelemetry.countSyncedTabEvent("open", "toolbarbutton-subview");
});
return item;
},
_createShowMoreElement(doc, clientId, showCount) {
let labelAttr, tooltipAttr;
if (showCount === Infinity) {
labelAttr = "showAllLabel";
tooltipAttr = "showAllTooltipText";
} else {
labelAttr = "showMoreLabel";
tooltipAttr = "showMoreTooltipText";
}
let showAllItem = doc.createElementNS(kNSXUL, "toolbarbutton");
showAllItem.setAttribute("itemtype", "showmorebutton");
showAllItem.setAttribute("class", "subviewbutton");
let label = this._tabsList.getAttribute(labelAttr);
showAllItem.setAttribute("label", label);
let tooltipText = this._tabsList.getAttribute(tooltipAttr);
showAllItem.setAttribute("tooltiptext", tooltipText);
showAllItem.addEventListener("click", e => {
e.preventDefault();
e.stopPropagation();
this._showTabs({ clientId, maxTabs: showCount });
});
return showAllItem;
}
}, {
id: "privatebrowsing-button",
shortcutId: "key_privatebrowsing",
@ -874,6 +628,256 @@ const CustomizableWidgets = [
}
}];
if (Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
CustomizableWidgets.push({
id: "sync-button",
label: "remotetabs-panelmenu.label",
tooltiptext: "remotetabs-panelmenu.tooltiptext2",
type: "view",
viewId: "PanelUI-remotetabs",
deckIndices: {
DECKINDEX_TABS: 0,
DECKINDEX_TABSDISABLED: 1,
DECKINDEX_FETCHING: 2,
DECKINDEX_NOCLIENTS: 3,
},
TABS_PER_PAGE: 25,
NEXT_PAGE_MIN_TABS: 5, // Minimum number of tabs displayed when we click "Show All"
onCreated(aNode) {
this._initialize(aNode);
},
_initialize(aNode) {
if (this._initialized) {
return;
}
// Add an observer to the button so we get the animation during sync.
// (Note the observer sets many attributes, including label and
// tooltiptext, but we only want the 'syncstatus' attribute for the
// animation)
let doc = aNode.ownerDocument;
let obnode = doc.createElementNS(kNSXUL, "observes");
obnode.setAttribute("element", "sync-status");
obnode.setAttribute("attribute", "syncstatus");
aNode.appendChild(obnode);
this._initialized = true;
},
onViewShowing(aEvent) {
this._initialize(aEvent.target);
let doc = aEvent.target.ownerDocument;
this._tabsList = doc.getElementById("PanelUI-remotetabs-tabslist");
Services.obs.addObserver(this, SyncedTabs.TOPIC_TABS_CHANGED);
if (SyncedTabs.isConfiguredToSyncTabs) {
if (SyncedTabs.hasSyncedThisSession) {
this.setDeckIndex(this.deckIndices.DECKINDEX_TABS);
} else {
// Sync hasn't synced tabs yet, so show the "fetching" panel.
this.setDeckIndex(this.deckIndices.DECKINDEX_FETCHING);
}
// force a background sync.
SyncedTabs.syncTabs().catch(ex => {
Cu.reportError(ex);
});
// show the current list - it will be updated by our observer.
this._showTabs();
} else {
// not configured to sync tabs, so no point updating the list.
this.setDeckIndex(this.deckIndices.DECKINDEX_TABSDISABLED);
}
},
onViewHiding() {
Services.obs.removeObserver(this, SyncedTabs.TOPIC_TABS_CHANGED);
this._tabsList = null;
},
_tabsList: null,
observe(subject, topic, data) {
switch (topic) {
case SyncedTabs.TOPIC_TABS_CHANGED:
this._showTabs();
break;
default:
break;
}
},
setDeckIndex(index) {
let deck = this._tabsList.ownerDocument.getElementById("PanelUI-remotetabs-deck");
// We call setAttribute instead of relying on the XBL property setter due
// to things going wrong when we try and set the index before the XBL
// binding has been created - see bug 1241851 for the gory details.
deck.setAttribute("selectedIndex", index);
},
_showTabsPromise: Promise.resolve(),
// Update the tab list after any existing in-flight updates are complete.
_showTabs(paginationInfo) {
this._showTabsPromise = this._showTabsPromise.then(() => {
return this.__showTabs(paginationInfo);
}, e => {
Cu.reportError(e);
});
},
// Return a new promise to update the tab list.
__showTabs(paginationInfo) {
if (!this._tabsList) {
// Closed between the previous `this._showTabsPromise`
// resolving and now.
return undefined;
}
let doc = this._tabsList.ownerDocument;
return SyncedTabs.getTabClients().then(clients => {
// The view may have been hidden while the promise was resolving.
if (!this._tabsList) {
return;
}
if (clients.length === 0 && !SyncedTabs.hasSyncedThisSession) {
// the "fetching tabs" deck is being shown - let's leave it there.
// When that first sync completes we'll be notified and update.
return;
}
if (clients.length === 0) {
this.setDeckIndex(this.deckIndices.DECKINDEX_NOCLIENTS);
return;
}
this.setDeckIndex(this.deckIndices.DECKINDEX_TABS);
this._clearTabList();
SyncedTabs.sortTabClientsByLastUsed(clients);
let fragment = doc.createDocumentFragment();
for (let client of clients) {
// add a menu separator for all clients other than the first.
if (fragment.lastChild) {
let separator = doc.createElementNS(kNSXUL, "menuseparator");
fragment.appendChild(separator);
}
if (paginationInfo && paginationInfo.clientId == client.id) {
this._appendClient(client, fragment, paginationInfo.maxTabs);
} else {
this._appendClient(client, fragment);
}
}
this._tabsList.appendChild(fragment);
PanelView.forNode(this._tabsList.closest("panelview"))
.descriptionHeightWorkaround();
}).catch(err => {
Cu.reportError(err);
}).then(() => {
// an observer for tests.
Services.obs.notifyObservers(null, "synced-tabs-menu:test:tabs-updated");
});
},
_clearTabList() {
let list = this._tabsList;
while (list.lastChild) {
list.lastChild.remove();
}
},
_showNoClientMessage() {
this._appendMessageLabel("notabslabel");
},
_appendMessageLabel(messageAttr, appendTo = null) {
if (!appendTo) {
appendTo = this._tabsList;
}
let message = this._tabsList.getAttribute(messageAttr);
let doc = this._tabsList.ownerDocument;
let messageLabel = doc.createElementNS(kNSXUL, "label");
messageLabel.textContent = message;
appendTo.appendChild(messageLabel);
return messageLabel;
},
_appendClient(client, attachFragment, maxTabs = this.TABS_PER_PAGE) {
let doc = attachFragment.ownerDocument;
// Create the element for the remote client.
let clientItem = doc.createElementNS(kNSXUL, "label");
clientItem.setAttribute("itemtype", "client");
let window = doc.defaultView;
clientItem.setAttribute("tooltiptext",
window.gSync.formatLastSyncDate(new Date(client.lastModified)));
clientItem.textContent = client.name;
attachFragment.appendChild(clientItem);
if (client.tabs.length == 0) {
let label = this._appendMessageLabel("notabsforclientlabel", attachFragment);
label.setAttribute("class", "PanelUI-remotetabs-notabsforclient-label");
} else {
// If this page will display all tabs, show no additional buttons.
// If the next page will display all the remaining tabs, show a "Show All" button
// Otherwise, show a "Shore More" button
let hasNextPage = client.tabs.length > maxTabs;
let nextPageIsLastPage = hasNextPage && maxTabs + this.TABS_PER_PAGE >= client.tabs.length;
if (nextPageIsLastPage) {
// When the user clicks "Show All", try to have at least NEXT_PAGE_MIN_TABS more tabs
// to display in order to avoid user frustration
maxTabs = Math.min(client.tabs.length - this.NEXT_PAGE_MIN_TABS, maxTabs);
}
if (hasNextPage) {
client.tabs = client.tabs.slice(0, maxTabs);
}
for (let tab of client.tabs) {
let tabEnt = this._createTabElement(doc, tab);
attachFragment.appendChild(tabEnt);
}
if (hasNextPage) {
let showAllEnt = this._createShowMoreElement(doc, client.id,
nextPageIsLastPage ?
Infinity :
maxTabs + this.TABS_PER_PAGE);
attachFragment.appendChild(showAllEnt);
}
}
},
_createTabElement(doc, tabInfo) {
let item = doc.createElementNS(kNSXUL, "toolbarbutton");
let tooltipText = (tabInfo.title ? tabInfo.title + "\n" : "") + tabInfo.url;
item.setAttribute("itemtype", "tab");
item.setAttribute("class", "subviewbutton");
item.setAttribute("targetURI", tabInfo.url);
item.setAttribute("label", tabInfo.title != "" ? tabInfo.title : tabInfo.url);
item.setAttribute("image", tabInfo.icon);
item.setAttribute("tooltiptext", tooltipText);
// We need to use "click" instead of "command" here so openUILink
// respects different buttons (eg, to open in a new tab).
item.addEventListener("click", e => {
doc.defaultView.openUILink(tabInfo.url, e);
if (doc.defaultView.whereToOpenLink(e) != "current") {
e.preventDefault();
e.stopPropagation();
} else {
CustomizableUI.hidePanelForNode(item);
}
BrowserUITelemetry.countSyncedTabEvent("open", "toolbarbutton-subview");
});
return item;
},
_createShowMoreElement(doc, clientId, showCount) {
let labelAttr, tooltipAttr;
if (showCount === Infinity) {
labelAttr = "showAllLabel";
tooltipAttr = "showAllTooltipText";
} else {
labelAttr = "showMoreLabel";
tooltipAttr = "showMoreTooltipText";
}
let showAllItem = doc.createElementNS(kNSXUL, "toolbarbutton");
showAllItem.setAttribute("itemtype", "showmorebutton");
showAllItem.setAttribute("class", "subviewbutton");
let label = this._tabsList.getAttribute(labelAttr);
showAllItem.setAttribute("label", label);
let tooltipText = this._tabsList.getAttribute(tooltipAttr);
showAllItem.setAttribute("tooltiptext", tooltipText);
showAllItem.addEventListener("click", e => {
e.preventDefault();
e.stopPropagation();
this._showTabs({ clientId, maxTabs: showCount });
});
return showAllItem;
}
});
}
let preferencesButton = {
id: "preferences-button",
onCommand(aEvent) {

View File

@ -177,7 +177,7 @@
oncommand="PanelUI._onBannerItemSelected(event)"
wrap="true"
hidden="true"/>
<toolbaritem id="appMenu-fxa-container" class="toolbaritem-combined-buttons">
<toolbaritem id="appMenu-fxa-container" class="toolbaritem-combined-buttons sync-ui-item">
<hbox id="appMenu-fxa-status"
flex="1"
defaultlabel="&fxaSignIn.label;"
@ -201,7 +201,7 @@
<observes element="sync-status" attribute="tooltiptext"/>
</toolbarbutton>
</toolbaritem>
<toolbarseparator/>
<toolbarseparator class="sync-ui-item"/>
<toolbarbutton id="appMenu-new-window-button"
class="subviewbutton subviewbutton-iconic"
label="&newNavigatorCmd.label;"
@ -637,7 +637,7 @@
closemenu="none"
oncommand="DownloadsSubview.show(this);"/>
<toolbarbutton id="appMenu-library-remotetabs-button"
class="subviewbutton subviewbutton-iconic subviewbutton-nav"
class="subviewbutton subviewbutton-iconic subviewbutton-nav sync-ui-item"
label="&appMenuRemoteTabs.label;"
closemenu="none"
oncommand="PanelUI.showSubView('PanelUI-remotetabs', this)"/>

View File

@ -467,6 +467,11 @@ var gMainPane = {
OS.File.stat(ignoreSeparateProfile).then(() => separateProfileModeCheckbox.checked = false,
() => separateProfileModeCheckbox.checked = true);
if (!Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
document.getElementById("sync-dev-edition-root").hidden = true;
return;
}
fxAccounts.getSignedInUser().then(data => {
document.getElementById("getStarted").selectedIndex = data ? 1 : 0;
})

View File

@ -33,7 +33,7 @@
<vbox id="separateProfileBox">
<checkbox id="separateProfileMode"
label="&separateProfileMode.label;"/>
<hbox align="center" class="indent">
<hbox id="sync-dev-edition-root" align="center" class="indent">
<label id="useFirefoxSync">&useFirefoxSync.label;</label>
<deck id="getStarted">
<label class="text-link">&getStarted.notloggedin.label;</label>

View File

@ -55,7 +55,10 @@ function init_all() {
register_module("paneSearch", gSearchPane);
register_module("panePrivacy", gPrivacyPane);
register_module("paneContainers", gContainersPane);
register_module("paneSync", gSyncPane);
if (Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
document.getElementById("category-sync").hidden = false;
register_module("paneSync", gSyncPane);
}
register_module("paneSearchResults", gSearchResultsPane);
gSearchResultsPane.init();
gMainPane.preInit();

View File

@ -172,6 +172,7 @@
<richlistitem id="category-sync"
class="category"
hidden="true"
value="paneSync"
helpTopic="prefs-weave"
data-l10n-id="category-sync"

View File

@ -98,6 +98,9 @@ let syncTourChecker = {
},
init() {
if (!Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
return;
}
// Check if we've already logged in at startup.
const state = UIState.get();
if (state.status != UIState.STATUS_NOT_CONFIGURED) {

View File

@ -479,7 +479,12 @@ class Onboarding {
_getTourIDList() {
let tours = Services.prefs.getStringPref(`browser.onboarding.${this._tourType}tour`, "");
return tours.split(",").filter(tourId => tourId !== "").map(tourId => tourId.trim());
return tours.split(",").filter(tourId => {
if (tourId === "sync" && !Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
return false;
}
return tourId !== "";
}).map(tourId => tourId.trim());
}
_initPrefObserver() {

View File

@ -1218,8 +1218,11 @@ var gBuiltInActions = [
onCommand(event, buttonNode) {
browserPageActions(buttonNode).emailLink.onCommand(event, buttonNode);
},
},
}
];
if (Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
gBuiltInActions.push(
// send to device
{
id: "sendToDevice",
@ -1247,8 +1250,8 @@ var gBuiltInActions = [
.onShowingSubview(panelViewNode);
},
},
}
];
});
}
/**

View File

@ -36,6 +36,9 @@ ChromeUtils.defineModuleGetter(this, "FxAccountsProfile",
ChromeUtils.defineModuleGetter(this, "Utils",
"resource://services-sync/util.js");
XPCOMUtils.defineLazyPreferenceGetter(this, "FXA_ENABLED",
"identity.fxaccounts.enabled", true);
// All properties exposed by the public FxAccounts API.
var publicProperties = [
"accountStatus",
@ -521,20 +524,23 @@ FxAccountsInternal.prototype = {
* }
* or null if no user is signed in.
*/
getSignedInUser: function getSignedInUser() {
async getSignedInUser() {
let currentState = this.currentAccountState;
return currentState.getUserAccountData().then(data => {
if (!data) {
return null;
}
if (!this.isUserEmailVerified(data)) {
// If the email is not verified, start polling for verification,
// but return null right away. We don't want to return a promise
// that might not be fulfilled for a long time.
this.startVerifiedCheck(data);
}
return data;
}).then(result => currentState.resolve(result));
const data = await currentState.getUserAccountData();
if (!data) {
return currentState.resolve(null);
}
if (!FXA_ENABLED) {
await this.signOut();
return currentState.resolve(null);
}
if (!this.isUserEmailVerified(data)) {
// If the email is not verified, start polling for verification,
// but return null right away. We don't want to return a promise
// that might not be fulfilled for a long time.
this.startVerifiedCheck(data);
}
return currentState.resolve(data);
},
/**
@ -558,37 +564,32 @@ FxAccountsInternal.prototype = {
* The promise resolves to null when the data is saved
* successfully and is rejected on error.
*/
setSignedInUser: function setSignedInUser(credentials) {
async setSignedInUser(credentials) {
if (!FXA_ENABLED) {
throw new Error("Cannot call setSignedInUser when FxA is disabled.");
}
log.debug("setSignedInUser - aborting any existing flows");
return this.getSignedInUser().then(signedInUser => {
if (signedInUser) {
return this.deleteDeviceRegistration(signedInUser.sessionToken, signedInUser.deviceId);
}
return null;
}).then(() =>
this.abortExistingFlow()
).then(() => {
let currentAccountState = this.currentAccountState = this.newAccountState(
Cu.cloneInto(credentials, {}) // Pass a clone of the credentials object.
);
// This promise waits for storage, but not for verification.
// We're telling the caller that this is durable now (although is that
// really something we should commit to? Why not let the write happen in
// the background? Already does for updateAccountData ;)
return currentAccountState.promiseInitialized.then(() => {
// Starting point for polling if new user
if (!this.isUserEmailVerified(credentials)) {
this.startVerifiedCheck(credentials);
}
return this.updateDeviceRegistration();
}).then(() => {
Services.telemetry.getHistogramById("FXA_CONFIGURED").add(1);
return this.notifyObservers(ONLOGIN_NOTIFICATION);
}).then(() => {
return currentAccountState.resolve();
});
});
const signedInUser = await this.getSignedInUser();
if (signedInUser) {
await this.deleteDeviceRegistration(signedInUser.sessionToken, signedInUser.deviceId);
}
await this.abortExistingFlow();
let currentAccountState = this.currentAccountState = this.newAccountState(
Cu.cloneInto(credentials, {}) // Pass a clone of the credentials object.
);
// This promise waits for storage, but not for verification.
// We're telling the caller that this is durable now (although is that
// really something we should commit to? Why not let the write happen in
// the background? Already does for updateAccountData ;)
await currentAccountState.promiseInitialized;
// Starting point for polling if new user
if (!this.isUserEmailVerified(credentials)) {
this.startVerifiedCheck(credentials);
}
await this.updateDeviceRegistration();
Services.telemetry.getHistogramById("FXA_CONFIGURED").add(1);
await this.notifyObservers(ONLOGIN_NOTIFICATION);
return currentAccountState.resolve();
},
/**

View File

@ -117,13 +117,13 @@ WeaveService.prototype = {
/**
* Whether Sync appears to be enabled.
*
* This returns true if we have an associated FxA account
* This returns true if we have an associated FxA account and Sync is enabled.
*
* It does *not* perform a robust check to see if the client is working.
* For that, you'll want to check Weave.Status.checkSetup().
*/
get enabled() {
return !!syncUsername;
return !!syncUsername && Services.prefs.getBoolPref("identity.fxaccounts.enabled");
}
};