зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 22901b9f9199 (bug 1434706) for browser-chrome failures browser_contextmenu_sendtab.js. CLOSED TREE
This commit is contained in:
Родитель
c5b3bb6ce0
Коммит
c8a2d994c8
|
@ -1416,10 +1416,6 @@ 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/");
|
||||
|
||||
|
|
|
@ -263,13 +263,11 @@
|
|||
label="&savePageCmd.label;"
|
||||
accesskey="&savePageCmd.accesskey2;"
|
||||
oncommand="gContextMenu.savePageAs();"/>
|
||||
<menuseparator id="context-sep-sendpagetodevice" class="sync-ui-item"
|
||||
hidden="true"/>
|
||||
<menuseparator id="context-sep-sendpagetodevice" hidden="true"/>
|
||||
<menu id="context-sendpagetodevice"
|
||||
class="sync-ui-item"
|
||||
label="&sendPageToDevice.label;"
|
||||
accesskey="&sendPageToDevice.accesskey;"
|
||||
hidden="true">
|
||||
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>
|
||||
|
@ -312,13 +310,11 @@
|
|||
oncommand="AddKeywordForSearchField();"/>
|
||||
<menuitem id="context-searchselect"
|
||||
oncommand="BrowserSearch.loadSearchFromContext(this.searchTerms);"/>
|
||||
<menuseparator id="context-sep-sendlinktodevice" class="sync-ui-item"
|
||||
hidden="true"/>
|
||||
<menuseparator id="context-sep-sendlinktodevice" hidden="true"/>
|
||||
<menu id="context-sendlinktodevice"
|
||||
class="sync-ui-item"
|
||||
label="&sendLinkToDevice.label;"
|
||||
accesskey="&sendLinkToDevice.accesskey;"
|
||||
hidden="true">
|
||||
label="&sendLinkToDevice.label;"
|
||||
accesskey="&sendLinkToDevice.accesskey;"
|
||||
hidden="true">
|
||||
<menupopup id="context-sendlinktodevice-popup"
|
||||
onpopupshowing="gSync.populateSendTabToDevicesMenu(event.target, gContextMenu.linkURL, gContextMenu.linkTextStr);"/>
|
||||
</menu>
|
||||
|
|
|
@ -486,25 +486,21 @@
|
|||
|
||||
<!-- 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"
|
||||
|
|
|
@ -95,8 +95,6 @@ var gSync = {
|
|||
});
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "PRODUCT_INFO_BASE_URL",
|
||||
"app.productInfo.baseURL");
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "SYNC_ENABLED",
|
||||
"identity.fxaccounts.enabled");
|
||||
},
|
||||
|
||||
_maybeUpdateUIState() {
|
||||
|
@ -116,13 +114,6 @@ 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) {
|
||||
|
@ -141,6 +132,7 @@ var gSync = {
|
|||
}
|
||||
|
||||
this._generateNodeGetters();
|
||||
this._definePrefGetters();
|
||||
|
||||
this._maybeUpdateUIState();
|
||||
|
||||
|
@ -478,10 +470,6 @@ 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);
|
||||
|
||||
|
@ -490,10 +478,6 @@ 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
|
||||
|
@ -667,13 +651,6 @@ 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
|
||||
|
|
|
@ -97,9 +97,8 @@
|
|||
accesskey="&moveToNewWindow.accesskey;"
|
||||
tbattr="tabbrowser-multiple"
|
||||
oncommand="gBrowser.replaceTabWithWindow(TabContextMenu.contextTab);"/>
|
||||
<menuseparator id="context_sendTabToDevice_separator" class="sync-ui-item"/>
|
||||
<menuseparator id="context_sendTabToDevice_separator"/>
|
||||
<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);"/>
|
||||
|
|
|
@ -7,8 +7,6 @@ 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");
|
||||
});
|
||||
|
||||
|
@ -30,7 +28,7 @@ add_task(async function test_page_contextmenu() {
|
|||
sandbox.restore();
|
||||
});
|
||||
|
||||
add_task(async function test_page_contextmenu_no_remote_clients() {
|
||||
add_task(async function test_page_contextmenu_sendtab_no_remote_clients() {
|
||||
const sandbox = setupSendTabMocks({ syncReady: true, clientsSynced: true, remoteClients: [],
|
||||
state: UIState.STATUS_SIGNED_IN, isSendableURI: true });
|
||||
|
||||
|
@ -48,7 +46,7 @@ add_task(async function test_page_contextmenu_no_remote_clients() {
|
|||
sandbox.restore();
|
||||
});
|
||||
|
||||
add_task(async function test_page_contextmenu_one_remote_client() {
|
||||
add_task(async function test_page_contextmenu_sendtab_one_remote_client() {
|
||||
const sandbox = setupSendTabMocks({ syncReady: true, clientsSynced: true, remoteClients: [{ id: 1, name: "Foo"}],
|
||||
state: UIState.STATUS_SIGNED_IN, isSendableURI: true });
|
||||
|
||||
|
@ -177,16 +175,6 @@ 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();
|
||||
});
|
||||
|
||||
// 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.
|
||||
|
|
|
@ -95,15 +95,3 @@ 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();
|
||||
});
|
||||
|
|
|
@ -238,6 +238,252 @@ 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",
|
||||
|
@ -628,256 +874,6 @@ 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) {
|
||||
|
|
|
@ -177,7 +177,7 @@
|
|||
oncommand="PanelUI._onBannerItemSelected(event)"
|
||||
wrap="true"
|
||||
hidden="true"/>
|
||||
<toolbaritem id="appMenu-fxa-container" class="toolbaritem-combined-buttons sync-ui-item">
|
||||
<toolbaritem id="appMenu-fxa-container" class="toolbaritem-combined-buttons">
|
||||
<hbox id="appMenu-fxa-status"
|
||||
flex="1"
|
||||
defaultlabel="&fxaSignIn.label;"
|
||||
|
@ -201,7 +201,7 @@
|
|||
<observes element="sync-status" attribute="tooltiptext"/>
|
||||
</toolbarbutton>
|
||||
</toolbaritem>
|
||||
<toolbarseparator class="sync-ui-item"/>
|
||||
<toolbarseparator/>
|
||||
<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 sync-ui-item"
|
||||
class="subviewbutton subviewbutton-iconic subviewbutton-nav"
|
||||
label="&appMenuRemoteTabs.label;"
|
||||
closemenu="none"
|
||||
oncommand="PanelUI.showSubView('PanelUI-remotetabs', this)"/>
|
||||
|
|
|
@ -467,11 +467,6 @@ 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;
|
||||
})
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<vbox id="separateProfileBox">
|
||||
<checkbox id="separateProfileMode"
|
||||
label="&separateProfileMode.label;"/>
|
||||
<hbox id="sync-dev-edition-root" align="center" class="indent">
|
||||
<hbox align="center" class="indent">
|
||||
<label id="useFirefoxSync">&useFirefoxSync.label;</label>
|
||||
<deck id="getStarted">
|
||||
<label class="text-link">&getStarted.notloggedin.label;</label>
|
||||
|
|
|
@ -55,10 +55,7 @@ function init_all() {
|
|||
register_module("paneSearch", gSearchPane);
|
||||
register_module("panePrivacy", gPrivacyPane);
|
||||
register_module("paneContainers", gContainersPane);
|
||||
if (Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
|
||||
document.getElementById("category-sync").hidden = false;
|
||||
register_module("paneSync", gSyncPane);
|
||||
}
|
||||
register_module("paneSync", gSyncPane);
|
||||
register_module("paneSearchResults", gSearchResultsPane);
|
||||
gSearchResultsPane.init();
|
||||
gMainPane.preInit();
|
||||
|
|
|
@ -172,7 +172,6 @@
|
|||
|
||||
<richlistitem id="category-sync"
|
||||
class="category"
|
||||
hidden="true"
|
||||
value="paneSync"
|
||||
helpTopic="prefs-weave"
|
||||
data-l10n-id="category-sync"
|
||||
|
|
|
@ -98,9 +98,6 @@ 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) {
|
||||
|
|
|
@ -479,12 +479,7 @@ class Onboarding {
|
|||
|
||||
_getTourIDList() {
|
||||
let tours = Services.prefs.getStringPref(`browser.onboarding.${this._tourType}tour`, "");
|
||||
return tours.split(",").filter(tourId => {
|
||||
if (tourId === "sync" && !Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
|
||||
return false;
|
||||
}
|
||||
return tourId !== "";
|
||||
}).map(tourId => tourId.trim());
|
||||
return tours.split(",").filter(tourId => tourId !== "").map(tourId => tourId.trim());
|
||||
}
|
||||
|
||||
_initPrefObserver() {
|
||||
|
|
|
@ -1218,11 +1218,8 @@ 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",
|
||||
|
@ -1250,8 +1247,8 @@ if (Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
|
|||
.onShowingSubview(panelViewNode);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -36,9 +36,6 @@ 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",
|
||||
|
@ -524,23 +521,20 @@ FxAccountsInternal.prototype = {
|
|||
* }
|
||||
* or null if no user is signed in.
|
||||
*/
|
||||
async getSignedInUser() {
|
||||
getSignedInUser: function getSignedInUser() {
|
||||
let currentState = this.currentAccountState;
|
||||
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);
|
||||
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));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -564,32 +558,37 @@ FxAccountsInternal.prototype = {
|
|||
* The promise resolves to null when the data is saved
|
||||
* successfully and is rejected on error.
|
||||
*/
|
||||
async setSignedInUser(credentials) {
|
||||
if (!FXA_ENABLED) {
|
||||
throw new Error("Cannot call setSignedInUser when FxA is disabled.");
|
||||
}
|
||||
setSignedInUser: function setSignedInUser(credentials) {
|
||||
log.debug("setSignedInUser - aborting any existing flows");
|
||||
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();
|
||||
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();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -117,13 +117,13 @@ WeaveService.prototype = {
|
|||
/**
|
||||
* Whether Sync appears to be enabled.
|
||||
*
|
||||
* This returns true if we have an associated FxA account and Sync is enabled.
|
||||
* This returns true if we have an associated FxA account
|
||||
*
|
||||
* 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 && Services.prefs.getBoolPref("identity.fxaccounts.enabled");
|
||||
return !!syncUsername;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче