LCOV - code coverage report
Current view: directory - layout/xul/base/src - nsMenuFrame.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 633 0 0.0 %
Date: 2012-06-02 Functions: 73 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Original Author: David W. Hyatt (hyatt@netscape.com)
      24                 :  *   Michael Lowe <michael.lowe@bigfoot.com>
      25                 :  *   Pierre Phaneuf <pp@ludusdesign.com>
      26                 :  *   Dean Tessman <dean_tessman@hotmail.com>
      27                 :  *
      28                 :  * Alternatively, the contents of this file may be used under the terms of
      29                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      30                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      31                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      32                 :  * of those above. If you wish to allow use of your version of this file only
      33                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      34                 :  * use your version of this file under the terms of the MPL, indicate your
      35                 :  * decision by deleting the provisions above and replace them with the notice
      36                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      37                 :  * the provisions above, a recipient may use your version of this file under
      38                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      39                 :  *
      40                 :  * ***** END LICENSE BLOCK ***** */
      41                 : 
      42                 : #include "nsGkAtoms.h"
      43                 : #include "nsHTMLParts.h"
      44                 : #include "nsMenuFrame.h"
      45                 : #include "nsBoxFrame.h"
      46                 : #include "nsIContent.h"
      47                 : #include "prtypes.h"
      48                 : #include "nsIAtom.h"
      49                 : #include "nsPresContext.h"
      50                 : #include "nsIPresShell.h"
      51                 : #include "nsStyleContext.h"
      52                 : #include "nsCSSRendering.h"
      53                 : #include "nsINameSpaceManager.h"
      54                 : #include "nsMenuPopupFrame.h"
      55                 : #include "nsMenuBarFrame.h"
      56                 : #include "nsIDocument.h"
      57                 : #include "nsIDOMElement.h"
      58                 : #include "nsIComponentManager.h"
      59                 : #include "nsBoxLayoutState.h"
      60                 : #include "nsIScrollableFrame.h"
      61                 : #include "nsBindingManager.h"
      62                 : #include "nsIServiceManager.h"
      63                 : #include "nsCSSFrameConstructor.h"
      64                 : #include "nsIDOMKeyEvent.h"
      65                 : #include "nsEventDispatcher.h"
      66                 : #include "nsIPrivateDOMEvent.h"
      67                 : #include "nsXPIDLString.h"
      68                 : #include "nsReadableUtils.h"
      69                 : #include "nsUnicharUtils.h"
      70                 : #include "nsIStringBundle.h"
      71                 : #include "nsGUIEvent.h"
      72                 : #include "nsContentUtils.h"
      73                 : #include "nsDisplayList.h"
      74                 : #include "nsIReflowCallback.h"
      75                 : #include "nsISound.h"
      76                 : #include "nsEventStateManager.h"
      77                 : #include "nsIDOMXULMenuListElement.h"
      78                 : #include "mozilla/Services.h"
      79                 : #include "mozilla/Preferences.h"
      80                 : #include "mozilla/LookAndFeel.h"
      81                 : 
      82                 : using namespace mozilla;
      83                 : 
      84                 : #define NS_MENU_POPUP_LIST_INDEX 0
      85                 : 
      86                 : #if defined(XP_WIN) || defined(XP_OS2)
      87                 : #define NSCONTEXTMENUISMOUSEUP 1
      88                 : #endif
      89                 : 
      90                 : static void
      91               0 : AssertNotCalled(void* aPropertyValue)
      92                 : {
      93               0 :   NS_ERROR("popup list should never be destroyed by the FramePropertyTable");
      94               0 : }
      95               0 : NS_DECLARE_FRAME_PROPERTY(PopupListProperty, AssertNotCalled)
      96                 : 
      97                 : static PRInt32 gEatMouseMove = false;
      98                 : 
      99                 : const PRInt32 kBlinkDelay = 67; // milliseconds
     100                 : 
     101                 : // this class is used for dispatching menu activation events asynchronously.
     102                 : class nsMenuActivateEvent : public nsRunnable
     103               0 : {
     104                 : public:
     105               0 :   nsMenuActivateEvent(nsIContent *aMenu,
     106                 :                       nsPresContext* aPresContext,
     107                 :                       bool aIsActivate)
     108               0 :     : mMenu(aMenu), mPresContext(aPresContext), mIsActivate(aIsActivate)
     109                 :   {
     110               0 :   }
     111                 : 
     112               0 :   NS_IMETHOD Run()
     113                 :   {
     114               0 :     nsAutoString domEventToFire;
     115                 : 
     116               0 :     if (mIsActivate) {
     117                 :       // Highlight the menu.
     118                 :       mMenu->SetAttr(kNameSpaceID_None, nsGkAtoms::menuactive,
     119               0 :                      NS_LITERAL_STRING("true"), true);
     120                 :       // The menuactivated event is used by accessibility to track the user's
     121                 :       // movements through menus
     122               0 :       domEventToFire.AssignLiteral("DOMMenuItemActive");
     123                 :     }
     124                 :     else {
     125                 :       // Unhighlight the menu.
     126               0 :       mMenu->UnsetAttr(kNameSpaceID_None, nsGkAtoms::menuactive, true);
     127               0 :       domEventToFire.AssignLiteral("DOMMenuItemInactive");
     128                 :     }
     129                 : 
     130               0 :     nsCOMPtr<nsIDOMEvent> event;
     131               0 :     if (NS_SUCCEEDED(nsEventDispatcher::CreateEvent(mPresContext, nsnull,
     132                 :                                                     NS_LITERAL_STRING("Events"),
     133                 :                                                     getter_AddRefs(event)))) {
     134               0 :       event->InitEvent(domEventToFire, true, true);
     135                 : 
     136               0 :       nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event));
     137               0 :       privateEvent->SetTrusted(true);
     138                 : 
     139                 :       nsEventDispatcher::DispatchDOMEvent(mMenu, nsnull, event,
     140               0 :                                           mPresContext, nsnull);
     141                 :     }
     142                 : 
     143               0 :     return NS_OK;
     144                 :   }
     145                 : 
     146                 : private:
     147                 :   nsCOMPtr<nsIContent> mMenu;
     148                 :   nsRefPtr<nsPresContext> mPresContext;
     149                 :   bool mIsActivate;
     150                 : };
     151                 : 
     152                 : class nsMenuAttributeChangedEvent : public nsRunnable
     153               0 : {
     154                 : public:
     155               0 :   nsMenuAttributeChangedEvent(nsIFrame* aFrame, nsIAtom* aAttr)
     156               0 :   : mFrame(aFrame), mAttr(aAttr)
     157                 :   {
     158               0 :   }
     159                 : 
     160               0 :   NS_IMETHOD Run()
     161                 :   {
     162               0 :     nsMenuFrame* frame = static_cast<nsMenuFrame*>(mFrame.GetFrame());
     163               0 :     NS_ENSURE_STATE(frame);
     164               0 :     if (mAttr == nsGkAtoms::checked) {
     165               0 :       frame->UpdateMenuSpecialState(frame->PresContext());
     166               0 :     } else if (mAttr == nsGkAtoms::acceltext) {
     167                 :       // someone reset the accelText attribute,
     168                 :       // so clear the bit that says *we* set it
     169               0 :       frame->RemoveStateBits(NS_STATE_ACCELTEXT_IS_DERIVED);
     170               0 :       frame->BuildAcceleratorText(true);
     171                 :     }
     172               0 :     else if (mAttr == nsGkAtoms::key) {
     173               0 :       frame->BuildAcceleratorText(true);
     174               0 :     } else if (mAttr == nsGkAtoms::type || mAttr == nsGkAtoms::name) {
     175               0 :       frame->UpdateMenuType(frame->PresContext());
     176                 :     }
     177               0 :     return NS_OK;
     178                 :   }
     179                 : protected:
     180                 :   nsWeakFrame       mFrame;
     181                 :   nsCOMPtr<nsIAtom> mAttr;
     182                 : };
     183                 : 
     184                 : //
     185                 : // NS_NewMenuFrame and NS_NewMenuItemFrame
     186                 : //
     187                 : // Wrappers for creating a new menu popup container
     188                 : //
     189                 : nsIFrame*
     190               0 : NS_NewMenuFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
     191                 : {
     192               0 :   nsMenuFrame* it = new (aPresShell) nsMenuFrame (aPresShell, aContext);
     193                 :   
     194               0 :   if (it)
     195               0 :     it->SetIsMenu(true);
     196                 : 
     197               0 :   return it;
     198                 : }
     199                 : 
     200                 : nsIFrame*
     201               0 : NS_NewMenuItemFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
     202                 : {
     203               0 :   nsMenuFrame* it = new (aPresShell) nsMenuFrame (aPresShell, aContext);
     204                 : 
     205               0 :   if (it)
     206               0 :     it->SetIsMenu(false);
     207                 : 
     208               0 :   return it;
     209                 : }
     210                 : 
     211               0 : NS_IMPL_FRAMEARENA_HELPERS(nsMenuFrame)
     212                 : 
     213               0 : NS_QUERYFRAME_HEAD(nsMenuFrame)
     214               0 :   NS_QUERYFRAME_ENTRY(nsMenuFrame)
     215               0 : NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
     216                 : 
     217               0 : nsMenuFrame::nsMenuFrame(nsIPresShell* aShell, nsStyleContext* aContext):
     218                 :   nsBoxFrame(aShell, aContext),
     219                 :     mIsMenu(false),
     220                 :     mChecked(false),
     221                 :     mIgnoreAccelTextChange(false),
     222                 :     mType(eMenuType_Normal),
     223                 :     mMenuParent(nsnull),
     224               0 :     mBlinkState(0)
     225                 : {
     226               0 : }
     227                 : 
     228                 : void
     229               0 : nsMenuFrame::SetParent(nsIFrame* aParent)
     230                 : {
     231               0 :   nsBoxFrame::SetParent(aParent);
     232               0 :   InitMenuParent(aParent);
     233               0 : }
     234                 : 
     235                 : void
     236               0 : nsMenuFrame::InitMenuParent(nsIFrame* aParent)
     237                 : {
     238               0 :   while (aParent) {
     239               0 :     nsIAtom* type = aParent->GetType();
     240               0 :     if (type == nsGkAtoms::menuPopupFrame) {
     241               0 :       mMenuParent = static_cast<nsMenuPopupFrame *>(aParent);
     242               0 :       break;
     243                 :     }
     244               0 :     else if (type == nsGkAtoms::menuBarFrame) {
     245               0 :       mMenuParent = static_cast<nsMenuBarFrame *>(aParent);
     246               0 :       break;
     247                 :     }
     248               0 :     aParent = aParent->GetParent();
     249                 :   }
     250               0 : }
     251                 : 
     252                 : class nsASyncMenuInitialization : public nsIReflowCallback
     253               0 : {
     254                 : public:
     255               0 :   nsASyncMenuInitialization(nsIFrame* aFrame)
     256               0 :     : mWeakFrame(aFrame)
     257                 :   {
     258               0 :   }
     259                 : 
     260               0 :   virtual bool ReflowFinished()
     261                 :   {
     262               0 :     bool shouldFlush = false;
     263               0 :     if (mWeakFrame.IsAlive()) {
     264               0 :       if (mWeakFrame.GetFrame()->GetType() == nsGkAtoms::menuFrame) {
     265               0 :         nsMenuFrame* menu = static_cast<nsMenuFrame*>(mWeakFrame.GetFrame());
     266               0 :         menu->UpdateMenuType(menu->PresContext());
     267               0 :         shouldFlush = true;
     268                 :       }
     269                 :     }
     270               0 :     delete this;
     271               0 :     return shouldFlush;
     272                 :   }
     273                 : 
     274               0 :   virtual void ReflowCallbackCanceled()
     275                 :   {
     276               0 :     delete this;
     277               0 :   }
     278                 : 
     279                 :   nsWeakFrame mWeakFrame;
     280                 : };
     281                 : 
     282                 : NS_IMETHODIMP
     283               0 : nsMenuFrame::Init(nsIContent*      aContent,
     284                 :                   nsIFrame*        aParent,
     285                 :                   nsIFrame*        aPrevInFlow)
     286                 : {
     287               0 :   nsresult  rv = nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
     288                 : 
     289                 :   // Set up a mediator which can be used for callbacks on this frame.
     290               0 :   mTimerMediator = new nsMenuTimerMediator(this);
     291               0 :   if (NS_UNLIKELY(!mTimerMediator))
     292               0 :     return NS_ERROR_OUT_OF_MEMORY;
     293                 : 
     294               0 :   InitMenuParent(aParent);
     295                 : 
     296               0 :   BuildAcceleratorText(false);
     297               0 :   nsIReflowCallback* cb = new nsASyncMenuInitialization(this);
     298               0 :   NS_ENSURE_TRUE(cb, NS_ERROR_OUT_OF_MEMORY);
     299               0 :   PresContext()->PresShell()->PostReflowCallback(cb);
     300               0 :   return rv;
     301                 : }
     302                 : 
     303                 : const nsFrameList&
     304               0 : nsMenuFrame::GetChildList(ChildListID aListID) const
     305                 : {
     306               0 :   if (kPopupList == aListID) {
     307               0 :     nsFrameList* list = GetPopupList();
     308               0 :     return list ? *list : nsFrameList::EmptyList();
     309                 :   }
     310               0 :   return nsBoxFrame::GetChildList(aListID);
     311                 : }
     312                 : 
     313                 : void
     314               0 : nsMenuFrame::GetChildLists(nsTArray<ChildList>* aLists) const
     315                 : {
     316               0 :   nsBoxFrame::GetChildLists(aLists);
     317               0 :   nsFrameList* list = GetPopupList();
     318               0 :   if (list) {
     319               0 :     list->AppendIfNonempty(aLists, kPopupList);
     320                 :   }
     321               0 : }
     322                 : 
     323                 : nsMenuPopupFrame*
     324               0 : nsMenuFrame::GetPopup()
     325                 : {
     326               0 :   nsFrameList* popupList = GetPopupList();
     327               0 :   return popupList ? static_cast<nsMenuPopupFrame*>(popupList->FirstChild()) :
     328               0 :                      nsnull;
     329                 : }
     330                 : 
     331                 : nsFrameList*
     332               0 : nsMenuFrame::GetPopupList() const
     333                 : {
     334               0 :   if (!HasPopup()) {
     335               0 :     return nsnull;
     336                 :   }
     337                 :   nsFrameList* prop =
     338               0 :     static_cast<nsFrameList*>(Properties().Get(PopupListProperty()));
     339               0 :   NS_ASSERTION(prop && prop->GetLength() == 1 &&
     340                 :                prop->FirstChild()->GetType() == nsGkAtoms::menuPopupFrame,
     341                 :                "popup list should have exactly one nsMenuPopupFrame");
     342               0 :   return prop;
     343                 : }
     344                 : 
     345                 : void
     346               0 : nsMenuFrame::DestroyPopupList()
     347                 : {
     348               0 :   NS_ASSERTION(HasPopup(), "huh?");
     349                 :   nsFrameList* prop =
     350               0 :     static_cast<nsFrameList*>(Properties().Remove(PopupListProperty()));
     351               0 :   NS_ASSERTION(prop && prop->IsEmpty(),
     352                 :                "popup list must exist and be empty when destroying");
     353               0 :   RemoveStateBits(NS_STATE_MENU_HAS_POPUP_LIST);
     354               0 :   delete prop;
     355               0 : }
     356                 : 
     357                 : void
     358               0 : nsMenuFrame::SetPopupFrame(nsFrameList& aFrameList)
     359                 : {
     360               0 :   for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
     361               0 :     if (e.get()->GetType() == nsGkAtoms::menuPopupFrame) {
     362                 :       // Remove the frame from the list and store it in a nsFrameList* property.
     363               0 :       nsIFrame* popupFrame = e.get();
     364               0 :       aFrameList.RemoveFrame(popupFrame);
     365               0 :       nsFrameList* popupList = new nsFrameList(popupFrame, popupFrame);
     366               0 :       Properties().Set(PopupListProperty(), popupList);
     367               0 :       AddStateBits(NS_STATE_MENU_HAS_POPUP_LIST);
     368               0 :       break;
     369                 :     }
     370                 :   }
     371               0 : }
     372                 : 
     373                 : NS_IMETHODIMP
     374               0 : nsMenuFrame::SetInitialChildList(ChildListID     aListID,
     375                 :                                  nsFrameList&    aChildList)
     376                 : {
     377               0 :   NS_ASSERTION(!HasPopup(), "SetInitialChildList called twice?");
     378               0 :   if (aListID == kPrincipalList || aListID == kPopupList) {
     379               0 :     SetPopupFrame(aChildList);
     380                 :   }
     381               0 :   return nsBoxFrame::SetInitialChildList(aListID, aChildList);
     382                 : }
     383                 : 
     384                 : void
     385               0 : nsMenuFrame::DestroyFrom(nsIFrame* aDestructRoot)
     386                 : {
     387                 :   // Kill our timer if one is active. This is not strictly necessary as
     388                 :   // the pointer to this frame will be cleared from the mediator, but
     389                 :   // this is done for added safety.
     390               0 :   if (mOpenTimer) {
     391               0 :     mOpenTimer->Cancel();
     392                 :   }
     393                 : 
     394               0 :   StopBlinking();
     395                 : 
     396                 :   // Null out the pointer to this frame in the mediator wrapper so that it 
     397                 :   // doesn't try to interact with a deallocated frame.
     398               0 :   mTimerMediator->ClearFrame();
     399                 : 
     400                 :   // if the menu content is just being hidden, it may be made visible again
     401                 :   // later, so make sure to clear the highlighting.
     402               0 :   mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::menuactive, false);
     403                 : 
     404                 :   // are we our menu parent's current menu item?
     405               0 :   if (mMenuParent && mMenuParent->GetCurrentMenuItem() == this) {
     406                 :     // yes; tell it that we're going away
     407               0 :     mMenuParent->CurrentMenuIsBeingDestroyed();
     408                 :   }
     409                 : 
     410               0 :   nsFrameList* popupList = GetPopupList();
     411               0 :   if (popupList) {
     412               0 :     popupList->DestroyFramesFrom(aDestructRoot);
     413               0 :     DestroyPopupList();
     414                 :   }
     415                 : 
     416               0 :   nsBoxFrame::DestroyFrom(aDestructRoot);
     417               0 : }
     418                 : 
     419                 : NS_IMETHODIMP
     420               0 : nsMenuFrame::BuildDisplayListForChildren(nsDisplayListBuilder*   aBuilder,
     421                 :                                          const nsRect&           aDirtyRect,
     422                 :                                          const nsDisplayListSet& aLists)
     423                 : {
     424               0 :   if (!aBuilder->IsForEventDelivery())
     425               0 :     return nsBoxFrame::BuildDisplayListForChildren(aBuilder, aDirtyRect, aLists);
     426                 :     
     427               0 :   nsDisplayListCollection set;
     428               0 :   nsresult rv = nsBoxFrame::BuildDisplayListForChildren(aBuilder, aDirtyRect, set);
     429               0 :   NS_ENSURE_SUCCESS(rv, rv);
     430                 :   
     431               0 :   return WrapListsInRedirector(aBuilder, set, aLists);
     432                 : }
     433                 : 
     434                 : NS_IMETHODIMP
     435               0 : nsMenuFrame::HandleEvent(nsPresContext* aPresContext, 
     436                 :                          nsGUIEvent*     aEvent,
     437                 :                          nsEventStatus*  aEventStatus)
     438                 : {
     439               0 :   NS_ENSURE_ARG_POINTER(aEventStatus);
     440               0 :   if (nsEventStatus_eConsumeNoDefault == *aEventStatus ||
     441               0 :       (mMenuParent && mMenuParent->IsMenuLocked())) {
     442               0 :     return NS_OK;
     443                 :   }
     444                 : 
     445               0 :   nsWeakFrame weakFrame(this);
     446               0 :   if (*aEventStatus == nsEventStatus_eIgnore)
     447               0 :     *aEventStatus = nsEventStatus_eConsumeDoDefault;
     448                 : 
     449               0 :   bool onmenu = IsOnMenu();
     450                 : 
     451               0 :   if (aEvent->message == NS_KEY_PRESS && !IsDisabled()) {
     452               0 :     nsKeyEvent* keyEvent = (nsKeyEvent*)aEvent;
     453               0 :     PRUint32 keyCode = keyEvent->keyCode;
     454                 : #ifdef XP_MACOSX
     455                 :     // On mac, open menulist on either up/down arrow or space (w/o Cmd pressed)
     456                 :     if (!IsOpen() && ((keyEvent->charCode == NS_VK_SPACE && !keyEvent->isMeta) ||
     457                 :         (keyCode == NS_VK_UP || keyCode == NS_VK_DOWN))) {
     458                 :       *aEventStatus = nsEventStatus_eConsumeNoDefault;
     459                 :       OpenMenu(false);
     460                 :     }
     461                 : #else
     462                 :     // On other platforms, toggle menulist on unmodified F4 or Alt arrow
     463               0 :     if ((keyCode == NS_VK_F4 && !keyEvent->isAlt) ||
     464                 :         ((keyCode == NS_VK_UP || keyCode == NS_VK_DOWN) && keyEvent->isAlt)) {
     465               0 :       *aEventStatus = nsEventStatus_eConsumeNoDefault;
     466               0 :       ToggleMenuState();
     467                 :     }
     468                 : #endif
     469                 :   }
     470               0 :   else if (aEvent->eventStructType == NS_MOUSE_EVENT &&
     471                 :            aEvent->message == NS_MOUSE_BUTTON_DOWN &&
     472                 :            static_cast<nsMouseEvent*>(aEvent)->button == nsMouseEvent::eLeftButton &&
     473               0 :            !IsDisabled() && IsMenu()) {
     474                 :     // The menu item was selected. Bring up the menu.
     475                 :     // We have children.
     476                 :     // Don't prevent the default action here, since that will also cancel
     477                 :     // potential drag starts.
     478               0 :     if (!mMenuParent || mMenuParent->IsMenuBar()) {
     479               0 :       ToggleMenuState();
     480                 :     }
     481                 :     else {
     482               0 :       if (!IsOpen()) {
     483               0 :         OpenMenu(false);
     484                 :       }
     485                 :     }
     486                 :   }
     487               0 :   else if (
     488                 : #ifndef NSCONTEXTMENUISMOUSEUP
     489                 :            (aEvent->eventStructType == NS_MOUSE_EVENT &&
     490                 :             aEvent->message == NS_MOUSE_BUTTON_UP &&
     491                 :             static_cast<nsMouseEvent*>(aEvent)->button ==
     492                 :               nsMouseEvent::eRightButton) &&
     493                 : #else
     494                 :             aEvent->message == NS_CONTEXTMENU &&
     495                 : #endif
     496               0 :             onmenu && !IsMenu() && !IsDisabled()) {
     497                 :     // if this menu is a context menu it accepts right-clicks...fire away!
     498                 :     // Make sure we cancel default processing of the context menu event so
     499                 :     // that it doesn't bubble and get seen again by the popuplistener and show
     500                 :     // another context menu.
     501                 :     //
     502                 :     // Furthermore (there's always more, isn't there?), on some platforms (win32
     503                 :     // being one of them) we get the context menu event on a mouse up while
     504                 :     // on others we get it on a mouse down. For the ones where we get it on a
     505                 :     // mouse down, we must continue listening for the right button up event to
     506                 :     // dismiss the menu.
     507               0 :     if (mMenuParent->IsContextMenu()) {
     508               0 :       *aEventStatus = nsEventStatus_eConsumeNoDefault;
     509               0 :       Execute(aEvent);
     510                 :     }
     511                 :   }
     512               0 :   else if (aEvent->eventStructType == NS_MOUSE_EVENT &&
     513                 :            aEvent->message == NS_MOUSE_BUTTON_UP &&
     514                 :            static_cast<nsMouseEvent*>(aEvent)->button == nsMouseEvent::eLeftButton &&
     515               0 :            !IsMenu() && !IsDisabled()) {
     516                 :     // Execute the execute event handler.
     517               0 :     *aEventStatus = nsEventStatus_eConsumeNoDefault;
     518               0 :     Execute(aEvent);
     519                 :   }
     520               0 :   else if (aEvent->message == NS_MOUSE_EXIT_SYNTH) {
     521                 :     // Kill our timer if one is active.
     522               0 :     if (mOpenTimer) {
     523               0 :       mOpenTimer->Cancel();
     524               0 :       mOpenTimer = nsnull;
     525                 :     }
     526                 : 
     527                 :     // Deactivate the menu.
     528               0 :     if (mMenuParent) {
     529               0 :       bool onmenubar = mMenuParent->IsMenuBar();
     530               0 :       if (!(onmenubar && mMenuParent->IsActive())) {
     531               0 :         if (IsMenu() && !onmenubar && IsOpen()) {
     532                 :           // Submenus don't get closed up immediately.
     533                 :         }
     534               0 :         else if (this == mMenuParent->GetCurrentMenuItem()) {
     535               0 :           mMenuParent->ChangeMenuItem(nsnull, false);
     536                 :         }
     537                 :       }
     538                 :     }
     539                 :   }
     540               0 :   else if (aEvent->message == NS_MOUSE_MOVE &&
     541               0 :            (onmenu || (mMenuParent && mMenuParent->IsMenuBar()))) {
     542               0 :     if (gEatMouseMove) {
     543               0 :       gEatMouseMove = false;
     544               0 :       return NS_OK;
     545                 :     }
     546                 : 
     547                 :     // Let the menu parent know we're the new item.
     548               0 :     mMenuParent->ChangeMenuItem(this, false);
     549               0 :     NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
     550               0 :     NS_ENSURE_TRUE(mMenuParent, NS_OK);
     551                 : 
     552                 :     // we need to check if we really became the current menu
     553                 :     // item or not
     554               0 :     nsMenuFrame *realCurrentItem = mMenuParent->GetCurrentMenuItem();
     555               0 :     if (realCurrentItem != this) {
     556                 :       // we didn't (presumably because a context menu was active)
     557               0 :       return NS_OK;
     558                 :     }
     559                 : 
     560                 :     // Hovering over a menu in a popup should open it without a need for a click.
     561                 :     // A timer is used so that it doesn't open if the user moves the mouse quickly
     562                 :     // past the menu. This conditional check ensures that only menus have this
     563                 :     // behaviour
     564               0 :     if (!IsDisabled() && IsMenu() && !IsOpen() && !mOpenTimer && !mMenuParent->IsMenuBar()) {
     565                 :       PRInt32 menuDelay =
     566               0 :         LookAndFeel::GetInt(LookAndFeel::eIntID_SubmenuDelay, 300); // ms
     567                 : 
     568                 :       // We're a menu, we're built, we're closed, and no timer has been kicked off.
     569               0 :       mOpenTimer = do_CreateInstance("@mozilla.org/timer;1");
     570               0 :       mOpenTimer->InitWithCallback(mTimerMediator, menuDelay, nsITimer::TYPE_ONE_SHOT);
     571                 :     }
     572                 :   }
     573                 :   
     574               0 :   return NS_OK;
     575                 : }
     576                 : 
     577                 : void
     578               0 : nsMenuFrame::ToggleMenuState()
     579                 : {
     580               0 :   if (IsOpen())
     581               0 :     CloseMenu(false);
     582                 :   else
     583               0 :     OpenMenu(false);
     584               0 : }
     585                 : 
     586                 : void
     587               0 : nsMenuFrame::PopupOpened()
     588                 : {
     589               0 :   nsWeakFrame weakFrame(this);
     590                 :   mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open,
     591               0 :                     NS_LITERAL_STRING("true"), true);
     592               0 :   if (!weakFrame.IsAlive())
     593                 :     return;
     594                 : 
     595               0 :   if (mMenuParent) {
     596               0 :     mMenuParent->SetActive(true);
     597                 :     // Make sure the current menu which is being toggled on
     598                 :     // the menubar is highlighted
     599               0 :     mMenuParent->SetCurrentMenuItem(this);
     600                 :   }
     601                 : }
     602                 : 
     603                 : void
     604               0 : nsMenuFrame::PopupClosed(bool aDeselectMenu)
     605                 : {
     606               0 :   nsWeakFrame weakFrame(this);
     607                 :   nsContentUtils::AddScriptRunner(
     608               0 :     new nsUnsetAttrRunnable(mContent, nsGkAtoms::open));
     609               0 :   if (!weakFrame.IsAlive())
     610                 :     return;
     611                 : 
     612                 :   // if the popup is for a menu on a menubar, inform menubar to deactivate
     613               0 :   if (mMenuParent && mMenuParent->MenuClosed()) {
     614               0 :     if (aDeselectMenu) {
     615               0 :       SelectMenu(false);
     616                 :     } else {
     617                 :       // We are not deselecting the parent menu while closing the popup, so send
     618                 :       // a DOMMenuItemActive event to the menu to indicate that the menu is
     619                 :       // becoming active again.
     620               0 :       nsMenuFrame *current = mMenuParent->GetCurrentMenuItem();
     621               0 :       if (current) {
     622                 :         nsCOMPtr<nsIRunnable> event =
     623               0 :           new nsMenuActivateEvent(current->GetContent(),
     624               0 :                                   PresContext(), true);
     625               0 :         NS_DispatchToCurrentThread(event);
     626                 :       }
     627                 :     }
     628                 :   }
     629                 : }
     630                 : 
     631                 : NS_IMETHODIMP
     632               0 : nsMenuFrame::SelectMenu(bool aActivateFlag)
     633                 : {
     634               0 :   if (mContent) {
     635                 :     // When a menu opens a submenu, the mouse will often be moved onto a
     636                 :     // sibling before moving onto an item within the submenu, causing the
     637                 :     // parent to become deselected. We need to ensure that the parent menu
     638                 :     // is reselected when an item in the submenu is selected, so navigate up
     639                 :     // from the item to its popup, and then to the popup above that.
     640               0 :     if (aActivateFlag) {
     641               0 :       nsIFrame* parent = GetParent();
     642               0 :       while (parent) {
     643               0 :         if (parent->GetType() == nsGkAtoms::menuPopupFrame) {
     644                 :           // a menu is always the direct parent of a menupopup
     645               0 :           parent = parent->GetParent();
     646               0 :           if (parent && parent->GetType() == nsGkAtoms::menuFrame) {
     647                 :             // a popup however is not necessarily the direct parent of a menu
     648               0 :             nsIFrame* popupParent = parent->GetParent();
     649               0 :             while (popupParent) {
     650               0 :               if (popupParent->GetType() == nsGkAtoms::menuPopupFrame) {
     651               0 :                 nsMenuPopupFrame* popup = static_cast<nsMenuPopupFrame *>(popupParent);
     652               0 :                 popup->SetCurrentMenuItem(static_cast<nsMenuFrame *>(parent));
     653               0 :                 break;
     654                 :               }
     655               0 :               popupParent = popupParent->GetParent();
     656                 :             }
     657                 :           }
     658               0 :           break;
     659                 :         }
     660               0 :         parent = parent->GetParent();
     661                 :       }
     662                 :     }
     663                 : 
     664                 :     // cancel the close timer if selecting a menu within the popup to be closed
     665               0 :     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     666               0 :     if (pm)
     667               0 :       pm->CancelMenuTimer(mMenuParent);
     668                 : 
     669                 :     nsCOMPtr<nsIRunnable> event =
     670               0 :       new nsMenuActivateEvent(mContent, PresContext(), aActivateFlag);
     671               0 :     NS_DispatchToCurrentThread(event);
     672                 :   }
     673                 : 
     674               0 :   return NS_OK;
     675                 : }
     676                 : 
     677                 : NS_IMETHODIMP
     678               0 : nsMenuFrame::AttributeChanged(PRInt32 aNameSpaceID,
     679                 :                               nsIAtom* aAttribute,
     680                 :                               PRInt32 aModType)
     681                 : {
     682               0 :   if (aAttribute == nsGkAtoms::acceltext && mIgnoreAccelTextChange) {
     683                 :     // Reset the flag so that only one change is ignored.
     684               0 :     mIgnoreAccelTextChange = false;
     685               0 :     return NS_OK;
     686                 :   }
     687                 : 
     688               0 :   if (aAttribute == nsGkAtoms::checked ||
     689                 :       aAttribute == nsGkAtoms::acceltext ||
     690                 :       aAttribute == nsGkAtoms::key ||
     691                 :       aAttribute == nsGkAtoms::type ||
     692                 :       aAttribute == nsGkAtoms::name) {
     693                 :     nsCOMPtr<nsIRunnable> event =
     694               0 :       new nsMenuAttributeChangedEvent(this, aAttribute);
     695               0 :     nsContentUtils::AddScriptRunner(event);
     696                 :   }
     697               0 :   return NS_OK;
     698                 : }
     699                 : 
     700                 : void
     701               0 : nsMenuFrame::OpenMenu(bool aSelectFirstItem)
     702                 : {
     703               0 :   if (!mContent)
     704               0 :     return;
     705                 : 
     706               0 :   gEatMouseMove = true;
     707                 : 
     708               0 :   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     709               0 :   if (pm) {
     710               0 :     pm->KillMenuTimer();
     711                 :     // This opens the menu asynchronously
     712               0 :     pm->ShowMenu(mContent, aSelectFirstItem, true);
     713                 :   }
     714                 : }
     715                 : 
     716                 : void
     717               0 : nsMenuFrame::CloseMenu(bool aDeselectMenu)
     718                 : {
     719               0 :   gEatMouseMove = true;
     720                 : 
     721                 :   // Close the menu asynchronously
     722               0 :   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     723               0 :   if (pm && HasPopup())
     724               0 :     pm->HidePopup(GetPopup()->GetContent(), false, aDeselectMenu, true);
     725               0 : }
     726                 : 
     727                 : bool
     728               0 : nsMenuFrame::IsSizedToPopup(nsIContent* aContent, bool aRequireAlways)
     729                 : {
     730                 :   bool sizeToPopup;
     731               0 :   if (aContent->Tag() == nsGkAtoms::select)
     732               0 :     sizeToPopup = true;
     733                 :   else {
     734               0 :     nsAutoString sizedToPopup;
     735               0 :     aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::sizetopopup, sizedToPopup);
     736               0 :     sizeToPopup = sizedToPopup.EqualsLiteral("always") ||
     737               0 :                   (!aRequireAlways && sizedToPopup.EqualsLiteral("pref"));
     738                 :   }
     739                 :   
     740               0 :   return sizeToPopup;
     741                 : }
     742                 : 
     743                 : nsSize
     744               0 : nsMenuFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState)
     745                 : {
     746               0 :   nsSize size = nsBoxFrame::GetMinSize(aBoxLayoutState);
     747               0 :   DISPLAY_MIN_SIZE(this, size);
     748                 : 
     749               0 :   if (IsSizedToPopup(mContent, true))
     750               0 :     SizeToPopup(aBoxLayoutState, size);
     751                 : 
     752                 :   return size;
     753                 : }
     754                 : 
     755                 : NS_IMETHODIMP
     756               0 : nsMenuFrame::DoLayout(nsBoxLayoutState& aState)
     757                 : {
     758                 :   // lay us out
     759               0 :   nsresult rv = nsBoxFrame::DoLayout(aState);
     760                 : 
     761               0 :   nsMenuPopupFrame* popupFrame = GetPopup();
     762               0 :   if (popupFrame) {
     763               0 :     bool sizeToPopup = IsSizedToPopup(mContent, false);
     764               0 :     popupFrame->LayoutPopup(aState, this, sizeToPopup);
     765                 :   }
     766                 : 
     767               0 :   return rv;
     768                 : }
     769                 : 
     770                 : #ifdef DEBUG_LAYOUT
     771                 : NS_IMETHODIMP
     772                 : nsMenuFrame::SetDebug(nsBoxLayoutState& aState, bool aDebug)
     773                 : {
     774                 :   // see if our state matches the given debug state
     775                 :   bool debugSet = mState & NS_STATE_CURRENTLY_IN_DEBUG;
     776                 :   bool debugChanged = (!aDebug && debugSet) || (aDebug && !debugSet);
     777                 : 
     778                 :   // if it doesn't then tell each child below us the new debug state
     779                 :   if (debugChanged)
     780                 :   {
     781                 :       nsBoxFrame::SetDebug(aState, aDebug);
     782                 :       nsMenuPopupFrame* popupFrame = GetPopup();
     783                 :       if (popupFrame)
     784                 :         SetDebug(aState, popupFrame, aDebug);
     785                 :   }
     786                 : 
     787                 :   return NS_OK;
     788                 : }
     789                 : 
     790                 : nsresult
     791                 : nsMenuFrame::SetDebug(nsBoxLayoutState& aState, nsIFrame* aList, bool aDebug)
     792                 : {
     793                 :       if (!aList)
     794                 :           return NS_OK;
     795                 : 
     796                 :       while (aList) {
     797                 :         if (aList->IsBoxFrame())
     798                 :           aList->SetDebug(aState, aDebug);
     799                 : 
     800                 :         aList = aList->GetNextSibling();
     801                 :       }
     802                 : 
     803                 :       return NS_OK;
     804                 : }
     805                 : #endif
     806                 : 
     807                 : //
     808                 : // Enter
     809                 : //
     810                 : // Called when the user hits the <Enter>/<Return> keys or presses the
     811                 : // shortcut key. If this is a leaf item, the item's action will be executed.
     812                 : // In either case, do nothing if the item is disabled.
     813                 : //
     814                 : nsMenuFrame*
     815               0 : nsMenuFrame::Enter(nsGUIEvent *aEvent)
     816                 : {
     817               0 :   if (IsDisabled()) {
     818                 : #ifdef XP_WIN
     819                 :     // behavior on Windows - close the popup chain
     820                 :     if (mMenuParent) {
     821                 :       nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     822                 :       if (pm) {
     823                 :         nsIFrame* popup = pm->GetTopPopup(ePopupTypeAny);
     824                 :         if (popup)
     825                 :           pm->HidePopup(popup->GetContent(), true, true, true);
     826                 :       }
     827                 :     }
     828                 : #endif   // #ifdef XP_WIN
     829                 :     // this menu item was disabled - exit
     830               0 :     return nsnull;
     831                 :   }
     832                 : 
     833               0 :   if (!IsOpen()) {
     834                 :     // The enter key press applies to us.
     835               0 :     if (!IsMenu() && mMenuParent)
     836               0 :       Execute(aEvent);          // Execute our event handler
     837                 :     else
     838               0 :       return this;
     839                 :   }
     840                 : 
     841               0 :   return nsnull;
     842                 : }
     843                 : 
     844                 : bool
     845               0 : nsMenuFrame::IsOpen()
     846                 : {
     847               0 :   nsMenuPopupFrame* popupFrame = GetPopup();
     848               0 :   return popupFrame && popupFrame->IsOpen();
     849                 : }
     850                 : 
     851                 : bool
     852               0 : nsMenuFrame::IsMenu()
     853                 : {
     854               0 :   return mIsMenu;
     855                 : }
     856                 : 
     857                 : nsMenuListType
     858               0 : nsMenuFrame::GetParentMenuListType()
     859                 : {
     860               0 :   if (mMenuParent && mMenuParent->IsMenu()) {
     861               0 :     nsMenuPopupFrame* popupFrame = static_cast<nsMenuPopupFrame*>(mMenuParent);
     862               0 :     nsIFrame* parentMenu = popupFrame->GetParent();
     863               0 :     if (parentMenu) {
     864               0 :       nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(parentMenu->GetContent());
     865               0 :       if (menulist) {
     866               0 :         bool isEditable = false;
     867               0 :         menulist->GetEditable(&isEditable);
     868               0 :         return isEditable ? eEditableMenuList : eReadonlyMenuList;
     869                 :       }
     870                 :     }
     871                 :   }
     872               0 :   return eNotMenuList;
     873                 : }
     874                 : 
     875                 : nsresult
     876               0 : nsMenuFrame::Notify(nsITimer* aTimer)
     877                 : {
     878                 :   // Our timer has fired.
     879               0 :   if (aTimer == mOpenTimer.get()) {
     880               0 :     mOpenTimer = nsnull;
     881                 : 
     882               0 :     if (!IsOpen() && mMenuParent) {
     883                 :       // make sure we didn't open a context menu in the meantime
     884                 :       // (i.e. the user right-clicked while hovering over a submenu).
     885               0 :       nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     886               0 :       if (pm) {
     887               0 :         if ((!pm->HasContextMenu(nsnull) || mMenuParent->IsContextMenu()) &&
     888                 :             mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menuactive,
     889               0 :                                   nsGkAtoms::_true, eCaseMatters)) {
     890               0 :           OpenMenu(false);
     891                 :         }
     892                 :       }
     893                 :     }
     894               0 :   } else if (aTimer == mBlinkTimer) {
     895               0 :     switch (mBlinkState++) {
     896                 :       case 0:
     897               0 :         NS_ASSERTION(false, "Blink timer fired while not blinking");
     898               0 :         StopBlinking();
     899               0 :         break;
     900                 :       case 1:
     901                 :         {
     902                 :           // Turn the highlight back on and wait for a while before closing the menu.
     903               0 :           nsWeakFrame weakFrame(this);
     904                 :           mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::menuactive,
     905               0 :                             NS_LITERAL_STRING("true"), true);
     906               0 :           if (weakFrame.IsAlive()) {
     907               0 :             aTimer->InitWithCallback(mTimerMediator, kBlinkDelay, nsITimer::TYPE_ONE_SHOT);
     908                 :           }
     909                 :         }
     910               0 :         break;
     911                 :       default:
     912               0 :         if (mMenuParent) {
     913               0 :           mMenuParent->LockMenuUntilClosed(false);
     914                 :         }
     915               0 :         PassMenuCommandEventToPopupManager();
     916               0 :         StopBlinking();
     917               0 :         break;
     918                 :     }
     919                 :   }
     920                 : 
     921               0 :   return NS_OK;
     922                 : }
     923                 : 
     924                 : bool 
     925               0 : nsMenuFrame::IsDisabled()
     926                 : {
     927                 :   return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
     928               0 :                                nsGkAtoms::_true, eCaseMatters);
     929                 : }
     930                 : 
     931                 : void
     932               0 : nsMenuFrame::UpdateMenuType(nsPresContext* aPresContext)
     933                 : {
     934                 :   static nsIContent::AttrValuesArray strings[] =
     935                 :     {&nsGkAtoms::checkbox, &nsGkAtoms::radio, nsnull};
     936               0 :   switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type,
     937               0 :                                     strings, eCaseMatters)) {
     938               0 :     case 0: mType = eMenuType_Checkbox; break;
     939                 :     case 1:
     940               0 :       mType = eMenuType_Radio;
     941               0 :       mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, mGroupName);
     942               0 :       break;
     943                 : 
     944                 :     default:
     945               0 :       if (mType != eMenuType_Normal) {
     946               0 :         nsWeakFrame weakFrame(this);
     947                 :         mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked,
     948               0 :                             true);
     949               0 :         ENSURE_TRUE(weakFrame.IsAlive());
     950                 :       }
     951               0 :       mType = eMenuType_Normal;
     952               0 :       break;
     953                 :   }
     954               0 :   UpdateMenuSpecialState(aPresContext);
     955                 : }
     956                 : 
     957                 : /* update checked-ness for type="checkbox" and type="radio" */
     958                 : void
     959               0 : nsMenuFrame::UpdateMenuSpecialState(nsPresContext* aPresContext)
     960                 : {
     961                 :   bool newChecked =
     962                 :     mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked,
     963               0 :                           nsGkAtoms::_true, eCaseMatters); 
     964               0 :   if (newChecked == mChecked) {
     965                 :     /* checked state didn't change */
     966                 : 
     967               0 :     if (mType != eMenuType_Radio)
     968               0 :       return; // only Radio possibly cares about other kinds of change
     969                 : 
     970               0 :     if (!mChecked || mGroupName.IsEmpty())
     971               0 :       return;                   // no interesting change
     972                 :   } else { 
     973               0 :     mChecked = newChecked;
     974               0 :     if (mType != eMenuType_Radio || !mChecked)
     975                 :       /*
     976                 :        * Unchecking something requires no further changes, and only
     977                 :        * menuRadio has to do additional work when checked.
     978                 :        */
     979               0 :       return;
     980                 :   }
     981                 : 
     982                 :   /*
     983                 :    * If we get this far, we're type=radio, and:
     984                 :    * - our name= changed, or
     985                 :    * - we went from checked="false" to checked="true"
     986                 :    */
     987                 : 
     988                 :   /*
     989                 :    * Behavioural note:
     990                 :    * If we're checked and renamed _into_ an existing radio group, we are
     991                 :    * made the new checked item, and we unselect the previous one.
     992                 :    *
     993                 :    * The only other reasonable behaviour would be to check for another selected
     994                 :    * item in that group.  If found, unselect ourselves, otherwise we're the
     995                 :    * selected item.  That, however, would be a lot more work, and I don't think
     996                 :    * it's better at all.
     997                 :    */
     998                 : 
     999                 :   /* walk siblings, looking for the other checked item with the same name */
    1000                 :   // get the first sibling in this menu popup. This frame may be it, and if we're
    1001                 :   // being called at creation time, this frame isn't yet in the parent's child list.
    1002                 :   // All I'm saying is that this may fail, but it's most likely alright.
    1003               0 :   nsIFrame* sib = GetParent()->GetFirstPrincipalChild();
    1004                 : 
    1005               0 :   while (sib) {
    1006               0 :     if (sib != this && sib->GetType() == nsGkAtoms::menuFrame) {
    1007               0 :       nsMenuFrame* menu = static_cast<nsMenuFrame*>(sib);
    1008               0 :       if (menu->GetMenuType() == eMenuType_Radio &&
    1009               0 :           menu->IsChecked() &&
    1010               0 :           (menu->GetRadioGroupName() == mGroupName)) {      
    1011                 :         /* uncheck the old item */
    1012               0 :         sib->GetContent()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked,
    1013               0 :                                      true);
    1014                 :         /* XXX in DEBUG, check to make sure that there aren't two checked items */
    1015               0 :         return;
    1016                 :       }
    1017                 :     }
    1018                 : 
    1019               0 :     sib = sib->GetNextSibling();
    1020                 :   } 
    1021                 : }
    1022                 : 
    1023                 : void 
    1024               0 : nsMenuFrame::BuildAcceleratorText(bool aNotify)
    1025                 : {
    1026               0 :   nsAutoString accelText;
    1027                 : 
    1028               0 :   if ((GetStateBits() & NS_STATE_ACCELTEXT_IS_DERIVED) == 0) {
    1029               0 :     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::acceltext, accelText);
    1030               0 :     if (!accelText.IsEmpty())
    1031                 :       return;
    1032                 :   }
    1033                 :   // accelText is definitely empty here.
    1034                 : 
    1035                 :   // Now we're going to compute the accelerator text, so remember that we did.
    1036               0 :   AddStateBits(NS_STATE_ACCELTEXT_IS_DERIVED);
    1037                 : 
    1038                 :   // If anything below fails, just leave the accelerator text blank.
    1039               0 :   nsWeakFrame weakFrame(this);
    1040               0 :   mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::acceltext, aNotify);
    1041               0 :   ENSURE_TRUE(weakFrame.IsAlive());
    1042                 : 
    1043                 :   // See if we have a key node and use that instead.
    1044               0 :   nsAutoString keyValue;
    1045               0 :   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyValue);
    1046               0 :   if (keyValue.IsEmpty())
    1047                 :     return;
    1048                 : 
    1049                 :   // Turn the document into a DOM document so we can use getElementById
    1050               0 :   nsIDocument *document = mContent->GetDocument();
    1051               0 :   if (!document)
    1052                 :     return;
    1053                 : 
    1054               0 :   nsIContent *keyElement = document->GetElementById(keyValue);
    1055               0 :   if (!keyElement) {
    1056                 : #ifdef DEBUG
    1057               0 :     nsAutoString label;
    1058               0 :     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, label);
    1059               0 :     nsAutoString msg = NS_LITERAL_STRING("Key '") +
    1060               0 :                        keyValue +
    1061               0 :                        NS_LITERAL_STRING("' of menu item '") +
    1062               0 :                        label +
    1063               0 :                        NS_LITERAL_STRING("' could not be found");
    1064               0 :     NS_WARNING(NS_ConvertUTF16toUTF8(msg).get());
    1065                 : #endif
    1066                 :     return;
    1067                 :   }
    1068                 : 
    1069                 :   // get the string to display as accelerator text
    1070                 :   // check the key element's attributes in this order:
    1071                 :   // |keytext|, |key|, |keycode|
    1072               0 :   nsAutoString accelString;
    1073               0 :   keyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::keytext, accelString);
    1074                 : 
    1075               0 :   if (accelString.IsEmpty()) {
    1076               0 :     keyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::key, accelString);
    1077                 : 
    1078               0 :     if (!accelString.IsEmpty()) {
    1079               0 :       ToUpperCase(accelString);
    1080                 :     } else {
    1081               0 :       nsAutoString keyCode;
    1082               0 :       keyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::keycode, keyCode);
    1083               0 :       ToUpperCase(keyCode);
    1084                 : 
    1085                 :       nsresult rv;
    1086                 :       nsCOMPtr<nsIStringBundleService> bundleService =
    1087               0 :         mozilla::services::GetStringBundleService();
    1088               0 :       if (bundleService) {
    1089               0 :         nsCOMPtr<nsIStringBundle> bundle;
    1090               0 :         rv = bundleService->CreateBundle("chrome://global/locale/keys.properties",
    1091               0 :                                          getter_AddRefs(bundle));
    1092                 : 
    1093               0 :         if (NS_SUCCEEDED(rv) && bundle) {
    1094               0 :           nsXPIDLString keyName;
    1095               0 :           rv = bundle->GetStringFromName(keyCode.get(), getter_Copies(keyName));
    1096               0 :           if (keyName)
    1097               0 :             accelString = keyName;
    1098                 :         }
    1099                 :       }
    1100                 : 
    1101                 :       // nothing usable found, bail
    1102               0 :       if (accelString.IsEmpty())
    1103                 :         return;
    1104                 :     }
    1105                 :   }
    1106                 : 
    1107                 :   static PRInt32 accelKey = 0;
    1108                 : 
    1109               0 :   if (!accelKey)
    1110                 :   {
    1111                 :     // Compiled-in defaults, in case we can't get LookAndFeel --
    1112                 :     // command for mac, control for all other platforms.
    1113                 : #ifdef XP_MACOSX
    1114                 :     accelKey = nsIDOMKeyEvent::DOM_VK_META;
    1115                 : #else
    1116               0 :     accelKey = nsIDOMKeyEvent::DOM_VK_CONTROL;
    1117                 : #endif
    1118                 : 
    1119                 :     // Get the accelerator key value from prefs, overriding the default:
    1120               0 :     accelKey = Preferences::GetInt("ui.key.accelKey", accelKey);
    1121                 :   }
    1122                 : 
    1123               0 :   nsAutoString modifiers;
    1124               0 :   keyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers, modifiers);
    1125                 :   
    1126               0 :   char* str = ToNewCString(modifiers);
    1127                 :   char* newStr;
    1128               0 :   char* token = nsCRT::strtok(str, ", \t", &newStr);
    1129                 : 
    1130               0 :   nsAutoString shiftText;
    1131               0 :   nsAutoString altText;
    1132               0 :   nsAutoString metaText;
    1133               0 :   nsAutoString controlText;
    1134               0 :   nsAutoString modifierSeparator;
    1135                 : 
    1136               0 :   nsContentUtils::GetShiftText(shiftText);
    1137               0 :   nsContentUtils::GetAltText(altText);
    1138               0 :   nsContentUtils::GetMetaText(metaText);
    1139               0 :   nsContentUtils::GetControlText(controlText);
    1140               0 :   nsContentUtils::GetModifierSeparatorText(modifierSeparator);
    1141                 : 
    1142               0 :   while (token) {
    1143                 :       
    1144               0 :     if (PL_strcmp(token, "shift") == 0)
    1145               0 :       accelText += shiftText;
    1146               0 :     else if (PL_strcmp(token, "alt") == 0) 
    1147               0 :       accelText += altText; 
    1148               0 :     else if (PL_strcmp(token, "meta") == 0) 
    1149               0 :       accelText += metaText; 
    1150               0 :     else if (PL_strcmp(token, "control") == 0) 
    1151               0 :       accelText += controlText; 
    1152               0 :     else if (PL_strcmp(token, "accel") == 0) {
    1153               0 :       switch (accelKey)
    1154                 :       {
    1155                 :         case nsIDOMKeyEvent::DOM_VK_META:
    1156               0 :           accelText += metaText;
    1157               0 :           break;
    1158                 : 
    1159                 :         case nsIDOMKeyEvent::DOM_VK_ALT:
    1160               0 :           accelText += altText;
    1161               0 :           break;
    1162                 : 
    1163                 :         case nsIDOMKeyEvent::DOM_VK_CONTROL:
    1164                 :         default:
    1165               0 :           accelText += controlText;
    1166               0 :           break;
    1167                 :       }
    1168                 :     }
    1169                 :     
    1170               0 :     accelText += modifierSeparator;
    1171                 : 
    1172               0 :     token = nsCRT::strtok(newStr, ", \t", &newStr);
    1173                 :   }
    1174                 : 
    1175               0 :   nsMemory::Free(str);
    1176                 : 
    1177               0 :   accelText += accelString;
    1178                 : 
    1179               0 :   mIgnoreAccelTextChange = true;
    1180               0 :   mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::acceltext, accelText, aNotify);
    1181               0 :   ENSURE_TRUE(weakFrame.IsAlive());
    1182                 : 
    1183               0 :   mIgnoreAccelTextChange = false;
    1184                 : }
    1185                 : 
    1186                 : void
    1187               0 : nsMenuFrame::Execute(nsGUIEvent *aEvent)
    1188                 : {
    1189                 :   // flip "checked" state if we're a checkbox menu, or an un-checked radio menu
    1190               0 :   bool needToFlipChecked = false;
    1191               0 :   if (mType == eMenuType_Checkbox || (mType == eMenuType_Radio && !mChecked)) {
    1192                 :     needToFlipChecked = !mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::autocheck,
    1193               0 :                                                nsGkAtoms::_false, eCaseMatters);
    1194                 :   }
    1195                 : 
    1196               0 :   nsCOMPtr<nsISound> sound(do_CreateInstance("@mozilla.org/sound;1"));
    1197               0 :   if (sound)
    1198               0 :     sound->PlayEventSound(nsISound::EVENT_MENU_EXECUTE);
    1199                 : 
    1200               0 :   StartBlinking(aEvent, needToFlipChecked);
    1201               0 : }
    1202                 : 
    1203                 : bool
    1204               0 : nsMenuFrame::ShouldBlink()
    1205                 : {
    1206                 :   PRInt32 shouldBlink =
    1207               0 :     LookAndFeel::GetInt(LookAndFeel::eIntID_ChosenMenuItemsShouldBlink, 0);
    1208               0 :   if (!shouldBlink)
    1209               0 :     return false;
    1210                 : 
    1211                 :   // Don't blink in editable menulists.
    1212               0 :   if (GetParentMenuListType() == eEditableMenuList)
    1213               0 :     return false;
    1214                 : 
    1215               0 :   return true;
    1216                 : }
    1217                 : 
    1218                 : void
    1219               0 : nsMenuFrame::StartBlinking(nsGUIEvent *aEvent, bool aFlipChecked)
    1220                 : {
    1221               0 :   StopBlinking();
    1222               0 :   CreateMenuCommandEvent(aEvent, aFlipChecked);
    1223                 : 
    1224               0 :   if (!ShouldBlink()) {
    1225               0 :     PassMenuCommandEventToPopupManager();
    1226               0 :     return;
    1227                 :   }
    1228                 : 
    1229                 :   // Blink off.
    1230               0 :   nsWeakFrame weakFrame(this);
    1231               0 :   mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::menuactive, true);
    1232               0 :   if (!weakFrame.IsAlive())
    1233                 :     return;
    1234                 : 
    1235               0 :   if (mMenuParent) {
    1236                 :     // Make this menu ignore events from now on.
    1237               0 :     mMenuParent->LockMenuUntilClosed(true);
    1238                 :   }
    1239                 : 
    1240                 :   // Set up a timer to blink back on.
    1241               0 :   mBlinkTimer = do_CreateInstance("@mozilla.org/timer;1");
    1242               0 :   mBlinkTimer->InitWithCallback(mTimerMediator, kBlinkDelay, nsITimer::TYPE_ONE_SHOT);
    1243               0 :   mBlinkState = 1;
    1244                 : }
    1245                 : 
    1246                 : void
    1247               0 : nsMenuFrame::StopBlinking()
    1248                 : {
    1249               0 :   mBlinkState = 0;
    1250               0 :   if (mBlinkTimer) {
    1251               0 :     mBlinkTimer->Cancel();
    1252               0 :     mBlinkTimer = nsnull;
    1253                 :   }
    1254               0 :   mDelayedMenuCommandEvent = nsnull;
    1255               0 : }
    1256                 : 
    1257                 : void
    1258               0 : nsMenuFrame::CreateMenuCommandEvent(nsGUIEvent *aEvent, bool aFlipChecked)
    1259                 : {
    1260                 :   // Create a trusted event if the triggering event was trusted, or if
    1261                 :   // we're called from chrome code (since at least one of our caller
    1262                 :   // passes in a null event).
    1263                 :   bool isTrusted = aEvent ? NS_IS_TRUSTED_EVENT(aEvent) :
    1264               0 :                               nsContentUtils::IsCallerChrome();
    1265                 : 
    1266               0 :   bool shift = false, control = false, alt = false, meta = false;
    1267               0 :   if (aEvent && (aEvent->eventStructType == NS_MOUSE_EVENT ||
    1268                 :                  aEvent->eventStructType == NS_KEY_EVENT)) {
    1269               0 :     shift = static_cast<nsInputEvent *>(aEvent)->isShift;
    1270               0 :     control = static_cast<nsInputEvent *>(aEvent)->isControl;
    1271               0 :     alt = static_cast<nsInputEvent *>(aEvent)->isAlt;
    1272               0 :     meta = static_cast<nsInputEvent *>(aEvent)->isMeta;
    1273                 :   }
    1274                 : 
    1275                 :   // Because the command event is firing asynchronously, a flag is needed to
    1276                 :   // indicate whether user input is being handled. This ensures that a popup
    1277                 :   // window won't get blocked.
    1278               0 :   bool userinput = nsEventStateManager::IsHandlingUserInput();
    1279                 : 
    1280                 :   mDelayedMenuCommandEvent =
    1281                 :     new nsXULMenuCommandEvent(mContent, isTrusted, shift, control, alt, meta,
    1282               0 :                               userinput, aFlipChecked);
    1283               0 : }
    1284                 : 
    1285                 : void
    1286               0 : nsMenuFrame::PassMenuCommandEventToPopupManager()
    1287                 : {
    1288               0 :   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    1289               0 :   if (pm && mMenuParent && mDelayedMenuCommandEvent) {
    1290               0 :     pm->ExecuteMenu(mContent, mDelayedMenuCommandEvent);
    1291                 :   }
    1292               0 :   mDelayedMenuCommandEvent = nsnull;
    1293               0 : }
    1294                 : 
    1295                 : NS_IMETHODIMP
    1296               0 : nsMenuFrame::RemoveFrame(ChildListID     aListID,
    1297                 :                          nsIFrame*       aOldFrame)
    1298                 : {
    1299               0 :   nsFrameList* popupList = GetPopupList();
    1300               0 :   if (popupList && popupList->DestroyFrameIfPresent(aOldFrame)) {
    1301               0 :     DestroyPopupList();
    1302               0 :     PresContext()->PresShell()->
    1303                 :       FrameNeedsReflow(this, nsIPresShell::eTreeChange,
    1304               0 :                        NS_FRAME_HAS_DIRTY_CHILDREN);
    1305               0 :     return NS_OK;
    1306                 :   }
    1307               0 :   return nsBoxFrame::RemoveFrame(aListID, aOldFrame);
    1308                 : }
    1309                 : 
    1310                 : NS_IMETHODIMP
    1311               0 : nsMenuFrame::InsertFrames(ChildListID     aListID,
    1312                 :                           nsIFrame*       aPrevFrame,
    1313                 :                           nsFrameList&    aFrameList)
    1314                 : {
    1315               0 :   if (!HasPopup() && (aListID == kPrincipalList || aListID == kPopupList)) {
    1316               0 :     SetPopupFrame(aFrameList);
    1317               0 :     if (HasPopup()) {
    1318                 : #ifdef DEBUG_LAYOUT
    1319                 :       nsBoxLayoutState state(PresContext());
    1320                 :       SetDebug(state, aFrameList, mState & NS_STATE_CURRENTLY_IN_DEBUG);
    1321                 : #endif
    1322                 : 
    1323               0 :       PresContext()->PresShell()->
    1324                 :         FrameNeedsReflow(this, nsIPresShell::eTreeChange,
    1325               0 :                          NS_FRAME_HAS_DIRTY_CHILDREN);
    1326                 :     }
    1327                 :   }
    1328                 : 
    1329               0 :   if (aFrameList.IsEmpty())
    1330               0 :     return NS_OK;
    1331                 : 
    1332               0 :   if (NS_UNLIKELY(aPrevFrame && aPrevFrame == GetPopup())) {
    1333               0 :     aPrevFrame = nsnull;
    1334                 :   }
    1335                 : 
    1336               0 :   return nsBoxFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
    1337                 : }
    1338                 : 
    1339                 : NS_IMETHODIMP
    1340               0 : nsMenuFrame::AppendFrames(ChildListID     aListID,
    1341                 :                           nsFrameList&    aFrameList)
    1342                 : {
    1343               0 :   if (!HasPopup() && (aListID == kPrincipalList || aListID == kPopupList)) {
    1344               0 :     SetPopupFrame(aFrameList);
    1345               0 :     if (HasPopup()) {
    1346                 : 
    1347                 : #ifdef DEBUG_LAYOUT
    1348                 :       nsBoxLayoutState state(PresContext());
    1349                 :       SetDebug(state, aFrameList, mState & NS_STATE_CURRENTLY_IN_DEBUG);
    1350                 : #endif
    1351               0 :       PresContext()->PresShell()->
    1352                 :         FrameNeedsReflow(this, nsIPresShell::eTreeChange,
    1353               0 :                          NS_FRAME_HAS_DIRTY_CHILDREN);
    1354                 :     }
    1355                 :   }
    1356                 : 
    1357               0 :   if (aFrameList.IsEmpty())
    1358               0 :     return NS_OK;
    1359                 : 
    1360               0 :   return nsBoxFrame::AppendFrames(aListID, aFrameList); 
    1361                 : }
    1362                 : 
    1363                 : bool
    1364               0 : nsMenuFrame::SizeToPopup(nsBoxLayoutState& aState, nsSize& aSize)
    1365                 : {
    1366               0 :   if (!IsCollapsed()) {
    1367                 :     bool widthSet, heightSet;
    1368               0 :     nsSize tmpSize(-1, 0);
    1369               0 :     nsIBox::AddCSSPrefSize(this, tmpSize, widthSet, heightSet);
    1370               0 :     if (!widthSet && GetFlex(aState) == 0) {
    1371               0 :       nsMenuPopupFrame* popupFrame = GetPopup();
    1372               0 :       if (!popupFrame)
    1373               0 :         return false;
    1374               0 :       tmpSize = popupFrame->GetPrefSize(aState);
    1375                 : 
    1376                 :       // Produce a size such that:
    1377                 :       //  (1) the menu and its popup can be the same width
    1378                 :       //  (2) there's enough room in the menu for the content and its
    1379                 :       //      border-padding
    1380                 :       //  (3) there's enough room in the popup for the content and its
    1381                 :       //      scrollbar
    1382               0 :       nsMargin borderPadding;
    1383               0 :       GetBorderAndPadding(borderPadding);
    1384                 : 
    1385                 :       // if there is a scroll frame, add the desired width of the scrollbar as well
    1386               0 :       nsIScrollableFrame* scrollFrame = do_QueryFrame(popupFrame->GetFirstPrincipalChild());
    1387               0 :       nscoord scrollbarWidth = 0;
    1388               0 :       if (scrollFrame) {
    1389                 :         scrollbarWidth =
    1390               0 :           scrollFrame->GetDesiredScrollbarSizes(&aState).LeftRight();
    1391                 :       }
    1392                 : 
    1393                 :       aSize.width =
    1394               0 :         tmpSize.width + NS_MAX(borderPadding.LeftRight(), scrollbarWidth);
    1395                 : 
    1396               0 :       return true;
    1397                 :     }
    1398                 :   }
    1399                 : 
    1400               0 :   return false;
    1401                 : }
    1402                 : 
    1403                 : nsSize
    1404               0 : nsMenuFrame::GetPrefSize(nsBoxLayoutState& aState)
    1405                 : {
    1406               0 :   nsSize size = nsBoxFrame::GetPrefSize(aState);
    1407               0 :   DISPLAY_PREF_SIZE(this, size);
    1408                 : 
    1409                 :   // If we are using sizetopopup="always" then
    1410                 :   // nsBoxFrame will already have enforced the minimum size
    1411               0 :   if (!IsSizedToPopup(mContent, true) &&
    1412               0 :       IsSizedToPopup(mContent, false) &&
    1413               0 :       SizeToPopup(aState, size)) {
    1414                 :     // We now need to ensure that size is within the min - max range.
    1415               0 :     nsSize minSize = nsBoxFrame::GetMinSize(aState);
    1416               0 :     nsSize maxSize = GetMaxSize(aState);
    1417               0 :     size = BoundsCheck(minSize, size, maxSize);
    1418                 :   }
    1419                 : 
    1420                 :   return size;
    1421                 : }
    1422                 : 
    1423                 : NS_IMETHODIMP
    1424               0 : nsMenuFrame::GetActiveChild(nsIDOMElement** aResult)
    1425                 : {
    1426               0 :   nsMenuPopupFrame* popupFrame = GetPopup();
    1427               0 :   if (!popupFrame)
    1428               0 :     return NS_ERROR_FAILURE;
    1429                 : 
    1430               0 :   nsMenuFrame* menuFrame = popupFrame->GetCurrentMenuItem();
    1431               0 :   if (!menuFrame) {
    1432               0 :     *aResult = nsnull;
    1433                 :   }
    1434                 :   else {
    1435               0 :     nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(menuFrame->GetContent()));
    1436               0 :     *aResult = elt;
    1437               0 :     NS_IF_ADDREF(*aResult);
    1438                 :   }
    1439                 : 
    1440               0 :   return NS_OK;
    1441                 : }
    1442                 : 
    1443                 : NS_IMETHODIMP
    1444               0 : nsMenuFrame::SetActiveChild(nsIDOMElement* aChild)
    1445                 : {
    1446               0 :   nsMenuPopupFrame* popupFrame = GetPopup();
    1447               0 :   if (!popupFrame)
    1448               0 :     return NS_ERROR_FAILURE;
    1449                 : 
    1450               0 :   if (!aChild) {
    1451                 :     // Remove the current selection
    1452               0 :     popupFrame->ChangeMenuItem(nsnull, false);
    1453               0 :     return NS_OK;
    1454                 :   }
    1455                 : 
    1456               0 :   nsCOMPtr<nsIContent> child(do_QueryInterface(aChild));
    1457                 : 
    1458               0 :   nsIFrame* kid = child->GetPrimaryFrame();
    1459               0 :   if (kid && kid->GetType() == nsGkAtoms::menuFrame)
    1460               0 :     popupFrame->ChangeMenuItem(static_cast<nsMenuFrame *>(kid), false);
    1461               0 :   return NS_OK;
    1462                 : }
    1463                 : 
    1464               0 : nsIScrollableFrame* nsMenuFrame::GetScrollTargetFrame()
    1465                 : {
    1466               0 :   nsMenuPopupFrame* popupFrame = GetPopup();
    1467               0 :   if (!popupFrame)
    1468               0 :     return nsnull;
    1469               0 :   nsIFrame* childFrame = popupFrame->GetFirstPrincipalChild();
    1470               0 :   if (childFrame)
    1471               0 :     return popupFrame->GetScrollFrame(childFrame);
    1472               0 :   return nsnull;
    1473                 : }
    1474                 : 
    1475                 : // nsMenuTimerMediator implementation.
    1476               0 : NS_IMPL_ISUPPORTS1(nsMenuTimerMediator, nsITimerCallback)
    1477                 : 
    1478                 : /**
    1479                 :  * Constructs a wrapper around an nsMenuFrame.
    1480                 :  * @param aFrame nsMenuFrame to create a wrapper around.
    1481                 :  */
    1482               0 : nsMenuTimerMediator::nsMenuTimerMediator(nsMenuFrame *aFrame) :
    1483               0 :   mFrame(aFrame)
    1484                 : {
    1485               0 :   NS_ASSERTION(mFrame, "Must have frame");
    1486               0 : }
    1487                 : 
    1488               0 : nsMenuTimerMediator::~nsMenuTimerMediator()
    1489                 : {
    1490               0 : }
    1491                 : 
    1492                 : /**
    1493                 :  * Delegates the notification to the contained frame if it has not been destroyed.
    1494                 :  * @param aTimer Timer which initiated the callback.
    1495                 :  * @return NS_ERROR_FAILURE if the frame has been destroyed.
    1496                 :  */
    1497               0 : NS_IMETHODIMP nsMenuTimerMediator::Notify(nsITimer* aTimer)
    1498                 : {
    1499               0 :   if (!mFrame)
    1500               0 :     return NS_ERROR_FAILURE;
    1501                 : 
    1502               0 :   return mFrame->Notify(aTimer);
    1503                 : }
    1504                 : 
    1505                 : /**
    1506                 :  * Clear the pointer to the contained nsMenuFrame. This should be called
    1507                 :  * when the contained nsMenuFrame is destroyed.
    1508                 :  */
    1509               0 : void nsMenuTimerMediator::ClearFrame()
    1510                 : {
    1511               0 :   mFrame = nsnull;
    1512               0 : }

Generated by: LCOV version 1.7