layout/xul/nsMenuBarListener.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsMenuBarListener.h"
     7 #include "nsMenuBarFrame.h"
     8 #include "nsMenuPopupFrame.h"
     9 #include "nsIDOMEvent.h"
    11 // Drag & Drop, Clipboard
    12 #include "nsIServiceManager.h"
    13 #include "nsWidgetsCID.h"
    14 #include "nsCOMPtr.h"
    15 #include "nsIDOMKeyEvent.h"
    16 #include "nsIContent.h"
    17 #include "nsIDOMNode.h"
    18 #include "nsIDOMElement.h"
    20 #include "nsContentUtils.h"
    21 #include "mozilla/Preferences.h"
    22 #include "mozilla/TextEvents.h"
    24 using namespace mozilla;
    26 /*
    27  * nsMenuBarListener implementation
    28  */
    30 NS_IMPL_ISUPPORTS(nsMenuBarListener, nsIDOMEventListener)
    32 #define MODIFIER_SHIFT    1
    33 #define MODIFIER_CONTROL  2
    34 #define MODIFIER_ALT      4
    35 #define MODIFIER_META     8
    36 #define MODIFIER_OS       16
    38 ////////////////////////////////////////////////////////////////////////
    40 int32_t nsMenuBarListener::mAccessKey = -1;
    41 uint32_t nsMenuBarListener::mAccessKeyMask = 0;
    42 bool nsMenuBarListener::mAccessKeyFocuses = false;
    44 nsMenuBarListener::nsMenuBarListener(nsMenuBarFrame* aMenuBar) 
    45   :mAccessKeyDown(false), mAccessKeyDownCanceled(false)
    46 {
    47   mMenuBarFrame = aMenuBar;
    48 }
    50 ////////////////////////////////////////////////////////////////////////
    51 nsMenuBarListener::~nsMenuBarListener() 
    52 {
    53 }
    55 void
    56 nsMenuBarListener::InitializeStatics()
    57 {
    58   Preferences::AddBoolVarCache(&mAccessKeyFocuses,
    59                                "ui.key.menuAccessKeyFocuses");
    60 }
    62 nsresult
    63 nsMenuBarListener::GetMenuAccessKey(int32_t* aAccessKey)
    64 {
    65   if (!aAccessKey)
    66     return NS_ERROR_INVALID_POINTER;
    67   InitAccessKey();
    68   *aAccessKey = mAccessKey;
    69   return NS_OK;
    70 }
    72 void nsMenuBarListener::InitAccessKey()
    73 {
    74   if (mAccessKey >= 0)
    75     return;
    77   // Compiled-in defaults, in case we can't get LookAndFeel --
    78   // mac doesn't have menu shortcuts, other platforms use alt.
    79 #ifdef XP_MACOSX
    80   mAccessKey = 0;
    81   mAccessKeyMask = 0;
    82 #else
    83   mAccessKey = nsIDOMKeyEvent::DOM_VK_ALT;
    84   mAccessKeyMask = MODIFIER_ALT;
    85 #endif
    87   // Get the menu access key value from prefs, overriding the default:
    88   mAccessKey = Preferences::GetInt("ui.key.menuAccessKey", mAccessKey);
    89   if (mAccessKey == nsIDOMKeyEvent::DOM_VK_SHIFT)
    90     mAccessKeyMask = MODIFIER_SHIFT;
    91   else if (mAccessKey == nsIDOMKeyEvent::DOM_VK_CONTROL)
    92     mAccessKeyMask = MODIFIER_CONTROL;
    93   else if (mAccessKey == nsIDOMKeyEvent::DOM_VK_ALT)
    94     mAccessKeyMask = MODIFIER_ALT;
    95   else if (mAccessKey == nsIDOMKeyEvent::DOM_VK_META)
    96     mAccessKeyMask = MODIFIER_META;
    97   else if (mAccessKey == nsIDOMKeyEvent::DOM_VK_WIN)
    98     mAccessKeyMask = MODIFIER_OS;
    99 }
   101 void
   102 nsMenuBarListener::ToggleMenuActiveState()
   103 {
   104   nsMenuFrame* closemenu = mMenuBarFrame->ToggleMenuActiveState();
   105   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   106   if (pm && closemenu) {
   107     nsMenuPopupFrame* popupFrame = closemenu->GetPopup();
   108     if (popupFrame)
   109       pm->HidePopup(popupFrame->GetContent(), false, false, true, false);
   110   }
   111 }
   113 ////////////////////////////////////////////////////////////////////////
   114 nsresult
   115 nsMenuBarListener::KeyUp(nsIDOMEvent* aKeyEvent)
   116 {  
   117   nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
   118   if (!keyEvent) {
   119     return NS_OK;
   120   }
   122   InitAccessKey();
   124   //handlers shouldn't be triggered by non-trusted events.
   125   bool trustedEvent = false;
   126   aKeyEvent->GetIsTrusted(&trustedEvent);
   128   if (!trustedEvent) {
   129     return NS_OK;
   130   }
   132   if (mAccessKey && mAccessKeyFocuses)
   133   {
   134     bool defaultPrevented = false;
   135     aKeyEvent->GetDefaultPrevented(&defaultPrevented);
   137     // On a press of the ALT key by itself, we toggle the menu's 
   138     // active/inactive state.
   139     // Get the ascii key code.
   140     uint32_t theChar;
   141     keyEvent->GetKeyCode(&theChar);
   143     if (!defaultPrevented && mAccessKeyDown && !mAccessKeyDownCanceled &&
   144         (int32_t)theChar == mAccessKey)
   145     {
   146       // The access key was down and is now up, and no other
   147       // keys were pressed in between.
   148       if (!mMenuBarFrame->IsActive()) {
   149         mMenuBarFrame->SetActiveByKeyboard();
   150       }
   151       ToggleMenuActiveState();
   152     }
   153     mAccessKeyDown = false;
   154     mAccessKeyDownCanceled = false;
   156     bool active = mMenuBarFrame->IsActive();
   157     if (active) {
   158       aKeyEvent->StopPropagation();
   159       aKeyEvent->PreventDefault();
   160       return NS_OK; // I am consuming event
   161     }
   162   }
   164   return NS_OK; // means I am NOT consuming event
   165 }
   167 ////////////////////////////////////////////////////////////////////////
   168 nsresult
   169 nsMenuBarListener::KeyPress(nsIDOMEvent* aKeyEvent)
   170 {
   171   // if event has already been handled, bail
   172   if (aKeyEvent) {
   173     bool eventHandled = false;
   174     aKeyEvent->GetDefaultPrevented(&eventHandled);
   175     if (eventHandled) {
   176       return NS_OK;       // don't consume event
   177     }
   178   }
   180   //handlers shouldn't be triggered by non-trusted events.
   181   bool trustedEvent = false;
   182   if (aKeyEvent) {
   183     aKeyEvent->GetIsTrusted(&trustedEvent);
   184   }
   186   if (!trustedEvent)
   187     return NS_OK;
   189   nsresult retVal = NS_OK;  // default is to not consume event
   191   InitAccessKey();
   193   if (mAccessKey)
   194   {
   195     bool preventDefault;
   196     aKeyEvent->GetDefaultPrevented(&preventDefault);
   197     if (!preventDefault) {
   198       nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
   199       uint32_t keyCode, charCode;
   200       keyEvent->GetKeyCode(&keyCode);
   201       keyEvent->GetCharCode(&charCode);
   203       bool hasAccessKeyCandidates = charCode != 0;
   204       if (!hasAccessKeyCandidates) {
   205         WidgetKeyboardEvent* nativeKeyEvent =
   206           aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
   207         if (nativeKeyEvent) {
   208           nsAutoTArray<uint32_t, 10> keys;
   209           nsContentUtils::GetAccessKeyCandidates(nativeKeyEvent, keys);
   210           hasAccessKeyCandidates = !keys.IsEmpty();
   211         }
   212       }
   214       // Cancel the access key flag unless we are pressing the access key.
   215       if (keyCode != (uint32_t)mAccessKey) {
   216         mAccessKeyDownCanceled = true;
   217       }
   219       if (IsAccessKeyPressed(keyEvent) && hasAccessKeyCandidates) {
   220         // Do shortcut navigation.
   221         // A letter was pressed. We want to see if a shortcut gets matched. If
   222         // so, we'll know the menu got activated.
   223         nsMenuFrame* result = mMenuBarFrame->FindMenuWithShortcut(keyEvent);
   224         if (result) {
   225           mMenuBarFrame->SetActiveByKeyboard();
   226           mMenuBarFrame->SetActive(true);
   227           result->OpenMenu(true);
   229           // The opened menu will listen next keyup event.
   230           // Therefore, we should clear the keydown flags here.
   231           mAccessKeyDown = mAccessKeyDownCanceled = false;
   233           aKeyEvent->StopPropagation();
   234           aKeyEvent->PreventDefault();
   235           retVal = NS_OK;       // I am consuming event
   236         }
   237       }    
   238 #ifndef XP_MACOSX
   239       // Also need to handle F10 specially on Non-Mac platform.
   240       else if (keyCode == NS_VK_F10) {
   241         if ((GetModifiers(keyEvent) & ~MODIFIER_CONTROL) == 0) {
   242           // The F10 key just went down by itself or with ctrl pressed.
   243           // In Windows, both of these activate the menu bar.
   244           mMenuBarFrame->SetActiveByKeyboard();
   245           ToggleMenuActiveState();
   247           if (mMenuBarFrame->IsActive()) {
   248 #ifdef MOZ_WIDGET_GTK
   249             // In GTK, this also opens the first menu.
   250             mMenuBarFrame->GetCurrentMenuItem()->OpenMenu(true);
   251 #endif
   252             aKeyEvent->StopPropagation();
   253             aKeyEvent->PreventDefault();
   254             return NS_OK; // consume the event
   255           }
   256         }
   257       }
   258 #endif // !XP_MACOSX
   259     } 
   260   }
   262   return retVal;
   263 }
   265 bool
   266 nsMenuBarListener::IsAccessKeyPressed(nsIDOMKeyEvent* aKeyEvent)
   267 {
   268   InitAccessKey();
   269   // No other modifiers are allowed to be down except for Shift.
   270   uint32_t modifiers = GetModifiers(aKeyEvent);
   272   return (mAccessKeyMask != MODIFIER_SHIFT &&
   273           (modifiers & mAccessKeyMask) &&
   274           (modifiers & ~(mAccessKeyMask | MODIFIER_SHIFT)) == 0);
   275 }
   277 uint32_t
   278 nsMenuBarListener::GetModifiers(nsIDOMKeyEvent* aKeyEvent)
   279 {
   280   uint32_t modifiers = 0;
   281   WidgetInputEvent* inputEvent =
   282     aKeyEvent->GetInternalNSEvent()->AsInputEvent();
   283   MOZ_ASSERT(inputEvent);
   285   if (inputEvent->IsShift()) {
   286     modifiers |= MODIFIER_SHIFT;
   287   }
   289   if (inputEvent->IsControl()) {
   290     modifiers |= MODIFIER_CONTROL;
   291   }
   293   if (inputEvent->IsAlt()) {
   294     modifiers |= MODIFIER_ALT;
   295   }
   297   if (inputEvent->IsMeta()) {
   298     modifiers |= MODIFIER_META;
   299   }
   301   if (inputEvent->IsOS()) {
   302     modifiers |= MODIFIER_OS;
   303   }
   305   return modifiers;
   306 }
   308 ////////////////////////////////////////////////////////////////////////
   309 nsresult
   310 nsMenuBarListener::KeyDown(nsIDOMEvent* aKeyEvent)
   311 {
   312   InitAccessKey();
   314   //handlers shouldn't be triggered by non-trusted events.
   315   bool trustedEvent = false;
   316   if (aKeyEvent) {
   317     aKeyEvent->GetIsTrusted(&trustedEvent);
   318   }
   320   if (!trustedEvent)
   321     return NS_OK;
   323   if (mAccessKey && mAccessKeyFocuses)
   324   {
   325     bool defaultPrevented = false;
   326     aKeyEvent->GetDefaultPrevented(&defaultPrevented);
   328     nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
   329     uint32_t theChar;
   330     keyEvent->GetKeyCode(&theChar);
   332     // No other modifiers can be down.
   333     // Especially CTRL.  CTRL+ALT == AltGR, and we'll fuck up on non-US
   334     // enhanced 102-key keyboards if we don't check this.
   335     bool isAccessKeyDownEvent =
   336       ((theChar == (uint32_t)mAccessKey) &&
   337        (GetModifiers(keyEvent) & ~mAccessKeyMask) == 0);
   339     if (!mAccessKeyDown) {
   340       // If accesskey isn't being pressed and the key isn't the accesskey,
   341       // ignore the event.
   342       if (!isAccessKeyDownEvent) {
   343         return NS_OK;
   344       }
   346       // Otherwise, accept the accesskey state.
   347       mAccessKeyDown = true;
   348       // If default is prevented already, cancel the access key down.
   349       mAccessKeyDownCanceled = defaultPrevented;
   350       return NS_OK;
   351     }
   353     // If the pressed accesskey was canceled already or the event was
   354     // consumed already, ignore the event.
   355     if (mAccessKeyDownCanceled || defaultPrevented) {
   356       return NS_OK;
   357     }
   359     // Some key other than the access key just went down,
   360     // so we won't activate the menu bar when the access key is released.
   361     mAccessKeyDownCanceled = !isAccessKeyDownEvent;
   362   }
   364   return NS_OK; // means I am NOT consuming event
   365 }
   367 ////////////////////////////////////////////////////////////////////////
   369 nsresult
   370 nsMenuBarListener::Blur(nsIDOMEvent* aEvent)
   371 {
   372   if (!mMenuBarFrame->IsMenuOpen() && mMenuBarFrame->IsActive()) {
   373     ToggleMenuActiveState();
   374   }
   375   // Reset the accesskey state because we cannot receive the keyup event for
   376   // the pressing accesskey.
   377   mAccessKeyDown = false;
   378   mAccessKeyDownCanceled = false;
   379   return NS_OK; // means I am NOT consuming event
   380 }
   382 ////////////////////////////////////////////////////////////////////////
   383 nsresult 
   384 nsMenuBarListener::MouseDown(nsIDOMEvent* aMouseEvent)
   385 {
   386   // NOTE: MouseDown method listens all phases
   388   // Even if the mousedown event is canceled, it means the user don't want
   389   // to activate the menu.  Therefore, we need to record it at capturing (or
   390   // target) phase.
   391   if (mAccessKeyDown) {
   392     mAccessKeyDownCanceled = true;
   393   }
   395   uint16_t phase = 0;
   396   nsresult rv = aMouseEvent->GetEventPhase(&phase);
   397   NS_ENSURE_SUCCESS(rv, rv);
   398   // Don't do anything at capturing phase, any behavior should be cancelable.
   399   if (phase == nsIDOMEvent::CAPTURING_PHASE) {
   400     return NS_OK;
   401   }
   403   if (!mMenuBarFrame->IsMenuOpen() && mMenuBarFrame->IsActive())
   404     ToggleMenuActiveState();
   406   return NS_OK; // means I am NOT consuming event
   407 }
   409 ////////////////////////////////////////////////////////////////////////
   410 nsresult
   411 nsMenuBarListener::HandleEvent(nsIDOMEvent* aEvent)
   412 {
   413   nsAutoString eventType;
   414   aEvent->GetType(eventType);
   416   if (eventType.EqualsLiteral("keyup")) {
   417     return KeyUp(aEvent);
   418   }
   419   if (eventType.EqualsLiteral("keydown")) {
   420     return KeyDown(aEvent);
   421   }
   422   if (eventType.EqualsLiteral("keypress")) {
   423     return KeyPress(aEvent);
   424   }
   425   if (eventType.EqualsLiteral("blur")) {
   426     return Blur(aEvent);
   427   }
   428   if (eventType.EqualsLiteral("mousedown")) {
   429     return MouseDown(aEvent);
   430   }
   432   NS_ABORT();
   434   return NS_OK;
   435 }

mercurial