Bug 1395154 - If Send Page to Device pageAction is Pinned, it should dismiss when a user sends a tab. r?jaws draft
authorDrew Willcoxon <adw@mozilla.com>
Thu, 31 Aug 2017 15:27:22 -0700
changeset 656959 a815cfd494999014b4d839437cc9fcf27f944719
parent 656843 d790f3be71037aeee7d8b55880280beb310afb79
child 657000 c5f7d95ea691b948d826306c4e398503af1682b6
push id77387
push userdwillcoxon@mozilla.com
push dateThu, 31 Aug 2017 22:27:38 +0000
reviewersjaws
bugs1395154
milestone57.0a1
Bug 1395154 - If Send Page to Device pageAction is Pinned, it should dismiss when a user sends a tab. r?jaws MozReview-Commit-ID: HkuzOOp8PDP
browser/base/content/browser-pageActions.js
browser/base/content/browser-sync.js
browser/base/content/test/urlbar/browser_page_action_menu.js
--- a/browser/base/content/browser-pageActions.js
+++ b/browser/base/content/browser-pageActions.js
@@ -216,16 +216,21 @@ var BrowserPageActions = {
     panelNode.setAttribute("actionID", action.id);
     panelNode.setAttribute("role", "group");
     panelNode.setAttribute("type", "arrow");
     panelNode.setAttribute("flip", "slide");
     panelNode.setAttribute("noautofocus", "true");
     panelNode.setAttribute("tabspecific", "true");
     panelNode.setAttribute("photon", "true");
 
+    // For tests.
+    if (this._disableActivatedActionPanelAnimation) {
+      panelNode.setAttribute("animate", "false");
+    }
+
     let panelViewNode = null;
     let iframeNode = null;
 
     if (action.subview) {
       let multiViewNode = document.createElement("photonpanelmultiview");
       panelViewNode = this._makePanelViewNodeForAction(action, true);
       multiViewNode.appendChild(panelViewNode);
       panelNode.appendChild(multiViewNode);
@@ -801,30 +806,34 @@ BrowserPageActions.sendToDevice = {
   },
 
   onShowingSubview(panelViewNode) {
     let browser = gBrowser.selectedBrowser;
     let url = browser.currentURI.spec;
     let title = browser.contentTitle;
 
     let bodyNode = panelViewNode.firstChild;
+    let panelNode = panelViewNode.closest("panel");
 
     // This is on top because it also clears the device list between state
     // changes.
     gSync.populateSendTabToDevicesMenu(bodyNode, url, title, (clientId, name, clientType) => {
       if (!name) {
         return document.createElement("toolbarseparator");
       }
       let item = document.createElement("toolbarbutton");
       item.classList.add("pageAction-sendToDevice-device", "subviewbutton");
       if (clientId) {
         item.classList.add("subviewbutton-iconic");
       }
       item.setAttribute("tooltiptext", name);
       item.addEventListener("command", event => {
+        if (panelNode) {
+          panelNode.hidePopup();
+        }
         // 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")) {
           BrowserPageActionFeedback.show("sendToDevice", event);
         }
       });
       return item;
     });
--- a/browser/base/content/browser-sync.js
+++ b/browser/base/content/browser-sync.js
@@ -335,17 +335,16 @@ var gSync = {
 
   _appendSendTabDeviceList(fragment, createDeviceNodeFn, url, title) {
     const onTargetDeviceCommand = (event) => {
       let clients = event.target.getAttribute("clientId") ?
         [event.target.getAttribute("clientId")] :
         this.remoteClients.map(client => client.id);
 
       clients.forEach(clientId => this.sendTabToDevice(url, clientId, title));
-      BrowserPageActions.panelNode.hidePopup();
     }
 
     function addTargetDevice(clientId, name, clientType) {
       const targetDevice = createDeviceNodeFn(clientId, name, clientType);
       targetDevice.addEventListener("command", onTargetDeviceCommand, true);
       targetDevice.classList.add("sync-menuitem", "sendtab-target");
       targetDevice.setAttribute("clientId", clientId);
       targetDevice.setAttribute("clientType", clientType);
@@ -368,49 +367,45 @@ var gSync = {
     }
   },
 
   _appendSendTabSingleDevice(fragment, createDeviceNodeFn) {
     const noDevices = this.fxaStrings.GetStringFromName("sendTabToDevice.singledevice.status");
     const learnMore = this.fxaStrings.GetStringFromName("sendTabToDevice.singledevice");
     this._appendSendTabInfoItems(fragment, createDeviceNodeFn, noDevices, learnMore, () => {
       this.openSendToDevicePromo();
-      BrowserPageActions.panelNode.hidePopup();
     });
   },
 
   _appendSendTabVerify(fragment, createDeviceNodeFn) {
     const notVerified = this.fxaStrings.GetStringFromName("sendTabToDevice.verify.status");
     const verifyAccount = this.fxaStrings.GetStringFromName("sendTabToDevice.verify");
     this._appendSendTabInfoItems(fragment, createDeviceNodeFn, notVerified, verifyAccount, () => {
       this.openPrefs("sendtab");
-      BrowserPageActions.panelNode.hidePopup();
     });
   },
 
   _appendSendTabUnconfigured(fragment, createDeviceNodeFn) {
     const notConnected = this.fxaStrings.GetStringFromName("sendTabToDevice.unconfigured.status");
     const learnMore = this.fxaStrings.GetStringFromName("sendTabToDevice.unconfigured");
     this._appendSendTabInfoItems(fragment, createDeviceNodeFn, notConnected, learnMore, () => {
       this.openSendToDevicePromo();
-      BrowserPageActions.panelNode.hidePopup();
     });
 
     // Now add a 'sign in to sync' item above the 'learn more' item.
     const signInToSync = this.fxaStrings.GetStringFromName("sendTabToDevice.signintosync");
     let signInItem = createDeviceNodeFn(null, signInToSync, null);
     signInItem.classList.add("sync-menuitem");
     signInItem.setAttribute("label", signInToSync);
     // Show an icon if opened in the page action panel:
     if (signInItem.classList.contains("subviewbutton")) {
       signInItem.classList.add("subviewbutton-iconic", "signintosync");
     }
     signInItem.addEventListener("command", () => {
       this.openPrefs("sendtab");
-      BrowserPageActions.panelNode.hidePopup();
     });
     fragment.insertBefore(signInItem, fragment.lastChild);
   },
 
   _appendSendTabInfoItems(fragment, createDeviceNodeFn, statusLabel, actionLabel, actionCommand) {
     const status = createDeviceNodeFn(null, statusLabel, null);
     status.setAttribute("label", statusLabel);
     status.setAttribute("disabled", true);
--- a/browser/base/content/test/urlbar/browser_page_action_menu.js
+++ b/browser/base/content/test/urlbar/browser_page_action_menu.js
@@ -457,16 +457,116 @@ add_task(async function sendToDevice_dev
     let hiddenPromise = promisePageActionPanelHidden();
     BrowserPageActions.panelNode.hidePopup();
     await hiddenPromise;
 
     cleanUp();
   });
 });
 
+add_task(async function sendToDevice_inUrlbar() {
+  // Open a tab that's sendable.
+  await BrowserTestUtils.withNewTab("http://example.com/", async () => {
+    await promiseSyncReady();
+    const sandbox = sinon.sandbox.create();
+    sandbox.stub(gSync, "syncReady").get(() => true);
+    sandbox.stub(Weave.Service.clientsEngine, "lastSync").get(() => Date.now());
+    sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_SIGNED_IN });
+    sandbox.stub(gSync, "isSendableURI").returns(true);
+    sandbox.stub(gSync, "remoteClients").get(() => mockRemoteClients);
+
+    let cleanUp = () => {
+      sandbox.restore();
+    };
+    registerCleanupFunction(cleanUp);
+
+    // Disable the activated-action panel animation when it opens.  Otherwise
+    // it's necessary to wait a moment before trying to click the device menu
+    // item below.
+    BrowserPageActions._disableActivatedActionPanelAnimation = true;
+
+    // Add Send to Device to the urlbar.
+    let action = PageActions.actionForID("sendToDevice");
+    action.shownInUrlbar = true;
+
+    // Click it to open its panel.
+    let urlbarButton = document.getElementById(
+      BrowserPageActions._urlbarButtonNodeIDForActionID(action.id)
+    );
+    Assert.ok(!urlbarButton.disabled);
+    let panelPromise =
+      promisePanelShown(BrowserPageActions._activatedActionPanelID);
+    EventUtils.synthesizeMouseAtCenter(urlbarButton, {});
+    await panelPromise;
+
+    // The devices should be shown in the subview.
+    let expectedItems = [
+      {
+        id: "pageAction-urlbar-sendToDevice-notReady",
+        display: "none",
+        disabled: true,
+      },
+    ];
+    for (let client of mockRemoteClients) {
+      expectedItems.push({
+        attrs: {
+          clientId: client.id,
+          label: client.name,
+          clientType: client.type,
+        },
+      });
+    }
+    expectedItems.push(
+      null,
+      {
+        attrs: {
+          label: "Send to All Devices"
+        }
+      }
+    );
+    checkSendToDeviceItems(expectedItems, true);
+
+    // Get the first device menu item in the panel.
+    let bodyID =
+      BrowserPageActions._panelViewNodeIDForActionID("sendToDevice", true) +
+      "-body";
+    let body = document.getElementById(bodyID);
+    let deviceMenuItem = body.querySelector(".sendtab-target");
+    Assert.notEqual(deviceMenuItem, null);
+
+    // For good measure, wait until it's visible.
+    let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                    .getInterface(Ci.nsIDOMWindowUtils);
+    await BrowserTestUtils.waitForCondition(() => {
+      let bounds = dwu.getBoundsWithoutFlushing(deviceMenuItem);
+      return bounds.height > 0 && bounds.width > 0;
+    }, "Waiting for first device menu item to appear");
+
+    // Click it, which should cause the panel to close.
+    let hiddenPromise =
+      promisePanelHidden(BrowserPageActions._activatedActionPanelID);
+    EventUtils.synthesizeMouseAtCenter(deviceMenuItem, {});
+    info("Waiting for Send to Device panel to close after clicking a device");
+    await hiddenPromise;
+
+    // And then the "Sent!" notification panel should open and close by itself
+    // after a moment.
+    info("Waiting for the Sent! notification panel to open");
+    await promisePanelShown(BrowserPageActionFeedback.panelNode.id);
+    info("Waiting for the Sent! notification panel to close");
+    await promisePanelHidden(BrowserPageActionFeedback.panelNode.id);
+
+    // Remove Send to Device from the urlbar.
+    action.shownInUrlbar = false;
+    BrowserPageActions._disableActivatedActionPanelAnimation = false;
+
+    cleanUp();
+  });
+});
+
 add_task(async function contextMenu() {
   // Open an actionable page so that the main page action button appears.
   let url = "http://example.com/";
   await BrowserTestUtils.withNewTab(url, async () => {
     // Open the panel and then open the context menu on the bookmark button.
     await promisePageActionPanelOpen();
     let bookmarkButton = document.getElementById("pageAction-panel-bookmark");
     let contextMenuPromise = promisePanelShown("pageActionPanelContextMenu");
@@ -572,19 +672,21 @@ function promiseSyncReady() {
                   .getService(Components.interfaces.nsISupports)
                   .wrappedJSObject;
   return service.whenLoaded().then(() => {
     UIState.isReady();
     return UIState.refresh();
   });
 }
 
-function checkSendToDeviceItems(expectedItems) {
-  let body =
-    document.getElementById("pageAction-panel-sendToDevice-subview-body");
+function checkSendToDeviceItems(expectedItems, forUrlbar = false) {
+  let bodyID =
+    BrowserPageActions._panelViewNodeIDForActionID("sendToDevice", forUrlbar) +
+    "-body";
+  let body = document.getElementById(bodyID);
   Assert.equal(body.childNodes.length, expectedItems.length);
   for (let i = 0; i < expectedItems.length; i++) {
     let expected = expectedItems[i];
     let actual = body.childNodes[i];
     if (!expected) {
       Assert.equal(actual.localName, "toolbarseparator");
       continue;
     }