Bug 1461522 - Update browser_toolbox_zoom_popup.js to handle doorhanger menus too; r?jdescottes draft
authorBrian Birtles <birtles@gmail.com>
Thu, 28 Jun 2018 15:14:40 +0900
changeset 813906 375096e708b2b85363ab8220b20e7d35ed772860
parent 813905 da24e12f5324de58d5617343356391fb501517e0
child 813907 9691534455ed45727efca0bce98f65693c4f55d9
push id115042
push userbbirtles@mozilla.com
push dateWed, 04 Jul 2018 04:36:27 +0000
reviewersjdescottes
bugs1461522
milestone63.0a1
Bug 1461522 - Update browser_toolbox_zoom_popup.js to handle doorhanger menus too; r?jdescottes MozReview-Commit-ID: 6OoqIeW8Agk
devtools/client/framework/test/browser_toolbox_zoom_popup.js
--- a/devtools/client/framework/test/browser_toolbox_zoom_popup.js
+++ b/devtools/client/framework/test/browser_toolbox_zoom_popup.js
@@ -2,17 +2,18 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test the popup menu position when zooming in the devtools panel.
 
 const {Toolbox} = require("devtools/client/framework/toolbox");
 
-// Use simple URL in order to prevent displacing the left positon of frames menu.
+// Use a simple URL in order to prevent displacing the left position of the
+// frames menu.
 const TEST_URL = "data:text/html;charset=utf-8,<iframe/>";
 
 add_task(async function() {
   registerCleanupFunction(async function() {
     Services.prefs.clearUserPref("devtools.toolbox.zoomValue");
   });
   const zoom = 1.4;
   Services.prefs.setCharPref("devtools.toolbox.zoomValue", zoom.toString(10));
@@ -31,20 +32,22 @@ add_task(async function() {
       .getInterface(Ci.nsIDOMWindowUtils);
 
   info("Waiting for the toolbox window will to be rendered with zoom x1.4");
   await waitUntil(() => {
     return parseFloat(windowUtils.fullZoom.toFixed(1)) === zoom;
   });
 
   info("Resizing and moving the toolbox window in order to display the chevron menu.");
-  // If window is displayed bottom of screen, menu might be displayed above of button.
+  // If the window is displayed bottom of screen, the menu might be displayed
+  // above the button so move it to the top of the screen first.
   hostWindow.moveTo(0, 0);
 
-  // This size will display inspector's tabs menu button and chevron menu button of toolbox.
+  // Shrink the width of the window such that the inspector's tab menu button
+  // and chevron button are visible.
   const prevTabs = toolbox.doc.querySelectorAll(".devtools-tab").length;
   hostWindow.resizeTo(400, hostWindow.outerHeight);
   await waitUntil(() => {
     return hostWindow.screen.top === 0 &&
       hostWindow.screen.left === 0 &&
       hostWindow.outerWidth === 400 &&
       toolbox.doc.getElementById("tools-chevron-menu-button") &&
       inspector.panelDoc.querySelector(".all-tabs-menu") &&
@@ -53,55 +56,107 @@ add_task(async function() {
 
   const menuList =
     [toolbox.win.document.getElementById("toolbox-meatball-menu-button"),
      toolbox.win.document.getElementById("command-button-frames"),
      toolbox.win.document.getElementById("tools-chevron-menu-button"),
      inspector.panelDoc.querySelector(".all-tabs-menu")];
 
   for (const menu of menuList) {
-    const [btnRect, menuRect] = await getButtonAndMenuRects(toolbox, menu);
+    const { buttonBounds, menuType, menuBounds, arrowBounds } =
+      await getButtonAndMenuInfo(toolbox.doc, menu);
 
-    // Allow rounded error and platform offset value.
-    // horizontal : eIntID_ContextMenuOffsetHorizontal of GTK and Windows uses 2.
-    // vertical: eIntID_ContextMenuOffsetVertical of macOS uses -6.
-    const xDelta = Math.abs(menuRect.left - btnRect.left);
-    const yDelta = Math.abs(menuRect.top - btnRect.bottom);
-    ok(xDelta < 2, "xDelta is lower than 2: " + xDelta + ". #" + menu.id);
-    ok(yDelta < 6, "yDelta is lower than 6: " + yDelta + ". #" + menu.id);
+    switch (menuType) {
+      case "native":
+        {
+          // Allow rounded error and platform offset value.
+          // horizontal : eIntID_ContextMenuOffsetHorizontal of GTK and Windows
+          //              uses 2.
+          // vertical: eIntID_ContextMenuOffsetVertical of macOS uses -6.
+          const xDelta = Math.abs(menuBounds.left - buttonBounds.left);
+          const yDelta = Math.abs(menuBounds.top - buttonBounds.bottom);
+          ok(xDelta < 2, "xDelta is lower than 2: " + xDelta + ". #" + menu.id);
+          ok(yDelta < 6, "yDelta is lower than 6: " + yDelta + ". #" + menu.id);
+        }
+        break;
+
+      case "doorhanger":
+        {
+          // Calculate the center of the button and center of the arrow and
+          // check they align.
+          const buttonCenter = buttonBounds.left + buttonBounds.width / 2;
+          const arrowCenter = arrowBounds.left + arrowBounds.width / 2;
+          const delta = Math.abs(arrowCenter - buttonCenter);
+          ok(delta < 1, "Center of arrow is within 1px of button center" +
+             ` (delta: ${delta})`);
+        }
+        break;
+    }
   }
 
   const onResize = once(hostWindow, "resize");
   hostWindow.resizeTo(originWidth, originHeight);
   await onResize;
 
   await toolbox.destroy();
   gBrowser.removeCurrentTab();
 });
 
 /**
- * Getting the rectangle of the menu button and popup menu.
- *  - The menu button rectangle will be calculated from specified button.
- *  - The popup menu rectangle will be calculated from displayed popup menu
- *    which clicking the specified button.
+ * Get the bounds of a menu button and its popup panel. The popup panel is
+ * measured by clicking the menu button and looking for its panel (and then
+ * hiding it again).
+ *
+ * @param {Object} doc
+ *        The toolbox document to query.
+ * @param {Object} menuButton
+ *        The button whose size and popup size we should measure.
+ * @return {Object}
+ *         An object with the following properties:
+ *         - buttonBounds {DOMRect} Bounds of the button.
+ *         - menuType {string} Type of the menu, "native" or "doorhanger".
+ *         - menuBounds {DOMRect} Bounds of the menu panel.
+ *         - arrowBounds {DOMRect|null} Bounds of the arrow. Only set when
+ *                       menuType is "doorhanger", null otherwise.
  */
-async function getButtonAndMenuRects(toolbox, menuButton) {
+async function getButtonAndMenuInfo(doc, menuButton) {
   info("Show popup menu with click event.");
   menuButton.click();
 
-  const popupset = toolbox.doc.querySelector("popupset");
   let menuPopup;
-  await waitUntil(() => {
-    menuPopup = popupset.querySelector("menupopup[menu-api=\"true\"]");
-    return !!menuPopup && menuPopup.state === "open";
-  });
+  let menuType;
+  let arrowBounds = null;
+  if (menuButton.hasAttribute("aria-controls")) {
+    menuType = "doorhanger";
+    menuPopup = doc.getElementById(menuButton.getAttribute("aria-controls"));
+    await waitUntil(() => menuPopup.classList.contains("tooltip-visible"));
+  } else {
+    menuType = "native";
+    const popupset = doc.querySelector("popupset");
+    await waitUntil(() => {
+      menuPopup = popupset.querySelector("menupopup[menu-api=\"true\"]");
+      return !!menuPopup && menuPopup.state === "open";
+    });
+  }
   ok(menuPopup, "Menu popup is displayed.");
 
-  const btnRect = menuButton.getBoxQuads({relativeTo: toolbox.doc})[0].getBounds();
-  const menuRect = menuPopup.getBoxQuads({relativeTo: toolbox.doc})[0].getBounds();
+  const buttonBounds = menuButton
+    .getBoxQuads({ relativeTo: doc })[0]
+    .getBounds();
+  const menuBounds = menuPopup.getBoxQuads({ relativeTo: doc })[0].getBounds();
+
+  if (menuType === "doorhanger") {
+    const arrow = menuPopup.querySelector(".tooltip-arrow");
+    arrowBounds = arrow.getBoxQuads({ relativeTo: doc })[0].getBounds();
+  }
 
   info("Hide popup menu.");
-  const onPopupHidden = once(menuPopup, "popuphidden");
-  menuPopup.hidePopup();
-  await onPopupHidden;
+  if (menuType === "doorhanger") {
+    EventUtils.sendKey("Escape", doc.defaultView);
+    await waitUntil(() => !menuPopup.classList.contains("tooltip-visible"));
+  } else {
+    const popupHidden = once(menuPopup, "popuphidden");
+    menuPopup.hidePopup();
+    await popupHidden;
+  }
 
-  return [btnRect, menuRect];
+  return { buttonBounds, menuType, menuBounds, arrowBounds };
 }