Bug 1524665 - Add send tab to device button, r=gijs, a=pascalc
authorVijay Budhram <vbudhram@mozilla.com>
Wed, 13 Mar 2019 20:31:59 +0000
changeset 525842 fbad8125c402cef4a29c923701b230843f8140e1
parent 525841 9f8e10c3d5c06b5194f26c619545d7eee3579901
child 525843 1e5265ef2db39bfaf7913976d822ebaab7edb03e
push id2032
push userffxbld-merge
push dateMon, 13 May 2019 09:36:57 +0000
treeherdermozilla-release@455c1065dcbe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgijs, pascalc
bugs1524665
milestone67.0
Bug 1524665 - Add send tab to device button, r=gijs, a=pascalc Differential Revision: https://phabricator.services.mozilla.com//D23387
browser/base/content/browser-pageActions.js
browser/base/content/browser-sync.js
browser/base/content/browser.js
browser/components/customizableui/content/panelUI.inc.xul
browser/themes/shared/customizableui/panelUI.inc.css
browser/themes/shared/urlbar-searchbar.inc.css
--- a/browser/base/content/browser-pageActions.js
+++ b/browser/base/content/browser-pageActions.js
@@ -1078,68 +1078,17 @@ BrowserPageActions.sendToDevice = {
   onLocationChange() {
     let action = PageActions.actionForID("sendToDevice");
     let browser = gBrowser.selectedBrowser;
     let url = browser.currentURI.spec;
     action.setDisabled(!gSync.isSendableURI(url), window);
   },
 
   onShowingSubview(panelViewNode) {
-    let bodyNode = panelViewNode.querySelector(".panel-subview-body");
-    let panelNode = panelViewNode.closest("panel");
-    let browser = gBrowser.selectedBrowser;
-    let url = browser.currentURI.spec;
-    let title = browser.contentTitle;
-    let multiselected = gBrowser.selectedTab.multiselected;
-
-    // This is on top because it also clears the device list between state
-    // changes.
-    gSync.populateSendTabToDevicesMenu(bodyNode, url, title, multiselected, (clientId, name, clientType, lastModified) => {
-      if (!name) {
-        return document.createXULElement("toolbarseparator");
-      }
-      let item = document.createXULElement("toolbarbutton");
-      item.classList.add("pageAction-sendToDevice-device", "subviewbutton");
-      if (clientId) {
-        item.classList.add("subviewbutton-iconic");
-        if (lastModified) {
-          item.setAttribute("tooltiptext", gSync.formatLastSyncDate(lastModified));
-        }
-      }
-
-      item.addEventListener("command", event => {
-        if (panelNode) {
-          PanelMultiView.hidePopup(panelNode);
-        }
-        // There are items in the subview that don't represent devices: "Sign
-        // in", "Learn about Sync", etc.  Device items will be .sendtab-target.
-        if (event.target.classList.contains("sendtab-target")) {
-          let action = PageActions.actionForID("sendToDevice");
-          let messageId = gSync.offline && "sendToDeviceOffline";
-          showBrowserPageActionFeedback(action, event, messageId);
-        }
-      });
-      return item;
-    });
-
-    bodyNode.removeAttribute("state");
-    // In the first ~10 sec after startup, Sync may not be loaded and the list
-    // of devices will be empty.
-    if (gSync.sendTabConfiguredAndLoading) {
-      bodyNode.setAttribute("state", "notready");
-      // Force a background Sync
-      Services.tm.dispatchToMainThread(async () => {
-        await Weave.Service.sync({why: "pageactions", engines: []}); // [] = clients engine only
-        // There's no way Sync is still syncing at this point, but we check
-        // anyway to avoid infinite looping.
-        if (!window.closed && !gSync.sendTabConfiguredAndLoading) {
-          this.onShowingSubview(panelViewNode);
-        }
-      });
-    }
+    gSync.populateSendTabToDevicesView(panelViewNode, this.onShowingSubview.bind(this));
   },
 };
 
 // add search engine
 BrowserPageActions.addSearchEngine = {
   get action() {
     return PageActions.actionForID("addSearchEngine");
   },
--- a/browser/base/content/browser-sync.js
+++ b/browser/base/content/browser-sync.js
@@ -196,16 +196,88 @@ var gSync = {
   updateAllUI(state) {
     this.updatePanelPopup(state);
     this.updateState(state);
     this.updateSyncButtonsTooltip(state);
     this.updateSyncStatus(state);
     this.updateFxAToolbarPanel(state);
   },
 
+  updateSendToDeviceTitle() {
+    let string = gBrowserBundle.GetStringFromName("sendTabsToDevice.label");
+    let title = PluralForm.get(1, string).replace("#1", 1);
+    if (gBrowser.selectedTab.multiselected) {
+      let tabCount = gBrowser.selectedTabs.length;
+      title = PluralForm.get(tabCount, string).replace("#1", tabCount);
+    }
+
+    document.getElementById("PanelUI-fxa-menu-sendtab-button").setAttribute("label", title);
+  },
+
+  showSendToDeviceView(anchor) {
+    PanelUI.showSubView("PanelUI-sendTabToDevice", anchor);
+    let panelViewNode = document.getElementById("PanelUI-sendTabToDevice");
+    this.populateSendTabToDevicesView(panelViewNode, this.populateSendTabToDevicesView);
+  },
+
+  populateSendTabToDevicesView(panelViewNode, reloadFunc) {
+    let bodyNode = panelViewNode.querySelector(".panel-subview-body");
+    let panelNode = panelViewNode.closest("panel");
+    let browser = gBrowser.selectedBrowser;
+    let url = browser.currentURI.spec;
+    let title = browser.contentTitle;
+    let multiselected = gBrowser.selectedTab.multiselected;
+
+    // This is on top because it also clears the device list between state
+    // changes.
+    this.populateSendTabToDevicesMenu(bodyNode, url, title, multiselected, (clientId, name, clientType, lastModified) => {
+      if (!name) {
+        return document.createXULElement("toolbarseparator");
+      }
+      let item = document.createXULElement("toolbarbutton");
+      item.classList.add("pageAction-sendToDevice-device", "subviewbutton");
+      if (clientId) {
+        item.classList.add("subviewbutton-iconic");
+        if (lastModified) {
+          item.setAttribute("tooltiptext", gSync.formatLastSyncDate(lastModified));
+        }
+      }
+
+      item.addEventListener("command", event => {
+        if (panelNode) {
+          PanelMultiView.hidePopup(panelNode);
+        }
+        // There are items in the subview that don't represent devices: "Sign
+        // in", "Learn about Sync", etc.  Device items will be .sendtab-target.
+        if (event.target.classList.contains("sendtab-target")) {
+          let action = PageActions.actionForID("sendToDevice");
+          let messageId = gSync.offline && "sendToDeviceOffline";
+          showBrowserPageActionFeedback(action, event, messageId);
+        }
+      });
+      return item;
+    });
+
+    bodyNode.removeAttribute("state");
+    // In the first ~10 sec after startup, Sync may not be loaded and the list
+    // of devices will be empty.
+    if (gSync.sendTabConfiguredAndLoading) {
+      bodyNode.setAttribute("state", "notready");
+      // Force a background Sync
+      Services.tm.dispatchToMainThread(async () => {
+        await Weave.Service.sync({why: "pageactions", engines: []}); // [] = clients engine only
+        // There's no way Sync is still syncing at this point, but we check
+        // anyway to avoid infinite looping.
+        if (!window.closed && !gSync.sendTabConfiguredAndLoading) {
+          reloadFunc(panelViewNode);
+        }
+      });
+    }
+  },
+
   toggleAccountPanel(viewId, aEvent) {
     // Don't show the panel if the window is in customization mode.
     if (document.documentElement.hasAttribute("customizing")) {
       return;
     }
 
     if ((aEvent.type == "mousedown" && aEvent.button != 0) ||
         (aEvent.type == "keypress" && aEvent.charCode != KeyEvent.DOM_VK_SPACE &&
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -470,32 +470,36 @@ var gNavigatorBundle = {
   },
 };
 
 function showFxaToolbarMenu(enable) {
   // We only show the Firefox Account toolbar menu if the feature is enabled and
   // if sync is enabled.
   const syncEnabled = Services.prefs.getBoolPref("identity.fxaccounts.enabled", false);
   const mainWindowEl = document.documentElement;
+  const fxaPanelEl = document.getElementById("PanelUI-fxa");
   if (enable && syncEnabled) {
+    fxaPanelEl.addEventListener("ViewShowing", gSync.updateSendToDeviceTitle);
+
     mainWindowEl.setAttribute("fxastatus", "not_configured");
     // We have to manually update the sync state UI when toggling the FxA toolbar
     // because it could show an invalid icon if the user is logged in and no sync
     // event was performed yet.
     gSync.maybeUpdateUIState();
 
     // We set an attribute here so that we can toggle the custom
     // badge depending on whether the FxA menu was ever accessed.
     if (!gFxaToolbarAccessed) {
       mainWindowEl.setAttribute("fxa_avatar_badged", "badged");
     } else {
       mainWindowEl.removeAttribute("fxa_avatar_badged");
     }
   } else {
     mainWindowEl.removeAttribute("fxastatus");
+    fxaPanelEl.removeEventListener("ViewShowing", gSync.updateSendToDeviceTitle);
   }
 }
 
 function UpdateBackForwardCommands(aWebNavigation) {
   var backCommand = document.getElementById("Browser:Back");
   var forwardCommand = document.getElementById("Browser:Forward");
 
   // Avoid setting attributes on commands if the value hasn't changed!
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -742,16 +742,20 @@
               <image id="fxa-menu-avatar"/>
             </toolbarbutton>
             <vbox flex="1">
               <label class="fxa-avatar-subpanel-description" >&fxa.menu.signedInAs.label;</label>
               <label id="fxa-menu-email"></label>
             </vbox>
           </hbox>
           <toolbarseparator/>
+          <toolbarbutton id="PanelUI-fxa-menu-sendtab-button"
+                         class="subviewbutton subviewbutton-iconic subviewbutton-nav"
+                         closemenu="none"
+                         oncommand="gSync.showSendToDeviceView(this);"/>
           <toolbarbutton id="PanelUI-fxa-menu-remotetabs-button"
                          class="subviewbutton subviewbutton-iconic subviewbutton-nav"
                          label="&appMenuRemoteTabs.label;"
                          closemenu="none"
                          oncommand="PanelUI.showSubView('PanelUI-remotetabs', this)"/>
           <toolbarbutton id="PanelUI-fxa-menu-view-sidebar"
                          class="subviewbutton subviewbutton-iconic"
                          label="&appMenuRemoteTabs.sidebar.label;"
@@ -764,16 +768,24 @@
           <toolbarbutton id="PanelUI-fxa-menu-syncnow-button"
                          label="&syncSyncNowItem.label;"
                          class="subviewbutton subviewbutton-iconic"
                          oncommand="gSync.doSync();"
                          closemenu="none"/>
         </vbox>
       </vbox>
     </panelview>
+    <!-- This panelview is used to contain the dynamically created buttons for send tab to devices -->
+    <panelview id="PanelUI-sendTabToDevice" flex="1" class="PanelUI-subView">
+      <vbox class="panel-subview-body">
+        <toolbarbutton id="PanelUI-sendTabToDevice-syncingDevices" class="subviewbutton subviewbutton-iconic pageAction-sendToDevice-notReady"
+                       label="&sendToDevice.syncNotReady.label;"
+                       disabled="true"/>
+      </vbox>
+    </panelview>
 
     <panelview id="PanelUI-bookmarkingTools" class="PanelUI-subView">
       <vbox class="panel-subview-body">
         <toolbarbutton id="panelMenu_toggleBookmarksMenu"
                        class="subviewbutton subviewbutton-iconic"
                        label-show="&addBookmarksMenu.label;"
                        label-hide="&removeBookmarksMenu.label;"
                        oncommand="BookmarkingUI.toggleMenuButtonInToolbar(this);"/>
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -756,16 +756,20 @@ toolbarbutton[constrain-size="true"][cui
 
 /* From the FxA menu -> remote tabs, we don't need to clutter the view with
    redundant buttons because these are accessible from the main menu */
 panelmultiview[mainViewId="PanelUI-fxa"] #PanelUI-remotetabs-view-sidebar,
 panelmultiview[mainViewId="PanelUI-fxa"] #PanelUI-remotetabs-syncnow {
   display: none;
 }
 
+#PanelUI-sendTabToDevice > .panel-subview-body:not([state]) > #PanelUI-sendTabToDevice-syncingDevices {
+  display: none;
+}
+
 .fxaChooseWhatToSyncDevices {
   height: 102px;
   width: 201px;
   list-style-image: url(chrome://browser/skin/fxa/choose-what-to-sync-devices.svg);
 }
 
 .fxaGraphicMail {
   height: 71px;
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -175,16 +175,17 @@
   list-style-image: url("chrome://browser/skin/link.svg");
 }
 
 #pageAction-panel-emailLink,
 #pageAction-urlbar-emailLink {
   list-style-image: url("chrome://browser/skin/mail.svg");
 }
 
+#PanelUI-fxa-menu-sendtab-button,
 #pageAction-panel-sendToDevice,
 #pageAction-urlbar-sendToDevice {
   list-style-image: url("chrome://browser/skin/send-to-device.svg");
 }
 
 #pageAction-panel-pinTab:-moz-locale-dir(rtl) > .toolbarbutton-icon,
 #pageAction-urlbar-pinTab:-moz-locale-dir(rtl),
 #pageAction-panel-sendToDevice:-moz-locale-dir(rtl) > .toolbarbutton-icon,