Bug 1257759 part.8 nsXBLWindowKeyHandler should handle eKeyDownOnPlugin and eKeyUpOnPlugin events only with reserved shortcut key handlers r=smaug draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Sat, 23 Apr 2016 02:12:54 +0900
changeset 355642 361be61bc6ff0213e3386427878d2f81321ca0df
parent 355641 c6852423e47c40e9953b72061262730f7cce35d7
child 355643 387ce72dcea23a92bd8c774fc54a8bff8da6c844
push id16345
push usermasayuki@d-toybox.com
push dateSat, 23 Apr 2016 09:42:11 +0000
reviewerssmaug
bugs1257759
milestone48.0a1
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,