diff options
Diffstat (limited to '')
-rw-r--r-- | browser/base/content/browser-sync.js | 326 |
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/"), |