LCOV - code coverage report
Current view: top level - layout/xul - nsMenuBarFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 13 178 7.3 %
Date: 2017-07-14 16:53:18 Functions: 5 21 23.8 %
Legend: Lines: hit not hit

          Line data    Source 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/. */
       5             : 
       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"
      29             : #include "mozilla/dom/Event.h"
      30             : 
      31             : using namespace mozilla;
      32             : 
      33             : //
      34             : // NS_NewMenuBarFrame
      35             : //
      36             : // Wrapper for creating a new menu Bar container
      37             : //
      38             : nsIFrame*
      39           1 : NS_NewMenuBarFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
      40             : {
      41           1 :   return new (aPresShell) nsMenuBarFrame(aContext);
      42             : }
      43             : 
      44           1 : NS_IMPL_FRAMEARENA_HELPERS(nsMenuBarFrame)
      45             : 
      46         466 : NS_QUERYFRAME_HEAD(nsMenuBarFrame)
      47         231 :   NS_QUERYFRAME_ENTRY(nsMenuBarFrame)
      48         235 : NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
      49             : 
      50             : //
      51             : // nsMenuBarFrame cntr
      52             : //
      53           1 : nsMenuBarFrame::nsMenuBarFrame(nsStyleContext* aContext)
      54             :   : nsBoxFrame(aContext, kClassID)
      55             :   , mStayActive(false)
      56             :   , mIsActive(false)
      57             :   , mActiveByKeyboard(false)
      58           1 :   , mCurrentMenu(nullptr)
      59             : {
      60           1 : } // cntr
      61             : 
      62             : void
      63           1 : nsMenuBarFrame::Init(nsIContent*       aContent,
      64             :                      nsContainerFrame* aParent,
      65             :                      nsIFrame*         aPrevInFlow)
      66             : {
      67           1 :   nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
      68             : 
      69             :   // Create the menu bar listener.
      70           1 :   mMenuBarListener = new nsMenuBarListener(this, aContent);
      71           1 : }
      72             : 
      73             : NS_IMETHODIMP
      74           0 : nsMenuBarFrame::SetActive(bool aActiveFlag)
      75             : {
      76             :   // If the activity is not changed, there is nothing to do.
      77           0 :   if (mIsActive == aActiveFlag)
      78           0 :     return NS_OK;
      79             : 
      80           0 :   if (!aActiveFlag) {
      81             :     // Don't deactivate when switching between menus on the menubar.
      82           0 :     if (mStayActive)
      83           0 :       return NS_OK;
      84             : 
      85             :     // if there is a request to deactivate the menu bar, check to see whether
      86             :     // there is a menu popup open for the menu bar. In this case, don't
      87             :     // deactivate the menu bar.
      88           0 :     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
      89           0 :     if (pm && pm->IsPopupOpenForMenuParent(this))
      90           0 :       return NS_OK;
      91             :   }
      92             : 
      93           0 :   mIsActive = aActiveFlag;
      94           0 :   if (mIsActive) {
      95           0 :     InstallKeyboardNavigator();
      96             :   }
      97             :   else {
      98           0 :     mActiveByKeyboard = false;
      99           0 :     RemoveKeyboardNavigator();
     100             :   }
     101             : 
     102           0 :   NS_NAMED_LITERAL_STRING(active, "DOMMenuBarActive");
     103           0 :   NS_NAMED_LITERAL_STRING(inactive, "DOMMenuBarInactive");
     104             : 
     105           0 :   FireDOMEvent(mIsActive ? active : inactive, mContent);
     106             : 
     107           0 :   return NS_OK;
     108             : }
     109             : 
     110             : nsMenuFrame*
     111           0 : nsMenuBarFrame::ToggleMenuActiveState()
     112             : {
     113           0 :   if (mIsActive) {
     114             :     // Deactivate the menu bar
     115           0 :     SetActive(false);
     116           0 :     if (mCurrentMenu) {
     117           0 :       nsMenuFrame* closeframe = mCurrentMenu;
     118           0 :       closeframe->SelectMenu(false);
     119           0 :       mCurrentMenu = nullptr;
     120           0 :       return closeframe;
     121             :     }
     122             :   }
     123             :   else {
     124             :     // if the menu bar is already selected (eg. mouseover), deselect it
     125           0 :     if (mCurrentMenu)
     126           0 :       mCurrentMenu->SelectMenu(false);
     127             : 
     128             :     // Set the active menu to be the top left item (e.g., the File menu).
     129             :     // We use an attribute called "menuactive" to track the current
     130             :     // active menu.
     131           0 :     nsMenuFrame* firstFrame = nsXULPopupManager::GetNextMenuItem(this, nullptr, false, false);
     132           0 :     if (firstFrame) {
     133             :       // Activate the menu bar
     134           0 :       SetActive(true);
     135           0 :       firstFrame->SelectMenu(true);
     136             : 
     137             :       // Track this item for keyboard navigation.
     138           0 :       mCurrentMenu = firstFrame;
     139             :     }
     140             :   }
     141             : 
     142           0 :   return nullptr;
     143             : }
     144             : 
     145             : nsMenuFrame*
     146           0 : nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent)
     147             : {
     148             :   uint32_t charCode;
     149           0 :   aKeyEvent->GetCharCode(&charCode);
     150             : 
     151           0 :   AutoTArray<uint32_t, 10> accessKeys;
     152             :   WidgetKeyboardEvent* nativeKeyEvent =
     153           0 :     aKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
     154           0 :   if (nativeKeyEvent) {
     155           0 :     nativeKeyEvent->GetAccessKeyCandidates(accessKeys);
     156             :   }
     157           0 :   if (accessKeys.IsEmpty() && charCode)
     158           0 :     accessKeys.AppendElement(charCode);
     159             : 
     160           0 :   if (accessKeys.IsEmpty())
     161           0 :     return nullptr; // no character was pressed so just return
     162             : 
     163             :   // Enumerate over our list of frames.
     164             :   auto insertion = PresContext()->PresShell()->FrameConstructor()->
     165           0 :     GetInsertionPoint(GetContent(), nullptr);
     166           0 :   nsContainerFrame* immediateParent = insertion.mParentFrame;
     167           0 :   if (!immediateParent)
     168           0 :     immediateParent = this;
     169             : 
     170             :   // Find a most preferred accesskey which should be returned.
     171           0 :   nsIFrame* foundMenu = nullptr;
     172           0 :   size_t foundIndex = accessKeys.NoIndex;
     173           0 :   nsIFrame* currFrame = immediateParent->PrincipalChildList().FirstChild();
     174             : 
     175           0 :   while (currFrame) {
     176           0 :     nsIContent* current = currFrame->GetContent();
     177             : 
     178             :     // See if it's a menu item.
     179           0 :     if (nsXULPopupManager::IsValidMenuItem(current, false)) {
     180             :       // Get the shortcut attribute.
     181           0 :       nsAutoString shortcutKey;
     182           0 :       current->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, shortcutKey);
     183           0 :       if (!shortcutKey.IsEmpty()) {
     184           0 :         ToLowerCase(shortcutKey);
     185           0 :         const char16_t* start = shortcutKey.BeginReading();
     186           0 :         const char16_t* end = shortcutKey.EndReading();
     187           0 :         uint32_t ch = UTF16CharEnumerator::NextChar(&start, end);
     188           0 :         size_t index = accessKeys.IndexOf(ch);
     189           0 :         if (index != accessKeys.NoIndex &&
     190           0 :             (foundIndex == accessKeys.NoIndex || index < foundIndex)) {
     191           0 :           foundMenu = currFrame;
     192           0 :           foundIndex = index;
     193             :         }
     194             :       }
     195             :     }
     196           0 :     currFrame = currFrame->GetNextSibling();
     197             :   }
     198           0 :   if (foundMenu) {
     199           0 :     return do_QueryFrame(foundMenu);
     200             :   }
     201             : 
     202             :   // didn't find a matching menu item
     203             : #ifdef XP_WIN
     204             :   // behavior on Windows - this item is on the menu bar, beep and deactivate the menu bar
     205             :   if (mIsActive) {
     206             :     nsCOMPtr<nsISound> soundInterface = do_CreateInstance("@mozilla.org/sound;1");
     207             :     if (soundInterface)
     208             :       soundInterface->Beep();
     209             :   }
     210             : 
     211             :   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     212             :   if (pm) {
     213             :     nsIFrame* popup = pm->GetTopPopup(ePopupTypeAny);
     214             :     if (popup)
     215             :       pm->HidePopup(popup->GetContent(), true, true, true, false);
     216             :   }
     217             : 
     218             :   SetCurrentMenuItem(nullptr);
     219             :   SetActive(false);
     220             : 
     221             : #endif  // #ifdef XP_WIN
     222             : 
     223           0 :   return nullptr;
     224             : }
     225             : 
     226             : /* virtual */ nsMenuFrame*
     227           0 : nsMenuBarFrame::GetCurrentMenuItem()
     228             : {
     229           0 :   return mCurrentMenu;
     230             : }
     231             : 
     232             : NS_IMETHODIMP
     233           0 : nsMenuBarFrame::SetCurrentMenuItem(nsMenuFrame* aMenuItem)
     234             : {
     235           0 :   if (mCurrentMenu == aMenuItem)
     236           0 :     return NS_OK;
     237             : 
     238           0 :   if (mCurrentMenu)
     239           0 :     mCurrentMenu->SelectMenu(false);
     240             : 
     241           0 :   if (aMenuItem)
     242           0 :     aMenuItem->SelectMenu(true);
     243             : 
     244           0 :   mCurrentMenu = aMenuItem;
     245             : 
     246           0 :   return NS_OK;
     247             : }
     248             : 
     249             : void
     250           0 : nsMenuBarFrame::CurrentMenuIsBeingDestroyed()
     251             : {
     252           0 :   mCurrentMenu->SelectMenu(false);
     253           0 :   mCurrentMenu = nullptr;
     254           0 : }
     255             : 
     256           0 : class nsMenuBarSwitchMenu : public Runnable
     257             : {
     258             : public:
     259           0 :   nsMenuBarSwitchMenu(nsIContent* aMenuBar,
     260             :                       nsIContent* aOldMenu,
     261             :                       nsIContent* aNewMenu,
     262             :                       bool aSelectFirstItem)
     263           0 :     : mozilla::Runnable("nsMenuBarSwitchMenu")
     264             :     , mMenuBar(aMenuBar)
     265             :     , mOldMenu(aOldMenu)
     266             :     , mNewMenu(aNewMenu)
     267           0 :     , mSelectFirstItem(aSelectFirstItem)
     268             :   {
     269           0 :   }
     270             : 
     271           0 :   NS_IMETHOD Run() override
     272             :   {
     273           0 :     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     274           0 :     if (!pm)
     275           0 :       return NS_ERROR_UNEXPECTED;
     276             : 
     277             :     // if switching from one menu to another, set a flag so that the call to
     278             :     // HidePopup doesn't deactivate the menubar when the first menu closes.
     279           0 :     nsMenuBarFrame* menubar = nullptr;
     280           0 :     if (mOldMenu && mNewMenu) {
     281           0 :       menubar = do_QueryFrame(mMenuBar->GetPrimaryFrame());
     282           0 :       if (menubar)
     283           0 :         menubar->SetStayActive(true);
     284             :     }
     285             : 
     286           0 :     if (mOldMenu) {
     287           0 :       AutoWeakFrame weakMenuBar(menubar);
     288           0 :       pm->HidePopup(mOldMenu, false, false, false, false);
     289             :       // clear the flag again
     290           0 :       if (mNewMenu && weakMenuBar.IsAlive())
     291           0 :         menubar->SetStayActive(false);
     292             :     }
     293             : 
     294           0 :     if (mNewMenu)
     295           0 :       pm->ShowMenu(mNewMenu, mSelectFirstItem, false);
     296             : 
     297           0 :     return NS_OK;
     298             :   }
     299             : 
     300             : private:
     301             :   nsCOMPtr<nsIContent> mMenuBar;
     302             :   nsCOMPtr<nsIContent> mOldMenu;
     303             :   nsCOMPtr<nsIContent> mNewMenu;
     304             :   bool mSelectFirstItem;
     305             : };
     306             : 
     307             : NS_IMETHODIMP
     308           0 : nsMenuBarFrame::ChangeMenuItem(nsMenuFrame* aMenuItem,
     309             :                                bool aSelectFirstItem,
     310             :                                bool aFromKey)
     311             : {
     312           0 :   if (mCurrentMenu == aMenuItem)
     313           0 :     return NS_OK;
     314             : 
     315             :   // check if there's an open context menu, we ignore this
     316           0 :   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     317           0 :   if (pm && pm->HasContextMenu(nullptr))
     318           0 :     return NS_OK;
     319             : 
     320           0 :   nsIContent* aOldMenu = nullptr;
     321           0 :   nsIContent* aNewMenu = nullptr;
     322             : 
     323             :   // Unset the current child.
     324           0 :   bool wasOpen = false;
     325           0 :   if (mCurrentMenu) {
     326           0 :     wasOpen = mCurrentMenu->IsOpen();
     327           0 :     mCurrentMenu->SelectMenu(false);
     328           0 :     if (wasOpen) {
     329           0 :       nsMenuPopupFrame* popupFrame = mCurrentMenu->GetPopup();
     330           0 :       if (popupFrame)
     331           0 :         aOldMenu = popupFrame->GetContent();
     332             :     }
     333             :   }
     334             : 
     335             :   // set to null first in case the IsAlive check below returns false
     336           0 :   mCurrentMenu = nullptr;
     337             : 
     338             :   // Set the new child.
     339           0 :   if (aMenuItem) {
     340           0 :     nsCOMPtr<nsIContent> content = aMenuItem->GetContent();
     341           0 :     aMenuItem->SelectMenu(true);
     342           0 :     mCurrentMenu = aMenuItem;
     343           0 :     if (wasOpen && !aMenuItem->IsDisabled())
     344           0 :       aNewMenu = content;
     345             :   }
     346             : 
     347             :   // use an event so that hiding and showing can be done synchronously, which
     348             :   // avoids flickering
     349             :   nsCOMPtr<nsIRunnable> event =
     350           0 :     new nsMenuBarSwitchMenu(GetContent(), aOldMenu, aNewMenu, aSelectFirstItem);
     351           0 :   return mContent->OwnerDoc()->Dispatch("nsMenuBarSwitchMenu",
     352             :                                         TaskCategory::Other,
     353           0 :                                         event.forget());
     354             : }
     355             : 
     356             : nsMenuFrame*
     357           0 : nsMenuBarFrame::Enter(WidgetGUIEvent* aEvent)
     358             : {
     359           0 :   if (!mCurrentMenu)
     360           0 :     return nullptr;
     361             : 
     362           0 :   if (mCurrentMenu->IsOpen())
     363           0 :     return mCurrentMenu->Enter(aEvent);
     364             : 
     365           0 :   return mCurrentMenu;
     366             : }
     367             : 
     368             : bool
     369           0 : nsMenuBarFrame::MenuClosed()
     370             : {
     371           0 :   SetActive(false);
     372           0 :   if (!mIsActive && mCurrentMenu) {
     373           0 :     mCurrentMenu->SelectMenu(false);
     374           0 :     mCurrentMenu = nullptr;
     375           0 :     return true;
     376             :   }
     377           0 :   return false;
     378             : }
     379             : 
     380             : void
     381           0 : nsMenuBarFrame::InstallKeyboardNavigator()
     382             : {
     383           0 :   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     384           0 :   if (pm)
     385           0 :     pm->SetActiveMenuBar(this, true);
     386           0 : }
     387             : 
     388             : void
     389           0 : nsMenuBarFrame::RemoveKeyboardNavigator()
     390             : {
     391           0 :   if (!mIsActive) {
     392           0 :     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     393           0 :     if (pm)
     394           0 :       pm->SetActiveMenuBar(this, false);
     395             :   }
     396           0 : }
     397             : 
     398             : void
     399           0 : nsMenuBarFrame::DestroyFrom(nsIFrame* aDestructRoot)
     400             : {
     401           0 :   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     402           0 :   if (pm)
     403           0 :     pm->SetActiveMenuBar(this, false);
     404             : 
     405           0 :   mMenuBarListener->OnDestroyMenuBarFrame();
     406           0 :   mMenuBarListener = nullptr;
     407             : 
     408           0 :   nsBoxFrame::DestroyFrom(aDestructRoot);
     409           0 : }

Generated by: LCOV version 1.13