summaryrefslogtreecommitdiffstats
path: root/browser/base/content/browser-sync.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/base/content/browser-sync.js')
-rw-r--r--browser/base/content/browser-sync.js326
1 files changed, 210 insertions, 116 deletions
diff --git a/browser/base/content/browser-sync.js b/browser/base/content/browser-sync.js
index 065546d7b8..9aa6cc5cd4 100644
--- a/browser/base/content/browser-sync.js
+++ b/browser/base/content/browser-sync.js
@@ -202,10 +202,7 @@ this.SyncedTabsPanelList = class SyncedTabsPanelList {
}
_appendSyncClient(client, container, labelId, paginationInfo) {
- let {
- maxTabs = SyncedTabsPanelList.sRemoteTabsPerPage,
- showInactive = false,
- } = paginationInfo;
+ let { maxTabs = SyncedTabsPanelList.sRemoteTabsPerPage } = paginationInfo;
// Create the element for the remote client.
let clientItem = document.createXULElement("label");
clientItem.setAttribute("id", labelId);
@@ -227,11 +224,24 @@ this.SyncedTabsPanelList = class SyncedTabsPanelList {
);
label.setAttribute("class", "PanelUI-remotetabs-notabsforclient-label");
} else {
- let tabs = client.tabs.filter(t => showInactive || !t.inactive);
- let numInactive = client.tabs.length - tabs.length;
+ // We have the client obj but we need the FxA device obj so we use the clients
+ // engine to get us the FxA device
+ let device =
+ fxAccounts.device.recentDeviceList &&
+ fxAccounts.device.recentDeviceList.find(
+ d =>
+ d.id === Weave.Service.clientsEngine.getClientFxaDeviceId(client.id)
+ );
+ let remoteTabCloseAvailable =
+ device && fxAccounts.commands.closeTab.isDeviceCompatible(device);
+
+ let tabs = client.tabs.filter(t => !t.inactive);
+ let hasInactive = tabs.length != client.tabs.length;
- // If this page will display all tabs, show no additional buttons.
- // Otherwise, show a "Show More" button
+ if (hasInactive) {
+ container.append(this._createShowInactiveTabsElement(client, device));
+ }
+ // If this page isn't displaying all (regular, active) tabs, show a "Show More" button.
let hasNextPage = tabs.length > maxTabs;
let nextPageIsLastPage =
hasNextPage &&
@@ -248,15 +258,13 @@ this.SyncedTabsPanelList = class SyncedTabsPanelList {
tabs = tabs.slice(0, maxTabs);
}
for (let [index, tab] of tabs.entries()) {
- let tabEnt = this._createSyncedTabElement(tab, index);
- container.appendChild(tabEnt);
- }
- if (numInactive) {
- let elt = this._createShowInactiveTabsElement(
- paginationInfo,
- numInactive
+ let tabEnt = this._createSyncedTabElement(
+ tab,
+ index,
+ device,
+ remoteTabCloseAvailable
);
- container.appendChild(elt);
+ container.appendChild(tabEnt);
}
if (hasNextPage) {
let showAllEnt = this._createShowMoreSyncedTabsElement(paginationInfo);
@@ -265,7 +273,10 @@ this.SyncedTabsPanelList = class SyncedTabsPanelList {
}
}
- _createSyncedTabElement(tabInfo, index) {
+ _createSyncedTabElement(tabInfo, index, device, canCloseTabs) {
+ let tabContainer = document.createXULElement("hbox");
+ tabContainer.setAttribute("class", "PanelUI-tabitem-container");
+
let item = document.createXULElement("toolbarbutton");
let tooltipText = (tabInfo.title ? tabInfo.title + "\n" : "") + tabInfo.url;
item.setAttribute("itemtype", "tab");
@@ -296,25 +307,29 @@ this.SyncedTabsPanelList = class SyncedTabsPanelList {
{}
),
});
- if (document.defaultView.whereToOpenLink(e) != "current") {
+ if (BrowserUtils.whereToOpenLink(e) != "current") {
e.preventDefault();
e.stopPropagation();
} else {
CustomizableUI.hidePanelForNode(item);
}
});
- return item;
+ tabContainer.appendChild(item);
+ // We should only add an X button next to tabs if the device
+ // is broadcasting that it can remotely close tabs
+ if (canCloseTabs) {
+ tabContainer.appendChild(
+ this._createCloseTabElement(tabInfo.url, device)
+ );
+ }
+ return tabContainer;
}
_createShowMoreSyncedTabsElement(paginationInfo) {
let showMoreItem = document.createXULElement("toolbarbutton");
showMoreItem.setAttribute("itemtype", "showmorebutton");
showMoreItem.setAttribute("closemenu", "none");
- showMoreItem.classList.add(
- "subviewbutton",
- "subviewbutton-nav",
- "subviewbutton-nav-down"
- );
+ showMoreItem.classList.add("subviewbutton", "subviewbutton-nav-down");
document.l10n.setAttributes(showMoreItem, "appmenu-remote-tabs-showmore");
paginationInfo.maxTabs = Infinity;
@@ -326,27 +341,56 @@ this.SyncedTabsPanelList = class SyncedTabsPanelList {
return showMoreItem;
}
- _createShowInactiveTabsElement(paginationInfo, count) {
+ _createShowInactiveTabsElement(client, device) {
let showItem = document.createXULElement("toolbarbutton");
- showItem.setAttribute("itemtype", "showmorebutton");
showItem.setAttribute("closemenu", "none");
- showItem.classList.add(
- "subviewbutton",
- "subviewbutton-nav",
- "subviewbutton-nav-down"
+ showItem.classList.add("subviewbutton", "subviewbutton-nav");
+ document.l10n.setAttributes(
+ showItem,
+ "appmenu-remote-tabs-show-inactive-tabs"
);
- document.l10n.setAttributes(showItem, "appmenu-remote-tabs-showinactive");
- document.l10n.setArgs(showItem, { count });
- paginationInfo.showInactive = true;
+ let canClose =
+ device && fxAccounts.commands.closeTab.isDeviceCompatible(device);
+
showItem.addEventListener("click", e => {
- e.preventDefault();
- e.stopPropagation();
- this._showSyncedTabs(paginationInfo);
+ let node = PanelMultiView.getViewNode(
+ document,
+ "PanelUI-fxa-menu-inactive-tabs"
+ );
+
+ // device name.
+ let label = node.querySelector("label[itemtype='client']");
+ label.textContent = client.name;
+
+ // Update the tab list.
+ let container = node.querySelector(".panel-subview-body");
+ container.replaceChildren(
+ ...client.tabs
+ .filter(t => t.inactive)
+ .map((tab, index) =>
+ this._createSyncedTabElement(tab, index, device, canClose)
+ )
+ );
+ PanelUI.showSubView("PanelUI-fxa-menu-inactive-tabs", showItem, e);
});
return showItem;
}
+ _createCloseTabElement(url, device) {
+ let closeBtn = document.createXULElement("image");
+ closeBtn.setAttribute("class", "close-icon remotetabs-close");
+
+ closeBtn.addEventListener("click", function (e) {
+ e.stopPropagation();
+ // The user could be hitting multiple tabs across multiple devices, with a few
+ // seconds in-between -- we should not immediately fire off pushes, so we
+ // add it to a queue and send in bulk at a later time
+ fxAccounts.commands.closeTab.enqueueTabToClose(device, url);
+ });
+ return closeBtn;
+ }
+
destroy() {
Services.obs.removeObserver(this, SyncedTabs.TOPIC_TABS_CHANGED);
this.tabsList = null;
@@ -384,7 +428,7 @@ var gSync = {
"browser/accounts.ftl",
"browser/appmenu.ftl",
"browser/sync.ftl",
- "toolkit/branding/accounts.ftl",
+ "browser/syncedTabs.ftl",
],
true
));
@@ -445,7 +489,7 @@ var gSync = {
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
- "PXI_TOOLBAR_ENABLED",
+ "FXA_CTA_MENU_ENABLED",
"identity.fxaccounts.toolbar.pxiToolbarEnabled"
);
},
@@ -533,10 +577,23 @@ var gSync = {
let fxaPanelView = PanelMultiView.getViewNode(document, "PanelUI-fxa");
fxaPanelView.addEventListener("ViewShowing", this);
fxaPanelView.addEventListener("ViewHiding", this);
+ fxaPanelView.addEventListener("command", this);
+ PanelMultiView.getViewNode(
+ document,
+ "PanelUI-fxa-menu-syncnow-button"
+ ).addEventListener("mouseover", this);
+ PanelMultiView.getViewNode(
+ document,
+ "PanelUI-fxa-menu-sendtab-not-configured-button"
+ ).addEventListener("command", this);
+ PanelMultiView.getViewNode(
+ document,
+ "PanelUI-fxa-menu-sendtab-connect-device-button"
+ ).addEventListener("command", this);
// If the experiment is enabled, we'll need to update the panels
// to show some different text to the user
- if (this.PXI_TOOLBAR_ENABLED) {
+ if (this.FXA_CTA_MENU_ENABLED) {
this.updateFxAPanel(UIState.get());
this.updateCTAPanel();
}
@@ -558,6 +615,13 @@ var gSync = {
handleEvent(event) {
switch (event.type) {
+ case "mouseover":
+ this.refreshSyncButtonsTooltip();
+ break;
+ case "command": {
+ this.onCommand(event.target);
+ break;
+ }
case "ViewShowing": {
this.onFxAPanelViewShowing(event.target);
break;
@@ -606,16 +670,61 @@ var gSync = {
panelview.syncedTabsPanelList = null;
},
+ onCommand(button) {
+ switch (button.id) {
+ case "PanelUI-fxa-menu-sync-prefs-button":
+ // fall through
+ case "PanelUI-fxa-menu-setup-sync-button":
+ this.openPrefsFromFxaMenu("sync_settings", button);
+ break;
+
+ case "PanelUI-fxa-menu-sendtab-connect-device-button":
+ // fall through
+ case "PanelUI-fxa-menu-connect-device-button":
+ this.openConnectAnotherDeviceFromFxaMenu(button);
+ break;
+
+ case "fxa-manage-account-button":
+ this.clickFxAMenuHeaderButton(button);
+ break;
+ case "PanelUI-fxa-menu-syncnow-button":
+ this.doSyncFromFxaMenu(button);
+ break;
+ case "PanelUI-fxa-menu-sendtab-button":
+ this.showSendToDeviceViewFromFxaMenu(button);
+ break;
+ case "PanelUI-fxa-menu-account-signout-button":
+ this.disconnect();
+ break;
+ case "PanelUI-fxa-menu-sync-button":
+ this.openPrefsFromFxaButton("sync_cta", button);
+ break;
+ case "PanelUI-fxa-menu-monitor-button":
+ this.openMonitorLink(button);
+ break;
+ case "PanelUI-fxa-menu-relay-button":
+ this.openRelayLink(button);
+ break;
+ case "PanelUI-fxa-menu-vpn-button":
+ this.openVPNLink(button);
+ break;
+ case "PanelUI-fxa-menu-sendtab-not-configured-button":
+ this.openPrefsFromFxaMenu("send_tab", button);
+ break;
+ }
+ },
+
observe(subject, topic, data) {
if (!this._initialized) {
console.error("browser-sync observer called after unload: ", topic);
return;
}
switch (topic) {
- case UIState.ON_UPDATE:
+ case UIState.ON_UPDATE: {
const state = UIState.get();
this.updateAllUI(state);
break;
+ }
case "quit-application":
// Stop the animation timer on shutdown, since we can't update the UI
// after this.
@@ -637,7 +746,6 @@ var gSync = {
this.updateSyncButtonsTooltip(state);
this.updateSyncStatus(state);
this.updateFxAPanel(state);
- this.updateCTAPanel(state);
// Ensure we have something in the device list in the background.
this.ensureFxaDevices();
},
@@ -720,16 +828,6 @@ var gSync = {
this.emitFxaToolbarTelemetry("send_tab", anchor);
},
- showRemoteTabsFromFxaMenu(panel) {
- PanelUI.showSubView("PanelUI-remotetabs", panel);
- this.emitFxaToolbarTelemetry("sync_tabs", panel);
- },
-
- showSidebarFromFxaMenu(panel) {
- SidebarUI.toggle("viewTabsSidebar");
- this.emitFxaToolbarTelemetry("sync_tabs_sidebar", panel);
- },
-
_populateSendTabToDevicesView(panelViewNode, reloadDevices = true) {
let bodyNode = panelViewNode.querySelector(".panel-subview-body");
let panelNode = panelViewNode.closest("panel");
@@ -830,12 +928,20 @@ var gSync = {
let fxaStatus = document.documentElement.getAttribute("fxastatus");
if (fxaStatus == "not_configured") {
+ // sign in button in app (hamburger) menu
+ // should take you straight to fxa sign in page
+ if (anchor.id == "appMenu-fxa-label2") {
+ this.openFxAEmailFirstPageFromFxaMenu(anchor);
+ PanelUI.hide();
+ return;
+ }
+
// If we're signed out but have the PXI pref enabled
// we should show the PXI panel instead of taking the user
// straight to FxA sign-in
- if (this.PXI_TOOLBAR_ENABLED) {
+ if (this.FXA_CTA_MENU_ENABLED) {
this.updateFxAPanel(UIState.get());
- this.updateCTAPanel();
+ this.updateCTAPanel(anchor);
PanelUI.showSubView("PanelUI-fxa", anchor, aEvent);
} else if (anchor == document.getElementById("fxa-toolbar-menu-button")) {
// The fxa toolbar button doesn't have much context before the user
@@ -844,20 +950,13 @@ var gSync = {
this.emitFxaToolbarTelemetry("toolbar_icon", anchor);
openTrustedLinkIn("about:preferences#sync", "tab");
PanelUI.hide();
- } else {
- let panel =
- anchor.id == "appMenu-fxa-label2"
- ? PanelMultiView.getViewNode(document, "PanelUI-fxa")
- : undefined;
- this.openFxAEmailFirstPageFromFxaMenu(panel);
- PanelUI.hide();
}
return;
}
// If the user is signed in and we have the PXI pref enabled then add
// the pxi panel to the existing toolbar
- if (this.PXI_TOOLBAR_ENABLED) {
- this.updateCTAPanel();
+ if (this.FXA_CTA_MENU_ENABLED) {
+ this.updateCTAPanel(anchor);
}
if (!gFxaToolbarAccessed) {
@@ -932,21 +1031,16 @@ var gSync = {
fxaMenuAccountButtonEl.removeAttribute("closemenu");
syncSetupButtonEl.removeAttribute("hidden");
- let headerTitleL10nId = this.PXI_TOOLBAR_ENABLED
- ? "appmenuitem-sign-in-account"
- : "appmenuitem-fxa-sign-in";
+ let headerTitleL10nId = this.FXA_CTA_MENU_ENABLED
+ ? "synced-tabs-fxa-sign-in"
+ : "appmenuitem-sign-in-account";
let headerDescription;
if (state.status === UIState.STATUS_NOT_CONFIGURED) {
mainWindowEl.style.removeProperty("--avatar-image-url");
- headerDescription = this.fluentStrings.formatValueSync(
- "appmenu-fxa-signed-in-label"
- );
- // Signed out, expeirment enabled is the only state we want to hide the
- // header description, so we make it empty and check for that when setting
- // the value
- if (this.PXI_TOOLBAR_ENABLED) {
- headerDescription = "";
- }
+ const headerDescString = this.FXA_CTA_MENU_ENABLED
+ ? "fxa-menu-sync-description"
+ : "appmenu-fxa-signed-in-label";
+ headerDescription = this.fluentStrings.formatValueSync(headerDescString);
} else if (state.status === UIState.STATUS_LOGIN_FAILED) {
stateValue = "login-failed";
headerTitleL10nId = "account-disconnected2";
@@ -1020,8 +1114,8 @@ var gSync = {
).hidden = !canSendAllURIs;
},
- emitFxaToolbarTelemetry(type, panel) {
- if (UIState.isReady() && panel) {
+ emitFxaToolbarTelemetry(type, sourceElement) {
+ if (UIState.isReady() && sourceElement) {
const state = UIState.get();
const hasAvatar = state.avatarURL && !state.avatarIsDefault;
let extraOptions = {
@@ -1029,10 +1123,10 @@ var gSync = {
fxa_avatar: hasAvatar ? "true" : "false",
};
- // When the fxa avatar panel is within the Firefox app menu,
+ // When the source element is within the Firefox app menu,
// we emit different telemetry.
let eventName = "fxa_avatar_menu";
- if (this.isPanelInsideAppMenu(panel)) {
+ if (this.isInsideAppMenu(sourceElement)) {
eventName = "fxa_app_menu";
}
@@ -1046,9 +1140,9 @@ var gSync = {
}
},
- isPanelInsideAppMenu(panel = undefined) {
+ isInsideAppMenu(sourceElement = undefined) {
const appMenuPanel = document.getElementById("appMenu-popup");
- if (panel && appMenuPanel.contains(panel)) {
+ if (sourceElement && appMenuPanel.contains(sourceElement)) {
return true;
}
return false;
@@ -1225,10 +1319,10 @@ var gSync = {
openTrustedLinkIn(url, "tab");
},
- async openConnectAnotherDeviceFromFxaMenu(panel = undefined) {
- this.emitFxaToolbarTelemetry("cad", panel);
+ async openConnectAnotherDeviceFromFxaMenu(sourceElement = undefined) {
+ this.emitFxaToolbarTelemetry("cad", sourceElement);
let entryPoint = "fxa_discoverability_native";
- if (this.isPanelInsideAppMenu(panel)) {
+ if (this.isInsideAppMenu(sourceElement)) {
entryPoint = "fxa_app_menu";
}
this.openConnectAnotherDevice(entryPoint);
@@ -1241,7 +1335,7 @@ var gSync = {
switchToTabHavingURI(url, true, { replaceQueryString: true });
},
- async clickFxAMenuHeaderButton(panel = undefined) {
+ async clickFxAMenuHeaderButton(sourceElement = undefined) {
// Depending on the current logged in state of a user,
// clicking the FxA header will either open
// a sign-in page, account management page, or sync
@@ -1249,16 +1343,16 @@ var gSync = {
const { status } = UIState.get();
switch (status) {
case UIState.STATUS_NOT_CONFIGURED:
- this.openFxAEmailFirstPageFromFxaMenu(panel);
+ this.openFxAEmailFirstPageFromFxaMenu(sourceElement);
break;
case UIState.STATUS_LOGIN_FAILED:
- this.openPrefsFromFxaMenu("sync_settings", panel);
+ this.openPrefsFromFxaMenu("sync_settings", sourceElement);
break;
case UIState.STATUS_NOT_VERIFIED:
this.openFxAEmailFirstPage("fxa_app_menu_reverify");
break;
case UIState.STATUS_SIGNED_IN:
- this.openFxAManagePageFromFxaMenu(panel);
+ this.openFxAManagePageFromFxaMenu(sourceElement);
}
},
@@ -1273,10 +1367,13 @@ var gSync = {
switchToTabHavingURI(url, true, { replaceQueryString: true });
},
- async openFxAEmailFirstPageFromFxaMenu(panel = undefined, extraParams = {}) {
- this.emitFxaToolbarTelemetry("login", panel);
+ async openFxAEmailFirstPageFromFxaMenu(
+ sourceElement = undefined,
+ extraParams = {}
+ ) {
+ this.emitFxaToolbarTelemetry("login", sourceElement);
let entryPoint = "fxa_discoverability_native";
- if (panel) {
+ if (sourceElement) {
entryPoint = "fxa_toolbar_button";
}
this.openFxAEmailFirstPage(entryPoint, extraParams);
@@ -1287,10 +1384,10 @@ var gSync = {
switchToTabHavingURI(url, true, { replaceQueryString: true });
},
- async openFxAManagePageFromFxaMenu(panel = undefined) {
- this.emitFxaToolbarTelemetry("account_settings", panel);
+ async openFxAManagePageFromFxaMenu(sourceElement = undefined) {
+ this.emitFxaToolbarTelemetry("account_settings", sourceElement);
let entryPoint = "fxa_discoverability_native";
- if (this.isPanelInsideAppMenu(panel)) {
+ if (this.isInsideAppMenu(sourceElement)) {
entryPoint = "fxa_app_menu";
}
this.openFxAManagePage(entryPoint);
@@ -1893,9 +1990,9 @@ var gSync = {
}
},
- doSyncFromFxaMenu(panel) {
+ doSyncFromFxaMenu(sourceElement) {
this.doSync();
- this.emitFxaToolbarTelemetry("sync_now", panel);
+ this.emitFxaToolbarTelemetry("sync_now", sourceElement);
},
openPrefs(entryPoint = "syncbutton", origin = undefined) {
@@ -1905,18 +2002,18 @@ var gSync = {
});
},
- openPrefsFromFxaMenu(type, panel) {
- this.emitFxaToolbarTelemetry(type, panel);
+ openPrefsFromFxaMenu(type, sourceElement) {
+ this.emitFxaToolbarTelemetry(type, sourceElement);
let entryPoint = "fxa_discoverability_native";
- if (this.isPanelInsideAppMenu(panel)) {
+ if (this.isInsideAppMenu(sourceElement)) {
entryPoint = "fxa_app_menu";
}
this.openPrefs(entryPoint);
},
- openPrefsFromFxaButton(type, panel) {
+ openPrefsFromFxaButton(type, sourceElement) {
let entryPoint = "fxa_toolbar_button_sync";
- this.emitFxaToolbarTelemetry(type, panel);
+ this.emitFxaToolbarTelemetry(type, sourceElement);
this.openPrefs(entryPoint);
},
@@ -2049,27 +2146,24 @@ var gSync = {
// This should only be shown if we have enabled the pxiPanel via
// an experiment or explicitly through prefs
- updateCTAPanel() {
+ updateCTAPanel(anchor) {
const mainPanelEl = PanelMultiView.getViewNode(
document,
"PanelUI-fxa-cta-menu"
);
- const syncCtaEl = PanelMultiView.getViewNode(
- document,
- "PanelUI-fxa-menu-sync-button"
- );
- // If we're not in the experiment then we do not enable this at all
- if (!this.PXI_TOOLBAR_ENABLED) {
+ // If we're not in the experiment or in the app menu (hamburger)
+ // do not show this CTA panel
+ if (
+ !this.FXA_CTA_MENU_ENABLED ||
+ (anchor && anchor.id === "appMenu-fxa-label2")
+ ) {
// If we've previously shown this but got disabled
// we should ensure we hide the panel
mainPanelEl.hidden = true;
return;
}
- // If we're already signed in an syncing, we shouldn't show the sync CTA
- syncCtaEl.hidden = this.isSignedIn;
-
// Monitor checks
let monitorPanelEl = PanelMultiView.getViewNode(
document,
@@ -2112,8 +2206,8 @@ var gSync = {
!monitorEnabled && !relayEnabled && !vpnEnabled;
mainPanelEl.hidden = false;
},
- async openMonitorLink(panel) {
- this.emitFxaToolbarTelemetry("monitor_cta", panel);
+ async openMonitorLink(sourceElement) {
+ this.emitFxaToolbarTelemetry("monitor_cta", sourceElement);
await this.openCtaLink(
FX_MONITOR_OAUTH_CLIENT_ID,
new URL("https://monitor.firefox.com"),
@@ -2121,8 +2215,8 @@ var gSync = {
);
},
- async openRelayLink(panel) {
- this.emitFxaToolbarTelemetry("relay_cta", panel);
+ async openRelayLink(sourceElement) {
+ this.emitFxaToolbarTelemetry("relay_cta", sourceElement);
await this.openCtaLink(
FX_RELAY_OAUTH_CLIENT_ID,
new URL("https://relay.firefox.com"),
@@ -2130,8 +2224,8 @@ var gSync = {
);
},
- async openVPNLink(panel) {
- this.emitFxaToolbarTelemetry("vpn_cta", panel);
+ async openVPNLink(sourceElement) {
+ this.emitFxaToolbarTelemetry("vpn_cta", sourceElement);
await this.openCtaLink(
VPN_OAUTH_CLIENT_ID,
new URL("https://www.mozilla.org/en-US/products/vpn/"),