layout/xul/nsMenuBarFrame.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     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 "nsMenuBarFrame.h"
     7 #include "nsIServiceManager.h"
     8 #include "nsIContent.h"
     9 #include "nsIAtom.h"
    10 #include "nsPresContext.h"
    11 #include "nsStyleContext.h"
    12 #include "nsCSSRendering.h"
    13 #include "nsNameSpaceManager.h"
    14 #include "nsIDocument.h"
    15 #include "nsGkAtoms.h"
    16 #include "nsMenuFrame.h"
    17 #include "nsMenuPopupFrame.h"
    18 #include "nsUnicharUtils.h"
    19 #include "nsPIDOMWindow.h"
    20 #include "nsIInterfaceRequestorUtils.h"
    21 #include "nsCSSFrameConstructor.h"
    22 #ifdef XP_WIN
    23 #include "nsISound.h"
    24 #include "nsWidgetsCID.h"
    25 #endif
    26 #include "nsContentUtils.h"
    27 #include "nsUTF8Utils.h"
    28 #include "mozilla/TextEvents.h"
    30 using namespace mozilla;
    32 //
    33 // NS_NewMenuBarFrame
    34 //
    35 // Wrapper for creating a new menu Bar container
    36 //
    37 nsIFrame*
    38 NS_NewMenuBarFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    39 {
    40   return new (aPresShell) nsMenuBarFrame (aPresShell, aContext);
    41 }
    43 NS_IMPL_FRAMEARENA_HELPERS(nsMenuBarFrame)
    45 NS_QUERYFRAME_HEAD(nsMenuBarFrame)
    46   NS_QUERYFRAME_ENTRY(nsMenuBarFrame)
    47 NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
    49 //
    50 // nsMenuBarFrame cntr
    51 //
    52 nsMenuBarFrame::nsMenuBarFrame(nsIPresShell* aShell, nsStyleContext* aContext):
    53   nsBoxFrame(aShell, aContext),
    54     mMenuBarListener(nullptr),
    55     mStayActive(false),
    56     mIsActive(false),
    57     mCurrentMenu(nullptr),
    58     mTarget(nullptr)
    59 {
    60 } // cntr
    62 void
    63 nsMenuBarFrame::Init(nsIContent*      aContent,
    64                      nsIFrame*        aParent,
    65                      nsIFrame*        aPrevInFlow)
    66 {
    67   nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
    69   // Create the menu bar listener.
    70   mMenuBarListener = new nsMenuBarListener(this);
    71   NS_ADDREF(mMenuBarListener);
    73   // Hook up the menu bar as a key listener on the whole document.  It will see every
    74   // key press that occurs, but after everyone else does.
    75   mTarget = aContent->GetDocument();
    77   // Also hook up the listener to the window listening for focus events. This is so we can keep proper
    78   // state as the user alt-tabs through processes.
    80   mTarget->AddEventListener(NS_LITERAL_STRING("keypress"), mMenuBarListener, false);
    81   mTarget->AddEventListener(NS_LITERAL_STRING("keydown"), mMenuBarListener, false);
    82   mTarget->AddEventListener(NS_LITERAL_STRING("keyup"), mMenuBarListener, false);
    84   // mousedown event should be handled in all phase
    85   mTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, true);
    86   mTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, false);
    87   mTarget->AddEventListener(NS_LITERAL_STRING("blur"), mMenuBarListener, true);
    88 }
    90 NS_IMETHODIMP
    91 nsMenuBarFrame::SetActive(bool aActiveFlag)
    92 {
    93   // If the activity is not changed, there is nothing to do.
    94   if (mIsActive == aActiveFlag)
    95     return NS_OK;
    97   if (!aActiveFlag) {
    98     // Don't deactivate when switching between menus on the menubar.
    99     if (mStayActive)
   100       return NS_OK;
   102     // if there is a request to deactivate the menu bar, check to see whether
   103     // there is a menu popup open for the menu bar. In this case, don't
   104     // deactivate the menu bar.
   105     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   106     if (pm && pm->IsPopupOpenForMenuParent(this))
   107       return NS_OK;
   108   }
   110   mIsActive = aActiveFlag;
   111   if (mIsActive) {
   112     InstallKeyboardNavigator();
   113   }
   114   else {
   115     mActiveByKeyboard = false;
   116     RemoveKeyboardNavigator();
   117   }
   119   NS_NAMED_LITERAL_STRING(active, "DOMMenuBarActive");
   120   NS_NAMED_LITERAL_STRING(inactive, "DOMMenuBarInactive");
   122   FireDOMEvent(mIsActive ? active : inactive, mContent);
   124   return NS_OK;
   125 }
   127 nsMenuFrame*
   128 nsMenuBarFrame::ToggleMenuActiveState()
   129 {
   130   if (mIsActive) {
   131     // Deactivate the menu bar
   132     SetActive(false);
   133     if (mCurrentMenu) {
   134       nsMenuFrame* closeframe = mCurrentMenu;
   135       closeframe->SelectMenu(false);
   136       mCurrentMenu = nullptr;
   137       return closeframe;
   138     }
   139   }
   140   else {
   141     // if the menu bar is already selected (eg. mouseover), deselect it
   142     if (mCurrentMenu)
   143       mCurrentMenu->SelectMenu(false);
   145     // Set the active menu to be the top left item (e.g., the File menu).
   146     // We use an attribute called "menuactive" to track the current 
   147     // active menu.
   148     nsMenuFrame* firstFrame = nsXULPopupManager::GetNextMenuItem(this, nullptr, false);
   149     if (firstFrame) {
   150       // Activate the menu bar
   151       SetActive(true);
   152       firstFrame->SelectMenu(true);
   154       // Track this item for keyboard navigation.
   155       mCurrentMenu = firstFrame;
   156     }
   157   }
   159   return nullptr;
   160 }
   162 static void
   163 GetInsertionPoint(nsIPresShell* aShell, nsIFrame* aFrame, nsIFrame* aChild,
   164                   nsIFrame** aResult)
   165 {
   166   nsIContent* child = nullptr;
   167   if (aChild)
   168     child = aChild->GetContent();
   169   *aResult = aShell->FrameConstructor()->
   170     GetInsertionPoint(aFrame->GetContent(), child);
   171 }
   173 nsMenuFrame*
   174 nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent)
   175 {
   176   uint32_t charCode;
   177   aKeyEvent->GetCharCode(&charCode);
   179   nsAutoTArray<uint32_t, 10> accessKeys;
   180   WidgetKeyboardEvent* nativeKeyEvent =
   181     aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
   182   if (nativeKeyEvent)
   183     nsContentUtils::GetAccessKeyCandidates(nativeKeyEvent, accessKeys);
   184   if (accessKeys.IsEmpty() && charCode)
   185     accessKeys.AppendElement(charCode);
   187   if (accessKeys.IsEmpty())
   188     return nullptr; // no character was pressed so just return
   190   // Enumerate over our list of frames.
   191   nsIFrame* immediateParent = nullptr;
   192   GetInsertionPoint(PresContext()->PresShell(), this, nullptr, &immediateParent);
   193   if (!immediateParent)
   194     immediateParent = this;
   196   // Find a most preferred accesskey which should be returned.
   197   nsIFrame* foundMenu = nullptr;
   198   uint32_t foundIndex = accessKeys.NoIndex;
   199   nsIFrame* currFrame = immediateParent->GetFirstPrincipalChild();
   201   while (currFrame) {
   202     nsIContent* current = currFrame->GetContent();
   204     // See if it's a menu item.
   205     if (nsXULPopupManager::IsValidMenuItem(PresContext(), current, false)) {
   206       // Get the shortcut attribute.
   207       nsAutoString shortcutKey;
   208       current->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, shortcutKey);
   209       if (!shortcutKey.IsEmpty()) {
   210         ToLowerCase(shortcutKey);
   211         const char16_t* start = shortcutKey.BeginReading();
   212         const char16_t* end = shortcutKey.EndReading();
   213         uint32_t ch = UTF16CharEnumerator::NextChar(&start, end);
   214         uint32_t index = accessKeys.IndexOf(ch);
   215         if (index != accessKeys.NoIndex &&
   216             (foundIndex == accessKeys.NoIndex || index < foundIndex)) {
   217           foundMenu = currFrame;
   218           foundIndex = index;
   219         }
   220       }
   221     }
   222     currFrame = currFrame->GetNextSibling();
   223   }
   224   if (foundMenu) {
   225     return do_QueryFrame(foundMenu);
   226   }
   228   // didn't find a matching menu item
   229 #ifdef XP_WIN
   230   // behavior on Windows - this item is on the menu bar, beep and deactivate the menu bar
   231   if (mIsActive) {
   232     nsCOMPtr<nsISound> soundInterface = do_CreateInstance("@mozilla.org/sound;1");
   233     if (soundInterface)
   234       soundInterface->Beep();
   235   }
   237   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   238   if (pm) {
   239     nsIFrame* popup = pm->GetTopPopup(ePopupTypeAny);
   240     if (popup)
   241       pm->HidePopup(popup->GetContent(), true, true, true, false);
   242   }
   244   SetCurrentMenuItem(nullptr);
   245   SetActive(false);
   247 #endif  // #ifdef XP_WIN
   249   return nullptr;
   250 }
   252 /* virtual */ nsMenuFrame*
   253 nsMenuBarFrame::GetCurrentMenuItem()
   254 {
   255   return mCurrentMenu;
   256 }
   258 NS_IMETHODIMP
   259 nsMenuBarFrame::SetCurrentMenuItem(nsMenuFrame* aMenuItem)
   260 {
   261   if (mCurrentMenu == aMenuItem)
   262     return NS_OK;
   264   if (mCurrentMenu)
   265     mCurrentMenu->SelectMenu(false);
   267   if (aMenuItem)
   268     aMenuItem->SelectMenu(true);
   270   mCurrentMenu = aMenuItem;
   272   return NS_OK;
   273 }
   275 void
   276 nsMenuBarFrame::CurrentMenuIsBeingDestroyed()
   277 {
   278   mCurrentMenu->SelectMenu(false);
   279   mCurrentMenu = nullptr;
   280 }
   282 class nsMenuBarSwitchMenu : public nsRunnable
   283 {
   284 public:
   285   nsMenuBarSwitchMenu(nsIContent* aMenuBar,
   286                       nsIContent *aOldMenu,
   287                       nsIContent *aNewMenu,
   288                       bool aSelectFirstItem)
   289     : mMenuBar(aMenuBar), mOldMenu(aOldMenu), mNewMenu(aNewMenu),
   290       mSelectFirstItem(aSelectFirstItem)
   291   {
   292   }
   294   NS_IMETHOD Run() MOZ_OVERRIDE
   295   {
   296     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   297     if (!pm)
   298       return NS_ERROR_UNEXPECTED;
   300     // if switching from one menu to another, set a flag so that the call to
   301     // HidePopup doesn't deactivate the menubar when the first menu closes.
   302     nsMenuBarFrame* menubar = nullptr;
   303     if (mOldMenu && mNewMenu) {
   304       menubar = do_QueryFrame(mMenuBar->GetPrimaryFrame());
   305       if (menubar)
   306         menubar->SetStayActive(true);
   307     }
   309     if (mOldMenu) {
   310       nsWeakFrame weakMenuBar(menubar);
   311       pm->HidePopup(mOldMenu, false, false, false, false);
   312       // clear the flag again
   313       if (mNewMenu && weakMenuBar.IsAlive())
   314         menubar->SetStayActive(false);
   315     }
   317     if (mNewMenu)
   318       pm->ShowMenu(mNewMenu, mSelectFirstItem, false);
   320     return NS_OK;
   321   }
   323 private:
   324   nsCOMPtr<nsIContent> mMenuBar;
   325   nsCOMPtr<nsIContent> mOldMenu;
   326   nsCOMPtr<nsIContent> mNewMenu;
   327   bool mSelectFirstItem;
   328 };
   330 NS_IMETHODIMP
   331 nsMenuBarFrame::ChangeMenuItem(nsMenuFrame* aMenuItem,
   332                                bool aSelectFirstItem)
   333 {
   334   if (mCurrentMenu == aMenuItem)
   335     return NS_OK;
   337   // check if there's an open context menu, we ignore this
   338   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   339   if (pm && pm->HasContextMenu(nullptr))
   340     return NS_OK;
   342   nsIContent* aOldMenu = nullptr, *aNewMenu = nullptr;
   344   // Unset the current child.
   345   bool wasOpen = false;
   346   if (mCurrentMenu) {
   347     wasOpen = mCurrentMenu->IsOpen();
   348     mCurrentMenu->SelectMenu(false);
   349     if (wasOpen) {
   350       nsMenuPopupFrame* popupFrame = mCurrentMenu->GetPopup();
   351       if (popupFrame)
   352         aOldMenu = popupFrame->GetContent();
   353     }
   354   }
   356   // set to null first in case the IsAlive check below returns false
   357   mCurrentMenu = nullptr;
   359   // Set the new child.
   360   if (aMenuItem) {
   361     nsCOMPtr<nsIContent> content = aMenuItem->GetContent();
   362     aMenuItem->SelectMenu(true);
   363     mCurrentMenu = aMenuItem;
   364     if (wasOpen && !aMenuItem->IsDisabled())
   365       aNewMenu = content;
   366   }
   368   // use an event so that hiding and showing can be done synchronously, which
   369   // avoids flickering
   370   nsCOMPtr<nsIRunnable> event =
   371     new nsMenuBarSwitchMenu(GetContent(), aOldMenu, aNewMenu, aSelectFirstItem);
   372   return NS_DispatchToCurrentThread(event);
   373 }
   375 nsMenuFrame*
   376 nsMenuBarFrame::Enter(WidgetGUIEvent* aEvent)
   377 {
   378   if (!mCurrentMenu)
   379     return nullptr;
   381   if (mCurrentMenu->IsOpen())
   382     return mCurrentMenu->Enter(aEvent);
   384   return mCurrentMenu;
   385 }
   387 bool
   388 nsMenuBarFrame::MenuClosed()
   389 {
   390   SetActive(false);
   391   if (!mIsActive && mCurrentMenu) {
   392     mCurrentMenu->SelectMenu(false);
   393     mCurrentMenu = nullptr;
   394     return true;
   395   }
   396   return false;
   397 }
   399 void
   400 nsMenuBarFrame::InstallKeyboardNavigator()
   401 {
   402   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   403   if (pm)
   404     pm->SetActiveMenuBar(this, true);
   405 }
   407 void
   408 nsMenuBarFrame::RemoveKeyboardNavigator()
   409 {
   410   if (!mIsActive) {
   411     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   412     if (pm)
   413       pm->SetActiveMenuBar(this, false);
   414   }
   415 }
   417 void
   418 nsMenuBarFrame::DestroyFrom(nsIFrame* aDestructRoot)
   419 {
   420   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   421   if (pm)
   422     pm->SetActiveMenuBar(this, false);
   424   mTarget->RemoveEventListener(NS_LITERAL_STRING("keypress"), mMenuBarListener, false); 
   425   mTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), mMenuBarListener, false);  
   426   mTarget->RemoveEventListener(NS_LITERAL_STRING("keyup"), mMenuBarListener, false);
   428   mTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, true);
   429   mTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, false);
   430   mTarget->RemoveEventListener(NS_LITERAL_STRING("blur"), mMenuBarListener, true);
   432   NS_IF_RELEASE(mMenuBarListener);
   434   nsBoxFrame::DestroyFrom(aDestructRoot);
   435 }

mercurial