Bug 1446961, add dictionary second argument directly to PopupBoxObject::OpenPopup as supported in popup.xml#openPopup, r=bz
authorNeil Deakin <neil@mozilla.com>
Fri, 27 Apr 2018 11:04:37 -0400
changeset 416132 46d7f3cc71027fd301be83c9d462b94cdcf27f7b
parent 416131 962c081b13149f3d35f1f38bd514697aacd1a82a
child 416133 12e768652a88d11b9c09ec63e1a5df00cdab4dc2
push id33917
push userapavel@mozilla.com
push dateSat, 28 Apr 2018 17:30:55 +0000
treeherdermozilla-central@08f68e2c892c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1446961
milestone61.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 1446961, add dictionary second argument directly to PopupBoxObject::OpenPopup as supported in popup.xml#openPopup, r=bz
dom/webidl/PopupBoxObject.webidl
layout/xul/PopupBoxObject.cpp
layout/xul/PopupBoxObject.h
toolkit/content/tests/chrome/popup_trigger.js
--- a/dom/webidl/PopupBoxObject.webidl
+++ b/dom/webidl/PopupBoxObject.webidl
@@ -1,13 +1,30 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+dictionary OpenPopupOptions {
+  // manner in which to anchor the popup to node
+  DOMString position = "";
+  // horizontal offset
+  long x = 0;
+  // vertical offset
+  long y = 0;
+  // isContextMenu true for context menus, false for other popups
+  boolean isContextMenu = false;
+  // true if popup node attributes override position
+  boolean attributesOverride = false;
+  // triggerEvent the event that triggered this popup (mouse click for example)
+  Event? triggerEvent = null;
+};
+
+typedef (DOMString or OpenPopupOptions) StringOrOpenPopupOptions;
+
 [Func="IsChromeOrXBL"]
 interface PopupBoxObject : BoxObject
 {
   /**
    * Allow the popup to automatically position itself.
    */
   attribute boolean autoPosition;
 
@@ -35,30 +52,30 @@ interface PopupBoxObject : BoxObject
    * be used to move to the left and upwards respectively.
    *
    * Unanchored popups may be created by supplying null as the anchor node.
    * An unanchored popup appears at the position specified by x and y,
    * relative to the viewport of the document containing the popup node. In
    * this case, position and attributesOverride are ignored.
    *
    * @param anchorElement the node to anchor the popup to, may be null
-   * @param position manner is which to anchor the popup to node
+   * @param options either options to use, or a string position
    * @param x horizontal offset
    * @param y vertical offset
    * @param isContextMenu true for context menus, false for other popups
    * @param attributesOverride true if popup node attributes override position
    * @param triggerEvent the event that triggered this popup (mouse click for example)
    */
-  void openPopup(Element? anchorElement,
-                 optional DOMString position = "",
-                 long x,
-                 long y,
-                 boolean isContextMenu,
-                 boolean attributesOverride,
-                 Event? triggerEvent);
+  void openPopup(optional Element? anchorElement = null,
+                 optional StringOrOpenPopupOptions options,
+                 optional long x = 0,
+                 optional long y = 0,
+                 optional boolean isContextMenu = false,
+                 optional boolean attributesOverride = false,
+                 optional Event? triggerEvent = null);
 
   /**
    * Open the popup at a specific screen position specified by x and y. This
    * position may be adjusted if it would cause the popup to be off of the
    * screen. The x and y coordinates are measured in CSS pixels, and like all
    * screen coordinates, are given relative to the top left of the primary
    * screen.
    *
--- a/layout/xul/PopupBoxObject.cpp
+++ b/layout/xul/PopupBoxObject.cpp
@@ -46,26 +46,40 @@ PopupBoxObject::HidePopup(bool aCancel)
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   if (pm && mContent) {
     pm->HidePopup(mContent, false, true, false, aCancel);
   }
 }
 
 void
 PopupBoxObject::OpenPopup(Element* aAnchorElement,
-                          const nsAString& aPosition,
+                          const StringOrOpenPopupOptions& aOptions,
                           int32_t aXPos, int32_t aYPos,
                           bool aIsContextMenu,
                           bool aAttributesOverride,
                           Event* aTriggerEvent)
 {
+  nsAutoString position;
+  if (aOptions.IsOpenPopupOptions()) {
+    const OpenPopupOptions& options = aOptions.GetAsOpenPopupOptions();
+    position = options.mPosition;
+    aXPos = options.mX;
+    aYPos = options.mY;
+    aIsContextMenu = options.mIsContextMenu;
+    aAttributesOverride = options.mAttributesOverride;
+    aTriggerEvent = options.mTriggerEvent;
+  }
+  else {
+    position = aOptions.GetAsString();
+  }
+
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   if (pm && mContent) {
     nsCOMPtr<nsIContent> anchorContent(do_QueryInterface(aAnchorElement));
-    pm->ShowPopup(mContent, anchorContent, aPosition, aXPos, aYPos,
+    pm->ShowPopup(mContent, anchorContent, position, aXPos, aYPos,
                   aIsContextMenu, aAttributesOverride, false, aTriggerEvent);
   }
 }
 
 void
 PopupBoxObject::OpenPopupAtScreen(int32_t aXPos, int32_t aYPos,
                                   bool aIsContextMenu,
                                   Event* aTriggerEvent)
--- a/layout/xul/PopupBoxObject.h
+++ b/layout/xul/PopupBoxObject.h
@@ -17,33 +17,34 @@
 struct JSContext;
 
 namespace mozilla {
 namespace dom {
 
 class DOMRect;
 class Element;
 class Event;
+class StringOrOpenPopupOptions;
 
 class PopupBoxObject final : public BoxObject
 {
 public:
   NS_INLINE_DECL_REFCOUNTING_INHERITED(PopupBoxObject, BoxObject)
 
   PopupBoxObject();
 
   nsIContent* GetParentObject() const;
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   bool AutoPosition();
 
   void SetAutoPosition(bool aShouldAutoPosition);
 
   void OpenPopup(Element* aAnchorElement,
-                 const nsAString& aPosition,
+                 const mozilla::dom::StringOrOpenPopupOptions& aOptions,
                  int32_t aXPos,
                  int32_t aYPos,
                  bool aIsContextMenu, bool aAttributesOverride,
                  Event* aTriggerEvent);
 
   void OpenPopupAtScreen(int32_t aXPos,
                          int32_t aYPos,
                          bool aIsContextMenu,
--- a/toolkit/content/tests/chrome/popup_trigger.js
+++ b/toolkit/content/tests/chrome/popup_trigger.js
@@ -775,16 +775,44 @@ var popupTests = [
     synthesizeMouse(gTrigger, 4, 4, { });
   },
   result(testname, step) {
     checkClosed("trigger", testname);
     gTrigger.removeAttribute("disabled");
   }
 },
 {
+  // openPopup using object as position argument
+  testname: "openPopup with object argument",
+  events: [ "popupshowing thepopup 0000", "popupshown thepopup" ],
+  autohide: "thepopup",
+  test(testname, step) {
+    gMenuPopup.openPopup(gTrigger, { position: "before_start", x: 5, y: 7 });
+    checkOpen("trigger", testname);
+  },
+  result(testname, step) {
+    var triggerrect = gTrigger.getBoundingClientRect();
+    var popuprect = gMenuPopup.getBoundingClientRect();
+    is(Math.round(popuprect.left), Math.round(triggerrect.left + 5), testname + " x position ");
+    is(Math.round(popuprect.bottom), Math.round(triggerrect.top + 7), testname + " y position ");
+  }
+},
+{
+  // openPopup using object as position argument with event
+  testname: "openPopup with object argument with event",
+  events: [ "popupshowing thepopup 1000", "popupshown thepopup" ],
+  autohide: "thepopup",
+  test(testname, step) {
+    gMenuPopup.openPopup(gTrigger, { position: "after_start", x: 0, y: 0,
+                                     triggerEvent: new MouseEvent("mousedown", { altKey: true })
+                                    });
+    checkOpen("trigger", testname);
+  }
+},
+{
   // openPopup should open the menu synchronously, however popupshown
   // is fired asynchronously
   testname: "openPopup synchronous",
   events: [ "popupshowing thepopup", "popupshowing submenupopup",
             "popupshown thepopup", "DOMMenuItemActive submenu",
             "popupshown submenupopup" ],
   test(testname, step) {
     gMenuPopup.openPopup(gTrigger, "after_start", 0, 0, false, true);