Bug 1257759 part.8 nsXBLWindowKeyHandler should handle eKeyDownOnPlugin and eKeyUpOnPlugin events only with reserved shortcut key handlers r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Sat, 23 Apr 2016 02:12:54 +0900
changeset 332512 52cc5374ec1b31933061d385a01bf6b27dec7648
parent 332511 502b6b849493d82d320ff091a440ab144c79ac62
child 332513 1bf2088320755b225f40a7b90ba0381460550421
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1257759
milestone48.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 1257759 part.8 nsXBLWindowKeyHandler should handle eKeyDownOnPlugin and eKeyUpOnPlugin events only with reserved shortcut key handlers r=smaug eKeyDownOnPlugin (mozkeydownonplugin) and eKeyUpOnPlugin (mozkeyuponplugin) should execute if the key combination is reserved by the linked <command> element. Note that there is no eKeyPressOnPlugin. Therefore, eKeyDownOnPlugin may execute shortcut key handler which is registered as a keypress event handler. MozReview-Commit-ID: CpjsFW02y26
dom/xbl/nsXBLWindowKeyHandler.cpp
dom/xbl/nsXBLWindowKeyHandler.h
--- a/dom/xbl/nsXBLWindowKeyHandler.cpp
+++ b/dom/xbl/nsXBLWindowKeyHandler.cpp
@@ -309,74 +309,128 @@ nsXBLWindowKeyHandler::InstallKeyboardEv
                            this, NS_LITERAL_STRING("keydown"),
                            TrustedEventsAtCapture());
   aEventListenerManager->AddEventListenerByType(
                            this, NS_LITERAL_STRING("keyup"),
                            TrustedEventsAtCapture());
   aEventListenerManager->AddEventListenerByType(
                            this, NS_LITERAL_STRING("keypress"),
                            TrustedEventsAtCapture());
+  aEventListenerManager->AddEventListenerByType(
+                           this, NS_LITERAL_STRING("mozkeydownonplugin"),
+                           TrustedEventsAtCapture());
+  aEventListenerManager->AddEventListenerByType(
+                           this, NS_LITERAL_STRING("mozkeyuponplugin"),
+                           TrustedEventsAtCapture());
 
   // For reducing the IPC cost, preventing to dispatch reserved keyboard
   // events into the content process.
   aEventListenerManager->AddEventListenerByType(
                            this, NS_LITERAL_STRING("keydown"),
                            TrustedEventsAtSystemGroupCapture());
   aEventListenerManager->AddEventListenerByType(
                            this, NS_LITERAL_STRING("keyup"),
                            TrustedEventsAtSystemGroupCapture());
   aEventListenerManager->AddEventListenerByType(
                            this, NS_LITERAL_STRING("keypress"),
                            TrustedEventsAtSystemGroupCapture());
+  aEventListenerManager->AddEventListenerByType(
+                           this, NS_LITERAL_STRING("mozkeydownonplugin"),
+                           TrustedEventsAtSystemGroupCapture());
+  aEventListenerManager->AddEventListenerByType(
+                           this, NS_LITERAL_STRING("mozkeyuponplugin"),
+                           TrustedEventsAtSystemGroupCapture());
 
   // Handle keyboard events in bubbling phase of the system event group.
   aEventListenerManager->AddEventListenerByType(
                            this, NS_LITERAL_STRING("keydown"),
                            TrustedEventsAtSystemGroupBubble());
   aEventListenerManager->AddEventListenerByType(
                            this, NS_LITERAL_STRING("keyup"),
                            TrustedEventsAtSystemGroupBubble());
   aEventListenerManager->AddEventListenerByType(
                            this, NS_LITERAL_STRING("keypress"),
                            TrustedEventsAtSystemGroupBubble());
+  aEventListenerManager->AddEventListenerByType(
+                           this, NS_LITERAL_STRING("mozkeydownonplugin"),
+                           TrustedEventsAtSystemGroupBubble());
+  aEventListenerManager->AddEventListenerByType(
+                           this, NS_LITERAL_STRING("mozkeyuponplugin"),
+                           TrustedEventsAtSystemGroupBubble());
 }
 
 void
 nsXBLWindowKeyHandler::RemoveKeyboardEventListenersFrom(
                          EventListenerManager* aEventListenerManager)
 {
   aEventListenerManager->RemoveEventListenerByType(
                            this, NS_LITERAL_STRING("keydown"),
                            TrustedEventsAtCapture());
   aEventListenerManager->RemoveEventListenerByType(
                            this, NS_LITERAL_STRING("keyup"),
                            TrustedEventsAtCapture());
   aEventListenerManager->RemoveEventListenerByType(
                            this, NS_LITERAL_STRING("keypress"),
                            TrustedEventsAtCapture());
+  aEventListenerManager->RemoveEventListenerByType(
+                           this, NS_LITERAL_STRING("mozkeydownonplugin"),
+                           TrustedEventsAtCapture());
+  aEventListenerManager->RemoveEventListenerByType(
+                           this, NS_LITERAL_STRING("mozkeyuponplugin"),
+                           TrustedEventsAtCapture());
 
   aEventListenerManager->RemoveEventListenerByType(
                            this, NS_LITERAL_STRING("keydown"),
                            TrustedEventsAtSystemGroupCapture());
   aEventListenerManager->RemoveEventListenerByType(
                            this, NS_LITERAL_STRING("keyup"),
                            TrustedEventsAtSystemGroupCapture());
   aEventListenerManager->RemoveEventListenerByType(
                            this, NS_LITERAL_STRING("keypress"),
                            TrustedEventsAtSystemGroupCapture());
+  aEventListenerManager->RemoveEventListenerByType(
+                           this, NS_LITERAL_STRING("mozkeydownonplugin"),
+                           TrustedEventsAtSystemGroupCapture());
+  aEventListenerManager->RemoveEventListenerByType(
+                           this, NS_LITERAL_STRING("mozkeyuponplugin"),
+                           TrustedEventsAtSystemGroupCapture());
 
   aEventListenerManager->RemoveEventListenerByType(
                            this, NS_LITERAL_STRING("keydown"),
                            TrustedEventsAtSystemGroupBubble());
   aEventListenerManager->RemoveEventListenerByType(
                            this, NS_LITERAL_STRING("keyup"),
                            TrustedEventsAtSystemGroupBubble());
   aEventListenerManager->RemoveEventListenerByType(
                            this, NS_LITERAL_STRING("keypress"),
                            TrustedEventsAtSystemGroupBubble());
+  aEventListenerManager->RemoveEventListenerByType(
+                           this, NS_LITERAL_STRING("mozkeydownonplugin"),
+                           TrustedEventsAtSystemGroupBubble());
+  aEventListenerManager->RemoveEventListenerByType(
+                           this, NS_LITERAL_STRING("mozkeyuponplugin"),
+                           TrustedEventsAtSystemGroupBubble());
+}
+
+nsIAtom*
+nsXBLWindowKeyHandler::ConvertEventToDOMEventType(
+                         const WidgetKeyboardEvent& aWidgetKeyboardEvent) const
+{
+  if (aWidgetKeyboardEvent.IsKeyDownOrKeyDownOnPlugin()) {
+    return nsGkAtoms::keydown;
+  }
+  if (aWidgetKeyboardEvent.IsKeyUpOrKeyUpOnPlugin()) {
+    return nsGkAtoms::keyup;
+  }
+  if (aWidgetKeyboardEvent.mMessage == eKeyPress) {
+    return nsGkAtoms::keypress;
+  }
+  MOZ_ASSERT_UNREACHABLE(
+    "All event messages which this instance listens to should be handled");
+  return nullptr;
 }
 
 NS_IMETHODIMP
 nsXBLWindowKeyHandler::HandleEvent(nsIDOMEvent* aEvent)
 {
   nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aEvent));
   NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
 
@@ -386,21 +440,43 @@ nsXBLWindowKeyHandler::HandleEvent(nsIDO
     if (aEvent->WidgetEventPtr()->mFlags.mInSystemGroup) {
       HandleEventOnCaptureInSystemEventGroup(keyEvent);
     } else {
       HandleEventOnCaptureInDefaultEventGroup(keyEvent);
     }
     return NS_OK;
   }
 
-  nsAutoString eventType;
-  aEvent->GetType(eventType);
-  nsCOMPtr<nsIAtom> eventTypeAtom = NS_Atomize(eventType);
-  NS_ENSURE_TRUE(eventTypeAtom, NS_ERROR_OUT_OF_MEMORY);
+  WidgetKeyboardEvent* widgetKeyboardEvent =
+    aEvent->WidgetEventPtr()->AsKeyboardEvent();
+  if (widgetKeyboardEvent->IsKeyEventOnPlugin()) {
+    // key events on plugin shouldn't execute shortcut key handlers which are
+    // not reserved.
+    if (!widgetKeyboardEvent->mIsReserved) {
+      return NS_OK;
+    }
 
+    // If the event is untrusted event or was already consumed, do nothing.
+    if (!widgetKeyboardEvent->IsTrusted() ||
+        widgetKeyboardEvent->DefaultPrevented()) {
+      return NS_OK;
+    }
+
+    // XXX Don't check isReserved here because even if the handler in this
+    //     instance isn't reserved but another instance reserves the key
+    //     combination, it will be executed when the event is normal keyboard
+    //     events...
+    bool isReserved = false;
+    if (!HasHandlerForEvent(keyEvent, &isReserved)) {
+      return NS_OK;
+    }
+  }
+
+  nsCOMPtr<nsIAtom> eventTypeAtom =
+    ConvertEventToDOMEventType(*widgetKeyboardEvent);
   return WalkHandlers(keyEvent, eventTypeAtom);
 }
 
 void
 nsXBLWindowKeyHandler::HandleEventOnCaptureInDefaultEventGroup(
                          nsIDOMKeyEvent* aEvent)
 {
   WidgetKeyboardEvent* widgetKeyboardEvent =
@@ -560,30 +636,48 @@ nsXBLWindowKeyHandler::WalkHandlersAndEx
                          nsIDOMKeyEvent* aKeyEvent,
                          nsIAtom* aEventType,
                          nsXBLPrototypeHandler* aFirstHandler,
                          uint32_t aCharCode,
                          const IgnoreModifierState& aIgnoreModifierState,
                          bool aExecute,
                          bool* aOutReservedForChrome)
 {
+  if (aOutReservedForChrome) {
+    *aOutReservedForChrome = false;
+  }
+
+  WidgetKeyboardEvent* widgetKeyboardEvent =
+    aKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
+  if (NS_WARN_IF(!widgetKeyboardEvent)) {
+    return false;
+  }
+
   // Try all of the handlers until we find one that matches the event.
   for (nsXBLPrototypeHandler* handler = aFirstHandler;
        handler;
        handler = handler->GetNextHandler()) {
     bool stopped = aKeyEvent->AsEvent()->IsDispatchStopped();
     if (stopped) {
       // The event is finished, don't execute any more handlers
       return false;
     }
 
     if (aExecute) {
-      // If it's executing matched handlers, the event type should exactly be
-      // matched.
-      if (!handler->EventTypeEquals(aEventType)) {
+      // If the event is eKeyDownOnPlugin, it should execute either keydown
+      // handler or keypress handler because eKeyDownOnPlugin events are
+      // never followed by keypress events.
+      if (widgetKeyboardEvent->mMessage == eKeyDownOnPlugin) {
+        if (!handler->EventTypeEquals(nsGkAtoms::keydown) &&
+            !handler->EventTypeEquals(nsGkAtoms::keypress)) {
+          continue;
+        }
+      // The other event types should exactly be matched with the handler's
+      // event type.
+      } else if (!handler->EventTypeEquals(aEventType)) {
         continue;
       }
     } else {
       if (handler->EventTypeEquals(nsGkAtoms::keypress)) {
         // If the handler is a keypress event handler, we also need to check
         // if coming keydown event is a preceding event of reserved key
         // combination because if default action of a keydown event is
         // prevented, following keypress event won't be fired.  However, if
@@ -637,16 +731,22 @@ nsXBLWindowKeyHandler::WalkHandlersAndEx
           aEventType == nsGkAtoms::keydown &&
           handler->EventTypeEquals(nsGkAtoms::keypress)) {
         return true;
       }
       // Otherwise, we've not found a handler for the event yet.
       continue;
     }
 
+    // If it's not reserved and the event is a key event on a plugin,
+    // the handler shouldn't be executed.
+    if (!isReserved && widgetKeyboardEvent->IsKeyEventOnPlugin()) {
+      return false;
+    }
+
     nsCOMPtr<EventTarget> target;
     nsCOMPtr<Element> chromeHandlerElement = GetElement();
     if (chromeHandlerElement) {
       // XXX commandElement may be nullptr...
       target = commandElement;
     } else {
       target = mTarget;
     }
@@ -659,53 +759,48 @@ nsXBLWindowKeyHandler::WalkHandlersAndEx
     }
   }
 
 #ifdef XP_WIN
   // Windows native applications ignore Windows-Logo key state when checking
   // shortcut keys even if the key is pressed.  Therefore, if there is no
   // shortcut key which exactly matches current modifier state, we should
   // retry to look for a shortcut key without the Windows-Logo key press.
-  if (!aIgnoreModifierState.mOS) {
-    WidgetKeyboardEvent* keyEvent =
-      aKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
-    if (keyEvent && keyEvent->IsOS()) {
-      IgnoreModifierState ignoreModifierState(aIgnoreModifierState);
-      ignoreModifierState.mOS = true;
-      return WalkHandlersAndExecute(aKeyEvent, aEventType, aFirstHandler,
-                                    aCharCode, ignoreModifierState, aExecute);
-    }
+  if (!aIgnoreModifierState.mOS && widgetKeyboardEvent->IsOS()) {
+    IgnoreModifierState ignoreModifierState(aIgnoreModifierState);
+    ignoreModifierState.mOS = true;
+    return WalkHandlersAndExecute(aKeyEvent, aEventType, aFirstHandler,
+                                  aCharCode, ignoreModifierState, aExecute);
   }
 #endif
 
   return false;
 }
 
 bool
 nsXBLWindowKeyHandler::HasHandlerForEvent(nsIDOMKeyEvent* aEvent,
                                           bool* aOutReservedForChrome)
 {
-  if (!aEvent->AsEvent()->InternalDOMEvent()->IsTrusted()) {
+  WidgetKeyboardEvent* widgetKeyboardEvent =
+    aEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
+  if (NS_WARN_IF(!widgetKeyboardEvent) || !widgetKeyboardEvent->IsTrusted()) {
     return false;
   }
 
   nsresult rv = EnsureHandlers();
   NS_ENSURE_SUCCESS(rv, false);
 
   bool isDisabled;
   nsCOMPtr<Element> el = GetElement(&isDisabled);
   if (el && isDisabled) {
     return false;
   }
 
-  nsAutoString eventType;
-  aEvent->AsEvent()->GetType(eventType);
-  nsCOMPtr<nsIAtom> eventTypeAtom = NS_Atomize(eventType);
-  NS_ENSURE_TRUE(eventTypeAtom, false);
-
+  nsCOMPtr<nsIAtom> eventTypeAtom =
+    ConvertEventToDOMEventType(*widgetKeyboardEvent);
   return WalkHandlersInternal(aEvent, eventTypeAtom, mHandler, false,
                               aOutReservedForChrome);
 }
 
 already_AddRefed<Element>
 nsXBLWindowKeyHandler::GetElement(bool* aIsDisabled)
 {
   nsCOMPtr<Element> element = do_QueryReferent(mWeakPtrForElement);
--- a/dom/xbl/nsXBLWindowKeyHandler.h
+++ b/dom/xbl/nsXBLWindowKeyHandler.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #ifndef nsXBLWindowKeyHandler_h__
 #define nsXBLWindowKeyHandler_h__
 
+#include "mozilla/EventForwards.h"
 #include "nsWeakPtr.h"
 #include "nsIDOMEventListener.h"
 
 class nsIAtom;
 class nsIDOMElement;
 class nsIDOMKeyEvent;
 class nsXBLSpecialDocInfo;
 class nsXBLPrototypeHandler;
@@ -68,16 +69,22 @@ protected:
   void HandleEventOnCaptureInSystemEventGroup(nsIDOMKeyEvent* aEvent);
 
   // Check if any handler would handle the given event. Optionally returns
   // whether the command handler for the event is marked with the "reserved"
   // attribute.
   bool HasHandlerForEvent(nsIDOMKeyEvent* aEvent,
                           bool* aOutReservedForChrome = nullptr);
 
+  // Returns event type for matching between aWidgetKeyboardEvent and
+  // shortcut key handlers.  This is used for calling WalkHandlers(),
+  // WalkHandlersInternal() and WalkHandlersAndExecute().
+  nsIAtom* ConvertEventToDOMEventType(
+             const mozilla::WidgetKeyboardEvent& aWidgetKeyboardEvent) const;
+
   // lazily load the handlers. Overridden to handle being attached
   // to a particular element rather than the document
   nsresult EnsureHandlers();
 
   // Is an HTML editable element focused
   bool IsHTMLEditableFieldFocused();
 
   // Returns the element which was passed as a parameter to the constructor,