Bug 865561 Dispatch key events when WM_APPCOMMAND is fired for a keypress r=jimm+smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 30 Dec 2014 10:47:56 +0900
changeset 247453 facf85b61c3762f32212d7fab840071bba1df184
parent 247452 d010d4c66c7f4751be57ae9cc96c9b5eb3153368
child 247454 f1fb6e0260a86ac5d976259b74e5740c7b3c45be
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs865561
milestone37.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 865561 Dispatch key events when WM_APPCOMMAND is fired for a keypress r=jimm+smaug
widget/NativeKeyToDOMKeyName.h
widget/windows/KeyboardLayout.cpp
widget/windows/KeyboardLayout.h
widget/windows/nsWindow.cpp
widget/windows/nsWindowBase.cpp
widget/windows/nsWindowBase.h
widget/windows/winrt/MetroWidget.cpp
--- a/widget/NativeKeyToDOMKeyName.h
+++ b/widget/NativeKeyToDOMKeyName.h
@@ -15,48 +15,62 @@
  * aKeyNameIndex is the widget::KeyNameIndex value.
  */
 
 // Windows (both Desktop and Metro)
 #define KEY_MAP_WIN(aCPPKeyName, aNativeKey)
 #define KEY_MAP_WIN_JPN(aCPPKeyName, aNativeKey)
 #define KEY_MAP_WIN_KOR(aCPPKeyName, aNativeKey)
 #define KEY_MAP_WIN_OTH(aCPPKeyName, aNativeKey)
+#define KEY_MAP_WIN_CMD(aCPPKeyName, aAppCommand)
 // Mac OS X
 #define KEY_MAP_COCOA(aCPPKeyName, aNativeKey)
 // GTK
 #define KEY_MAP_GTK(aCPPKeyName, aNativeKey)
 // Qt
 #define KEY_MAP_QT(aCPPKeyName, aNativeKey)
 // Android and Gonk
 #define KEY_MAP_ANDROID(aCPPKeyName, aNativeKey)
 
 #if defined(XP_WIN)
+#if defined(NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX)
 // KEY_MAP_WIN() defines the mapping not depending on keyboard layout.
 #undef KEY_MAP_WIN
 #define KEY_MAP_WIN(aCPPKeyName, aNativeKey) \
   NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, KEY_NAME_INDEX_##aCPPKeyName)
+#elif defined(NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX)
 // KEY_MAP_WIN_JPN() defines the mapping which is valid only with Japanese
 // keyboard layout.
 #undef KEY_MAP_WIN_JPN
 #define KEY_MAP_WIN_JPN(aCPPKeyName, aNativeKey) \
   NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, \
                                                KEY_NAME_INDEX_##aCPPKeyName)
+#elif defined(NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX)
 // KEY_MAP_WIN_KOR() defines the mapping which is valid only with Korean
 // keyboard layout.
 #undef KEY_MAP_WIN_KOR
 #define KEY_MAP_WIN_KOR(aCPPKeyName, aNativeKey) \
   NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, \
                                              KEY_NAME_INDEX_##aCPPKeyName)
+#elif defined(NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX)
 // KEY_MAP_WIN_OTH() defines the mapping which is valid with neither
 // Japanese keyboard layout nor Korean keyboard layout.
 #undef KEY_MAP_WIN_OTH
 #define KEY_MAP_WIN_OTH(aCPPKeyName, aNativeKey) \
   NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, \
                                             KEY_NAME_INDEX_##aCPPKeyName)
+#elif defined(NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX)
+// KEY_MAP_WIN_CMD() defines the mapping from APPCOMMAND_* of WM_APPCOMMAND.
+#undef KEY_MAP_WIN_CMD
+#define KEY_MAP_WIN_CMD(aCPPKeyName, aAppCommand) \
+  NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX(aAppCommand, \
+                                      KEY_NAME_INDEX_##aCPPKeyName)
+#else
+#error Any NS_*_TO_DOM_KEY_NAME_INDEX() is not defined.
+#endif // #if defined(NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX) ...
 #elif defined(XP_MACOSX)
 #undef KEY_MAP_COCOA
 #define KEY_MAP_COCOA(aCPPKeyName, aNativeKey) \
   NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, KEY_NAME_INDEX_##aCPPKeyName)
 #elif defined(MOZ_WIDGET_GTK)
 #undef KEY_MAP_GTK
 #define KEY_MAP_GTK(aCPPKeyName, aNativeKey) \
   NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, KEY_NAME_INDEX_##aCPPKeyName)
@@ -314,23 +328,26 @@ KEY_MAP_GTK     (ExSel, GDK_3270_ExSelec
 // Insert
 KEY_MAP_WIN     (Insert, VK_INSERT)
 KEY_MAP_GTK     (Insert, GDK_Insert)
 KEY_MAP_GTK     (Insert, GDK_KP_Insert)
 KEY_MAP_QT      (Insert, Qt::Key_Insert)
 KEY_MAP_ANDROID (Insert, AKEYCODE_INSERT)
 
 // Paste
+KEY_MAP_WIN_CMD (Paste, APPCOMMAND_PASTE)
 KEY_MAP_GTK     (Paste, GDK_Paste)
 KEY_MAP_QT      (Paste, Qt::Key_Paste)
 
 // Redo
+KEY_MAP_WIN_CMD (Redo, APPCOMMAND_REDO)
 KEY_MAP_GTK     (Redo, GDK_Redo)
 
 // Undo
+KEY_MAP_WIN_CMD (Undo, APPCOMMAND_UNDO)
 KEY_MAP_GTK     (Undo, GDK_Undo)
 
 /******************************************************************************
  * UI Keys
  ******************************************************************************/
 // Accept
 KEY_MAP_WIN     (Accept, VK_ACCEPT)
 KEY_MAP_ANDROID (Accept, AKEYCODE_DPAD_CENTER)
@@ -359,20 +376,22 @@ KEY_MAP_QT      (Escape, Qt::Key_Escape)
 KEY_MAP_ANDROID (Escape, AKEYCODE_ESCAPE)
 
 // Execute
 KEY_MAP_WIN     (Execute, VK_EXECUTE)
 KEY_MAP_GTK     (Execute, GDK_Execute)
 KEY_MAP_QT      (Execute, Qt::Key_Execute)
 
 // Find
+KEY_MAP_WIN_CMD (Find, APPCOMMAND_FIND)
 KEY_MAP_GTK     (Find, GDK_Find)
 
 // Help
 KEY_MAP_WIN     (Help, VK_HELP)
+KEY_MAP_WIN_CMD (Help, APPCOMMAND_HELP)
 KEY_MAP_COCOA   (Help, kVK_Help)
 KEY_MAP_GTK     (Help, GDK_Help)
 KEY_MAP_QT      (Help, Qt::Key_Help)
 KEY_MAP_ANDROID (Help, AKEYCODE_ASSIST)
 
 // Pause
 KEY_MAP_WIN     (Pause, VK_PAUSE)
 KEY_MAP_GTK     (Pause, GDK_Pause)
@@ -862,91 +881,108 @@ KEY_MAP_GTK     (F35, GDK_F35 /* same as
 KEY_MAP_QT      (F35, Qt::Key_F35)
 
 /******************************************************************************
  * Multimedia Keys
  ******************************************************************************/
 // Close
 // NOTE: This is not a key to close disk tray, this is a key to close document
 //       or window.
+KEY_MAP_WIN_CMD (Close, APPCOMMAND_CLOSE)
 KEY_MAP_GTK     (Close, GDK_Close)
 KEY_MAP_QT      (Close, Qt::Key_Close)
 
 // MailForward
+KEY_MAP_WIN_CMD (MailForward, APPCOMMAND_FORWARD_MAIL)
 KEY_MAP_GTK     (MailForward, GDK_MailForward)
 KEY_MAP_QT      (MailForward, Qt::Key_MailForward)
 
 // MailReply
+KEY_MAP_WIN_CMD (MailReply, APPCOMMAND_REPLY_TO_MAIL)
 KEY_MAP_GTK     (MailReply, GDK_Reply)
 KEY_MAP_QT      (MailReply, Qt::Key_Reply)
 
 // MailSend
+KEY_MAP_WIN_CMD (MailSend, APPCOMMAND_SEND_MAIL)
 KEY_MAP_GTK     (MailSend, GDK_Send)
 KEY_MAP_QT      (MailSend, Qt::Key_Send)
 
 // MediaPlayPause
 KEY_MAP_WIN     (MediaPlayPause, VK_MEDIA_PLAY_PAUSE)
+KEY_MAP_WIN_CMD (MediaPlayPause, APPCOMMAND_MEDIA_PLAY_PAUSE)
 KEY_MAP_QT      (MediaPlayPause, Qt::Key_MediaTogglePlayPause)
 KEY_MAP_ANDROID (MediaPlayPause, AKEYCODE_MEDIA_PLAY_PAUSE)
 
 // MediaSelect
 KEY_MAP_WIN     (MediaSelect, VK_LAUNCH_MEDIA_SELECT)
+KEY_MAP_WIN_CMD (MediaSelect, APPCOMMAND_LAUNCH_MEDIA_SELECT)
 KEY_MAP_GTK     (MediaSelect, GDK_AudioMedia)
 
 // MediaStop
 KEY_MAP_WIN     (MediaStop, VK_MEDIA_STOP)
+KEY_MAP_WIN_CMD (MediaStop, APPCOMMAND_MEDIA_STOP)
 KEY_MAP_GTK     (MediaStop, GDK_AudioStop)
 KEY_MAP_QT      (MediaStop, Qt::Key_MediaStop)
 KEY_MAP_ANDROID (MediaStop, AKEYCODE_MEDIA_STOP)
 
 // MediaTrackNext
 KEY_MAP_WIN     (MediaTrackNext, VK_MEDIA_NEXT_TRACK)
+KEY_MAP_WIN_CMD (MediaTrackNext, APPCOMMAND_MEDIA_NEXTTRACK)
 KEY_MAP_GTK     (MediaTrackNext, GDK_AudioNext)
 KEY_MAP_QT      (MediaTrackNext, Qt::Key_MediaNext)
 KEY_MAP_ANDROID (MediaTrackNext, AKEYCODE_MEDIA_NEXT)
 
 // MediaTrackPrevious
 KEY_MAP_WIN     (MediaTrackPrevious, VK_MEDIA_PREV_TRACK)
+KEY_MAP_WIN_CMD (MediaTrackPrevious, APPCOMMAND_MEDIA_PREVIOUSTRACK)
 KEY_MAP_GTK     (MediaTrackPrevious, GDK_AudioPrev)
 KEY_MAP_QT      (MediaTrackPrevious, Qt::Key_MediaPrevious)
 KEY_MAP_ANDROID (MediaTrackPrevious, AKEYCODE_MEDIA_PREVIOUS)
 
 // New
+KEY_MAP_WIN_CMD (New, APPCOMMAND_NEW)
 KEY_MAP_GTK     (New, GDK_New)
 
 // Open
+KEY_MAP_WIN_CMD (Open, APPCOMMAND_OPEN)
 KEY_MAP_GTK     (Open, GDK_Open)
 
 // Print
+KEY_MAP_WIN_CMD (Print, APPCOMMAND_PRINT)
 KEY_MAP_QT      (Print, Qt::Key_Printer)
 
 // Save
+KEY_MAP_WIN_CMD (Save, APPCOMMAND_SAVE)
 KEY_MAP_GTK     (Save, GDK_Save)
 KEY_MAP_QT      (Save, Qt::Key_Save)
 
 // SpellCheck
+KEY_MAP_WIN_CMD (SpellCheck, APPCOMMAND_SPELL_CHECK)
 KEY_MAP_GTK     (SpellCheck, GDK_Spell)
 KEY_MAP_QT      (SpellCheck, Qt::Key_Spell)
 
 // VolumeDown
 KEY_MAP_WIN     (VolumeDown, VK_VOLUME_DOWN)
+KEY_MAP_WIN_CMD (VolumeDown, APPCOMMAND_VOLUME_DOWN)
 KEY_MAP_COCOA   (VolumeDown, kVK_VolumeDown)
 KEY_MAP_GTK     (VolumeDown, GDK_AudioLowerVolume)
 KEY_MAP_QT      (VolumeDown, Qt::Key_VolumeDown)
 KEY_MAP_ANDROID (VolumeDown, AKEYCODE_VOLUME_DOWN)
 
 // VolumeUp
 KEY_MAP_WIN     (VolumeUp, VK_VOLUME_UP)
+KEY_MAP_WIN_CMD (VolumeUp, APPCOMMAND_VOLUME_UP)
 KEY_MAP_COCOA   (VolumeUp, kVK_VolumeUp)
 KEY_MAP_GTK     (VolumeUp, GDK_AudioRaiseVolume)
 KEY_MAP_QT      (VolumeUp, Qt::Key_VolumeUp)
 KEY_MAP_ANDROID (VolumeUp, AKEYCODE_VOLUME_UP)
 
 // VolumeMute
 KEY_MAP_WIN     (VolumeMute, VK_VOLUME_MUTE)
+KEY_MAP_WIN_CMD (VolumeMute, APPCOMMAND_VOLUME_MUTE)
 KEY_MAP_COCOA   (VolumeMute, kVK_Mute)
 KEY_MAP_GTK     (VolumeMute, GDK_AudioMute)
 KEY_MAP_QT      (VolumeMute, Qt::Key_VolumeMute)
 KEY_MAP_ANDROID (VolumeMute, AKEYCODE_VOLUME_MUTE)
 
 /******************************************************************************
  * Application Keys
  ******************************************************************************/
@@ -957,16 +993,17 @@ KEY_MAP_ANDROID (LaunchCalculator, AKEYC
 
 // LaunchCalendar
 KEY_MAP_GTK     (LaunchCalendar, GDK_Calendar)
 KEY_MAP_QT      (LaunchCalendar, Qt::Key_Calendar)
 KEY_MAP_ANDROID (LaunchCalendar, AKEYCODE_CALENDAR)
 
 // LaunchMail
 KEY_MAP_WIN     (LaunchMail, VK_LAUNCH_MAIL)
+KEY_MAP_WIN_CMD (LaunchMail, APPCOMMAND_LAUNCH_MAIL)
 KEY_MAP_GTK     (LaunchMail, GDK_Mail)
 KEY_MAP_QT      (LaunchMail, Qt::Key_LaunchMail)
 KEY_MAP_ANDROID (LaunchMail, AKEYCODE_ENVELOPE)
 
 // LaunchMediaPlayer
 // GDK_CD is defined as "Launch CD/DVD player" in XF86keysym.h.
 // Therefore, let's map it to media player rather than music player.
 KEY_MAP_GTK     (LaunchMediaPlayer, GDK_CD)
@@ -1003,21 +1040,23 @@ KEY_MAP_GTK     (LaunchWebCam, GDK_WebCa
 KEY_MAP_QT      (LaunchWebCam, Qt::Key_WebCam)
 
 // LaunchWordProcessor
 KEY_MAP_GTK     (LaunchWordProcessor, GDK_Word)
 KEY_MAP_QT      (LaunchWordProcessor, Qt::Key_Word)
 
 // LaunchApplication1
 KEY_MAP_WIN     (LaunchApplication1, VK_LAUNCH_APP1)
+KEY_MAP_WIN_CMD (LaunchApplication1, APPCOMMAND_LAUNCH_APP1)
 KEY_MAP_GTK     (LaunchApplication1, GDK_Launch0)
 KEY_MAP_QT      (LaunchApplication1, Qt::Key_Launch0)
 
 // LaunchApplication2
 KEY_MAP_WIN     (LaunchApplication2, VK_LAUNCH_APP2)
+KEY_MAP_WIN_CMD (LaunchApplication2, APPCOMMAND_LAUNCH_APP2)
 KEY_MAP_GTK     (LaunchApplication2, GDK_Launch1)
 KEY_MAP_QT      (LaunchApplication2, Qt::Key_Launch1)
 
 // LaunchApplication3
 KEY_MAP_GTK     (LaunchApplication3, GDK_Launch2)
 KEY_MAP_QT      (LaunchApplication3, Qt::Key_Launch2)
 
 // LaunchApplication4
@@ -1078,61 +1117,70 @@ KEY_MAP_QT      (LaunchApplication17, Qt
 // LaunchApplication18
 KEY_MAP_QT      (LaunchApplication18, Qt::Key_LaunchH)
 
 /******************************************************************************
  * Browser Keys
  ******************************************************************************/
 // BrowserBack
 KEY_MAP_WIN     (BrowserBack, VK_BROWSER_BACK)
+KEY_MAP_WIN_CMD (BrowserBack, APPCOMMAND_BROWSER_BACKWARD)
 KEY_MAP_GTK     (BrowserBack, GDK_Back)
 KEY_MAP_QT      (BrowserBack, Qt::Key_Back)
 KEY_MAP_ANDROID (BrowserBack, AKEYCODE_BACK)
 
 // BrowserFavorites
 KEY_MAP_WIN     (BrowserFavorites, VK_BROWSER_FAVORITES)
+KEY_MAP_WIN_CMD (BrowserFavorites, APPCOMMAND_BROWSER_FAVORITES)
 KEY_MAP_QT      (BrowserFavorites, Qt::Key_Favorites)
 KEY_MAP_ANDROID (BrowserFavorites, AKEYCODE_BOOKMARK)
 
 // BrowserForward
 KEY_MAP_WIN     (BrowserForward, VK_BROWSER_FORWARD)
+KEY_MAP_WIN_CMD (BrowserForward, APPCOMMAND_BROWSER_FORWARD)
 KEY_MAP_GTK     (BrowserForward, GDK_Forward)
 KEY_MAP_QT      (BrowserForward, Qt::Key_Forward)
 KEY_MAP_ANDROID (BrowserForward, AKEYCODE_FORWARD)
 
 // BrowserHome
 KEY_MAP_WIN     (BrowserHome, VK_BROWSER_HOME)
+KEY_MAP_WIN_CMD (BrowserHome, APPCOMMAND_BROWSER_HOME)
 KEY_MAP_GTK     (BrowserHome, GDK_HomePage)
 KEY_MAP_QT      (BrowserHome, Qt::Key_HomePage)
 
 // BrowserRefresh
 KEY_MAP_WIN     (BrowserRefresh, VK_BROWSER_REFRESH)
+KEY_MAP_WIN_CMD (BrowserRefresh, APPCOMMAND_BROWSER_REFRESH)
 KEY_MAP_GTK     (BrowserRefresh, GDK_Refresh)
 KEY_MAP_GTK     (BrowserRefresh, GDK_Reload)
 KEY_MAP_QT      (BrowserRefresh, Qt::Key_Refresh)
 KEY_MAP_QT      (BrowserRefresh, Qt::Key_Reload)
 
 // BrowserSearch
 KEY_MAP_WIN     (BrowserSearch, VK_BROWSER_SEARCH)
+KEY_MAP_WIN_CMD (BrowserSearch, APPCOMMAND_BROWSER_SEARCH)
 KEY_MAP_GTK     (BrowserSearch, GDK_Search)
 KEY_MAP_QT      (BrowserSearch, Qt::Key_Search)
 KEY_MAP_ANDROID (BrowserSearch, AKEYCODE_SEARCH)
 
 // BrowserStop
 KEY_MAP_WIN     (BrowserStop, VK_BROWSER_STOP)
+KEY_MAP_WIN_CMD (BrowserStop, APPCOMMAND_BROWSER_STOP)
 KEY_MAP_GTK     (BrowserStop, GDK_Stop)
 KEY_MAP_QT      (BrowserStop, Qt::Key_Stop)
 
 /******************************************************************************
  * Media Controller Keys
  ******************************************************************************/
 // AudioBassBoostDown
+KEY_MAP_WIN_CMD (AudioBassBoostDown, APPCOMMAND_BASS_DOWN)
 KEY_MAP_QT      (AudioBassBoostDown, Qt::Key_BassDown)
 
 // AudioBassBoostUp
+KEY_MAP_WIN_CMD (AudioBassBoostUp, APPCOMMAND_BASS_UP)
 KEY_MAP_QT      (AudioBassBoostUp, Qt::Key_BassUp)
 
 // AVRInput
 KEY_MAP_ANDROID (AVRInput, AKEYCODE_AVR_INPUT)
 
 // AVRPower
 KEY_MAP_ANDROID (AVRPower, AKEYCODE_AVR_POWER)
 
@@ -1175,31 +1223,35 @@ KEY_MAP_ANDROID (Info, AKEYCODE_INFO)
 // MediaFastForward
 KEY_MAP_QT      (MediaFastForward, Qt::Key_AudioForward)
 KEY_MAP_ANDROID (MediaFastForward, AKEYCODE_MEDIA_FAST_FORWARD)
 
 // MediaLast
 KEY_MAP_QT      (MediaLast, Qt::Key_MediaLast)
 
 // MediaPause
+KEY_MAP_WIN_CMD (MediaPause, APPCOMMAND_MEDIA_PAUSE)
 KEY_MAP_GTK     (MediaPause, GDK_AudioPause)
 KEY_MAP_QT      (MediaPause, Qt::Key_MediaPause)
 KEY_MAP_ANDROID (MediaPause, AKEYCODE_MEDIA_PAUSE)
 
 // MediaPlay
+KEY_MAP_WIN_CMD (MediaPlay, APPCOMMAND_MEDIA_PLAY)
 KEY_MAP_GTK     (MediaPlay, GDK_AudioPlay)
 KEY_MAP_QT      (MediaPlay, Qt::Key_MediaPlay)
 KEY_MAP_ANDROID (MediaPlay, AKEYCODE_MEDIA_PLAY)
 
 // MediaRecord
+KEY_MAP_WIN_CMD (MediaRecord, APPCOMMAND_MEDIA_RECORD)
 KEY_MAP_GTK     (MediaRecord, GDK_AudioRecord)
 KEY_MAP_QT      (MediaRecord, Qt::Key_MediaRecord)
 KEY_MAP_ANDROID (MediaRecord, AKEYCODE_MEDIA_RECORD)
 
 // MediaRewind
+KEY_MAP_WIN_CMD (MediaRewind, APPCOMMAND_MEDIA_REWIND)
 KEY_MAP_GTK     (MediaRewind, GDK_AudioRewind)
 KEY_MAP_QT      (MediaRewind, Qt::Key_AudioRewind)
 KEY_MAP_ANDROID (MediaRewind, AKEYCODE_MEDIA_REWIND)
 
 // PinPToggle
 KEY_MAP_ANDROID (PinPToggle, AKEYCODE_WINDOW)
 
 // RandomToggle
@@ -1235,12 +1287,13 @@ KEY_MAP_GTK     (VideoModeNext, GDK_Next
 // ZoomToggle
 KEY_MAP_WIN     (ZoomToggle, VK_ZOOM)
 KEY_MAP_QT      (ZoomToggle, Qt::Key_Zoom)
 
 #undef KEY_MAP_WIN
 #undef KEY_MAP_WIN_JPN
 #undef KEY_MAP_WIN_KOR
 #undef KEY_MAP_WIN_OTH
+#undef KEY_MAP_WIN_CMD
 #undef KEY_MAP_COCOA
 #undef KEY_MAP_GTK
 #undef KEY_MAP_QT
 #undef KEY_MAP_ANDROID
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -3,39 +3,39 @@
  * 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/. */
 
 #include "prlog.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MouseEvents.h"
+#include "mozilla/MiscEvents.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/WindowsVersion.h"
 
-#include "KeyboardLayout.h"
-#include "nsIMM32Handler.h"
-
-#include "nsMemory.h"
-#include "nsToolkit.h"
-#include "nsQuickSort.h"
 #include "nsAlgorithm.h"
-#include "nsUnicharUtils.h"
-#include "WidgetUtils.h"
-#include "WinUtils.h"
-#include "nsWindowDbg.h"
-#include "nsServiceManagerUtils.h"
-#include "nsPrintfCString.h"
-
-#include "nsIDOMKeyEvent.h"
-#include "nsIIdleServiceInternal.h"
-
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
+#include "nsGkAtoms.h"
+#include "nsIDOMKeyEvent.h"
+#include "nsIIdleServiceInternal.h"
+#include "nsIMM32Handler.h"
+#include "nsMemory.h"
+#include "nsPrintfCString.h"
+#include "nsQuickSort.h"
+#include "nsServiceManagerUtils.h"
+#include "nsToolkit.h"
+#include "nsUnicharUtils.h"
+#include "nsWindowDbg.h"
+
+#include "KeyboardLayout.h"
+#include "WidgetUtils.h"
+#include "WinUtils.h"
 
 #include "npapi.h"
 
 #include <windows.h>
 #include <winuser.h>
 #include <algorithm>
 
 #ifndef WINABLEAPI
@@ -664,28 +664,45 @@ VirtualKey::FillKbdState(PBYTE aKbdState
     aKbdState[VK_CAPITAL] &= ~0x01;
   }
 }
 
 /*****************************************************************************
  * mozilla::widget::NativeKey
  *****************************************************************************/
 
+uint8_t NativeKey::sDispatchedKeyOfAppCommand = 0;
+
 NativeKey::NativeKey(nsWindowBase* aWidget,
-                     const MSG& aKeyOrCharMessage,
+                     const MSG& aMessage,
                      const ModifierKeyState& aModKeyState,
-                     nsTArray<FakeCharMsg>* aFakeCharMsgs) :
-  mWidget(aWidget), mMsg(aKeyOrCharMessage), mDOMKeyCode(0),
-  mModKeyState(aModKeyState), mVirtualKeyCode(0), mOriginalVirtualKeyCode(0),
-  mFakeCharMsgs(aFakeCharMsgs && aFakeCharMsgs->Length() ?
-                  aFakeCharMsgs : nullptr)
+                     nsTArray<FakeCharMsg>* aFakeCharMsgs)
+  : mWidget(aWidget)
+  , mMsg(aMessage)
+  , mDOMKeyCode(0)
+  , mKeyNameIndex(KEY_NAME_INDEX_Unidentified)
+  , mCodeNameIndex(CODE_NAME_INDEX_UNKNOWN)
+  , mModKeyState(aModKeyState)
+  , mVirtualKeyCode(0)
+  , mOriginalVirtualKeyCode(0)
+  , mScanCode(0)
+  , mIsExtended(false)
+  , mIsDeadKey(false)
+  , mFakeCharMsgs(aFakeCharMsgs && aFakeCharMsgs->Length() ?
+                    aFakeCharMsgs : nullptr)
 {
   MOZ_ASSERT(aWidget);
   KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
   mKeyboardLayout = keyboardLayout->GetLayout();
+
+  if (mMsg.message == WM_APPCOMMAND) {
+    InitWithAppCommand();
+    return;
+  }
+
   mScanCode = WinUtils::GetScanCode(mMsg.lParam);
   mIsExtended = WinUtils::IsExtendedScanCode(mMsg.lParam);
   switch (mMsg.message) {
     case WM_KEYDOWN:
     case WM_SYSKEYDOWN:
     case WM_KEYUP:
     case WM_SYSKEYUP: {
       // If the key message is sent from other application like a11y tools, the
@@ -850,16 +867,112 @@ NativeKey::NativeKey(nsWindowBase* aWidg
   keyboardLayout->InitNativeKey(*this, mModKeyState);
 
   mIsDeadKey =
     (IsFollowedByDeadCharMessage() ||
      keyboardLayout->IsDeadKey(mOriginalVirtualKeyCode, mModKeyState));
   mIsPrintableKey = KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode);
 }
 
+void
+NativeKey::InitWithAppCommand()
+{
+  if (GET_DEVICE_LPARAM(mMsg.lParam) != FAPPCOMMAND_KEY) {
+    return;
+  }
+
+  uint32_t appCommand = GET_APPCOMMAND_LPARAM(mMsg.lParam);
+  switch (GET_APPCOMMAND_LPARAM(mMsg.lParam)) {
+
+#undef NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX
+#define NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX(aAppCommand, aKeyNameIndex) \
+    case aAppCommand: \
+      mKeyNameIndex = aKeyNameIndex; \
+      break;
+
+#include "NativeKeyToDOMKeyName.h"
+
+#undef NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX
+
+    default:
+      mKeyNameIndex = KEY_NAME_INDEX_Unidentified;
+  }
+
+  // Guess the virtual keycode which caused this message.
+  switch (appCommand) {
+    case APPCOMMAND_BROWSER_BACKWARD:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_BACK;
+      break;
+    case APPCOMMAND_BROWSER_FORWARD:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_FORWARD;
+      break;
+    case APPCOMMAND_BROWSER_REFRESH:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_REFRESH;
+      break;
+    case APPCOMMAND_BROWSER_STOP:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_STOP;
+      break;
+    case APPCOMMAND_BROWSER_SEARCH:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_SEARCH;
+      break;
+    case APPCOMMAND_BROWSER_FAVORITES:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_FAVORITES;
+      break;
+    case APPCOMMAND_BROWSER_HOME:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_HOME;
+      break;
+    case APPCOMMAND_VOLUME_MUTE:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_VOLUME_MUTE;
+      break;
+    case APPCOMMAND_VOLUME_DOWN:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_VOLUME_DOWN;
+      break;
+    case APPCOMMAND_VOLUME_UP:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_VOLUME_UP;
+      break;
+    case APPCOMMAND_MEDIA_NEXTTRACK:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_NEXT_TRACK;
+      break;
+    case APPCOMMAND_MEDIA_PREVIOUSTRACK:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_PREV_TRACK;
+      break;
+    case APPCOMMAND_MEDIA_STOP:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_STOP;
+      break;
+    case APPCOMMAND_MEDIA_PLAY_PAUSE:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_PLAY_PAUSE;
+      break;
+    case APPCOMMAND_LAUNCH_MAIL:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_MAIL;
+      break;
+    case APPCOMMAND_LAUNCH_MEDIA_SELECT:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_MEDIA_SELECT;
+      break;
+    case APPCOMMAND_LAUNCH_APP1:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_APP1;
+      break;
+    case APPCOMMAND_LAUNCH_APP2:
+      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_APP2;
+      break;
+    default:
+      return;
+  }
+
+  uint16_t scanCodeEx = ComputeScanCodeExFromVirtualKeyCode(mVirtualKeyCode);
+  mScanCode = static_cast<uint8_t>(scanCodeEx & 0xFF);
+  uint8_t extended = static_cast<uint8_t>((scanCodeEx & 0xFF00) >> 8);
+  mIsExtended = (extended == 0xE0) || (extended == 0xE1);
+  mDOMKeyCode =
+    KeyboardLayout::GetInstance()->
+      ConvertNativeKeyCodeToDOMKeyCode(mOriginalVirtualKeyCode);
+  mCodeNameIndex =
+    KeyboardLayout::ConvertScanCodeToCodeNameIndex(
+      GetScanCodeWithExtendedFlag());
+}
+
 bool
 NativeKey::IsFollowedByDeadCharMessage() const
 {
   MSG nextMsg;
   if (mFakeCharMsgs) {
     nextMsg = mFakeCharMsgs->ElementAt(0).GetCharMsg(mMsg.hwnd);
   } else {
     if (!WinUtils::PeekMessage(&nextMsg, mMsg.hwnd, WM_KEYFIRST, WM_KEYLAST,
@@ -1098,24 +1211,222 @@ NativeKey::DispatchKeyEvent(WidgetKeyboa
     pluginEvent.lParam = aMsgSentToPlugin->lParam;
     aKeyEvent.mPluginEvent.Copy(pluginEvent);
   }
 
   return (mWidget->DispatchKeyboardEvent(&aKeyEvent) || mWidget->Destroyed());
 }
 
 bool
+NativeKey::DispatchCommandEvent(uint32_t aEventCommand) const
+{
+  nsCOMPtr<nsIAtom> command;
+  switch (aEventCommand) {
+    case APPCOMMAND_BROWSER_BACKWARD:
+      command = nsGkAtoms::Back;
+      break;
+    case APPCOMMAND_BROWSER_FORWARD:
+      command = nsGkAtoms::Forward;
+      break;
+    case APPCOMMAND_BROWSER_REFRESH:
+      command = nsGkAtoms::Reload;
+      break;
+    case APPCOMMAND_BROWSER_STOP:
+      command = nsGkAtoms::Stop;
+      break;
+    case APPCOMMAND_BROWSER_SEARCH:
+      command = nsGkAtoms::Search;
+      break;
+    case APPCOMMAND_BROWSER_FAVORITES:
+      command = nsGkAtoms::Bookmarks;
+      break;
+    case APPCOMMAND_BROWSER_HOME:
+      command = nsGkAtoms::Home;
+      break;
+    case APPCOMMAND_CLOSE:
+      command = nsGkAtoms::Close;
+      break;
+    case APPCOMMAND_FIND:
+      command = nsGkAtoms::Find;
+      break;
+    case APPCOMMAND_HELP:
+      command = nsGkAtoms::Help;
+      break;
+    case APPCOMMAND_NEW:
+      command = nsGkAtoms::New;
+      break;
+    case APPCOMMAND_OPEN:
+      command = nsGkAtoms::Open;
+      break;
+    case APPCOMMAND_PRINT:
+      command = nsGkAtoms::Print;
+      break;
+    case APPCOMMAND_SAVE:
+      command = nsGkAtoms::Save;
+      break;
+    case APPCOMMAND_FORWARD_MAIL:
+      command = nsGkAtoms::ForwardMail;
+      break;
+    case APPCOMMAND_REPLY_TO_MAIL:
+      command = nsGkAtoms::ReplyToMail;
+      break;
+    case APPCOMMAND_SEND_MAIL:
+      command = nsGkAtoms::SendMail;
+      break;
+    case APPCOMMAND_MEDIA_NEXTTRACK:
+      command = nsGkAtoms::NextTrack;
+      break;
+    case APPCOMMAND_MEDIA_PREVIOUSTRACK:
+      command = nsGkAtoms::PreviousTrack;
+      break;
+    case APPCOMMAND_MEDIA_STOP:
+      command = nsGkAtoms::MediaStop;
+      break;
+    case APPCOMMAND_MEDIA_PLAY_PAUSE:
+      command = nsGkAtoms::PlayPause;
+      break;
+    default:
+      return false;
+  }
+  WidgetCommandEvent commandEvent(true, nsGkAtoms::onAppCommand,
+                                  command, mWidget);
+
+  mWidget->InitEvent(commandEvent);
+  return (mWidget->DispatchWindowEvent(&commandEvent) || mWidget->Destroyed());
+}
+
+bool
+NativeKey::HandleAppCommandMessage() const
+{
+  // NOTE: Typical behavior of WM_APPCOMMAND caused by key is, WM_APPCOMMAND
+  //       message is _sent_ first.  Then, the DefaultWndProc will _post_
+  //       WM_KEYDOWN message and WM_KEYUP message if the keycode for the
+  //       command is available (i.e., mVirtualKeyCode is not 0).
+
+  // NOTE: IntelliType (Microsoft's keyboard utility software) always consumes
+  //       WM_KEYDOWN and WM_KEYUP.
+
+  // Let's dispatch keydown message before our chrome handles the command
+  // when the message is caused by a keypress.  This behavior makes handling
+  // WM_APPCOMMAND be a default action of the keydown event.  This means that
+  // web applications can handle multimedia keys and prevent our default action.
+  // This allow web applications to provide better UX for multimedia keyboard
+  // users.
+  bool dispatchKeyEvent = (GET_DEVICE_LPARAM(mMsg.lParam) == FAPPCOMMAND_KEY);
+
+  bool consumed = false;
+
+  if (dispatchKeyEvent) {
+    WidgetKeyboardEvent keydownEvent(true, NS_KEY_DOWN, mWidget);
+    InitKeyEvent(keydownEvent, mModKeyState);
+    // NOTE: If the keydown event is consumed by web contents, we shouldn't
+    //       continue to handle the command.
+    consumed = DispatchKeyEvent(keydownEvent, &mMsg);
+    sDispatchedKeyOfAppCommand = mVirtualKeyCode;
+    if (mWidget->Destroyed()) {
+      return true;
+    }
+  }
+
+  // Dispatch a command event or a content command event if the command is
+  // supported.
+  if (!consumed) {
+    uint32_t appCommand = GET_APPCOMMAND_LPARAM(mMsg.lParam);
+    uint32_t contentCommandMessage = NS_EVENT_NULL;
+    switch (appCommand) {
+      case APPCOMMAND_BROWSER_BACKWARD:
+      case APPCOMMAND_BROWSER_FORWARD:
+      case APPCOMMAND_BROWSER_REFRESH:
+      case APPCOMMAND_BROWSER_STOP:
+      case APPCOMMAND_BROWSER_SEARCH:
+      case APPCOMMAND_BROWSER_FAVORITES:
+      case APPCOMMAND_BROWSER_HOME:
+      case APPCOMMAND_CLOSE:
+      case APPCOMMAND_FIND:
+      case APPCOMMAND_HELP:
+      case APPCOMMAND_NEW:
+      case APPCOMMAND_OPEN:
+      case APPCOMMAND_PRINT:
+      case APPCOMMAND_SAVE:
+      case APPCOMMAND_FORWARD_MAIL:
+      case APPCOMMAND_REPLY_TO_MAIL:
+      case APPCOMMAND_SEND_MAIL:
+      case APPCOMMAND_MEDIA_NEXTTRACK:
+      case APPCOMMAND_MEDIA_PREVIOUSTRACK:
+      case APPCOMMAND_MEDIA_STOP:
+      case APPCOMMAND_MEDIA_PLAY_PAUSE:
+        // We shouldn't consume the message always because if we don't handle
+        // the message, the sender (typically, utility of keyboard or mouse)
+        // may send other key messages which indicate well known shortcut key.
+        consumed = DispatchCommandEvent(appCommand);
+        break;
+
+      // Use content command for following commands:
+      case APPCOMMAND_COPY:
+        contentCommandMessage = NS_CONTENT_COMMAND_COPY;
+        break;
+      case APPCOMMAND_CUT:
+        contentCommandMessage = NS_CONTENT_COMMAND_CUT;
+        break;
+      case APPCOMMAND_PASTE:
+        contentCommandMessage = NS_CONTENT_COMMAND_PASTE;
+        break;
+      case APPCOMMAND_REDO:
+        contentCommandMessage = NS_CONTENT_COMMAND_REDO;
+        break;
+      case APPCOMMAND_UNDO:
+        contentCommandMessage = NS_CONTENT_COMMAND_UNDO;
+        break;
+    }
+
+    if (contentCommandMessage) {
+      WidgetContentCommandEvent contentCommandEvent(true, contentCommandMessage,
+                                                    mWidget);
+      mWidget->DispatchWindowEvent(&contentCommandEvent);
+      consumed = true;
+    }
+
+    if (mWidget->Destroyed()) {
+      return true;
+    }
+  }
+
+  // Dispatch a keyup event if the command is caused by pressing a key and
+  // the key isn't mapped to a virtual keycode.
+  if (dispatchKeyEvent && !mVirtualKeyCode) {
+    WidgetKeyboardEvent keyupEvent(true, NS_KEY_UP, mWidget);
+    InitKeyEvent(keyupEvent, mModKeyState);
+    // NOTE: Ignore if the keyup event is consumed because keyup event
+    //       represents just a physical key event state change.
+    DispatchKeyEvent(keyupEvent, &mMsg);
+    if (mWidget->Destroyed()) {
+      return true;
+    }
+  }
+
+  return consumed;
+}
+
+bool
 NativeKey::HandleKeyDownMessage(bool* aEventDispatched) const
 {
   MOZ_ASSERT(IsKeyDownMessage());
 
   if (aEventDispatched) {
     *aEventDispatched = false;
   }
 
+  if (sDispatchedKeyOfAppCommand &&
+      sDispatchedKeyOfAppCommand == mOriginalVirtualKeyCode) {
+    // The multimedia key event has already been dispatch from
+    // HandleAppCommandMessage().
+    sDispatchedKeyOfAppCommand = 0;
+    return true;
+  }
+
   bool defaultPrevented = false;
   if (mFakeCharMsgs ||
       !RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
     // Ignore [shift+]alt+space so the OS can handle it.
     if (mModKeyState.IsAlt() && !mModKeyState.IsControl() &&
         mVirtualKeyCode == VK_SPACE) {
       return false;
     }
@@ -2592,16 +2903,17 @@ KeyboardLayout::ConvertNativeKeyCodeToDO
     case VK_BROWSER_BACK:
     case VK_BROWSER_FORWARD:
     case VK_BROWSER_REFRESH:
     case VK_BROWSER_STOP:
     case VK_BROWSER_SEARCH:
     case VK_BROWSER_FAVORITES:
     case VK_BROWSER_HOME:
     case VK_MEDIA_NEXT_TRACK:
+    case VK_MEDIA_PREV_TRACK:
     case VK_MEDIA_STOP:
     case VK_MEDIA_PLAY_PAUSE:
     case VK_LAUNCH_MAIL:
     case VK_LAUNCH_MEDIA_SELECT:
     case VK_LAUNCH_APP1:
     case VK_LAUNCH_APP2:
       return 0;
 
@@ -2712,31 +3024,25 @@ KeyboardLayout::ConvertNativeKeyCodeToDO
 
 KeyNameIndex
 KeyboardLayout::ConvertNativeKeyCodeToKeyNameIndex(uint8_t aVirtualKey) const
 {
   if (IsPrintableCharKey(aVirtualKey)) {
     return KEY_NAME_INDEX_USE_STRING;
   }
 
-#define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)
-#define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)
-#define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)
-#define NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)
-
   switch (aVirtualKey) {
 
 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
     case aNativeKey: return aKeyNameIndex;
 
 #include "NativeKeyToDOMKeyName.h"
 
 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
-#define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)
 
     default:
       break;
   }
 
   HKL layout = GetLayout();
   WORD langID = LOWORD(static_cast<HKL>(layout));
   WORD primaryLangID = PRIMARYLANGID(langID);
@@ -2746,49 +3052,44 @@ KeyboardLayout::ConvertNativeKeyCodeToKe
 
 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
 #define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)\
       case aNativeKey: return aKeyNameIndex;
 
 #include "NativeKeyToDOMKeyName.h"
 
 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
-#define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)
 
       default:
         break;
     }
   } else if (primaryLangID == LANG_KOREAN) {
     switch (aVirtualKey) {
 
 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
 #define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)\
       case aNativeKey: return aKeyNameIndex;
 
 #include "NativeKeyToDOMKeyName.h"
 
 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
-#define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)
 
       default:
         return KEY_NAME_INDEX_Unidentified;
     }
   }
 
   switch (aVirtualKey) {
 
 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
 #define NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)\
     case aNativeKey: return aKeyNameIndex;
 
 #include "NativeKeyToDOMKeyName.h"
 
-#undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
-#undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
-#undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
 
     default:
       return KEY_NAME_INDEX_Unidentified;
   }
 }
 
 // static
--- a/widget/windows/KeyboardLayout.h
+++ b/widget/windows/KeyboardLayout.h
@@ -229,17 +229,17 @@ public:
       msg.lParam = static_cast<LPARAM>(mScanCode << 16);
       msg.time = 0;
       msg.pt.x = msg.pt.y = 0;
       return msg;
     }
   };
 
   NativeKey(nsWindowBase* aWidget,
-            const MSG& aKeyOrCharMessage,
+            const MSG& aMessage,
             const ModifierKeyState& aModKeyState,
             nsTArray<FakeCharMsg>* aFakeCharMsgs = nullptr);
 
   /**
    * Handle WM_KEYDOWN message or WM_SYSKEYDOWN message.  The instance must be
    * initialized with WM_KEYDOWN or WM_SYSKEYDOWN.
    * Returns true if dispatched keydown event or keypress event is consumed.
    * Otherwise, false.
@@ -255,16 +255,22 @@ public:
                          bool* aEventDispatched = nullptr) const;
 
   /**
    * Handles keyup message.  Returns true if the event is consumed.
    * Otherwise, false.
    */
   bool HandleKeyUpMessage(bool* aEventDispatched = nullptr) const;
 
+  /**
+   * Handles WM_APPCOMMAND message.  Returns true if the event is consumed.
+   * Otherwise, false.
+   */
+  bool HandleAppCommandMessage() const;
+
 private:
   nsRefPtr<nsWindowBase> mWidget;
   HKL mKeyboardLayout;
   MSG mMsg;
 
   uint32_t mDOMKeyCode;
   KeyNameIndex mKeyNameIndex;
   CodeNameIndex mCodeNameIndex;
@@ -289,34 +295,57 @@ private:
   // mIsPrintableKey is true if the key may be a printable key without
   // any modifier keys.  Otherwise, false.
   // Please note that the event may not cause any text input even if this
   // is true.  E.g., it might be dead key state or Ctrl key may be pressed.
   bool    mIsPrintableKey;
 
   nsTArray<FakeCharMsg>* mFakeCharMsgs;
 
+  // When a keydown event is dispatched at handling WM_APPCOMMAND, the computed
+  // virtual keycode is set to this.  Even if we consume WM_APPCOMMAND message,
+  // Windows may send WM_KEYDOWN and WM_KEYUP message for them.
+  // At that time, we should not dispatch key events for them.
+  static uint8_t sDispatchedKeyOfAppCommand;
+
   NativeKey()
   {
     MOZ_CRASH("The default constructor of NativeKey isn't available");
   }
 
+  void InitWithAppCommand();
+
   /**
    * Returns true if the key event is caused by auto repeat.
    */
   bool IsRepeat() const
   {
     switch (mMsg.message) {
       case WM_KEYDOWN:
       case WM_SYSKEYDOWN:
       case WM_CHAR:
       case WM_SYSCHAR:
       case WM_DEADCHAR:
       case WM_SYSDEADCHAR:
         return ((mMsg.lParam & (1 << 30)) != 0);
+      case WM_APPCOMMAND:
+        if (mVirtualKeyCode) {
+          // If we can map the WM_APPCOMMAND to a virtual keycode, we can trust
+          // the result of GetKeyboardState().
+          BYTE kbdState[256];
+          memset(kbdState, 0, sizeof(kbdState));
+          ::GetKeyboardState(kbdState);
+          return !!kbdState[mVirtualKeyCode];
+        }
+        // If there is no virtual keycode for the command, we dispatch both
+        // keydown and keyup events from WM_APPCOMMAND handler.  Therefore,
+        // even if WM_APPCOMMAND is caused by auto key repeat, web apps receive
+        // a pair of DOM keydown and keyup events.  I.e., KeyboardEvent.repeat
+        // should be never true of such keys.
+        return false;
       default:
         return false;
     }
   }
 
   UINT GetScanCodeWithExtendedFlag() const;
 
   // The result is one of nsIDOMKeyEvent::DOM_KEY_LOCATION_*.
@@ -410,16 +439,22 @@ private:
   /**
    * Initializes the aKeyEvent with the information stored in the instance.
    */
   void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent,
                     const ModifierKeyState& aModKeyState) const;
   void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const;
 
   /**
+   * Dispatches a command event for aEventCommand.
+   * Returns true if the event is consumed.  Otherwise, false.
+   */
+  bool DispatchCommandEvent(uint32_t aEventCommand) const;
+
+  /**
    * Dispatches the key event.  Returns true if the event is consumed.
    * Otherwise, false.
    */
   bool DispatchKeyEvent(WidgetKeyboardEvent& aKeyEvent,
                         const MSG* aMsgSentToPlugin = nullptr) const;
 
   /**
    * DispatchKeyPressEventsWithKeyboardLayout() dispatches keypress event(s)
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -5117,18 +5117,21 @@ nsWindow::ProcessMessage(UINT msg, WPARA
       result = 
         DispatchMouseEvent(NS_MOUSE_BUTTON_UP, 0, lParamToClient(lParam),
                            false, WidgetMouseEvent::eLeftButton,
                            MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_APPCOMMAND:
-      result = HandleAppCommandMsg(wParam, lParam, aRetValue);
+    {
+      MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd);
+      result = HandleAppCommandMsg(nativeMsg, aRetValue);
       break;
+    }
 
     // The WM_ACTIVATE event is fired when a window is raised or lowered,
     // and the loword of wParam specifies which. But we don't want to tell
     // the focus system about this until the WM_SETFOCUS or WM_KILLFOCUS
     // events are fired. Instead, set either the sJustGotActivate or
     // gJustGotDeactivate flags and activate/deactivate once the focus
     // events arrive.
     case WM_ACTIVATE:
--- a/widget/windows/nsWindowBase.cpp
+++ b/widget/windows/nsWindowBase.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 4; 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/. */
 
 #include "nsWindowBase.h"
 
 #include "mozilla/MiscEvents.h"
-#include "nsGkAtoms.h"
 #include "WinUtils.h"
 #include "npapi.h"
 
 using namespace mozilla;
 using namespace mozilla::widget;
 
 static const wchar_t kUser32LibName[] =  L"user32.dll";
 bool nsWindowBase::sTouchInjectInitialized = false;
@@ -190,156 +189,17 @@ nsWindowBase::ClearNativeTouchSequence()
   mActivePointers.Enumerate(CancelTouchPoints, (void*)this);
 
   nsBaseWidget::ClearNativeTouchSequence();
 
   return NS_OK;
 }
 
 bool
-nsWindowBase::DispatchCommandEvent(uint32_t aEventCommand)
-{
-  nsCOMPtr<nsIAtom> command;
-  switch (aEventCommand) {
-    case APPCOMMAND_BROWSER_BACKWARD:
-      command = nsGkAtoms::Back;
-      break;
-    case APPCOMMAND_BROWSER_FORWARD:
-      command = nsGkAtoms::Forward;
-      break;
-    case APPCOMMAND_BROWSER_REFRESH:
-      command = nsGkAtoms::Reload;
-      break;
-    case APPCOMMAND_BROWSER_STOP:
-      command = nsGkAtoms::Stop;
-      break;
-    case APPCOMMAND_BROWSER_SEARCH:
-      command = nsGkAtoms::Search;
-      break;
-    case APPCOMMAND_BROWSER_FAVORITES:
-      command = nsGkAtoms::Bookmarks;
-      break;
-    case APPCOMMAND_BROWSER_HOME:
-      command = nsGkAtoms::Home;
-      break;
-    case APPCOMMAND_CLOSE:
-      command = nsGkAtoms::Close;
-      break;
-    case APPCOMMAND_FIND:
-      command = nsGkAtoms::Find;
-      break;
-    case APPCOMMAND_HELP:
-      command = nsGkAtoms::Help;
-      break;
-    case APPCOMMAND_NEW:
-      command = nsGkAtoms::New;
-      break;
-    case APPCOMMAND_OPEN:
-      command = nsGkAtoms::Open;
-      break;
-    case APPCOMMAND_PRINT:
-      command = nsGkAtoms::Print;
-      break;
-    case APPCOMMAND_SAVE:
-      command = nsGkAtoms::Save;
-      break;
-    case APPCOMMAND_FORWARD_MAIL:
-      command = nsGkAtoms::ForwardMail;
-      break;
-    case APPCOMMAND_REPLY_TO_MAIL:
-      command = nsGkAtoms::ReplyToMail;
-      break;
-    case APPCOMMAND_SEND_MAIL:
-      command = nsGkAtoms::SendMail;
-      break;
-    case APPCOMMAND_MEDIA_NEXTTRACK:
-      command = nsGkAtoms::NextTrack;
-      break;
-    case APPCOMMAND_MEDIA_PREVIOUSTRACK:
-      command = nsGkAtoms::PreviousTrack;
-      break;
-    case APPCOMMAND_MEDIA_STOP:
-      command = nsGkAtoms::MediaStop;
-      break;
-    case APPCOMMAND_MEDIA_PLAY_PAUSE:
-      command = nsGkAtoms::PlayPause;
-      break;
-    default:
-      return false;
-  }
-  WidgetCommandEvent event(true, nsGkAtoms::onAppCommand, command, this);
-
-  InitEvent(event);
-  return DispatchWindowEvent(&event);
-}
-
-bool
-nsWindowBase::HandleAppCommandMsg(WPARAM aWParam,
-                                  LPARAM aLParam,
+nsWindowBase::HandleAppCommandMsg(const MSG& aAppCommandMsg,
                                   LRESULT *aRetValue)
 {
-  uint32_t appCommand = GET_APPCOMMAND_LPARAM(aLParam);
-  uint32_t contentCommandMessage = NS_EVENT_NULL;
-  // XXX After we implement KeyboardEvent.key, we should dispatch the
-  //     key event if (GET_DEVICE_LPARAM(lParam) == FAPPCOMMAND_KEY) is.
-  switch (appCommand)
-  {
-    case APPCOMMAND_BROWSER_BACKWARD:
-    case APPCOMMAND_BROWSER_FORWARD:
-    case APPCOMMAND_BROWSER_REFRESH:
-    case APPCOMMAND_BROWSER_STOP:
-    case APPCOMMAND_BROWSER_SEARCH:
-    case APPCOMMAND_BROWSER_FAVORITES:
-    case APPCOMMAND_BROWSER_HOME:
-    case APPCOMMAND_CLOSE:
-    case APPCOMMAND_FIND:
-    case APPCOMMAND_HELP:
-    case APPCOMMAND_NEW:
-    case APPCOMMAND_OPEN:
-    case APPCOMMAND_PRINT:
-    case APPCOMMAND_SAVE:
-    case APPCOMMAND_FORWARD_MAIL:
-    case APPCOMMAND_REPLY_TO_MAIL:
-    case APPCOMMAND_SEND_MAIL:
-    case APPCOMMAND_MEDIA_NEXTTRACK:
-    case APPCOMMAND_MEDIA_PREVIOUSTRACK:
-    case APPCOMMAND_MEDIA_STOP:
-    case APPCOMMAND_MEDIA_PLAY_PAUSE:
-      // We shouldn't consume the message always because if we don't handle
-      // the message, the sender (typically, utility of keyboard or mouse)
-      // may send other key messages which indicate well known shortcut key.
-      if (DispatchCommandEvent(appCommand)) {
-        // tell the driver that we handled the event
-        *aRetValue = 1;
-        return true;
-      }
-      break;
-
-    // Use content command for following commands:
-    case APPCOMMAND_COPY:
-      contentCommandMessage = NS_CONTENT_COMMAND_COPY;
-      break;
-    case APPCOMMAND_CUT:
-      contentCommandMessage = NS_CONTENT_COMMAND_CUT;
-      break;
-    case APPCOMMAND_PASTE:
-      contentCommandMessage = NS_CONTENT_COMMAND_PASTE;
-      break;
-    case APPCOMMAND_REDO:
-      contentCommandMessage = NS_CONTENT_COMMAND_REDO;
-      break;
-    case APPCOMMAND_UNDO:
-      contentCommandMessage = NS_CONTENT_COMMAND_UNDO;
-      break;
-  }
-
-  if (contentCommandMessage) {
-    WidgetContentCommandEvent contentCommand(true, contentCommandMessage,
-                                              this);
-    DispatchWindowEvent(&contentCommand);
-    // tell the driver that we handled the event
-    *aRetValue = 1;
-    return true;
-  }
-
-  // default = false - tell the driver that the event was not handled
-  return false;
+  ModifierKeyState modKeyState;
+  NativeKey nativeKey(this, aAppCommandMsg, modKeyState);
+  bool consumed = nativeKey.HandleAppCommandMessage();
+  *aRetValue = consumed ? 1 : 0;
+  return consumed;
 }
--- a/widget/windows/nsWindowBase.h
+++ b/widget/windows/nsWindowBase.h
@@ -84,24 +84,23 @@ public:
   virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId,
                                               TouchPointerState aPointerState,
                                               nsIntPoint aPointerScreenPoint,
                                               double aPointerPressure,
                                               uint32_t aPointerOrientation);
   virtual nsresult ClearNativeTouchSequence();
 
   /*
-   * WM_APPCOMMAND common handler. Sends events via DispatchWindowEvent.
+   * WM_APPCOMMAND common handler.
+   * Sends events via NativeKey::HandleAppCommandMessage().
    */
-  virtual bool HandleAppCommandMsg(WPARAM aWParam,
-                                   LPARAM aLParam,
+  virtual bool HandleAppCommandMsg(const MSG& aAppCommandMsg,
                                    LRESULT *aRetValue);
 
 protected:
-  bool DispatchCommandEvent(uint32_t aEventCommand);
   static bool InitTouchInjection();
   bool InjectTouchPoint(uint32_t aId, nsIntPoint& aPointerScreenPoint,
                         POINTER_FLAGS aFlags, uint32_t aPressure = 1024,
                         uint32_t aOrientation = 90);
 
   class PointerInfo
   {
   public:
--- a/widget/windows/winrt/MetroWidget.cpp
+++ b/widget/windows/winrt/MetroWidget.cpp
@@ -875,18 +875,21 @@ MetroWidget::WindowProcedure(HWND aWnd, 
     {
       KeyboardLayout::GetInstance()->
         OnLayoutChange(reinterpret_cast<HKL>(aLParam));
       processResult = 1;
       break;
     }
 
     case WM_APPCOMMAND:
-      processDefault = HandleAppCommandMsg(aWParam, aLParam, &processResult);
+    {
+      MSG msg = WinUtils::InitMSG(aMsg, aWParam, aLParam, aWnd);
+      processDefault = HandleAppCommandMsg(msg, &processResult);
       break;
+    }
 
     case WM_GETOBJECT:
     {
       DWORD dwObjId = (LPARAM)(DWORD) aLParam;
       // Passing this to CallWindowProc can result in a failure due to a timing issue
       // in winrt core window server code, so we call it directly here. Also, it's not
       // clear Windows::UI::Core::WindowServer::OnAutomationProviderRequestedEvent is
       // compatible with metro enabled desktop browsers, it makes an initial call to