Bug 1371219 - Add an inputSource attribute to XULCommandEvent. r=smaug
authorJohann Hofmann <jhofmann@mozilla.com>
Thu, 20 Jul 2017 17:45:56 +0200
changeset 418867 2c98845cb6d5dd69dbb5558ece3b710ab39bc207
parent 418866 fec3b51a474ce462b9ea8d358afc8e7b7fd1270c
child 418868 ee312146ee4ec55ac6829edcf5826c99079ee7cc
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1371219
milestone56.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 1371219 - Add an inputSource attribute to XULCommandEvent. r=smaug In the frontend we need to know if XUL buttons in the toolbar were triggered by a touch event, so we're passing on the inputSource in the command event. MozReview-Commit-ID: DMvgZULk9hT
addon-sdk/source/test/addons/private-browsing-supported/sidebar/utils.js
addon-sdk/source/test/sidebar/utils.js
browser/base/content/browser-gestureSupport.js
browser/base/content/browser.js
browser/components/customizableui/CustomizableWidgets.jsm
browser/components/preferences/in-content-new/tests/browser_applications_selection.js
browser/components/preferences/in-content-new/tests/browser_change_app_handler.js
browser/components/preferences/in-content/tests/browser_applications_selection.js
browser/components/preferences/in-content/tests/browser_change_app_handler.js
devtools/client/performance/test/helpers/input-utils.js
devtools/client/webaudioeditor/test/head.js
devtools/client/webconsole/test/browser_webconsole_bug_601667_filter_buttons.js
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/events/XULCommandEvent.cpp
dom/events/XULCommandEvent.h
dom/interfaces/xul/nsIDOMXULCommandEvent.idl
dom/webidl/XULCommandEvent.webidl
dom/xul/nsXULElement.cpp
dom/xul/test/test_bug1290965.xul
layout/xul/nsButtonBoxFrame.cpp
layout/xul/nsResizerFrame.cpp
layout/xul/nsTitleBarFrame.cpp
mobile/android/chrome/content/SelectHelper.js
widget/cocoa/nsMenuUtilsX.mm
--- a/addon-sdk/source/test/addons/private-browsing-supported/sidebar/utils.js
+++ b/addon-sdk/source/test/addons/private-browsing-supported/sidebar/utils.js
@@ -39,17 +39,17 @@ function makeID(id) {
 }
 exports.makeID = makeID;
 
 function simulateCommand(ele) {
   let window = ele.ownerGlobal;
   let { document } = window;
   var evt = document.createEvent('XULCommandEvent');
   evt.initCommandEvent('command', true, true, window,
-    0, false, false, false, false, null);
+    0, false, false, false, false, null, 0);
   ele.dispatchEvent(evt);
 }
 exports.simulateCommand = simulateCommand;
 
 function simulateClick(ele) {
   let window = ele.ownerGlobal;
   let { document } = window;
   let evt = document.createEvent('MouseEvents');
--- a/addon-sdk/source/test/sidebar/utils.js
+++ b/addon-sdk/source/test/sidebar/utils.js
@@ -46,17 +46,17 @@ function makeID(id) {
 }
 exports.makeID = makeID;
 
 function simulateCommand(ele) {
   let window = ele.ownerGlobal;
   let { document } = window;
   var evt = document.createEvent('XULCommandEvent');
   evt.initCommandEvent('command', true, true, window,
-    0, false, false, false, false, null);
+    0, false, false, false, false, null, 0);
   ele.dispatchEvent(evt);
 }
 exports.simulateCommand = simulateCommand;
 
 function simulateClick(ele) {
   let window = ele.ownerGlobal;
   let { document } = window;
   let evt = document.createEvent('MouseEvents');
--- a/browser/base/content/browser-gestureSupport.js
+++ b/browser/base/content/browser-gestureSupport.js
@@ -329,17 +329,18 @@ var gGestureSupport = {
    */
   _doCommand: function GS__doCommand(aEvent, aCommand) {
     let node = document.getElementById(aCommand);
     if (node) {
       if (node.getAttribute("disabled") != "true") {
         let cmdEvent = document.createEvent("xulcommandevent");
         cmdEvent.initCommandEvent("command", true, true, window, 0,
                                   aEvent.ctrlKey, aEvent.altKey,
-                                  aEvent.shiftKey, aEvent.metaKey, aEvent);
+                                  aEvent.shiftKey, aEvent.metaKey,
+                                  aEvent, aEvent.mozInputSource);
         node.dispatchEvent(cmdEvent);
       }
 
     } else {
       goDoCommand(aCommand);
     }
   },
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -375,17 +375,17 @@ const gClickAndHoldListenersOnElement = 
   _clickHandler(aEvent) {
     if (aEvent.button == 0 &&
         aEvent.target == aEvent.currentTarget &&
         !aEvent.currentTarget.open &&
         !aEvent.currentTarget.disabled) {
       let cmdEvent = document.createEvent("xulcommandevent");
       cmdEvent.initCommandEvent("command", true, true, window, 0,
                                 aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey,
-                                aEvent.metaKey, null);
+                                aEvent.metaKey, null, aEvent.mozInputSource);
       aEvent.currentTarget.dispatchEvent(cmdEvent);
 
       // This is here to cancel the XUL default event
       // dom.click() triggers a command even if there is a click handler
       // however this can now be prevented with preventDefault().
       aEvent.preventDefault();
     }
   },
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -128,17 +128,17 @@ function fillSubviewFromMenuItems(aMenuI
       }
 
       if (!item.hasAttribute("oncommand")) {
         subviewItem.addEventListener("command", event => {
           let newEvent = doc.createEvent("XULCommandEvent");
           newEvent.initCommandEvent(
             event.type, event.bubbles, event.cancelable, event.view,
             event.detail, event.ctrlKey, event.altKey, event.shiftKey,
-            event.metaKey, event.sourceEvent);
+            event.metaKey, event.sourceEvent, 0);
           item.dispatchEvent(newEvent);
         });
       }
     } else {
       continue;
     }
     for (let attr of attrs) {
       let attrVal = menuChild.getAttribute(attr);
--- a/browser/components/preferences/in-content-new/tests/browser_applications_selection.js
+++ b/browser/components/preferences/in-content-new/tests/browser_applications_selection.js
@@ -33,17 +33,17 @@ add_task(async function selectInternalOp
   info("Got list after item was selected");
 
   // Find the "Add Live bookmarks option".
   let chooseItems = list.getElementsByAttribute("action", Ci.nsIHandlerInfo.handleInternally);
   Assert.equal(chooseItems.length, 1, "Should only be one action to handle internally");
 
   // Select the option.
   let cmdEvent = win.document.createEvent("xulcommandevent");
-  cmdEvent.initCommandEvent("command", true, true, win, 0, false, false, false, false, null);
+  cmdEvent.initCommandEvent("command", true, true, win, 0, false, false, false, false, null, 0);
   chooseItems[0].dispatchEvent(cmdEvent);
 
   // Check that we display the correct result.
   list = await waitForCondition(() =>
     win.document.getAnonymousElementByAttribute(feedItem, "class", "actionsMenu"));
   info("Got list after item was selected");
   Assert.ok(list.selectedItem, "Should have a selected item.");
   Assert.equal(list.selectedItem.getAttribute("action"),
--- a/browser/components/preferences/in-content-new/tests/browser_change_app_handler.js
+++ b/browser/components/preferences/in-content-new/tests/browser_change_app_handler.js
@@ -29,17 +29,17 @@ add_task(async function() {
   ok(ourItem.selected, "Should be able to select our item.");
 
   let list = await waitForCondition(() => win.document.getAnonymousElementByAttribute(ourItem, "class", "actionsMenu"));
   info("Got list after item was selected");
 
   let chooseItem = list.firstChild.querySelector(".choose-app-item");
   let dialogLoadedPromise = promiseLoadSubDialog("chrome://global/content/appPicker.xul");
   let cmdEvent = win.document.createEvent("xulcommandevent");
-  cmdEvent.initCommandEvent("command", true, true, win, 0, false, false, false, false, null);
+  cmdEvent.initCommandEvent("command", true, true, win, 0, false, false, false, false, null, 0);
   chooseItem.dispatchEvent(cmdEvent);
 
   let dialog = await dialogLoadedPromise;
   info("Dialog loaded");
 
   let dialogDoc = dialog.document;
   let dialogList = dialogDoc.getElementById("app-picker-listbox");
   dialogList.selectItem(dialogList.firstChild);
@@ -58,17 +58,17 @@ add_task(async function() {
      "App should be visible as preferred item.");
 
 
   // Now try to 'manage' this list:
   dialogLoadedPromise = promiseLoadSubDialog("chrome://browser/content/preferences/applicationManager.xul");
 
   let manageItem = list.firstChild.querySelector(".manage-app-item");
   cmdEvent = win.document.createEvent("xulcommandevent");
-  cmdEvent.initCommandEvent("command", true, true, win, 0, false, false, false, false, null);
+  cmdEvent.initCommandEvent("command", true, true, win, 0, false, false, false, false, null, 0);
   manageItem.dispatchEvent(cmdEvent);
 
   dialog = await dialogLoadedPromise;
   info("Dialog loaded the second time");
 
   dialogDoc = dialog.document;
   dialogList = dialogDoc.getElementById("appList");
   let itemToRemove = dialogList.querySelector('listitem[label="' + selectedApp.name + '"]');
--- a/browser/components/preferences/in-content/tests/browser_applications_selection.js
+++ b/browser/components/preferences/in-content/tests/browser_applications_selection.js
@@ -33,17 +33,17 @@ add_task(async function selectInternalOp
   info("Got list after item was selected");
 
   // Find the "Add Live bookmarks option".
   let chooseItems = list.getElementsByAttribute("action", Ci.nsIHandlerInfo.handleInternally);
   Assert.equal(chooseItems.length, 1, "Should only be one action to handle internally");
 
   // Select the option.
   let cmdEvent = win.document.createEvent("xulcommandevent");
-  cmdEvent.initCommandEvent("command", true, true, win, 0, false, false, false, false, null);
+  cmdEvent.initCommandEvent("command", true, true, win, 0, false, false, false, false, null, 0);
   chooseItems[0].dispatchEvent(cmdEvent);
 
   // Check that we display the correct result.
   list = await waitForCondition(() =>
     win.document.getAnonymousElementByAttribute(feedItem, "class", "actionsMenu"));
   info("Got list after item was selected");
   Assert.ok(list.selectedItem, "Should have a selected item.");
   Assert.equal(list.selectedItem.getAttribute("action"),
--- a/browser/components/preferences/in-content/tests/browser_change_app_handler.js
+++ b/browser/components/preferences/in-content/tests/browser_change_app_handler.js
@@ -28,17 +28,17 @@ add_task(async function() {
   ok(ourItem.selected, "Should be able to select our item.");
 
   let list = await waitForCondition(() => win.document.getAnonymousElementByAttribute(ourItem, "class", "actionsMenu"));
   info("Got list after item was selected");
 
   let chooseItem = list.firstChild.querySelector(".choose-app-item");
   let dialogLoadedPromise = promiseLoadSubDialog("chrome://global/content/appPicker.xul");
   let cmdEvent = win.document.createEvent("xulcommandevent");
-  cmdEvent.initCommandEvent("command", true, true, win, 0, false, false, false, false, null);
+  cmdEvent.initCommandEvent("command", true, true, win, 0, false, false, false, false, null, 0);
   chooseItem.dispatchEvent(cmdEvent);
 
   let dialog = await dialogLoadedPromise;
   info("Dialog loaded");
 
   let dialogDoc = dialog.document;
   let dialogList = dialogDoc.getElementById("app-picker-listbox");
   dialogList.selectItem(dialogList.firstChild);
@@ -57,17 +57,17 @@ add_task(async function() {
      "App should be visible as preferred item.");
 
 
   // Now try to 'manage' this list:
   dialogLoadedPromise = promiseLoadSubDialog("chrome://browser/content/preferences/applicationManager.xul");
 
   let manageItem = list.firstChild.querySelector(".manage-app-item");
   cmdEvent = win.document.createEvent("xulcommandevent");
-  cmdEvent.initCommandEvent("command", true, true, win, 0, false, false, false, false, null);
+  cmdEvent.initCommandEvent("command", true, true, win, 0, false, false, false, false, null, 0);
   manageItem.dispatchEvent(cmdEvent);
 
   dialog = await dialogLoadedPromise;
   info("Dialog loaded the second time");
 
   dialogDoc = dialog.document;
   dialogList = dialogDoc.getElementById("appList");
   let itemToRemove = dialogList.querySelector('listitem[label="' + selectedApp.name + '"]');
--- a/devtools/client/performance/test/helpers/input-utils.js
+++ b/devtools/client/performance/test/helpers/input-utils.js
@@ -6,17 +6,17 @@ exports.HORIZONTAL_AXIS = 1;
 exports.VERTICAL_AXIS = 2;
 
 /**
  * Simulates a command event on an element.
  */
 exports.command = (node) => {
   let ev = node.ownerDocument.createEvent("XULCommandEvent");
   ev.initCommandEvent("command", true, true, node.ownerDocument.defaultView, 0, false,
-                      false, false, false, null);
+                      false, false, false, null, 0);
   node.dispatchEvent(ev);
 };
 
 /**
  * Simulates a click event on a devtools canvas graph.
  */
 exports.clickCanvasGraph = (graph, { x, y }) => {
   x = x || 0;
--- a/devtools/client/webaudioeditor/test/head.js
+++ b/devtools/client/webaudioeditor/test/head.js
@@ -358,17 +358,17 @@ function click(win, element) {
 }
 
 function mouseOver(win, element) {
   EventUtils.sendMouseEvent({ type: "mouseover" }, element, win);
 }
 
 function command(button) {
   let ev = button.ownerDocument.createEvent("XULCommandEvent");
-  ev.initCommandEvent("command", true, true, button.ownerDocument.defaultView, 0, false, false, false, false, null);
+  ev.initCommandEvent("command", true, true, button.ownerDocument.defaultView, 0, false, false, false, false, null, 0);
   button.dispatchEvent(ev);
 }
 
 function isVisible(element) {
   return !element.getAttribute("hidden");
 }
 
 /**
--- a/devtools/client/webconsole/test/browser_webconsole_bug_601667_filter_buttons.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_601667_filter_buttons.js
@@ -253,15 +253,15 @@ function clickButton(node) {
 
 function altClickButton(node) {
   EventUtils.sendMouseEvent({ type: "click", altKey: true }, node);
 }
 
 function chooseMenuItem(node) {
   let event = document.createEvent("XULCommandEvent");
   event.initCommandEvent("command", true, true, window, 0, false, false, false,
-                         false, null);
+                         false, null, 0);
   node.dispatchEvent(event);
 }
 
 function isChecked(node) {
   return node.getAttribute("checked") === "true";
 }
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -6612,30 +6612,31 @@ nsContentUtils::CanAccessNativeAnon()
 /* static */ nsresult
 nsContentUtils::DispatchXULCommand(nsIContent* aTarget,
                                    bool aTrusted,
                                    nsIDOMEvent* aSourceEvent,
                                    nsIPresShell* aShell,
                                    bool aCtrl,
                                    bool aAlt,
                                    bool aShift,
-                                   bool aMeta)
+                                   bool aMeta,
+                                   uint16_t aInputSource)
 {
   NS_ENSURE_STATE(aTarget);
   nsIDocument* doc = aTarget->OwnerDoc();
   nsIPresShell* shell = doc->GetShell();
   nsPresContext* presContext = nullptr;
   if (shell) {
     presContext = shell->GetPresContext();
   }
   RefPtr<XULCommandEvent> xulCommand = new XULCommandEvent(doc, presContext,
                                                            nullptr);
   xulCommand->InitCommandEvent(NS_LITERAL_STRING("command"), true, true,
                                doc->GetInnerWindow(), 0, aCtrl, aAlt, aShift,
-                               aMeta, aSourceEvent);
+                               aMeta, aSourceEvent, aInputSource);
 
   if (aShell) {
     nsEventStatus status = nsEventStatus_eIgnore;
     nsCOMPtr<nsIPresShell> kungFuDeathGrip = aShell;
     return aShell->HandleDOMEventWithTarget(aTarget, xulCommand, &status);
   }
 
   nsCOMPtr<EventTarget> target = do_QueryInterface(aTarget);
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -34,16 +34,17 @@
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/net/ReferrerPolicy.h"
 #include "mozilla/Logging.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/Maybe.h"
 #include "nsIContentPolicy.h"
 #include "nsIDocument.h"
+#include "nsIDOMMouseEvent.h"
 #include "nsPIDOMWindow.h"
 #include "nsRFPService.h"
 
 #if defined(XP_WIN)
 // Undefine LoadImage to prevent naming conflict with Windows.
 #undef LoadImage
 #endif
 
@@ -1989,17 +1990,18 @@ public:
    */
   static nsresult DispatchXULCommand(nsIContent* aTarget,
                                      bool aTrusted,
                                      nsIDOMEvent* aSourceEvent = nullptr,
                                      nsIPresShell* aShell = nullptr,
                                      bool aCtrl = false,
                                      bool aAlt = false,
                                      bool aShift = false,
-                                     bool aMeta = false);
+                                     bool aMeta = false,
+                                     uint16_t inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN);
 
   static bool CheckMayLoad(nsIPrincipal* aPrincipal, nsIChannel* aChannel, bool aAllowIfInheritsPrincipal);
 
   /**
    * The method checks whether the caller can access native anonymous content.
    * If there is no JS in the stack or privileged JS is running, this
    * method returns true, otherwise false.
    */
--- a/dom/events/XULCommandEvent.cpp
+++ b/dom/events/XULCommandEvent.cpp
@@ -87,16 +87,30 @@ XULCommandEvent::MetaKey()
 NS_IMETHODIMP
 XULCommandEvent::GetMetaKey(bool* aIsDown)
 {
   NS_ENSURE_ARG_POINTER(aIsDown);
   *aIsDown = MetaKey();
   return NS_OK;
 }
 
+uint16_t
+XULCommandEvent::InputSource()
+{
+  return mInputSource;
+}
+
+NS_IMETHODIMP
+XULCommandEvent::GetInputSource(uint16_t* aInputSource)
+{
+  NS_ENSURE_ARG_POINTER(aInputSource);
+  *aInputSource = InputSource();
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 XULCommandEvent::GetSourceEvent(nsIDOMEvent** aSourceEvent)
 {
   NS_ENSURE_ARG_POINTER(aSourceEvent);
   nsCOMPtr<nsIDOMEvent> event = GetSourceEvent();
   event.forget(aSourceEvent);
   return NS_OK;
 }
@@ -106,26 +120,28 @@ XULCommandEvent::InitCommandEvent(const 
                                   bool aCanBubble,
                                   bool aCancelable,
                                   mozIDOMWindow* aView,
                                   int32_t aDetail,
                                   bool aCtrlKey,
                                   bool aAltKey,
                                   bool aShiftKey,
                                   bool aMetaKey,
-                                  nsIDOMEvent* aSourceEvent)
+                                  nsIDOMEvent* aSourceEvent,
+                                  uint16_t aInputSource)
 {
   NS_ENSURE_TRUE(!mEvent->mFlags.mIsBeingDispatched, NS_OK);
 
   auto* view = nsGlobalWindow::Cast(nsPIDOMWindowInner::From(aView));
   UIEvent::InitUIEvent(aType, aCanBubble, aCancelable, view, aDetail);
 
   mEvent->AsInputEvent()->InitBasicModifiers(aCtrlKey, aAltKey,
                                              aShiftKey, aMetaKey);
   mSourceEvent = aSourceEvent;
+  mInputSource = aInputSource;
 
   return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
 
 using namespace mozilla;
--- a/dom/events/XULCommandEvent.h
+++ b/dom/events/XULCommandEvent.h
@@ -35,41 +35,44 @@ public:
   {
     return XULCommandEventBinding::Wrap(aCx, this, aGivenProto);
   }
 
   bool AltKey();
   bool CtrlKey();
   bool ShiftKey();
   bool MetaKey();
+  uint16_t InputSource();
 
   already_AddRefed<Event> GetSourceEvent()
   {
     RefPtr<Event> e =
       mSourceEvent ? mSourceEvent->InternalDOMEvent() : nullptr;
     return e.forget();
   }
 
   void InitCommandEvent(const nsAString& aType,
                         bool aCanBubble, bool aCancelable,
                         nsGlobalWindow* aView,
                         int32_t aDetail,
                         bool aCtrlKey, bool aAltKey,
                         bool aShiftKey, bool aMetaKey,
-                        Event* aSourceEvent)
+                        Event* aSourceEvent,
+                        uint16_t aInputSource)
   {
     InitCommandEvent(aType, aCanBubble, aCancelable, aView->AsInner(),
                      aDetail, aCtrlKey, aAltKey, aShiftKey, aMetaKey,
-                     aSourceEvent);
+                     aSourceEvent, aInputSource);
   }
 
 protected:
   ~XULCommandEvent() {}
 
   nsCOMPtr<nsIDOMEvent> mSourceEvent;
+  uint16_t mInputSource;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 already_AddRefed<mozilla::dom::XULCommandEvent>
 NS_NewDOMXULCommandEvent(mozilla::dom::EventTarget* aOwner,
                          nsPresContext* aPresContext,
--- a/dom/interfaces/xul/nsIDOMXULCommandEvent.idl
+++ b/dom/interfaces/xul/nsIDOMXULCommandEvent.idl
@@ -19,16 +19,21 @@ interface nsIDOMXULCommandEvent : nsIDOM
    * events.
    */
   readonly attribute boolean ctrlKey;
   readonly attribute boolean shiftKey;
   readonly attribute boolean altKey;
   readonly attribute boolean metaKey;
 
   /**
+   * The input source, if this event was triggered by a mouse event.
+  */
+  readonly attribute unsigned short inputSource;
+
+  /**
    * If the command event was redispatched because of a command= attribute
    * on the original target, sourceEvent will be set to the original DOM Event.
    * Otherwise, sourceEvent is null.
    */
   readonly attribute nsIDOMEvent sourceEvent;
 
   /**
    * Creates a new command event with the given attributes.
@@ -37,10 +42,11 @@ interface nsIDOMXULCommandEvent : nsIDOM
                         in boolean canBubbleArg,
                         in boolean cancelableArg,
                         in mozIDOMWindow viewArg,
                         in long detailArg,
                         in boolean ctrlKeyArg,
                         in boolean altKeyArg,
                         in boolean shiftKeyArg,
                         in boolean metaKeyArg,
-                        in nsIDOMEvent sourceEvent);
+                        in nsIDOMEvent sourceEvent,
+                        in unsigned short inputSource);
 };
--- a/dom/webidl/XULCommandEvent.webidl
+++ b/dom/webidl/XULCommandEvent.webidl
@@ -7,21 +7,24 @@
 [Func="IsChromeOrXBL"]
 interface XULCommandEvent : UIEvent
 {
   readonly attribute boolean ctrlKey;
   readonly attribute boolean shiftKey;
   readonly attribute boolean altKey;
   readonly attribute boolean metaKey;
 
+  readonly attribute unsigned short inputSource;
+
   readonly attribute Event? sourceEvent;
 
   void initCommandEvent(DOMString type,
                         optional boolean canBubble = false,
                         optional boolean cancelable = false,
                         optional Window? view = null,
                         optional long detail = 0,
                         optional boolean ctrlKey = false,
                         optional boolean altKey = false,
                         optional boolean shiftKey = false,
                         optional boolean metaKey = false,
-                        optional Event? sourceEvent = null);
+                        optional Event? sourceEvent = null,
+                        optional unsigned short inputSource = 0);
 };
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -1250,38 +1250,41 @@ nsXULElement::DispatchXULCommand(const E
     domDoc->GetElementById(aCommand, getter_AddRefs(commandElt));
     nsCOMPtr<nsIContent> commandContent(do_QueryInterface(commandElt));
     if (commandContent) {
         // Create a new command event to dispatch to the element
         // pointed to by the command attribute. The new event's
         // sourceEvent will be the original command event that we're
         // handling.
         nsCOMPtr<nsIDOMEvent> domEvent = aVisitor.mDOMEvent;
+        uint16_t inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
         while (domEvent) {
             Event* event = domEvent->InternalDOMEvent();
             NS_ENSURE_STATE(!SameCOMIdentity(event->GetOriginalTarget(),
                                             commandContent));
             nsCOMPtr<nsIDOMXULCommandEvent> commandEvent =
                 do_QueryInterface(domEvent);
             if (commandEvent) {
                 commandEvent->GetSourceEvent(getter_AddRefs(domEvent));
+                commandEvent->GetInputSource(&inputSource);
             } else {
                 domEvent = nullptr;
             }
         }
         WidgetInputEvent* orig = aVisitor.mEvent->AsInputEvent();
         nsContentUtils::DispatchXULCommand(
           commandContent,
           orig->IsTrusted(),
           aVisitor.mDOMEvent,
           nullptr,
           orig->IsControl(),
           orig->IsAlt(),
           orig->IsShift(),
-          orig->IsMeta());
+          orig->IsMeta(),
+          inputSource);
     } else {
         NS_WARNING("A XUL element is attached to a command that doesn't exist!\n");
     }
     return NS_OK;
 }
 
 nsresult
 nsXULElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
--- a/dom/xul/test/test_bug1290965.xul
+++ b/dom/xul/test/test_bug1290965.xul
@@ -12,25 +12,25 @@
     let countera = 0;
     let counterb = 0;
 
     aEl.addEventListener('click', function (aEvent) {
       aEvent.preventDefault();
       let cmdEvent = document.createEvent("xulcommandevent");
       cmdEvent.initCommandEvent("command", true, true, window, 0,
                                 aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey,
-                                aEvent.metaKey, null);
+                                aEvent.metaKey, null, aEvent.mozInputSource);
       aEvent.currentTarget.dispatchEvent(cmdEvent);
     });
 
     bEl.addEventListener('click', function (aEvent) {
       let cmdEvent = document.createEvent("xulcommandevent");
       cmdEvent.initCommandEvent("command", true, true, window, 0,
                                 aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey,
-                                aEvent.metaKey, null);
+                                aEvent.metaKey, null, aEvent.mozInputSource);
       aEvent.currentTarget.dispatchEvent(cmdEvent);
     });
 
     bEl.click();
     aEl.click();
 
     is(countera, 1, "Counter should be one as event fires once");
     is(counterb, 2, "Counter should be two as event fires twice");
--- a/layout/xul/nsButtonBoxFrame.cpp
+++ b/layout/xul/nsButtonBoxFrame.cpp
@@ -206,26 +206,33 @@ nsButtonBoxFrame::DoMouseClick(WidgetGUI
                             nsGkAtoms::_true, eCaseMatters))
     return;
 
   // Execute the oncommand event handler.
   bool isShift = false;
   bool isControl = false;
   bool isAlt = false;
   bool isMeta = false;
+  uint16_t inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
+
   if(aEvent) {
     WidgetInputEvent* inputEvent = aEvent->AsInputEvent();
     isShift = inputEvent->IsShift();
     isControl = inputEvent->IsControl();
     isAlt = inputEvent->IsAlt();
     isMeta = inputEvent->IsMeta();
+
+    WidgetMouseEventBase* mouseEvent = aEvent->AsMouseEventBase();
+    if (mouseEvent) {
+      inputSource = mouseEvent->inputSource;
+    }
   }
 
   // Have the content handle the event, propagating it according to normal DOM rules.
   nsCOMPtr<nsIPresShell> shell = PresContext()->GetPresShell();
   if (shell) {
     nsContentUtils::DispatchXULCommand(mContent,
                                        aEvent ?
                                          aEvent->IsTrusted() : aTrustEvent,
                                        nullptr, shell,
-                                       isControl, isAlt, isShift, isMeta);
+                                       isControl, isAlt, isShift, isMeta, inputSource);
   }
 }
--- a/layout/xul/nsResizerFrame.cpp
+++ b/layout/xul/nsResizerFrame.cpp
@@ -528,11 +528,28 @@ nsResizerFrame::GetDirection()
   }
 
   return directions[index];
 }
 
 void
 nsResizerFrame::MouseClicked(WidgetMouseEvent* aEvent)
 {
+  bool isTrusted = false;
+  bool isShift = false;
+  bool isControl = false;
+  bool isAlt = false;
+  bool isMeta = false;
+  uint16_t inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
+
+  if(aEvent) {
+    isShift = aEvent->IsShift();
+    isControl = aEvent->IsControl();
+    isAlt = aEvent->IsAlt();
+    isMeta = aEvent->IsMeta();
+    inputSource = aEvent->inputSource;
+  }
+
   // Execute the oncommand event handler.
-  nsContentUtils::DispatchXULCommand(mContent, aEvent && aEvent->IsTrusted());
+  nsContentUtils::DispatchXULCommand(mContent, isTrusted, nullptr,
+                                     nullptr, isControl, isAlt,
+                                     isShift, isMeta, inputSource);
 }
--- a/layout/xul/nsTitleBarFrame.cpp
+++ b/layout/xul/nsTitleBarFrame.cpp
@@ -162,11 +162,28 @@ nsTitleBarFrame::HandleEvent(nsPresConte
     return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
   else
     return NS_OK;
 }
 
 void
 nsTitleBarFrame::MouseClicked(WidgetMouseEvent* aEvent)
 {
+  bool isTrusted = false;
+  bool isShift = false;
+  bool isControl = false;
+  bool isAlt = false;
+  bool isMeta = false;
+  uint16_t inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
+
+  if(aEvent) {
+    isShift = aEvent->IsShift();
+    isControl = aEvent->IsControl();
+    isAlt = aEvent->IsAlt();
+    isMeta = aEvent->IsMeta();
+    inputSource = aEvent->inputSource;
+  }
+
   // Execute the oncommand event handler.
-  nsContentUtils::DispatchXULCommand(mContent, aEvent && aEvent->IsTrusted());
+  nsContentUtils::DispatchXULCommand(mContent, isTrusted, nullptr,
+                                     nullptr, isControl, isAlt,
+                                     isShift, isMeta, inputSource);
 }
--- a/mobile/android/chrome/content/SelectHelper.js
+++ b/mobile/android/chrome/content/SelectHelper.js
@@ -149,17 +149,17 @@ var SelectHelper = {
       element.dispatchEvent(event);
     }, 0);
   },
 
   fireOnCommand: function(element) {
     let win = element.ownerGlobal;
     let event = element.ownerDocument.createEvent("XULCommandEvent");
     event.initCommandEvent("command", true, true, element.defaultView, 0,
-        false, false, false, false, null);
+        false, false, false, false, null, 0);
     win.setTimeout(function() {
       element.dispatchEvent(event);
     }, 0);
   },
 
   _isDisabledElement : function(element) {
     let currentElement = element;
     while (currentElement) {
--- a/widget/cocoa/nsMenuUtilsX.mm
+++ b/widget/cocoa/nsMenuUtilsX.mm
@@ -35,17 +35,17 @@ void nsMenuUtilsX::DispatchCommandTo(nsI
 
     // FIXME: Should probably figure out how to init this with the actual
     // pressed keys, but this is a big old edge case anyway. -dwh
     if (command &&
         NS_SUCCEEDED(command->InitCommandEvent(NS_LITERAL_STRING("command"),
                                                true, true,
                                                doc->GetInnerWindow(), 0,
                                                false, false, false,
-                                               false, nullptr))) {
+                                               false, nullptr, 0))) {
       event->SetTrusted(true);
       bool dummy;
       aTargetContent->DispatchEvent(event, &dummy);
     }
   }
 }
 
 NSString* nsMenuUtilsX::GetTruncatedCocoaLabel(const nsString& itemLabel)