Bug 1395154 - If Send Page to Device pageAction is Pinned, it should dismiss when a user sends a tab. r=jaws
authorDrew Willcoxon <adw@mozilla.com>
Thu, 31 Aug 2017 15:27:22 -0700
changeset 378249 3357426af9840a372ba01467e1bd7c54a16c392f
parent 378248 de9a050e5ff08453e47e21e2490a4d40db15df49
child 378250 d98a26d6f1c734658a3829c0c1898b889cf16155
push id50239
push userdwillcoxon@mozilla.com
push dateFri, 01 Sep 2017 17:43:04 +0000
treeherderautoland@3357426af984 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws
bugs1395154
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);
@@ -370,49 +369,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;
     }