Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 09 Jan 2014 13:01:11 +0100
changeset 178704 28f2bd3bfb5e59d82c19a1c772cef240aae7dcaa
parent 178703 00d284a3a05afa7ee15257b216177ed3b603c2e1 (current diff)
parent 178694 124d80d7556d26d24167aeac49bd4493c781b975 (diff)
child 178705 434667e5fdeece1c6e8e0287eb0762cdb0a9831e
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone29.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
Merge mozilla-central to mozilla-inbound
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -373,18 +373,17 @@ let AdbController = {
       if (this.DEBUG) {
         this.debug("updateState: Waiting for all vars to be initialized");
       }
       return;
     }
 
     // Check if we have a remote debugging session going on. If so, we won't
     // disable adb even if the screen is locked.
-    let isDebugging = DebuggerServer._connections &&
-                      Object.keys(DebuggerServer._connections).length > 0;
+    let isDebugging = RemoteDebugger.isDebugging;
     if (this.DEBUG) {
       this.debug("isDebugging=" + isDebugging);
     }
 
     // If USB Mass Storage, USB tethering, or a debug session is active,
     // then we don't want to disable adb in an automatic fashion (i.e.
     // when the screen locks or due to timeout).
     let sysUsbConfig = libcutils.property_get("sys.usb.config");
@@ -455,31 +454,57 @@ let AdbController = {
 };
 
 SettingsListener.observe("lockscreen.locked", false,
                          AdbController.setLockscreenState.bind(AdbController));
 SettingsListener.observe("lockscreen.enabled", false,
                          AdbController.setLockscreenEnabled.bind(AdbController));
 #endif
 
+// Keep the old setting to not break people that won't have updated
+// gaia and gecko.
 SettingsListener.observe('devtools.debugger.remote-enabled', false, function(value) {
   Services.prefs.setBoolPref('devtools.debugger.remote-enabled', value);
   // This preference is consulted during startup
   Services.prefs.savePrefFile(null);
   try {
     value ? RemoteDebugger.start() : RemoteDebugger.stop();
   } catch(e) {
     dump("Error while initializing devtools: " + e + "\n" + e.stack + "\n");
   }
 
 #ifdef MOZ_WIDGET_GONK
   AdbController.setRemoteDebuggerState(value);
 #endif
 });
 
+SettingsListener.observe('debugger.remote-mode', false, function(value) {
+  if (['disabled', 'adb-only', 'adb-devtools'].indexOf(value) == -1) {
+    dump('Illegal value for debugger.remote-mode: ' + value + '\n');
+    return;
+  }
+
+  Services.prefs.setBoolPref('devtools.debugger.remote-enabled',
+                             value == 'adb-devtools');
+  // This preference is consulted during startup
+  Services.prefs.savePrefFile(null);
+
+  try {
+    (value == 'adb-devtools') ? RemoteDebugger.start()
+                              : RemoteDebugger.stop();
+  } catch(e) {
+    dump("Error while initializing devtools: " + e + "\n" + e.stack + "\n");
+  }
+
+#ifdef MOZ_WIDGET_GONK
+  AdbController.setRemoteDebuggerState(value != 'disabled');
+#endif
+});
+
+
 SettingsListener.observe('debug.log-animations.enabled', false, function(value) {
   Services.prefs.setBoolPref('layers.offmainthreadcomposition.log-animations', value);
 });
 
 // =================== Device Storage ====================
 SettingsListener.observe('device.storage.writable.name', 'sdcard', function(value) {
   if (Services.prefs.getPrefType('device.storage.writable.name') != Ci.nsIPrefBranch.PREF_STRING) {
     // We clear the pref because it used to be erroneously written as a bool
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -1026,16 +1026,17 @@ let IndexedDBPromptHelper = {
                        Ci.nsIPermissionManager.DENY_ACTION);
     }, 0);
   }
 }
 
 let RemoteDebugger = {
   _promptDone: false,
   _promptAnswer: false,
+  _running: false,
 
   prompt: function debugger_prompt() {
     this._promptDone = false;
 
     shell.sendChromeEvent({
       "type": "remote-debugger-prompt"
     });
 
@@ -1046,18 +1047,31 @@ let RemoteDebugger = {
     return this._promptAnswer;
   },
 
   handleEvent: function debugger_handleEvent(detail) {
     this._promptAnswer = detail.value;
     this._promptDone = true;
   },
 
+  get isDebugging() {
+    if (!this._running) {
+      return false;
+    }
+
+    return DebuggerServer._connections &&
+           Object.keys(DebuggerServer._connections).length > 0;
+  },
+
   // Start the debugger server.
   start: function debugger_start() {
+    if (this._running) {
+      return;
+    }
+
     if (!DebuggerServer.initialized) {
       // Ask for remote connections.
       DebuggerServer.init(this.prompt.bind(this));
       DebuggerServer.chromeWindowType = "navigator:browser";
       DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webbrowser.js");
       // Until we implement unix domain socket, we enable content actors
       // only on development devices
       if (Services.prefs.getBoolPref("devtools.debugger.enable-content-actors")) {
@@ -1083,31 +1097,39 @@ let RemoteDebugger = {
       }
 #endif
     }
 
     let path = Services.prefs.getCharPref("devtools.debugger.unix-domain-socket") ||
                "/data/local/debugger-socket";
     try {
       DebuggerServer.openListener(path);
+      this._running = true;
     } catch (e) {
       dump('Unable to start debugger server: ' + e + '\n');
     }
   },
 
   stop: function debugger_stop() {
+    if (!this._running) {
+      return;
+    }
+
     if (!DebuggerServer.initialized) {
+      // Can this really happen if we are running?
+      this._running = false;
       return;
     }
 
     try {
       DebuggerServer.closeListener();
     } catch (e) {
       dump('Unable to stop debugger server: ' + e + '\n');
     }
+    this._running = false;
   }
 }
 
 let KeyboardHelper = {
   handleEvent: function keyboard_handleEvent(detail) {
     Keyboard.setLayouts(detail.layouts);
   }
 };
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "3b850fa17c888bdd31f1a163fd7b92f6f020449d", 
+    "revision": "6e971c6a9f947d301a0401fcbc87141b8eba23b6", 
     "repo_path": "/integration/gaia-central"
 }
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/browser_mouse_events.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// from MouseEvents.h
+const leftButtonFlag = 1;
+const rightButtonFlag = 2;
+
+gTests.push({
+  desc: "Test native mouse events",
+  run: function () {
+    let tab = yield addTab("about:mozilla");
+
+    // Mousemove.
+    let waitForMove = waitForEvent(document, "mousemove");
+    synthesizeNativeMouseMove(tab.browser, 1, 1);
+    synthesizeNativeMouseMove(tab.browser, 100, 100);
+    let mousemove = yield waitForMove;
+    is(mousemove.cancelable, false, "mousemove is not cancelable");
+    is(mousemove.buttons, 0, "no buttons are down");
+
+    // Left button down.
+    let waitForDown1 = waitForEvent(document, "mousedown");
+    synthesizeNativeMouseLDown(tab.browser, 100, 100);
+    let mousedown1 = yield waitForDown1;
+    is(mousedown1.cancelable, true, "mousedown is cancelable");
+    is(mousedown1.buttons, leftButtonFlag, "left button is down");
+
+    // Right button down.
+    let waitForDown2 = waitForEvent(document, "mousedown");
+    synthesizeNativeMouseRDown(tab.browser, 100, 100);
+    let mousedown2 = yield waitForDown2;
+    is(mousedown2.buttons, leftButtonFlag | rightButtonFlag, "both buttons are down");
+
+    // Left button up.
+    let waitForUp1 = waitForEvent(document, "mouseup");
+    synthesizeNativeMouseLUp(tab.browser, 100, 100);
+    let mouseup1 = yield waitForUp1;
+    is(mouseup1.buttons, rightButtonFlag, "right button is down");
+
+    // Right button up.
+    let waitForUp2 = waitForEvent(document, "mouseup");
+    synthesizeNativeMouseRUp(tab.browser, 100, 100);
+    let mouseup2 = yield waitForUp2;
+    is(mouseup2.buttons, 0, "no buttons are down");
+
+    Browser.closeTab(tab, { forceClose: true });
+  }
+});
+
+let test = runTests;
--- a/browser/metro/base/tests/mochitest/browser_tabs_container.js
+++ b/browser/metro/base/tests/mochitest/browser_tabs_container.js
@@ -45,17 +45,17 @@ gTests.push({
     ok(!isElementVisible(tabStrip._scrollButtonUp), "left scrollbutton is hidden in imprecise mode");
     ok(!isElementVisible(tabStrip._scrollButtonDown), "right scrollbutton is hidden in imprecise mode");
 
     // test precise mode
     let precisePromise = waitForObserver("metro_precise_input");
     notifyPrecise();
     yield precisePromise;
 
-    todo(!isElementVisible(tabStrip._scrollButtonUp), "Bug 952297 - left scrollbutton is hidden in precise mode");
+    ok(!isElementVisible(tabStrip._scrollButtonUp), "Bug 952297 - left scrollbutton is hidden in precise mode");
     ok(!isElementVisible(tabStrip._scrollButtonDown), "right scrollbutton is hidden in precise mode");
   },
   tearDown: tearDown
 });
 
 gTests.push({
   desc: "Scrollbuttons not visible when tabstrip has overflow in imprecise input mode",
   setUp: function(){
@@ -93,22 +93,25 @@ gTests.push({
     let strip = Elements.tabList.strip;
     ok(strip && strip.scrollClientSize && strip.scrollSize, "Sanity check tabstrip is present and expected properties available");
 
     // add enough tabs to get overflow in the tabstrip
     while (strip.scrollSize <= strip.scrollClientSize) {
       yield addTab("about:mozilla");
     }
 
-    let tabs = Elements.tabList.strip.querySelectorAll("documenttab");
+    ok(isElementVisible(Elements.tabList.strip._scrollButtonUp), "left scrollbutton should be visible in precise mode");
+    ok(isElementVisible(Elements.tabList.strip._scrollButtonDown), "right scrollbutton should be visible in precise mode");
+
     // select the first tab
-    Elements.tabs.selectedTab = tabs[0];
-    ok(isElementVisible(Elements.tabList.strip._scrollButtonDown), "right scrollbutton should be visible when tabList has overflow");
-    todo(!isElementVisible(Elements.tabList.strip._scrollButtonUp), "Bug 952297 - left scrollbutton should not visible when 1st tab is selected and tablist has overflow");
-
+    Browser.selectedTab = Browser.tabs[0];
+    yield waitForMs(1000); // XXX maximum duration of arrowscrollbox _scrollAnim
+    ok(Elements.tabList.strip._scrollButtonUp.disabled, "left scrollbutton should be disabled when 1st tab is selected and tablist has overflow");
+    ok(!Elements.tabList.strip._scrollButtonDown.disabled, "right scrollbutton should be enabled when 1st tab is selected and tablist has overflow");
 
     // select the last tab
-    Elements.tabs.selectedTab = tabs[tabs.length-1];
-    ok(isElementVisible(Elements.tabList.strip._scrollButtonUp), "left scrollbutton should be visible when tablist has overflow and last tab is selected");
-    todo(!isElementVisible(Elements.tabList.strip._scrollButtonDown), "Bug 952297 - right scrollbutton should not visible when last tab is selected and tablist has overflow");
+    Browser.selectedTab = Browser.tabs[Browser.tabs.length - 1];
+    yield waitForMs(1000); // XXX maximum duration of arrowscrollbox _scrollAnim
+    ok(!Elements.tabList.strip._scrollButtonUp.disabled, "left scrollbutton should be enabled when 1st tab is selected and tablist has overflow");
+    ok(Elements.tabList.strip._scrollButtonDown.disabled, "right scrollbutton should be disabled when last tab is selected and tablist has overflow");
 
   }
 });
--- a/browser/metro/base/tests/mochitest/metro.ini
+++ b/browser/metro/base/tests/mochitest/metro.ini
@@ -27,44 +27,45 @@ support-files =
   res/textdivs01.html
   res/textinput01.html
   res/textarea01.html
   res/testEngine.xml
   res/blankpage1.html
   res/blankpage2.html
   res/blankpage3.html
 
+[browser_apzc_basic.js]
 [browser_bookmarks.js]
 [browser_canonizeURL.js]
 [browser_circular_progress_indicator.js]
 [browser_colorUtils.js]
 [browser_crashprompt.js]
 [browser_context_menu_tests.js]
 [browser_context_ui.js]
 [browser_downloads.js]
 [browser_findbar.js]
 [browser_form_auto_complete.js]
 [browser_history.js]
 [browser_inputsource.js]
 [browser_link_click.js]
+[browser_menu_hoverstate.js]
+[browser_mouse_events.js]
 [browser_onscreen_keyboard.js]
 [browser_prefs_ui.js]
 [browser_prompt.js]
 [browser_remotetabs.js]
 [browser_snappedState.js]
 [browser_tabs.js]
 [browser_tabs_container.js]
 [browser_test.js]
 [browser_tiles.js]
 [browser_topsites.js]
 [browser_urlbar.js]
 [browser_urlbar_highlightURLs.js]
 [browser_urlbar_trimURLs.js]
-[browser_apzc_basic.js]
-[browser_menu_hoverstate.js]
 
 # These tests have known failures in debug builds
 [browser_selection_basic.js]
 skip-if = debug
 [browser_selection_textarea.js]
 skip-if = debug
 [browser_selection_frame_content.js]
 skip-if = debug
--- a/browser/metro/theme/browser.css
+++ b/browser/metro/theme/browser.css
@@ -41,28 +41,24 @@
   display: block;
   -moz-user-focus: ignore;
   padding: 0;
   background-color: transparent;
   margin: 0;
   overflow: auto;
 }
 
-#tabs[input="precise"] > .tabs-scrollbox > .scrollbutton-up {
-  visibility: visible !important;
-}
-#tabs[input="imprecise"] > .tabs-scrollbox > .scrollbutton-up {
+.tabs-scrollbox > .scrollbutton-up[collapsed],
+.tabs-scrollbox > .scrollbutton-down[collapsed],
+#tabs[input="imprecise"] > .tabs-scrollbox > .scrollbutton-up,
+#tabs[input="imprecise"] > .tabs-scrollbox > .scrollbutton-down {
   visibility: hidden !important;
   pointer-events: none;
 }
 
-#tabs[input="imprecise"] > .tabs-scrollbox > .scrollbutton-down {
-  visibility: collapse !important;
-}
-
 #tabs > .tabs-scrollbox > .scrollbutton-up {
   list-style-image: url("images/tab-arrows.png") !important;
   -moz-image-region: rect(15px 58px 63px 14px) !important;
   padding-right: 13px;
 }
 #tabs > .tabs-scrollbox > .scrollbutton-up:hover {
   -moz-image-region: rect(14px 102px 62px 58px) !important;
 }
--- a/dom/downloads/src/DownloadsAPI.js
+++ b/dom/downloads/src/DownloadsAPI.js
@@ -193,25 +193,28 @@ downloadsCache.init();
   */
 
 function createDOMDownloadObject(aWindow, aDownload) {
   return downloadsCache.get(aWindow, aDownload);
 }
 
 function DOMDownloadImpl() {
   debug("DOMDownloadImpl constructor ");
+
   this.wrappedJSObject = this;
   this.totalBytes = 0;
   this.currentBytes = 0;
   this.url = null;
   this.path = null;
   this.state = "stopped";
   this.contentType = null;
-  this.startTime = Date.now();
-  this.error = null;
+
+  /* fields that require getters/setters */
+  this._error = null;
+  this._startTime = new Date();
 
   /* private fields */
   this.id = null;
 }
 
 DOMDownloadImpl.prototype = {
 
   createPromise: function(aPromiseInit) {
@@ -239,16 +242,37 @@ DOMDownloadImpl.prototype = {
   set onstatechange(aHandler) {
     this.__DOM_IMPL__.setEventHandler("onstatechange", aHandler);
   },
 
   get onstatechange() {
     return this.__DOM_IMPL__.getEventHandler("onstatechange");
   },
 
+  get error() {
+    return this._error;
+  },
+
+  set error(aError) {
+    this._error = aError;
+  },
+
+  get startTime() {
+    return this._startTime;
+  },
+
+  set startTime(aStartTime) {
+    if (aStartTime instanceof Date) {
+      this._startTime = aStartTime;
+    }
+    else {
+      this._startTime = new Date(aStartTime);
+    }
+  },
+
   _init: function(aWindow, aDownload) {
     this._window = aWindow;
     this.id = aDownload.id;
     this._update(aDownload);
     Services.obs.addObserver(this, "downloads-state-change-" + this.id,
                              /* ownsWeak */ true);
     debug("observer set for " + this.id);
   },
--- a/dom/downloads/tests/test_downloads_basic.html
+++ b/dom/downloads/tests/test_downloads_basic.html
@@ -19,66 +19,105 @@ https://bugzilla.mozilla.org/show_bug.cg
 <pre id="test">
 <script class="testbody" type="text/javascript;version=1.7">
 
 // Testing a simple download, waiting for it to be done.
 
 SimpleTest.waitForExplicitFinish();
 
 var index = -1;
+var todayDate = new Date();
+var baseServeURL = "http://mochi.test:8888/tests/dom/downloads/tests/";
+var baseDownloadPath = "/mnt/sdcard/downloads/";
+var lastKnownCurrentBytes = 0;
 
 function next() {
   index += 1;
   if (index >= steps.length) {
     ok(false, "Shouldn't get here!");
     return;
   }
   try {
     steps[index]();
   } catch(ex) {
     ok(false, "Caught exception", ex);
   }
 }
 
+function checkConsistentDownloadAttributes(download) {
+  var href = document.getElementById("download1").getAttribute("href");
+  var expectedServeURL = baseServeURL + href;
+  var expectedDownloadPath = baseDownloadPath + "test.bin";
+
+  // bug 945323: Download path isn't honoring download attribute
+  todo(download.path === expectedDownloadPath,
+       "Download path = " + expectedDownloadPath);
+
+  ok(download.startTime >= todayDate,
+     "Download start time should be greater than or equal to today");
+
+  is(download.error, null, "Download does not have an error");
+
+  is(download.url, expectedServeURL,
+     "Download URL = " + expectedServeURL);
+  ok(download.id !== null, "Download id is defined");
+  is(download.contentType, "application/octet-stream",
+     "Download content type is application/octet-stream");
+}
+
 function downloadChange(evt) {
   var download = evt.download;
-  if (download.state == "succeeded") {
-    ok(download.totalBytes == 1024, "Download size is 1024 bytes.");
-    ok(download.contentType == "application/octet-stream",
-       "contentType is application/octet-stream.");
+  checkConsistentDownloadAttributes(download);
+  is(download.totalBytes, 1024, "Download total size is 1024 bytes");
+
+  if (download.state === "succeeded") {
+    is(download.currentBytes, 1024, "Download current size is 1024 bytes");
     SimpleTest.finish();
+  } else if (download.state === "downloading") {
+    ok(download.currentBytes > lastKnownCurrentBytes,
+       "Download current size is larger than last download change event");
+    lastKnownCurrentBytes = download.currentBytes;
+  } else {
+    ok(false, "Unexpected download state = " + download.state);
   }
 }
 
+function downloadStart(evt) {
+  var download = evt.download;
+  checkConsistentDownloadAttributes(download);
+
+  is(download.currentBytes, 0, "Download current size is zero");
+  is(download.state, "downloading", "Download state is downloading");
+
+  download.onstatechange = downloadChange;
+}
+
 var steps = [
   // Start by setting the pref to true.
   function() {
     SpecialPowers.pushPrefEnv({
       set: [["dom.mozDownloads.enabled", true]]
     }, next);
   },
 
   // Setup the event listeners.
   function() {
     SpecialPowers.pushPermissions([
       {type: "downloads", allow: true, context: document}
     ], function() {
-      navigator.mozDownloadManager.ondownloadstart =
-        function(evt) {
-          ok(true, "Download started");
-          evt.download.addEventListener("statechange", downloadChange);
-        }
+      navigator.mozDownloadManager.ondownloadstart = downloadStart;
       next();
     });
   },
 
   // Click on the <a download> to start the download.
   function() {
     document.getElementById("download1").click();
   }
 ];
 
 next();
 
 </script>
 </pre>
 </body>
 </html>
+
--- a/dom/webidl/Downloads.webidl
+++ b/dom/webidl/Downloads.webidl
@@ -54,17 +54,17 @@ interface DOMDownload : EventTarget {
   readonly attribute Date startTime;
 
   // An opaque identifier for this download. All instances of the same
   // download (eg. in different windows) will have the same id.
   readonly attribute DOMString id;
 
   // A DOM error object, that will be not null when a download is stopped
   // because something failed.
-  readonly attribute DOMError error;
+  readonly attribute DOMError? error;
 
   // Pauses the download.
   Promise pause();
 
   // Resumes the download. This resolves only once the download has
   // succeeded.
   Promise resume();
 
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -195,16 +195,21 @@ void
 ModifierKeyState::InitMouseEvent(WidgetInputEvent& aMouseEvent) const
 {
   NS_ASSERTION(aMouseEvent.eventStructType == NS_MOUSE_EVENT ||
                aMouseEvent.eventStructType == NS_WHEEL_EVENT ||
                aMouseEvent.eventStructType == NS_DRAG_EVENT ||
                aMouseEvent.eventStructType == NS_SIMPLE_GESTURE_EVENT,
                "called with non-mouse event");
 
+  if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
+    // Buttons for immersive mode are handled in MetroInput.
+    return;
+  }
+
   WidgetMouseEventBase& mouseEvent = *aMouseEvent.AsMouseEventBase();
   mouseEvent.buttons = 0;
   if (::GetKeyState(VK_LBUTTON) < 0) {
     mouseEvent.buttons |= WidgetMouseEvent::eLeftButtonFlag;
   }
   if (::GetKeyState(VK_RBUTTON) < 0) {
     mouseEvent.buttons |= WidgetMouseEvent::eRightButtonFlag;
   }
--- a/widget/windows/winrt/MetroInput.cpp
+++ b/widget/windows/winrt/MetroInput.cpp
@@ -155,16 +155,47 @@ namespace {
                   == aDeviceType) {
       aMozInputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
     } else if (Devices::Input::PointerDeviceType::PointerDeviceType_Pen
                   == aDeviceType) {
       aMozInputSource = nsIDOMMouseEvent::MOZ_SOURCE_PEN;
     }
   }
 
+  int16_t
+  ButtonsForPointerPoint(UI::Input::IPointerPoint* aPoint) {
+    WRL::ComPtr<UI::Input::IPointerPointProperties> props;
+    aPoint->get_Properties(props.GetAddressOf());
+
+    int16_t buttons = 0;
+    boolean buttonPressed;
+
+    props->get_IsLeftButtonPressed(&buttonPressed);
+    if (buttonPressed) {
+      buttons |= WidgetMouseEvent::eLeftButtonFlag;
+    }
+    props->get_IsMiddleButtonPressed(&buttonPressed);
+    if (buttonPressed) {
+      buttons |= WidgetMouseEvent::eMiddleButtonFlag;
+    }
+    props->get_IsRightButtonPressed(&buttonPressed);
+    if (buttonPressed) {
+      buttons |= WidgetMouseEvent::eRightButtonFlag;
+    }
+    props->get_IsXButton1Pressed(&buttonPressed);
+    if (buttonPressed) {
+      buttons |= WidgetMouseEvent::e4thButtonFlag;
+    }
+    props->get_IsXButton2Pressed(&buttonPressed);
+    if (buttonPressed) {
+      buttons |= WidgetMouseEvent::e5thButtonFlag;
+    }
+    return buttons;
+  }
+
   /**
    * This function is for use with mTouches.Enumerate.  It will
    * append each element it encounters to the {@link nsTArray}
    * of {@link mozilla::dom::Touch}es passed in through the third (void*)
    * parameter.
    *
    * NOTE: This function will set the `mChanged` member of each
    * element it encounters to `false`, since this function is only
@@ -403,50 +434,53 @@ MetroInput::OnEdgeGestureCompleted(UI::I
 void
 MetroInput::OnPointerNonTouch(UI::Input::IPointerPoint* aPoint) {
   WRL::ComPtr<UI::Input::IPointerPointProperties> props;
   UI::Input::PointerUpdateKind pointerUpdateKind;
 
   aPoint->get_Properties(props.GetAddressOf());
   props->get_PointerUpdateKind(&pointerUpdateKind);
 
-  WidgetMouseEvent* event =
-    new WidgetMouseEvent(true, NS_MOUSE_MOVE, mWidget.Get(),
-                         WidgetMouseEvent::eReal,
-                         WidgetMouseEvent::eNormal);
+  uint32_t message = NS_MOUSE_MOVE;
+  int16_t button = 0;
 
   switch (pointerUpdateKind) {
     case UI::Input::PointerUpdateKind::PointerUpdateKind_LeftButtonPressed:
-      // We don't bother setting mouseEvent.button because it is already
-      // set to WidgetMouseEvent::buttonType::eLeftButton whose value is 0.
-      event->message = NS_MOUSE_BUTTON_DOWN;
+      button = WidgetMouseEvent::buttonType::eLeftButton;
+      message = NS_MOUSE_BUTTON_DOWN;
       break;
     case UI::Input::PointerUpdateKind::PointerUpdateKind_MiddleButtonPressed:
-      event->button = WidgetMouseEvent::buttonType::eMiddleButton;
-      event->message = NS_MOUSE_BUTTON_DOWN;
+      button = WidgetMouseEvent::buttonType::eMiddleButton;
+      message = NS_MOUSE_BUTTON_DOWN;
       break;
     case UI::Input::PointerUpdateKind::PointerUpdateKind_RightButtonPressed:
-      event->button = WidgetMouseEvent::buttonType::eRightButton;
-      event->message = NS_MOUSE_BUTTON_DOWN;
+      button = WidgetMouseEvent::buttonType::eRightButton;
+      message = NS_MOUSE_BUTTON_DOWN;
       break;
     case UI::Input::PointerUpdateKind::PointerUpdateKind_LeftButtonReleased:
-      // We don't bother setting mouseEvent.button because it is already
-      // set to WidgetMouseEvent::buttonType::eLeftButton whose value is 0.
-      event->message = NS_MOUSE_BUTTON_UP;
+      button = WidgetMouseEvent::buttonType::eLeftButton;
+      message = NS_MOUSE_BUTTON_UP;
       break;
     case UI::Input::PointerUpdateKind::PointerUpdateKind_MiddleButtonReleased:
-      event->button = WidgetMouseEvent::buttonType::eMiddleButton;
-      event->message = NS_MOUSE_BUTTON_UP;
+      button = WidgetMouseEvent::buttonType::eMiddleButton;
+      message = NS_MOUSE_BUTTON_UP;
       break;
     case UI::Input::PointerUpdateKind::PointerUpdateKind_RightButtonReleased:
-      event->button = WidgetMouseEvent::buttonType::eRightButton;
-      event->message = NS_MOUSE_BUTTON_UP;
+      button = WidgetMouseEvent::buttonType::eRightButton;
+      message = NS_MOUSE_BUTTON_UP;
       break;
   }
+
   UpdateInputLevel(LEVEL_PRECISE);
+
+  WidgetMouseEvent* event =
+    new WidgetMouseEvent(true, message, mWidget.Get(),
+                         WidgetMouseEvent::eReal,
+                         WidgetMouseEvent::eNormal);
+  event->button = button;
   InitGeckoMouseEventFromPointerPoint(event, aPoint);
   DispatchAsyncEventIgnoreStatus(event);
 }
 
 void
 MetroInput::InitTouchEventTouchList(WidgetTouchEvent* aEvent)
 {
   MOZ_ASSERT(aEvent);
@@ -741,16 +775,17 @@ MetroInput::InitGeckoMouseEventFromPoint
   TransformRefPoint(position, aEvent->refPoint);
 
   if (!canBeDoubleTap) {
     aEvent->clickCount = 1;
   } else {
     aEvent->clickCount = 2;
   }
   aEvent->pressure = pressure;
+  aEvent->buttons = ButtonsForPointerPoint(aPointerPoint);
 
   MozInputSourceFromDeviceType(deviceType, aEvent->inputSource);
 }
 
 // This event is raised when a precise pointer moves into the bounding box of
 // our window.  For touch input, this will be raised before the PointerPressed
 // event.
 HRESULT