Bug 1371219 - Add an inputSource attribute to XULCommandEvent. r=smaug draft
authorJohann Hofmann <jhofmann@mozilla.com>
Thu, 20 Jul 2017 17:45:56 +0200
changeset 612961 c455c8ec77e439bf02c1e3e8d34a36e1fb5e3bd0
parent 612696 7d2e89fb92331d7e4296391213c1e63db628e046
child 612962 71cec7055786687a23325ec667d59a2e630f5c57
push id69670
push userbmo:jhofmann@mozilla.com
push dateFri, 21 Jul 2017 09:38:48 +0000
reviewerssmaug
bugs1371219
milestone56.0a1
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
@@ -6605,30 +6605,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
 
@@ -1982,17 +1983,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)