LCOV - code coverage report
Current view: directory - layout/xul/base/src - nsXULPopupManager.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1136 26 2.3 %
Date: 2012-06-02 Functions: 72 8 11.1 %

       1                 : /* -*- Mode: C++; tab-width: 2; 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 Neil Deakin
      18                 :  * Portions created by the Initial Developer are Copyright (C) 2006
      19                 :  * the Initial Developer. All Rights Reserved.
      20                 :  *
      21                 :  * Contributor(s):
      22                 :  *
      23                 :  * Alternatively, the contents of this file may be used under the terms of
      24                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      25                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      26                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      27                 :  * of those above. If you wish to allow use of your version of this file only
      28                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      29                 :  * use your version of this file under the terms of the MPL, indicate your
      30                 :  * decision by deleting the provisions above and replace them with the notice
      31                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      32                 :  * the provisions above, a recipient may use your version of this file under
      33                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      34                 :  *
      35                 :  * ***** END LICENSE BLOCK ***** */
      36                 : 
      37                 : #include "nsGkAtoms.h"
      38                 : #include "nsXULPopupManager.h"
      39                 : #include "nsMenuFrame.h"
      40                 : #include "nsMenuPopupFrame.h"
      41                 : #include "nsMenuBarFrame.h"
      42                 : #include "nsIPopupBoxObject.h"
      43                 : #include "nsMenuBarListener.h"
      44                 : #include "nsContentUtils.h"
      45                 : #include "nsIDOMDocument.h"
      46                 : #include "nsIDOMNSEvent.h"
      47                 : #include "nsIDOMXULElement.h"
      48                 : #include "nsIXULDocument.h"
      49                 : #include "nsIXULTemplateBuilder.h"
      50                 : #include "nsIPrivateDOMEvent.h"
      51                 : #include "nsEventDispatcher.h"
      52                 : #include "nsEventStateManager.h"
      53                 : #include "nsCSSFrameConstructor.h"
      54                 : #include "nsLayoutUtils.h"
      55                 : #include "nsIViewManager.h"
      56                 : #include "nsIComponentManager.h"
      57                 : #include "nsITimer.h"
      58                 : #include "nsFocusManager.h"
      59                 : #include "nsIDocShellTreeItem.h"
      60                 : #include "nsIDocShell.h"
      61                 : #include "nsPIDOMWindow.h"
      62                 : #include "nsIInterfaceRequestorUtils.h"
      63                 : #include "nsIBaseWindow.h"
      64                 : #include "nsIDOMMouseEvent.h"
      65                 : #include "nsCaret.h"
      66                 : #include "nsIDocument.h"
      67                 : #include "nsPIWindowRoot.h"
      68                 : #include "nsFrameManager.h"
      69                 : #include "nsIObserverService.h"
      70                 : #include "mozilla/Services.h"
      71                 : #include "mozilla/LookAndFeel.h"
      72                 : 
      73                 : using namespace mozilla;
      74                 : 
      75                 : #define FLAG_ALT        0x01
      76                 : #define FLAG_CONTROL    0x02
      77                 : #define FLAG_SHIFT      0x04
      78                 : #define FLAG_META       0x08
      79                 : 
      80                 : const nsNavigationDirection DirectionFromKeyCodeTable[2][6] = {
      81                 :   {
      82                 :     eNavigationDirection_Last,   // NS_VK_END
      83                 :     eNavigationDirection_First,  // NS_VK_HOME
      84                 :     eNavigationDirection_Start,  // NS_VK_LEFT
      85                 :     eNavigationDirection_Before, // NS_VK_UP
      86                 :     eNavigationDirection_End,    // NS_VK_RIGHT
      87                 :     eNavigationDirection_After   // NS_VK_DOWN
      88                 :   },
      89                 :   {
      90                 :     eNavigationDirection_Last,   // NS_VK_END
      91                 :     eNavigationDirection_First,  // NS_VK_HOME
      92                 :     eNavigationDirection_End,    // NS_VK_LEFT
      93                 :     eNavigationDirection_Before, // NS_VK_UP
      94                 :     eNavigationDirection_Start,  // NS_VK_RIGHT
      95                 :     eNavigationDirection_After   // NS_VK_DOWN
      96                 :   }
      97                 : };
      98                 : 
      99                 : nsXULPopupManager* nsXULPopupManager::sInstance = nsnull;
     100                 : 
     101               0 : nsIContent* nsMenuChainItem::Content()
     102                 : {
     103               0 :   return mFrame->GetContent();
     104                 : }
     105                 : 
     106               0 : void nsMenuChainItem::SetParent(nsMenuChainItem* aParent)
     107                 : {
     108               0 :   if (mParent) {
     109               0 :     NS_ASSERTION(mParent->mChild == this, "Unexpected - parent's child not set to this");
     110               0 :     mParent->mChild = nsnull;
     111                 :   }
     112               0 :   mParent = aParent;
     113               0 :   if (mParent) {
     114               0 :     if (mParent->mChild)
     115               0 :       mParent->mChild->mParent = nsnull;
     116               0 :     mParent->mChild = this;
     117                 :   }
     118               0 : }
     119                 : 
     120               0 : void nsMenuChainItem::Detach(nsMenuChainItem** aRoot)
     121                 : {
     122                 :   // If the item has a child, set the child's parent to this item's parent,
     123                 :   // effectively removing the item from the chain. If the item has no child,
     124                 :   // just set the parent to null.
     125               0 :   if (mChild) {
     126               0 :     NS_ASSERTION(this != *aRoot, "Unexpected - popup with child at end of chain");
     127               0 :     mChild->SetParent(mParent);
     128                 :   }
     129                 :   else {
     130                 :     // An item without a child should be the first item in the chain, so set
     131                 :     // the first item pointer, pointed to by aRoot, to the parent.
     132               0 :     NS_ASSERTION(this == *aRoot, "Unexpected - popup with no child not at end of chain");
     133               0 :     *aRoot = mParent;
     134               0 :     SetParent(nsnull);
     135                 :   }
     136               0 : }
     137                 : 
     138           19654 : NS_IMPL_ISUPPORTS3(nsXULPopupManager,
     139                 :                    nsIDOMEventListener,
     140                 :                    nsITimerCallback,
     141                 :                    nsIObserver)
     142                 : 
     143            1404 : nsXULPopupManager::nsXULPopupManager() :
     144                 :   mRangeOffset(0),
     145                 :   mCachedMousePoint(0, 0),
     146                 :   mCachedModifiers(0),
     147                 :   mActiveMenuBar(nsnull),
     148                 :   mPopups(nsnull),
     149                 :   mNoHidePanels(nsnull),
     150            1404 :   mTimerMenu(nsnull)
     151                 : {
     152            2808 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     153            1404 :   if (obs) {
     154            1404 :     obs->AddObserver(this, "xpcom-shutdown", false);
     155                 :   }
     156            1404 : }
     157                 : 
     158            2806 : nsXULPopupManager::~nsXULPopupManager() 
     159                 : {
     160            1403 :   NS_ASSERTION(!mPopups && !mNoHidePanels, "XUL popups still open");
     161            1403 : }
     162                 : 
     163                 : nsresult
     164            1404 : nsXULPopupManager::Init()
     165                 : {
     166            1404 :   sInstance = new nsXULPopupManager();
     167            1404 :   NS_ENSURE_TRUE(sInstance, NS_ERROR_OUT_OF_MEMORY);
     168            1404 :   NS_ADDREF(sInstance);
     169            1404 :   return NS_OK;
     170                 : }
     171                 : 
     172                 : void
     173            1403 : nsXULPopupManager::Shutdown()
     174                 : {
     175            1403 :   NS_IF_RELEASE(sInstance);
     176            1403 : }
     177                 : 
     178                 : NS_IMETHODIMP
     179            1404 : nsXULPopupManager::Observe(nsISupports *aSubject,
     180                 :                            const char *aTopic,
     181                 :                            const PRUnichar *aData)
     182                 : {
     183            1404 :   if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
     184            1404 :     if (mKeyListener) {
     185               0 :       mKeyListener->RemoveEventListener(NS_LITERAL_STRING("keypress"), this, true);
     186               0 :       mKeyListener->RemoveEventListener(NS_LITERAL_STRING("keydown"), this, true);
     187               0 :       mKeyListener->RemoveEventListener(NS_LITERAL_STRING("keyup"), this, true);
     188               0 :       mKeyListener = nsnull;
     189                 :     }
     190            1404 :     mRangeParent = nsnull;
     191                 :     // mOpeningPopup is cleared explicitly soon after using it.
     192            2808 :     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     193            1404 :     if (obs) {
     194            1404 :       obs->RemoveObserver(this, "xpcom-shutdown");
     195                 :     }
     196                 :   }
     197                 : 
     198            1404 :   return NS_OK;
     199                 : }
     200                 : 
     201                 : nsXULPopupManager*
     202               0 : nsXULPopupManager::GetInstance()
     203                 : {
     204               0 :   return sInstance;
     205                 : }
     206                 : 
     207                 : nsIContent*
     208               0 : nsXULPopupManager::Rollup(PRUint32 aCount, bool aGetLastRolledUp)
     209                 : {
     210               0 :   nsIContent* lastRolledUpPopup = nsnull;
     211                 : 
     212               0 :   nsMenuChainItem* item = GetTopVisibleMenu();
     213               0 :   if (item) {
     214               0 :     if (aGetLastRolledUp) {
     215                 :       // we need to get the popup that will be closed last, so that
     216                 :       // widget can keep track of it so it doesn't reopen if a mouse
     217                 :       // down event is going to processed.
     218                 :       // Keep going up the menu chain to get the first level menu. This will
     219                 :       // be the one that closes up last. It's possible that this menu doesn't
     220                 :       // end up closing because the popuphiding event was cancelled, but in
     221                 :       // that case we don't need to deal with the menu reopening as it will
     222                 :       // already still be open.
     223               0 :       nsMenuChainItem* first = item;
     224               0 :       while (first->GetParent())
     225               0 :         first = first->GetParent();
     226               0 :       lastRolledUpPopup = first->Content();
     227                 :     }
     228                 : 
     229                 :     // if a number of popups to close has been specified, determine the last
     230                 :     // popup to close
     231               0 :     nsIContent* lastPopup = nsnull;
     232               0 :     if (aCount != PR_UINT32_MAX) {
     233               0 :       nsMenuChainItem* last = item;
     234               0 :       while (--aCount && last->GetParent()) {
     235               0 :         last = last->GetParent();
     236                 :       }
     237               0 :       if (last) {
     238               0 :         lastPopup = last->Content();
     239                 :       }
     240                 :     }
     241                 : 
     242               0 :     HidePopup(item->Content(), true, true, false, lastPopup);
     243                 :   }
     244                 : 
     245               0 :   return lastRolledUpPopup;
     246                 : }
     247                 : 
     248                 : ////////////////////////////////////////////////////////////////////////
     249               0 : bool nsXULPopupManager::ShouldRollupOnMouseWheelEvent()
     250                 : {
     251                 :   // should rollup only for autocomplete widgets
     252                 :   // XXXndeakin this should really be something the popup has more control over
     253                 : 
     254               0 :   nsMenuChainItem* item = GetTopVisibleMenu();
     255               0 :   if (!item)
     256               0 :     return false;
     257                 : 
     258               0 :   nsIContent* content = item->Frame()->GetContent();
     259               0 :   if (!content)
     260               0 :     return false;
     261                 : 
     262               0 :   nsAutoString value;
     263               0 :   content->GetAttr(kNameSpaceID_None, nsGkAtoms::type, value);
     264               0 :   return StringBeginsWith(value, NS_LITERAL_STRING("autocomplete"));
     265                 : }
     266                 : 
     267                 : // a menu should not roll up if activated by a mouse activate message (eg. X-mouse)
     268               0 : bool nsXULPopupManager::ShouldRollupOnMouseActivate()
     269                 : {
     270               0 :   return false;
     271                 : }
     272                 : 
     273                 : PRUint32
     274               0 : nsXULPopupManager::GetSubmenuWidgetChain(nsTArray<nsIWidget*> *aWidgetChain)
     275                 : {
     276                 :   // this method is used by the widget code to determine the list of popups
     277                 :   // that are open. If a mouse click occurs outside one of these popups, the
     278                 :   // panels will roll up. If the click is inside a popup, they will not roll up
     279               0 :   PRUint32 count = 0, sameTypeCount = 0;
     280                 : 
     281               0 :   NS_ASSERTION(aWidgetChain, "null parameter");
     282               0 :   nsMenuChainItem* item = GetTopVisibleMenu();
     283               0 :   while (item) {
     284               0 :     nsCOMPtr<nsIWidget> widget = item->Frame()->GetWidget();
     285               0 :     NS_ASSERTION(widget, "open popup has no widget");
     286               0 :     aWidgetChain->AppendElement(widget.get());
     287                 :     // In the case when a menulist inside a panel is open, clicking in the
     288                 :     // panel should still roll up the menu, so if a different type is found,
     289                 :     // stop scanning.
     290               0 :     nsMenuChainItem* parent = item->GetParent();
     291               0 :     if (!sameTypeCount) {
     292               0 :       count++;
     293               0 :       if (!parent || item->Frame()->PopupType() != parent->Frame()->PopupType() ||
     294               0 :                      item->IsContextMenu() != parent->IsContextMenu()) {
     295               0 :         sameTypeCount = count;
     296                 :       }
     297                 :     }
     298               0 :     item = parent;
     299                 :   }
     300                 : 
     301               0 :   return sameTypeCount;
     302                 : }
     303                 : 
     304                 : void
     305               0 : nsXULPopupManager::AdjustPopupsOnWindowChange(nsPIDOMWindow* aWindow)
     306                 : {
     307                 :   // When the parent window is moved, adjust any child popups. Dismissable
     308                 :   // menus and panels are expected to roll up when a window is moved, so there
     309                 :   // is no need to check these popups, only the noautohide popups.
     310               0 :   nsMenuChainItem* item = mNoHidePanels;
     311               0 :   while (item) {
     312                 :     // only move popups that are within the same window and where auto
     313                 :     // positioning has not been disabled
     314               0 :     nsMenuPopupFrame* frame= item->Frame();
     315               0 :     if (frame->GetAutoPosition()) {
     316               0 :       nsIContent* popup = frame->GetContent();
     317               0 :       if (popup) {
     318               0 :         nsIDocument* document = popup->GetCurrentDoc();
     319               0 :         if (document) {
     320               0 :           nsPIDOMWindow* window = document->GetWindow();
     321               0 :           if (window) {
     322               0 :             window = window->GetPrivateRoot();
     323               0 :             if (window == aWindow) {
     324               0 :               frame->SetPopupPosition(nsnull, true);
     325                 :             }
     326                 :           }
     327                 :         }
     328                 :       }
     329                 :     }
     330                 : 
     331               0 :     item = item->GetParent();
     332                 :   }
     333               0 : }
     334                 : 
     335                 : static
     336               0 : nsMenuPopupFrame* GetPopupToMoveOrResize(nsIFrame* aFrame)
     337                 : {
     338               0 :   if (!aFrame || aFrame->GetType() != nsGkAtoms::menuPopupFrame)
     339               0 :     return nsnull;
     340                 : 
     341                 :   // no point moving or resizing hidden popups
     342               0 :   nsMenuPopupFrame* menuPopupFrame = static_cast<nsMenuPopupFrame *>(aFrame);
     343               0 :   if (menuPopupFrame->PopupState() != ePopupOpenAndVisible)
     344               0 :     return nsnull;
     345                 : 
     346               0 :   return menuPopupFrame;
     347                 : }
     348                 : 
     349                 : void
     350               0 : nsXULPopupManager::PopupMoved(nsIFrame* aFrame, nsIntPoint aPnt)
     351                 : {
     352               0 :   nsMenuPopupFrame* menuPopupFrame = GetPopupToMoveOrResize(aFrame);
     353               0 :   if (!menuPopupFrame)
     354               0 :     return;
     355                 : 
     356                 :   // Don't do anything if the popup is already at the specified location. This
     357                 :   // prevents recursive calls when a popup is positioned.
     358               0 :   nsIntPoint currentPnt = menuPopupFrame->ScreenPosition();
     359               0 :   nsIWidget* widget = menuPopupFrame->GetWidget();
     360               0 :   if ((aPnt.x != currentPnt.x || aPnt.y != currentPnt.y) || (widget &&
     361               0 :       widget->GetClientOffset() != menuPopupFrame->GetLastClientOffset())) {
     362                 :     // Update the popup's position using SetPopupPosition if the popup is
     363                 :     // anchored and at the parent level as these maintain their position
     364                 :     // relative to the parent window. Otherwise, just update the popup to
     365                 :     // the specified screen coordinates.
     366               0 :     if (menuPopupFrame->IsAnchored() &&
     367               0 :         menuPopupFrame->PopupLevel() == ePopupLevelParent) {
     368               0 :       menuPopupFrame->SetPopupPosition(nsnull, true);
     369                 :     }
     370                 :     else {
     371               0 :       menuPopupFrame->MoveTo(aPnt.x, aPnt.y, false);
     372                 :     }
     373                 :   }
     374                 : }
     375                 : 
     376                 : void
     377               0 : nsXULPopupManager::PopupResized(nsIFrame* aFrame, nsIntSize aSize)
     378                 : {
     379               0 :   nsMenuPopupFrame* menuPopupFrame = GetPopupToMoveOrResize(aFrame);
     380               0 :   if (!menuPopupFrame)
     381               0 :     return;
     382                 : 
     383               0 :   nsPresContext* presContext = menuPopupFrame->PresContext();
     384                 : 
     385               0 :   nsSize currentSize = menuPopupFrame->GetSize();
     386               0 :   if (aSize.width != presContext->AppUnitsToDevPixels(currentSize.width) ||
     387               0 :       aSize.height != presContext->AppUnitsToDevPixels(currentSize.height)) {
     388                 :     // for resizes, we just set the width and height attributes
     389               0 :     nsIContent* popup = menuPopupFrame->GetContent();
     390               0 :     nsAutoString width, height;
     391               0 :     width.AppendInt(aSize.width);
     392               0 :     height.AppendInt(aSize.height);
     393               0 :     popup->SetAttr(kNameSpaceID_None, nsGkAtoms::width, width, false);
     394               0 :     popup->SetAttr(kNameSpaceID_None, nsGkAtoms::height, height, true);
     395                 :   }
     396                 : }
     397                 : 
     398                 : nsIFrame*
     399               0 : nsXULPopupManager::GetFrameOfTypeForContent(nsIContent* aContent,
     400                 :                                             nsIAtom* aFrameType,
     401                 :                                             bool aShouldFlush)
     402                 : {
     403               0 :   if (aShouldFlush) {
     404               0 :     nsIDocument *document = aContent->GetCurrentDoc();
     405               0 :     if (document) {
     406               0 :       nsCOMPtr<nsIPresShell> presShell = document->GetShell();
     407               0 :       if (presShell)
     408               0 :         presShell->FlushPendingNotifications(Flush_Layout);
     409                 :     }
     410                 :   }
     411                 : 
     412               0 :   nsIFrame* frame = aContent->GetPrimaryFrame();
     413               0 :   return (frame && frame->GetType() == aFrameType) ? frame : nsnull;
     414                 : }
     415                 : 
     416                 : nsMenuFrame*
     417               0 : nsXULPopupManager::GetMenuFrameForContent(nsIContent* aContent)
     418                 : {
     419                 :   // as ShowMenu is called from frames, don't flush to be safe.
     420                 :   return static_cast<nsMenuFrame *>
     421               0 :                     (GetFrameOfTypeForContent(aContent, nsGkAtoms::menuFrame, false));
     422                 : }
     423                 : 
     424                 : nsMenuPopupFrame*
     425               0 : nsXULPopupManager::GetPopupFrameForContent(nsIContent* aContent, bool aShouldFlush)
     426                 : {
     427                 :   return static_cast<nsMenuPopupFrame *>
     428               0 :                     (GetFrameOfTypeForContent(aContent, nsGkAtoms::menuPopupFrame, aShouldFlush));
     429                 : }
     430                 : 
     431                 : nsMenuChainItem*
     432               0 : nsXULPopupManager::GetTopVisibleMenu()
     433                 : {
     434               0 :   nsMenuChainItem* item = mPopups;
     435               0 :   while (item && item->Frame()->PopupState() == ePopupInvisible)
     436               0 :     item = item->GetParent();
     437               0 :   return item;
     438                 : }
     439                 : 
     440                 : void
     441               0 : nsXULPopupManager::GetMouseLocation(nsIDOMNode** aNode, PRInt32* aOffset)
     442                 : {
     443               0 :   *aNode = mRangeParent;
     444               0 :   NS_IF_ADDREF(*aNode);
     445               0 :   *aOffset = mRangeOffset;
     446               0 : }
     447                 : 
     448                 : void
     449               0 : nsXULPopupManager::InitTriggerEvent(nsIDOMEvent* aEvent, nsIContent* aPopup,
     450                 :                                     nsIContent** aTriggerContent)
     451                 : {
     452               0 :   mCachedMousePoint = nsIntPoint(0, 0);
     453                 : 
     454               0 :   if (aTriggerContent) {
     455               0 :     *aTriggerContent = nsnull;
     456               0 :     if (aEvent) {
     457                 :       // get the trigger content from the event
     458               0 :       nsCOMPtr<nsIDOMEventTarget> target;
     459               0 :       aEvent->GetTarget(getter_AddRefs(target));
     460               0 :       if (target) {
     461               0 :         CallQueryInterface(target, aTriggerContent);
     462                 :       }
     463                 :     }
     464                 :   }
     465                 : 
     466               0 :   mCachedModifiers = 0;
     467                 : 
     468               0 :   nsCOMPtr<nsIDOMUIEvent> uiEvent = do_QueryInterface(aEvent);
     469               0 :   if (uiEvent) {
     470               0 :     uiEvent->GetRangeParent(getter_AddRefs(mRangeParent));
     471               0 :     uiEvent->GetRangeOffset(&mRangeOffset);
     472                 : 
     473                 :     // get the event coordinates relative to the root frame of the document
     474                 :     // containing the popup.
     475               0 :     nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(aEvent));
     476               0 :     if (privateEvent) {
     477               0 :       NS_ASSERTION(aPopup, "Expected a popup node");
     478                 :       nsEvent* event;
     479               0 :       event = privateEvent->GetInternalNSEvent();
     480               0 :       if (event) {
     481               0 :         if (event->eventStructType == NS_MOUSE_EVENT ||
     482                 :             event->eventStructType == NS_KEY_EVENT) {
     483               0 :           nsInputEvent* inputEvent = static_cast<nsInputEvent*>(event);
     484               0 :           if (inputEvent->isAlt) {
     485               0 :             mCachedModifiers |= FLAG_ALT;
     486                 :           }
     487               0 :           if (inputEvent->isControl) {
     488               0 :             mCachedModifiers |= FLAG_CONTROL;
     489                 :           }
     490               0 :           if (inputEvent->isShift) {
     491               0 :             mCachedModifiers |= FLAG_SHIFT;
     492                 :           }
     493               0 :           if (inputEvent->isMeta) {
     494               0 :             mCachedModifiers |= FLAG_META;
     495                 :           }
     496                 :         }
     497               0 :         nsIDocument* doc = aPopup->GetCurrentDoc();
     498               0 :         if (doc) {
     499               0 :           nsIPresShell* presShell = doc->GetShell();
     500                 :           nsPresContext* presContext;
     501               0 :           if (presShell && (presContext = presShell->GetPresContext())) {
     502                 :             nsPresContext* rootDocPresContext =
     503               0 :               presContext->GetRootPresContext();
     504               0 :             if (!rootDocPresContext)
     505                 :               return;
     506                 :             nsIFrame* rootDocumentRootFrame = rootDocPresContext->
     507               0 :                 PresShell()->FrameManager()->GetRootFrame();
     508               0 :             if ((event->eventStructType == NS_MOUSE_EVENT || 
     509                 :                  event->eventStructType == NS_MOUSE_SCROLL_EVENT) &&
     510               0 :                  !(static_cast<nsGUIEvent *>(event))->widget) {
     511                 :               // no widget, so just use the client point if available
     512               0 :               nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
     513               0 :               nsIntPoint clientPt;
     514               0 :               mouseEvent->GetClientX(&clientPt.x);
     515               0 :               mouseEvent->GetClientY(&clientPt.y);
     516                 : 
     517                 :               // XXX this doesn't handle IFRAMEs in transforms
     518               0 :               nsPoint thisDocToRootDocOffset = presShell->FrameManager()->
     519               0 :                 GetRootFrame()->GetOffsetToCrossDoc(rootDocumentRootFrame);
     520                 :               // convert to device pixels
     521                 :               mCachedMousePoint.x = presContext->AppUnitsToDevPixels(
     522               0 :                   nsPresContext::CSSPixelsToAppUnits(clientPt.x) + thisDocToRootDocOffset.x);
     523                 :               mCachedMousePoint.y = presContext->AppUnitsToDevPixels(
     524               0 :                   nsPresContext::CSSPixelsToAppUnits(clientPt.y) + thisDocToRootDocOffset.y);
     525                 :             }
     526               0 :             else if (rootDocumentRootFrame) {
     527                 :               nsPoint pnt =
     528               0 :                 nsLayoutUtils::GetEventCoordinatesRelativeTo(event, rootDocumentRootFrame);
     529                 :               mCachedMousePoint = nsIntPoint(rootDocPresContext->AppUnitsToDevPixels(pnt.x),
     530               0 :                                              rootDocPresContext->AppUnitsToDevPixels(pnt.y));
     531                 :             }
     532                 :           }
     533                 :         }
     534                 :       }
     535                 :     }
     536                 :   }
     537                 :   else {
     538               0 :     mRangeParent = nsnull;
     539               0 :     mRangeOffset = 0;
     540                 :   }
     541                 : }
     542                 : 
     543                 : void
     544               0 : nsXULPopupManager::SetActiveMenuBar(nsMenuBarFrame* aMenuBar, bool aActivate)
     545                 : {
     546               0 :   if (aActivate)
     547               0 :     mActiveMenuBar = aMenuBar;
     548               0 :   else if (mActiveMenuBar == aMenuBar)
     549               0 :     mActiveMenuBar = nsnull;
     550                 : 
     551               0 :   UpdateKeyboardListeners();
     552               0 : }
     553                 : 
     554                 : void
     555               0 : nsXULPopupManager::ShowMenu(nsIContent *aMenu,
     556                 :                             bool aSelectFirstItem,
     557                 :                             bool aAsynchronous)
     558                 : {
     559                 :   // generate any template content first. Otherwise, the menupopup may not
     560                 :   // have been created yet.
     561               0 :   if (aMenu) {
     562               0 :     nsIContent* element = aMenu;
     563               0 :     do {
     564               0 :       nsCOMPtr<nsIDOMXULElement> xulelem = do_QueryInterface(element);
     565               0 :       if (xulelem) {
     566               0 :         nsCOMPtr<nsIXULTemplateBuilder> builder;
     567               0 :         xulelem->GetBuilder(getter_AddRefs(builder));
     568               0 :         if (builder) {
     569               0 :           builder->CreateContents(aMenu, true);
     570                 :           break;
     571                 :         }
     572                 :       }
     573               0 :       element = element->GetParent();
     574                 :     } while (element);
     575                 :   }
     576                 : 
     577               0 :   nsMenuFrame* menuFrame = GetMenuFrameForContent(aMenu);
     578               0 :   if (!menuFrame || !menuFrame->IsMenu())
     579               0 :     return;
     580                 : 
     581               0 :   nsMenuPopupFrame* popupFrame =  menuFrame->GetPopup();
     582               0 :   if (!popupFrame || !MayShowPopup(popupFrame))
     583               0 :     return;
     584                 : 
     585                 :   // inherit whether or not we're a context menu from the parent
     586               0 :   bool parentIsContextMenu = false;
     587               0 :   bool onMenuBar = false;
     588               0 :   bool onmenu = menuFrame->IsOnMenu();
     589                 : 
     590               0 :   nsMenuParent* parent = menuFrame->GetMenuParent();
     591               0 :   if (parent && onmenu) {
     592               0 :     parentIsContextMenu = parent->IsContextMenu();
     593               0 :     onMenuBar = parent->IsMenuBar();
     594                 :   }
     595                 : 
     596               0 :   nsAutoString position;
     597               0 :   if (onMenuBar || !onmenu)
     598               0 :     position.AssignLiteral("after_start");
     599                 :   else
     600               0 :     position.AssignLiteral("end_before");
     601                 : 
     602                 :   // there is no trigger event for menus
     603               0 :   InitTriggerEvent(nsnull, nsnull, nsnull);
     604               0 :   popupFrame->InitializePopup(aMenu, nsnull, position, 0, 0, true);
     605                 : 
     606               0 :   if (aAsynchronous) {
     607                 :     nsCOMPtr<nsIRunnable> event =
     608               0 :       new nsXULPopupShowingEvent(popupFrame->GetContent(),
     609               0 :                                  parentIsContextMenu, aSelectFirstItem);
     610               0 :     NS_DispatchToCurrentThread(event);
     611                 :   }
     612                 :   else {
     613               0 :     nsCOMPtr<nsIContent> popupContent = popupFrame->GetContent();
     614               0 :     FirePopupShowingEvent(popupContent, parentIsContextMenu, aSelectFirstItem);
     615                 :   }
     616                 : }
     617                 : 
     618                 : void
     619               0 : nsXULPopupManager::ShowPopup(nsIContent* aPopup,
     620                 :                              nsIContent* aAnchorContent,
     621                 :                              const nsAString& aPosition,
     622                 :                              PRInt32 aXPos, PRInt32 aYPos,
     623                 :                              bool aIsContextMenu,
     624                 :                              bool aAttributesOverride,
     625                 :                              bool aSelectFirstItem,
     626                 :                              nsIDOMEvent* aTriggerEvent)
     627                 : {
     628               0 :   nsMenuPopupFrame* popupFrame = GetPopupFrameForContent(aPopup, true);
     629               0 :   if (!popupFrame || !MayShowPopup(popupFrame))
     630               0 :     return;
     631                 : 
     632               0 :   nsCOMPtr<nsIContent> triggerContent;
     633               0 :   InitTriggerEvent(aTriggerEvent, aPopup, getter_AddRefs(triggerContent));
     634                 : 
     635                 :   popupFrame->InitializePopup(aAnchorContent, triggerContent, aPosition,
     636               0 :                               aXPos, aYPos, aAttributesOverride);
     637                 : 
     638               0 :   FirePopupShowingEvent(aPopup, aIsContextMenu, aSelectFirstItem);
     639                 : }
     640                 : 
     641                 : void
     642               0 : nsXULPopupManager::ShowPopupAtScreen(nsIContent* aPopup,
     643                 :                                      PRInt32 aXPos, PRInt32 aYPos,
     644                 :                                      bool aIsContextMenu,
     645                 :                                      nsIDOMEvent* aTriggerEvent)
     646                 : {
     647               0 :   nsMenuPopupFrame* popupFrame = GetPopupFrameForContent(aPopup, true);
     648               0 :   if (!popupFrame || !MayShowPopup(popupFrame))
     649               0 :     return;
     650                 : 
     651               0 :   nsCOMPtr<nsIContent> triggerContent;
     652               0 :   InitTriggerEvent(aTriggerEvent, aPopup, getter_AddRefs(triggerContent));
     653                 : 
     654               0 :   popupFrame->InitializePopupAtScreen(triggerContent, aXPos, aYPos, aIsContextMenu);
     655               0 :   FirePopupShowingEvent(aPopup, aIsContextMenu, false);
     656                 : }
     657                 : 
     658                 : void
     659               0 : nsXULPopupManager::ShowTooltipAtScreen(nsIContent* aPopup,
     660                 :                                        nsIContent* aTriggerContent,
     661                 :                                        PRInt32 aXPos, PRInt32 aYPos)
     662                 : {
     663               0 :   nsMenuPopupFrame* popupFrame = GetPopupFrameForContent(aPopup, true);
     664               0 :   if (!popupFrame || !MayShowPopup(popupFrame))
     665               0 :     return;
     666                 : 
     667               0 :   InitTriggerEvent(nsnull, nsnull, nsnull);
     668                 : 
     669               0 :   mCachedMousePoint = nsIntPoint(aXPos, aYPos);
     670                 :   // coordinates are relative to the root widget
     671                 :   nsPresContext* rootPresContext =
     672               0 :     popupFrame->PresContext()->GetRootPresContext();
     673               0 :   if (rootPresContext) {
     674               0 :     nsCOMPtr<nsIWidget> widget;
     675               0 :     rootPresContext->PresShell()->GetViewManager()->
     676               0 :       GetRootWidget(getter_AddRefs(widget));
     677               0 :     mCachedMousePoint -= widget->WidgetToScreenOffset();
     678                 :   }
     679                 : 
     680               0 :   popupFrame->InitializePopupAtScreen(aTriggerContent, aXPos, aYPos, false);
     681                 : 
     682               0 :   FirePopupShowingEvent(aPopup, false, false);
     683                 : }
     684                 : 
     685                 : void
     686               0 : nsXULPopupManager::ShowPopupWithAnchorAlign(nsIContent* aPopup,
     687                 :                                             nsIContent* aAnchorContent,
     688                 :                                             nsAString& aAnchor,
     689                 :                                             nsAString& aAlign,
     690                 :                                             PRInt32 aXPos, PRInt32 aYPos,
     691                 :                                             bool aIsContextMenu)
     692                 : {
     693               0 :   nsMenuPopupFrame* popupFrame = GetPopupFrameForContent(aPopup, true);
     694               0 :   if (!popupFrame || !MayShowPopup(popupFrame))
     695               0 :     return;
     696                 : 
     697               0 :   InitTriggerEvent(nsnull, nsnull, nsnull);
     698                 : 
     699                 :   popupFrame->InitializePopupWithAnchorAlign(aAnchorContent, aAnchor,
     700               0 :                                              aAlign, aXPos, aYPos);
     701               0 :   FirePopupShowingEvent(aPopup, aIsContextMenu, false);
     702                 : }
     703                 : 
     704                 : static void
     705               0 : CheckCaretDrawingState() {
     706                 : 
     707                 :   // There is 1 caret per document, we need to find the focused
     708                 :   // document and erase its caret.
     709               0 :   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
     710               0 :   if (fm) {
     711               0 :     nsCOMPtr<nsIDOMWindow> window;
     712               0 :     fm->GetFocusedWindow(getter_AddRefs(window));
     713               0 :     if (!window)
     714                 :       return;
     715                 : 
     716               0 :     nsCOMPtr<nsIDOMDocument> domDoc;
     717               0 :     nsCOMPtr<nsIDocument> focusedDoc;
     718               0 :     window->GetDocument(getter_AddRefs(domDoc));
     719               0 :     focusedDoc = do_QueryInterface(domDoc);
     720               0 :     if (!focusedDoc)
     721                 :       return;
     722                 : 
     723               0 :     nsIPresShell* presShell = focusedDoc->GetShell();
     724               0 :     if (!presShell)
     725                 :       return;
     726                 : 
     727               0 :     nsRefPtr<nsCaret> caret = presShell->GetCaret();
     728               0 :     if (!caret)
     729                 :       return;
     730               0 :     caret->CheckCaretDrawingState();
     731                 :   }
     732                 : }
     733                 : 
     734                 : void
     735               0 : nsXULPopupManager::ShowPopupCallback(nsIContent* aPopup,
     736                 :                                      nsMenuPopupFrame* aPopupFrame,
     737                 :                                      bool aIsContextMenu,
     738                 :                                      bool aSelectFirstItem)
     739                 : {
     740               0 :   nsPopupType popupType = aPopupFrame->PopupType();
     741               0 :   bool ismenu = (popupType == ePopupTypeMenu);
     742                 : 
     743                 :   nsMenuChainItem* item =
     744               0 :     new nsMenuChainItem(aPopupFrame, aIsContextMenu, popupType);
     745               0 :   if (!item)
     746               0 :     return;
     747                 : 
     748                 :   // install keyboard event listeners for navigating menus. For panels, the
     749                 :   // escape key may be used to close the panel. However, the ignorekeys
     750                 :   // attribute may be used to disable adding these event listeners for popups
     751                 :   // that want to handle their own keyboard events.
     752               0 :   if (aPopup->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ignorekeys,
     753               0 :                            nsGkAtoms::_true, eCaseMatters))
     754               0 :     item->SetIgnoreKeys(true);
     755                 : 
     756               0 :   if (ismenu) {
     757                 :     // if the menu is on a menubar, use the menubar's listener instead
     758               0 :     nsMenuFrame* menuFrame = aPopupFrame->GetParentMenu();
     759               0 :     if (menuFrame) {
     760               0 :       item->SetOnMenuBar(menuFrame->IsOnMenuBar());
     761                 :     }
     762                 :   }
     763                 : 
     764                 :   // use a weak frame as the popup will set an open attribute if it is a menu
     765               0 :   nsWeakFrame weakFrame(aPopupFrame);
     766               0 :   aPopupFrame->ShowPopup(aIsContextMenu, aSelectFirstItem);
     767               0 :   ENSURE_TRUE(weakFrame.IsAlive());
     768                 : 
     769                 :   // popups normally hide when an outside click occurs. Panels may use
     770                 :   // the noautohide attribute to disable this behaviour. It is expected
     771                 :   // that the application will hide these popups manually. The tooltip
     772                 :   // listener will handle closing the tooltip also.
     773               0 :   if (aPopupFrame->IsNoAutoHide() || popupType == ePopupTypeTooltip) {
     774               0 :     item->SetParent(mNoHidePanels);
     775               0 :     mNoHidePanels = item;
     776                 :   }
     777                 :   else {
     778               0 :     nsIContent* oldmenu = nsnull;
     779               0 :     if (mPopups)
     780               0 :       oldmenu = mPopups->Content();
     781               0 :     item->SetParent(mPopups);
     782               0 :     mPopups = item;
     783               0 :     SetCaptureState(oldmenu);
     784                 :   }
     785                 : 
     786               0 :   if (aSelectFirstItem) {
     787               0 :     nsMenuFrame* next = GetNextMenuItem(aPopupFrame, nsnull, true);
     788               0 :     aPopupFrame->SetCurrentMenuItem(next);
     789                 :   }
     790                 : 
     791               0 :   if (ismenu)
     792               0 :     UpdateMenuItems(aPopup);
     793                 : 
     794                 :   // Caret visibility may have been affected, ensure that
     795                 :   // the caret isn't now drawn when it shouldn't be.
     796               0 :   CheckCaretDrawingState();
     797                 : }
     798                 : 
     799                 : void
     800               0 : nsXULPopupManager::HidePopup(nsIContent* aPopup,
     801                 :                              bool aHideChain,
     802                 :                              bool aDeselectMenu,
     803                 :                              bool aAsynchronous,
     804                 :                              nsIContent* aLastPopup)
     805                 : {
     806                 :   // if the popup is on the nohide panels list, remove it but don't close any
     807                 :   // other panels
     808               0 :   nsMenuPopupFrame* popupFrame = nsnull;
     809               0 :   bool foundPanel = false;
     810               0 :   nsMenuChainItem* item = mNoHidePanels;
     811               0 :   while (item) {
     812               0 :     if (item->Content() == aPopup) {
     813               0 :       foundPanel = true;
     814               0 :       popupFrame = item->Frame();
     815               0 :       break;
     816                 :     }
     817               0 :     item = item->GetParent();
     818                 :   }
     819                 : 
     820                 :   // when removing a menu, all of the child popups must be closed
     821               0 :   nsMenuChainItem* foundMenu = nsnull;
     822               0 :   item = mPopups;
     823               0 :   while (item) {
     824               0 :     if (item->Content() == aPopup) {
     825               0 :       foundMenu = item;
     826               0 :       break;
     827                 :     }
     828               0 :     item = item->GetParent();
     829                 :   }
     830                 : 
     831               0 :   nsPopupType type = ePopupTypePanel;
     832               0 :   bool deselectMenu = false;
     833               0 :   nsCOMPtr<nsIContent> popupToHide, nextPopup, lastPopup;
     834               0 :   if (foundMenu) {
     835                 :     // at this point, foundMenu will be set to the found item in the list. If
     836                 :     // foundMenu is the topmost menu, the one to remove, then there are no other
     837                 :     // popups to hide. If foundMenu is not the topmost menu, then there may be
     838                 :     // open submenus below it. In this case, we need to make sure that those
     839                 :     // submenus are closed up first. To do this, we scan up the menu list to
     840                 :     // find the topmost popup with only menus between it and foundMenu and
     841                 :     // close that menu first. In synchronous mode, the FirePopupHidingEvent
     842                 :     // method will be called which in turn calls HidePopupCallback to close up
     843                 :     // the next popup in the chain. These two methods will be called in
     844                 :     // sequence recursively to close up all the necessary popups. In
     845                 :     // asynchronous mode, a similar process occurs except that the
     846                 :     // FirePopupHidingEvent method is called asynchronously. In either case,
     847                 :     // nextPopup is set to the content node of the next popup to close, and
     848                 :     // lastPopup is set to the last popup in the chain to close, which will be
     849                 :     // aPopup, or null to close up all menus.
     850                 : 
     851               0 :     nsMenuChainItem* topMenu = foundMenu;
     852                 :     // Use IsMenu to ensure that foundMenu is a menu and scan down the child
     853                 :     // list until a non-menu is found. If foundMenu isn't a menu at all, don't
     854                 :     // scan and just close up this menu.
     855               0 :     if (foundMenu->IsMenu()) {
     856               0 :       item = topMenu->GetChild();
     857               0 :       while (item && item->IsMenu()) {
     858               0 :         topMenu = item;
     859               0 :         item = item->GetChild();
     860                 :       }
     861                 :     }
     862                 :     
     863               0 :     deselectMenu = aDeselectMenu;
     864               0 :     popupToHide = topMenu->Content();
     865               0 :     popupFrame = topMenu->Frame();
     866               0 :     type = popupFrame->PopupType();
     867                 : 
     868               0 :     nsMenuChainItem* parent = topMenu->GetParent();
     869                 : 
     870                 :     // close up another popup if there is one, and we are either hiding the
     871                 :     // entire chain or the item to hide isn't the topmost popup.
     872               0 :     if (parent && (aHideChain || topMenu != foundMenu))
     873               0 :       nextPopup = parent->Content();
     874                 : 
     875               0 :     lastPopup = aLastPopup ? aLastPopup : (aHideChain ? nsnull : aPopup);
     876                 :   }
     877               0 :   else if (foundPanel) {
     878               0 :     popupToHide = aPopup;
     879                 :   }
     880                 : 
     881               0 :   if (popupFrame) {
     882               0 :     nsPopupState state = popupFrame->PopupState();
     883                 :     // if the popup is already being hidden, don't attempt to hide it again
     884               0 :     if (state == ePopupHiding)
     885                 :       return;
     886                 :     // change the popup state to hiding. Don't set the hiding state if the
     887                 :     // popup is invisible, otherwise nsMenuPopupFrame::HidePopup will
     888                 :     // run again. In the invisible state, we just want the events to fire.
     889               0 :     if (state != ePopupInvisible)
     890               0 :       popupFrame->SetPopupState(ePopupHiding);
     891                 : 
     892                 :     // for menus, popupToHide is always the frontmost item in the list to hide.
     893               0 :     if (aAsynchronous) {
     894                 :       nsCOMPtr<nsIRunnable> event =
     895                 :         new nsXULPopupHidingEvent(popupToHide, nextPopup, lastPopup,
     896               0 :                                   type, deselectMenu);
     897               0 :         NS_DispatchToCurrentThread(event);
     898                 :     }
     899                 :     else {
     900                 :       FirePopupHidingEvent(popupToHide, nextPopup, lastPopup,
     901               0 :                            popupFrame->PresContext(), type, deselectMenu);
     902                 :     }
     903                 :   }
     904                 : }
     905                 : 
     906                 : void
     907               0 : nsXULPopupManager::HidePopupCallback(nsIContent* aPopup,
     908                 :                                      nsMenuPopupFrame* aPopupFrame,
     909                 :                                      nsIContent* aNextPopup,
     910                 :                                      nsIContent* aLastPopup,
     911                 :                                      nsPopupType aPopupType,
     912                 :                                      bool aDeselectMenu)
     913                 : {
     914               0 :   if (mCloseTimer && mTimerMenu == aPopupFrame) {
     915               0 :     mCloseTimer->Cancel();
     916               0 :     mCloseTimer = nsnull;
     917               0 :     mTimerMenu = nsnull;
     918                 :   }
     919                 : 
     920                 :   // The popup to hide is aPopup. Search the list again to find the item that
     921                 :   // corresponds to the popup to hide aPopup. This is done because it's
     922                 :   // possible someone added another item (attempted to open another popup)
     923                 :   // or removed a popup frame during the event processing so the item isn't at
     924                 :   // the front anymore.
     925               0 :   nsMenuChainItem* item = mNoHidePanels;
     926               0 :   while (item) {
     927               0 :     if (item->Content() == aPopup) {
     928               0 :       item->Detach(&mNoHidePanels);
     929               0 :       break;
     930                 :     }
     931               0 :     item = item->GetParent();
     932                 :   }
     933                 : 
     934               0 :   if (!item) {
     935               0 :     item = mPopups;
     936               0 :     while (item) {
     937               0 :       if (item->Content() == aPopup) {
     938               0 :         item->Detach(&mPopups);
     939               0 :         SetCaptureState(aPopup);
     940               0 :         break;
     941                 :       }
     942               0 :       item = item->GetParent();
     943                 :     }
     944                 :   }
     945                 : 
     946               0 :   delete item;
     947                 : 
     948               0 :   nsWeakFrame weakFrame(aPopupFrame);
     949               0 :   aPopupFrame->HidePopup(aDeselectMenu, ePopupClosed);
     950               0 :   ENSURE_TRUE(weakFrame.IsAlive());
     951                 : 
     952                 :   // send the popuphidden event synchronously. This event has no default behaviour.
     953               0 :   nsEventStatus status = nsEventStatus_eIgnore;
     954               0 :   nsMouseEvent event(true, NS_XUL_POPUP_HIDDEN, nsnull, nsMouseEvent::eReal);
     955                 :   nsEventDispatcher::Dispatch(aPopup, aPopupFrame->PresContext(),
     956               0 :                               &event, nsnull, &status);
     957                 : 
     958                 :   // if there are more popups to close, look for the next one
     959               0 :   if (aNextPopup && aPopup != aLastPopup) {
     960               0 :     nsMenuChainItem* foundMenu = nsnull;
     961               0 :     nsMenuChainItem* item = mPopups;
     962               0 :     while (item) {
     963               0 :       if (item->Content() == aNextPopup) {
     964               0 :         foundMenu = item;
     965               0 :         break;
     966                 :       }
     967               0 :       item = item->GetParent();
     968                 :     }
     969                 : 
     970                 :     // continue hiding the chain of popups until the last popup aLastPopup
     971                 :     // is reached, or until a popup of a different type is reached. This
     972                 :     // last check is needed so that a menulist inside a non-menu panel only
     973                 :     // closes the menu and not the panel as well.
     974               0 :     if (foundMenu &&
     975               0 :         (aLastPopup || aPopupType == foundMenu->PopupType())) {
     976                 : 
     977               0 :       nsCOMPtr<nsIContent> popupToHide = item->Content();
     978               0 :       nsMenuChainItem* parent = item->GetParent();
     979                 : 
     980               0 :       nsCOMPtr<nsIContent> nextPopup;
     981               0 :       if (parent && popupToHide != aLastPopup)
     982               0 :         nextPopup = parent->Content();
     983                 : 
     984               0 :       nsMenuPopupFrame* popupFrame = item->Frame();
     985               0 :       nsPopupState state = popupFrame->PopupState();
     986               0 :       if (state == ePopupHiding)
     987                 :         return;
     988               0 :       if (state != ePopupInvisible)
     989               0 :         popupFrame->SetPopupState(ePopupHiding);
     990                 : 
     991                 :       FirePopupHidingEvent(popupToHide, nextPopup, aLastPopup,
     992                 :                            popupFrame->PresContext(),
     993               0 :                            foundMenu->PopupType(), aDeselectMenu);
     994                 :     }
     995                 :   }
     996                 : }
     997                 : 
     998                 : void
     999               0 : nsXULPopupManager::HidePopup(nsIFrame* aFrame)
    1000                 : {
    1001               0 :   if (aFrame && aFrame->GetType() == nsGkAtoms::menuPopupFrame)
    1002               0 :     HidePopup(aFrame->GetContent(), false, true, false);
    1003               0 : }
    1004                 : 
    1005                 : void
    1006               0 : nsXULPopupManager::HidePopupAfterDelay(nsMenuPopupFrame* aPopup)
    1007                 : {
    1008                 :   // Don't close up immediately.
    1009                 :   // Kick off a close timer.
    1010               0 :   KillMenuTimer();
    1011                 : 
    1012                 :   PRInt32 menuDelay =
    1013               0 :     LookAndFeel::GetInt(LookAndFeel::eIntID_SubmenuDelay, 300); // ms
    1014                 : 
    1015                 :   // Kick off the timer.
    1016               0 :   mCloseTimer = do_CreateInstance("@mozilla.org/timer;1");
    1017               0 :   mCloseTimer->InitWithCallback(this, menuDelay, nsITimer::TYPE_ONE_SHOT);
    1018                 : 
    1019                 :   // the popup will call PopupDestroyed if it is destroyed, which checks if it
    1020                 :   // is set to mTimerMenu, so it should be safe to keep a reference to it
    1021               0 :   mTimerMenu = aPopup;
    1022               0 : }
    1023                 : 
    1024                 : void
    1025               0 : nsXULPopupManager::HidePopupsInList(const nsTArray<nsMenuPopupFrame *> &aFrames,
    1026                 :                                     bool aDeselectMenu)
    1027                 : {
    1028                 :   // Create a weak frame list. This is done in a separate array with the
    1029                 :   // right capacity predetermined, otherwise the array would get resized and
    1030                 :   // move the weak frame pointers around.
    1031               0 :   nsTArray<nsWeakFrame> weakPopups(aFrames.Length());
    1032                 :   PRUint32 f;
    1033               0 :   for (f = 0; f < aFrames.Length(); f++) {
    1034               0 :     nsWeakFrame* wframe = weakPopups.AppendElement();
    1035               0 :     if (wframe)
    1036               0 :       *wframe = aFrames[f];
    1037                 :   }
    1038                 : 
    1039               0 :   for (f = 0; f < weakPopups.Length(); f++) {
    1040                 :     // check to ensure that the frame is still alive before hiding it.
    1041               0 :     if (weakPopups[f].IsAlive()) {
    1042                 :       nsMenuPopupFrame* frame =
    1043               0 :         static_cast<nsMenuPopupFrame *>(weakPopups[f].GetFrame());
    1044               0 :       frame->HidePopup(true, ePopupInvisible);
    1045                 :     }
    1046                 :   }
    1047                 : 
    1048               0 :   SetCaptureState(nsnull);
    1049               0 : }
    1050                 : 
    1051                 : bool
    1052               0 : nsXULPopupManager::IsChildOfDocShell(nsIDocument* aDoc, nsIDocShellTreeItem* aExpected)
    1053                 : {
    1054               0 :   nsCOMPtr<nsISupports> doc = aDoc->GetContainer();
    1055               0 :   nsCOMPtr<nsIDocShellTreeItem> docShellItem(do_QueryInterface(doc));
    1056               0 :   while(docShellItem) {
    1057               0 :     if (docShellItem == aExpected)
    1058               0 :       return true;
    1059                 : 
    1060               0 :     nsCOMPtr<nsIDocShellTreeItem> parent;
    1061               0 :     docShellItem->GetParent(getter_AddRefs(parent));
    1062               0 :     docShellItem = parent;
    1063                 :   }
    1064                 : 
    1065               0 :   return false;
    1066                 : }
    1067                 : 
    1068                 : void
    1069               0 : nsXULPopupManager::HidePopupsInDocShell(nsIDocShellTreeItem* aDocShellToHide)
    1070                 : {
    1071               0 :   nsTArray<nsMenuPopupFrame *> popupsToHide;
    1072                 : 
    1073                 :   // iterate to get the set of popup frames to hide
    1074               0 :   nsMenuChainItem* item = mPopups;
    1075               0 :   while (item) {
    1076               0 :     nsMenuChainItem* parent = item->GetParent();
    1077               0 :     if (item->Frame()->PopupState() != ePopupInvisible &&
    1078               0 :         IsChildOfDocShell(item->Content()->OwnerDoc(), aDocShellToHide)) {
    1079               0 :       nsMenuPopupFrame* frame = item->Frame();
    1080               0 :       item->Detach(&mPopups);
    1081               0 :       delete item;
    1082               0 :       popupsToHide.AppendElement(frame);
    1083                 :     }
    1084               0 :     item = parent;
    1085                 :   }
    1086                 : 
    1087                 :   // now look for panels to hide
    1088               0 :   item = mNoHidePanels;
    1089               0 :   while (item) {
    1090               0 :     nsMenuChainItem* parent = item->GetParent();
    1091               0 :     if (item->Frame()->PopupState() != ePopupInvisible &&
    1092               0 :         IsChildOfDocShell(item->Content()->OwnerDoc(), aDocShellToHide)) {
    1093               0 :       nsMenuPopupFrame* frame = item->Frame();
    1094               0 :       item->Detach(&mNoHidePanels);
    1095               0 :       delete item;
    1096               0 :       popupsToHide.AppendElement(frame);
    1097                 :     }
    1098               0 :     item = parent;
    1099                 :   }
    1100                 : 
    1101               0 :   HidePopupsInList(popupsToHide, true);
    1102               0 : }
    1103                 : 
    1104                 : void
    1105               0 : nsXULPopupManager::ExecuteMenu(nsIContent* aMenu, nsXULMenuCommandEvent* aEvent)
    1106                 : {
    1107               0 :   CloseMenuMode cmm = CloseMenuMode_Auto;
    1108                 : 
    1109                 :   static nsIContent::AttrValuesArray strings[] =
    1110                 :     {&nsGkAtoms::none, &nsGkAtoms::single, nsnull};
    1111                 : 
    1112               0 :   switch (aMenu->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::closemenu,
    1113               0 :                                  strings, eCaseMatters)) {
    1114                 :     case 0:
    1115               0 :       cmm = CloseMenuMode_None;
    1116               0 :       break;
    1117                 :     case 1:
    1118               0 :       cmm = CloseMenuMode_Single;
    1119               0 :       break;
    1120                 :     default:
    1121               0 :       break;
    1122                 :   }
    1123                 : 
    1124                 :   // When a menuitem is selected to be executed, first hide all the open
    1125                 :   // popups, but don't remove them yet. This is needed when a menu command
    1126                 :   // opens a modal dialog. The views associated with the popups needed to be
    1127                 :   // hidden and the accesibility events fired before the command executes, but
    1128                 :   // the popuphiding/popuphidden events are fired afterwards.
    1129               0 :   nsTArray<nsMenuPopupFrame *> popupsToHide;
    1130               0 :   nsMenuChainItem* item = GetTopVisibleMenu();
    1131               0 :   if (cmm != CloseMenuMode_None) {
    1132               0 :     while (item) {
    1133                 :       // if it isn't a <menupopup>, don't close it automatically
    1134               0 :       if (!item->IsMenu())
    1135               0 :         break;
    1136               0 :       nsMenuChainItem* next = item->GetParent();
    1137               0 :       popupsToHide.AppendElement(item->Frame());
    1138               0 :       if (cmm == CloseMenuMode_Single) // only close one level of menu
    1139               0 :         break;
    1140               0 :       item = next;
    1141                 :     }
    1142                 : 
    1143                 :     // Now hide the popups. If the closemenu mode is auto, deselect the menu,
    1144                 :     // otherwise only one popup is closing, so keep the parent menu selected.
    1145               0 :     HidePopupsInList(popupsToHide, cmm == CloseMenuMode_Auto);
    1146                 :   }
    1147                 : 
    1148               0 :   aEvent->SetCloseMenuMode(cmm);
    1149               0 :   nsCOMPtr<nsIRunnable> event = aEvent;
    1150               0 :   NS_DispatchToCurrentThread(event);
    1151               0 : }
    1152                 : 
    1153                 : void
    1154               0 : nsXULPopupManager::FirePopupShowingEvent(nsIContent* aPopup,
    1155                 :                                          bool aIsContextMenu,
    1156                 :                                          bool aSelectFirstItem)
    1157                 : {
    1158               0 :   nsCOMPtr<nsIContent> popup = aPopup; // keep a strong reference to the popup
    1159                 : 
    1160               0 :   nsIFrame* frame = aPopup->GetPrimaryFrame();
    1161               0 :   if (!frame || frame->GetType() != nsGkAtoms::menuPopupFrame)
    1162                 :     return;
    1163                 : 
    1164               0 :   nsMenuPopupFrame* popupFrame = static_cast<nsMenuPopupFrame *>(frame);
    1165               0 :   nsPresContext *presContext = popupFrame->PresContext();
    1166               0 :   nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
    1167               0 :   nsPopupType popupType = popupFrame->PopupType();
    1168                 : 
    1169                 :   // generate the child frames if they have not already been generated
    1170               0 :   if (!popupFrame->HasGeneratedChildren()) {
    1171               0 :     popupFrame->SetGeneratedChildren();
    1172               0 :     presShell->FrameConstructor()->GenerateChildFrames(popupFrame);
    1173                 :   }
    1174                 : 
    1175                 :   // get the frame again
    1176               0 :   frame = aPopup->GetPrimaryFrame();
    1177               0 :   if (!frame)
    1178                 :     return;
    1179                 : 
    1180               0 :   presShell->FrameNeedsReflow(frame, nsIPresShell::eTreeChange,
    1181               0 :                               NS_FRAME_HAS_DIRTY_CHILDREN);
    1182                 : 
    1183                 :   // cache the popup so that document.popupNode can retrieve the trigger node
    1184                 :   // during the popupshowing event. It will be cleared below after the event
    1185                 :   // has fired.
    1186               0 :   mOpeningPopup = aPopup;
    1187                 : 
    1188               0 :   nsEventStatus status = nsEventStatus_eIgnore;
    1189               0 :   nsMouseEvent event(true, NS_XUL_POPUP_SHOWING, nsnull, nsMouseEvent::eReal);
    1190                 : 
    1191                 :   // coordinates are relative to the root widget
    1192                 :   nsPresContext* rootPresContext =
    1193               0 :     presShell->GetPresContext()->GetRootPresContext();
    1194               0 :   if (rootPresContext) {
    1195               0 :     rootPresContext->PresShell()->GetViewManager()->
    1196               0 :       GetRootWidget(getter_AddRefs(event.widget));
    1197                 :   }
    1198                 :   else {
    1199               0 :     event.widget = nsnull;
    1200                 :   }
    1201                 : 
    1202               0 :   event.refPoint = mCachedMousePoint;
    1203                 : 
    1204               0 :   event.isAlt = !!(mCachedModifiers & FLAG_ALT);
    1205               0 :   event.isControl = !!(mCachedModifiers & FLAG_CONTROL);
    1206               0 :   event.isShift = !!(mCachedModifiers & FLAG_SHIFT);
    1207               0 :   event.isMeta = !!(mCachedModifiers & FLAG_META);
    1208                 : 
    1209               0 :   nsEventDispatcher::Dispatch(popup, presContext, &event, nsnull, &status);
    1210                 : 
    1211               0 :   mCachedMousePoint = nsIntPoint(0, 0);
    1212               0 :   mOpeningPopup = nsnull;
    1213                 : 
    1214               0 :   mCachedModifiers = 0;
    1215                 : 
    1216                 :   // if a panel, blur whatever has focus so that the panel can take the focus.
    1217                 :   // This is done after the popupshowing event in case that event is cancelled.
    1218                 :   // Using noautofocus="true" will disable this behaviour, which is needed for
    1219                 :   // the autocomplete widget as it manages focus itself.
    1220               0 :   if (popupType == ePopupTypePanel &&
    1221               0 :       !popup->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautofocus,
    1222               0 :                            nsGkAtoms::_true, eCaseMatters)) {
    1223               0 :     nsIFocusManager* fm = nsFocusManager::GetFocusManager();
    1224               0 :     if (fm) {
    1225               0 :       nsIDocument* doc = popup->GetCurrentDoc();
    1226                 : 
    1227                 :       // Only remove the focus if the currently focused item is ouside the
    1228                 :       // popup. It isn't a big deal if the current focus is in a child popup
    1229                 :       // inside the popup as that shouldn't be visible. This check ensures that
    1230                 :       // a node inside the popup that is focused during a popupshowing event
    1231                 :       // remains focused.
    1232               0 :       nsCOMPtr<nsIDOMElement> currentFocusElement;
    1233               0 :       fm->GetFocusedElement(getter_AddRefs(currentFocusElement));
    1234               0 :       nsCOMPtr<nsIContent> currentFocus = do_QueryInterface(currentFocusElement);
    1235               0 :       if (doc && currentFocus &&
    1236               0 :           !nsContentUtils::ContentIsCrossDocDescendantOf(currentFocus, popup)) {
    1237               0 :         fm->ClearFocus(doc->GetWindow());
    1238                 :       }
    1239                 :     }
    1240                 :   }
    1241                 : 
    1242                 :   // clear these as they are no longer valid
    1243               0 :   mRangeParent = nsnull;
    1244               0 :   mRangeOffset = 0;
    1245                 : 
    1246                 :   // get the frame again in case it went away
    1247               0 :   frame = aPopup->GetPrimaryFrame();
    1248               0 :   if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) {
    1249               0 :     nsMenuPopupFrame* popupFrame = static_cast<nsMenuPopupFrame *>(frame);
    1250                 : 
    1251                 :     // if the event was cancelled, don't open the popup, reset its state back
    1252                 :     // to closed and clear its trigger content.
    1253               0 :     if (status == nsEventStatus_eConsumeNoDefault) {
    1254               0 :       popupFrame->SetPopupState(ePopupClosed);
    1255               0 :       popupFrame->ClearTriggerContent();
    1256                 :     }
    1257                 :     else {
    1258               0 :       ShowPopupCallback(aPopup, popupFrame, aIsContextMenu, aSelectFirstItem);
    1259                 :     }
    1260                 :   }
    1261                 : }
    1262                 : 
    1263                 : void
    1264               0 : nsXULPopupManager::FirePopupHidingEvent(nsIContent* aPopup,
    1265                 :                                         nsIContent* aNextPopup,
    1266                 :                                         nsIContent* aLastPopup,
    1267                 :                                         nsPresContext *aPresContext,
    1268                 :                                         nsPopupType aPopupType,
    1269                 :                                         bool aDeselectMenu)
    1270                 : {
    1271               0 :   nsCOMPtr<nsIPresShell> presShell = aPresContext->PresShell();
    1272                 : 
    1273               0 :   nsEventStatus status = nsEventStatus_eIgnore;
    1274               0 :   nsMouseEvent event(true, NS_XUL_POPUP_HIDING, nsnull, nsMouseEvent::eReal);
    1275               0 :   nsEventDispatcher::Dispatch(aPopup, aPresContext, &event, nsnull, &status);
    1276                 : 
    1277                 :   // when a panel is closed, blur whatever has focus inside the popup
    1278               0 :   if (aPopupType == ePopupTypePanel &&
    1279                 :       !aPopup->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautofocus,
    1280               0 :                            nsGkAtoms::_true, eCaseMatters)) {
    1281               0 :     nsIFocusManager* fm = nsFocusManager::GetFocusManager();
    1282               0 :     if (fm) {
    1283               0 :       nsIDocument* doc = aPopup->GetCurrentDoc();
    1284                 : 
    1285                 :       // Remove the focus from the focused node only if it is inside the popup.
    1286               0 :       nsCOMPtr<nsIDOMElement> currentFocusElement;
    1287               0 :       fm->GetFocusedElement(getter_AddRefs(currentFocusElement));
    1288               0 :       nsCOMPtr<nsIContent> currentFocus = do_QueryInterface(currentFocusElement);
    1289               0 :       if (doc && currentFocus &&
    1290               0 :           nsContentUtils::ContentIsCrossDocDescendantOf(currentFocus, aPopup)) {
    1291               0 :         fm->ClearFocus(doc->GetWindow());
    1292                 :       }
    1293                 :     }
    1294                 :   }
    1295                 : 
    1296                 :   // get frame again in case it went away
    1297               0 :   nsIFrame* frame = aPopup->GetPrimaryFrame();
    1298               0 :   if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) {
    1299               0 :     nsMenuPopupFrame* popupFrame = static_cast<nsMenuPopupFrame *>(frame);
    1300                 : 
    1301                 :     // if the event was cancelled, don't hide the popup, and reset its
    1302                 :     // state back to open. Only popups in chrome shells can prevent a popup
    1303                 :     // from hiding.
    1304               0 :     if (status == nsEventStatus_eConsumeNoDefault &&
    1305               0 :         !popupFrame->IsInContentShell()) {
    1306               0 :       popupFrame->SetPopupState(ePopupOpenAndVisible);
    1307                 :     }
    1308                 :     else {
    1309                 :       HidePopupCallback(aPopup, popupFrame, aNextPopup, aLastPopup,
    1310               0 :                         aPopupType, aDeselectMenu);
    1311                 :     }
    1312                 :   }
    1313               0 : }
    1314                 : 
    1315                 : bool
    1316               0 : nsXULPopupManager::IsPopupOpen(nsIContent* aPopup)
    1317                 : {
    1318                 :   // a popup is open if it is in the open list. The assertions ensure that the
    1319                 :   // frame is in the correct state. If the popup is in the hiding or invisible
    1320                 :   // state, it will still be in the open popup list until it is closed.
    1321               0 :   nsMenuChainItem* item = mPopups;
    1322               0 :   while (item) {
    1323               0 :     if (item->Content() == aPopup) {
    1324               0 :       NS_ASSERTION(item->Frame()->IsOpen() ||
    1325                 :                    item->Frame()->PopupState() == ePopupHiding ||
    1326                 :                    item->Frame()->PopupState() == ePopupInvisible,
    1327                 :                    "popup in open list not actually open");
    1328               0 :       return true;
    1329                 :     }
    1330               0 :     item = item->GetParent();
    1331                 :   }
    1332                 : 
    1333               0 :   item = mNoHidePanels;
    1334               0 :   while (item) {
    1335               0 :     if (item->Content() == aPopup) {
    1336               0 :       NS_ASSERTION(item->Frame()->IsOpen() ||
    1337                 :                    item->Frame()->PopupState() == ePopupHiding ||
    1338                 :                    item->Frame()->PopupState() == ePopupInvisible,
    1339                 :                    "popup in open list not actually open");
    1340               0 :       return true;
    1341                 :     }
    1342               0 :     item = item->GetParent();
    1343                 :   }
    1344                 : 
    1345               0 :   return false;
    1346                 : }
    1347                 : 
    1348                 : bool
    1349               0 : nsXULPopupManager::IsPopupOpenForMenuParent(nsMenuParent* aMenuParent)
    1350                 : {
    1351               0 :   nsMenuChainItem* item = GetTopVisibleMenu();
    1352               0 :   while (item) {
    1353               0 :     nsMenuPopupFrame* popup = item->Frame();
    1354               0 :     if (popup && popup->IsOpen()) {
    1355               0 :       nsMenuFrame* menuFrame = popup->GetParentMenu();
    1356               0 :       if (menuFrame && menuFrame->GetMenuParent() == aMenuParent) {
    1357               0 :         return true;
    1358                 :       }
    1359                 :     }
    1360               0 :     item = item->GetParent();
    1361                 :   }
    1362                 : 
    1363               0 :   return false;
    1364                 : }
    1365                 : 
    1366                 : nsIFrame*
    1367               0 : nsXULPopupManager::GetTopPopup(nsPopupType aType)
    1368                 : {
    1369               0 :   if ((aType == ePopupTypePanel || aType == ePopupTypeTooltip) && mNoHidePanels)
    1370               0 :     return mNoHidePanels->Frame();
    1371                 : 
    1372               0 :   nsMenuChainItem* item = GetTopVisibleMenu();
    1373               0 :   while (item) {
    1374               0 :     if (item->PopupType() == aType || aType == ePopupTypeAny)
    1375               0 :       return item->Frame();
    1376               0 :     item = item->GetParent();
    1377                 :   }
    1378                 : 
    1379               0 :   return nsnull;
    1380                 : }
    1381                 : 
    1382                 : nsTArray<nsIFrame *>
    1383               0 : nsXULPopupManager::GetVisiblePopups()
    1384                 : {
    1385               0 :   nsTArray<nsIFrame *> popups;
    1386                 : 
    1387               0 :   nsMenuChainItem* item = mPopups;
    1388               0 :   while (item) {
    1389               0 :     if (item->Frame()->PopupState() == ePopupOpenAndVisible)
    1390               0 :       popups.AppendElement(static_cast<nsIFrame*>(item->Frame()));
    1391               0 :     item = item->GetParent();
    1392                 :   }
    1393                 : 
    1394               0 :   item = mNoHidePanels;
    1395               0 :   while (item) {
    1396                 :     // skip panels which are not open and visible as well as draggable popups,
    1397                 :     // as those don't respond to events.
    1398               0 :     if (item->Frame()->PopupState() == ePopupOpenAndVisible && !item->Frame()->IsDragPopup()) {
    1399               0 :       popups.AppendElement(static_cast<nsIFrame*>(item->Frame()));
    1400                 :     }
    1401               0 :     item = item->GetParent();
    1402                 :   }
    1403                 : 
    1404                 :   return popups;
    1405                 : }
    1406                 : 
    1407                 : already_AddRefed<nsIDOMNode>
    1408               0 : nsXULPopupManager::GetLastTriggerNode(nsIDocument* aDocument, bool aIsTooltip)
    1409                 : {
    1410               0 :   if (!aDocument)
    1411               0 :     return nsnull;
    1412                 : 
    1413               0 :   nsCOMPtr<nsIDOMNode> node;
    1414                 : 
    1415                 :   // if mOpeningPopup is set, it means that a popupshowing event is being
    1416                 :   // fired. In this case, just use the cached node, as the popup is not yet in
    1417                 :   // the list of open popups.
    1418               0 :   if (mOpeningPopup && mOpeningPopup->GetCurrentDoc() == aDocument &&
    1419               0 :       aIsTooltip == (mOpeningPopup->Tag() == nsGkAtoms::tooltip)) {
    1420               0 :     node = do_QueryInterface(nsMenuPopupFrame::GetTriggerContent(GetPopupFrameForContent(mOpeningPopup, false)));
    1421                 :   }
    1422                 :   else {
    1423               0 :     nsMenuChainItem* item = aIsTooltip ? mNoHidePanels : mPopups;
    1424               0 :     while (item) {
    1425                 :       // look for a popup of the same type and document.
    1426               0 :       if ((item->PopupType() == ePopupTypeTooltip) == aIsTooltip &&
    1427               0 :           item->Content()->GetCurrentDoc() == aDocument) {
    1428               0 :         node = do_QueryInterface(nsMenuPopupFrame::GetTriggerContent(item->Frame()));
    1429               0 :         if (node)
    1430               0 :           break;
    1431                 :       }
    1432               0 :       item = item->GetParent();
    1433                 :     }
    1434                 :   }
    1435                 : 
    1436               0 :   return node.forget();
    1437                 : }
    1438                 : 
    1439                 : bool
    1440               0 : nsXULPopupManager::MayShowPopup(nsMenuPopupFrame* aPopup)
    1441                 : {
    1442                 :   // if a popup's IsOpen method returns true, then the popup must always be in
    1443                 :   // the popup chain scanned in IsPopupOpen.
    1444               0 :   NS_ASSERTION(!aPopup->IsOpen() || IsPopupOpen(aPopup->GetContent()),
    1445                 :                "popup frame state doesn't match XULPopupManager open state");
    1446                 : 
    1447               0 :   nsPopupState state = aPopup->PopupState();
    1448                 : 
    1449                 :   // if the popup is not in the open popup chain, then it must have a state that
    1450                 :   // is either closed, in the process of being shown, or invisible.
    1451               0 :   NS_ASSERTION(IsPopupOpen(aPopup->GetContent()) || state == ePopupClosed ||
    1452                 :                state == ePopupShowing || state == ePopupInvisible,
    1453                 :                "popup not in XULPopupManager open list is open");
    1454                 : 
    1455                 :   // don't show popups unless they are closed or invisible
    1456               0 :   if (state != ePopupClosed && state != ePopupInvisible)
    1457               0 :     return false;
    1458                 : 
    1459                 :   // Don't show popups that we already have in our popup chain
    1460               0 :   if (IsPopupOpen(aPopup->GetContent())) {
    1461               0 :     NS_WARNING("Refusing to show duplicate popup");
    1462               0 :     return false;
    1463                 :   }
    1464                 : 
    1465                 :   // if the popup was just rolled up, don't reopen it
    1466               0 :   nsCOMPtr<nsIWidget> widget = aPopup->GetWidget();
    1467               0 :   if (widget && widget->GetLastRollup() == aPopup->GetContent())
    1468               0 :       return false;
    1469                 : 
    1470               0 :   nsCOMPtr<nsISupports> cont = aPopup->PresContext()->GetContainer();
    1471               0 :   nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(cont);
    1472               0 :   nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(dsti);
    1473               0 :   if (!baseWin)
    1474               0 :     return false;
    1475                 : 
    1476               0 :   PRInt32 type = -1;
    1477               0 :   if (NS_FAILED(dsti->GetItemType(&type)))
    1478               0 :     return false;
    1479                 : 
    1480                 :   // chrome shells can always open popups, but other types of shells can only
    1481                 :   // open popups when they are focused and visible
    1482               0 :   if (type != nsIDocShellTreeItem::typeChrome) {
    1483                 :     // only allow popups in active windows
    1484               0 :     nsCOMPtr<nsIDocShellTreeItem> root;
    1485               0 :     dsti->GetRootTreeItem(getter_AddRefs(root));
    1486               0 :     nsCOMPtr<nsIDOMWindow> rootWin = do_GetInterface(root);
    1487                 : 
    1488               0 :     nsIFocusManager* fm = nsFocusManager::GetFocusManager();
    1489               0 :     if (!fm || !rootWin)
    1490               0 :       return false;
    1491                 : 
    1492               0 :     nsCOMPtr<nsIDOMWindow> activeWindow;
    1493               0 :     fm->GetActiveWindow(getter_AddRefs(activeWindow));
    1494               0 :     if (activeWindow != rootWin)
    1495               0 :       return false;
    1496                 : 
    1497                 :     // only allow popups in visible frames
    1498                 :     bool visible;
    1499               0 :     baseWin->GetVisibility(&visible);
    1500               0 :     if (!visible)
    1501               0 :       return false;
    1502                 :   }
    1503                 : 
    1504                 :   // platforms respond differently when an popup is opened in a minimized
    1505                 :   // window, so this is always disabled.
    1506               0 :   nsCOMPtr<nsIWidget> mainWidget;
    1507               0 :   baseWin->GetMainWidget(getter_AddRefs(mainWidget));
    1508               0 :   if (mainWidget) {
    1509                 :     PRInt32 sizeMode;
    1510               0 :     mainWidget->GetSizeMode(&sizeMode);
    1511               0 :     if (sizeMode == nsSizeMode_Minimized)
    1512               0 :       return false;
    1513                 :   }
    1514                 : 
    1515                 :   // cannot open a popup that is a submenu of a menupopup that isn't open.
    1516               0 :   nsMenuFrame* menuFrame = aPopup->GetParentMenu();
    1517               0 :   if (menuFrame) {
    1518               0 :     nsMenuParent* parentPopup = menuFrame->GetMenuParent();
    1519               0 :     if (parentPopup && !parentPopup->IsOpen())
    1520               0 :       return false;
    1521                 :   }
    1522                 : 
    1523               0 :   return true;
    1524                 : }
    1525                 : 
    1526                 : void
    1527               0 : nsXULPopupManager::PopupDestroyed(nsMenuPopupFrame* aPopup)
    1528                 : {
    1529                 :   // when a popup frame is destroyed, just unhook it from the list of popups
    1530               0 :   if (mTimerMenu == aPopup) {
    1531               0 :     if (mCloseTimer) {
    1532               0 :       mCloseTimer->Cancel();
    1533               0 :       mCloseTimer = nsnull;
    1534                 :     }
    1535               0 :     mTimerMenu = nsnull;
    1536                 :   }
    1537                 : 
    1538               0 :   nsMenuChainItem* item = mNoHidePanels;
    1539               0 :   while (item) {
    1540               0 :     if (item->Frame() == aPopup) {
    1541               0 :       item->Detach(&mNoHidePanels);
    1542               0 :       delete item;
    1543               0 :       break;
    1544                 :     }
    1545               0 :     item = item->GetParent();
    1546                 :   }
    1547                 : 
    1548               0 :   nsTArray<nsMenuPopupFrame *> popupsToHide;
    1549                 : 
    1550               0 :   item = mPopups;
    1551               0 :   while (item) {
    1552               0 :     nsMenuPopupFrame* frame = item->Frame();
    1553               0 :     if (frame == aPopup) {
    1554               0 :       if (frame->PopupState() != ePopupInvisible) {
    1555                 :         // Iterate through any child menus and hide them as well, since the
    1556                 :         // parent is going away. We won't remove them from the list yet, just
    1557                 :         // hide them, as they will be removed from the list when this function
    1558                 :         // gets called for that child frame.
    1559               0 :         nsMenuChainItem* child = item->GetChild();
    1560               0 :         while (child) {
    1561                 :           // if the popup is a child frame of the menu that was destroyed, add
    1562                 :           // it to the list of popups to hide. Don't bother with the events
    1563                 :           // since the frames are going away. If the child menu is not a child
    1564                 :           // frame, for example, a context menu, use HidePopup instead, but call
    1565                 :           // it asynchronously since we are in the middle of frame destruction.
    1566               0 :           nsMenuPopupFrame* childframe = child->Frame();
    1567               0 :           if (nsLayoutUtils::IsProperAncestorFrame(frame, childframe)) {
    1568               0 :             popupsToHide.AppendElement(childframe);
    1569                 :           }
    1570                 :           else {
    1571                 :             // HidePopup will take care of hiding any of its children, so
    1572                 :             // break out afterwards
    1573               0 :             HidePopup(child->Content(), false, false, true);
    1574               0 :             break;
    1575                 :           }
    1576                 : 
    1577               0 :           child = child->GetChild();
    1578                 :         }
    1579                 :       }
    1580                 : 
    1581               0 :       item->Detach(&mPopups);
    1582               0 :       delete item;
    1583               0 :       break;
    1584                 :     }
    1585                 : 
    1586               0 :     item = item->GetParent();
    1587                 :   }
    1588                 : 
    1589               0 :   HidePopupsInList(popupsToHide, false);
    1590               0 : }
    1591                 : 
    1592                 : bool
    1593               0 : nsXULPopupManager::HasContextMenu(nsMenuPopupFrame* aPopup)
    1594                 : {
    1595               0 :   nsMenuChainItem* item = GetTopVisibleMenu();
    1596               0 :   while (item && item->Frame() != aPopup) {
    1597               0 :     if (item->IsContextMenu())
    1598               0 :       return true;
    1599               0 :     item = item->GetParent();
    1600                 :   }
    1601                 : 
    1602               0 :   return false;
    1603                 : }
    1604                 : 
    1605                 : void
    1606               0 : nsXULPopupManager::SetCaptureState(nsIContent* aOldPopup)
    1607                 : {
    1608               0 :   nsMenuChainItem* item = GetTopVisibleMenu();
    1609               0 :   if (item && aOldPopup == item->Content())
    1610               0 :     return;
    1611                 : 
    1612               0 :   if (mWidget) {
    1613               0 :     mWidget->CaptureRollupEvents(this, false, false);
    1614               0 :     mWidget = nsnull;
    1615                 :   }
    1616                 : 
    1617               0 :   if (item) {
    1618               0 :     nsMenuPopupFrame* popup = item->Frame();
    1619               0 :     mWidget = popup->GetWidget();
    1620               0 :     if (mWidget) {
    1621               0 :       mWidget->CaptureRollupEvents(this, true, popup->ConsumeOutsideClicks());
    1622               0 :       popup->AttachedDismissalListener();
    1623                 :     }
    1624                 :   }
    1625                 : 
    1626               0 :   UpdateKeyboardListeners();
    1627                 : }
    1628                 : 
    1629                 : void
    1630               0 : nsXULPopupManager::UpdateKeyboardListeners()
    1631                 : {
    1632               0 :   nsCOMPtr<nsIDOMEventTarget> newTarget;
    1633               0 :   bool isForMenu = false;
    1634               0 :   nsMenuChainItem* item = GetTopVisibleMenu();
    1635               0 :   if (item) {
    1636               0 :     if (!item->IgnoreKeys())
    1637               0 :       newTarget = do_QueryInterface(item->Content()->GetDocument());
    1638               0 :     isForMenu = item->PopupType() == ePopupTypeMenu;
    1639                 :   }
    1640               0 :   else if (mActiveMenuBar) {
    1641               0 :     newTarget = do_QueryInterface(mActiveMenuBar->GetContent()->GetDocument());
    1642               0 :     isForMenu = true;
    1643                 :   }
    1644                 : 
    1645               0 :   if (mKeyListener != newTarget) {
    1646               0 :     if (mKeyListener) {
    1647               0 :       mKeyListener->RemoveEventListener(NS_LITERAL_STRING("keypress"), this, true);
    1648               0 :       mKeyListener->RemoveEventListener(NS_LITERAL_STRING("keydown"), this, true);
    1649               0 :       mKeyListener->RemoveEventListener(NS_LITERAL_STRING("keyup"), this, true);
    1650               0 :       mKeyListener = nsnull;
    1651               0 :       nsContentUtils::NotifyInstalledMenuKeyboardListener(false);
    1652                 :     }
    1653                 : 
    1654               0 :     if (newTarget) {
    1655               0 :       newTarget->AddEventListener(NS_LITERAL_STRING("keypress"), this, true);
    1656               0 :       newTarget->AddEventListener(NS_LITERAL_STRING("keydown"), this, true);
    1657               0 :       newTarget->AddEventListener(NS_LITERAL_STRING("keyup"), this, true);
    1658               0 :       nsContentUtils::NotifyInstalledMenuKeyboardListener(isForMenu);
    1659               0 :       mKeyListener = newTarget;
    1660                 :     }
    1661                 :   }
    1662               0 : }
    1663                 : 
    1664                 : void
    1665               0 : nsXULPopupManager::UpdateMenuItems(nsIContent* aPopup)
    1666                 : {
    1667                 :   // Walk all of the menu's children, checking to see if any of them has a
    1668                 :   // command attribute. If so, then several attributes must potentially be updated.
    1669                 :  
    1670               0 :   nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(aPopup->GetDocument()));
    1671               0 :   for (nsCOMPtr<nsIContent> grandChild = aPopup->GetFirstChild();
    1672               0 :        grandChild;
    1673               0 :        grandChild = grandChild->GetNextSibling()) {
    1674               0 :     if (grandChild->NodeInfo()->Equals(nsGkAtoms::menuitem, kNameSpaceID_XUL)) {
    1675                 :       // See if we have a command attribute.
    1676               0 :       nsAutoString command;
    1677               0 :       grandChild->GetAttr(kNameSpaceID_None, nsGkAtoms::command, command);
    1678               0 :       if (!command.IsEmpty()) {
    1679                 :         // We do! Look it up in our document
    1680               0 :         nsCOMPtr<nsIDOMElement> commandElt;
    1681               0 :         domDoc->GetElementById(command, getter_AddRefs(commandElt));
    1682               0 :         nsCOMPtr<nsIContent> commandContent(do_QueryInterface(commandElt));
    1683               0 :         if (commandContent) {
    1684               0 :           nsAutoString commandValue;
    1685                 :           // The menu's disabled state needs to be updated to match the command.
    1686               0 :           if (commandContent->GetAttr(kNameSpaceID_None, nsGkAtoms::disabled, commandValue))
    1687               0 :             grandChild->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, commandValue, true);
    1688                 :           else
    1689               0 :             grandChild->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
    1690                 : 
    1691                 :           // The menu's label, accesskey and checked states need to be updated
    1692                 :           // to match the command. Note that unlike the disabled state if the
    1693                 :           // command has *no* value, we assume the menu is supplying its own.
    1694               0 :           if (commandContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, commandValue))
    1695               0 :             grandChild->SetAttr(kNameSpaceID_None, nsGkAtoms::label, commandValue, true);
    1696                 : 
    1697               0 :           if (commandContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, commandValue))
    1698               0 :             grandChild->SetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, commandValue, true);
    1699                 : 
    1700               0 :           if (commandContent->GetAttr(kNameSpaceID_None, nsGkAtoms::checked, commandValue))
    1701               0 :             grandChild->SetAttr(kNameSpaceID_None, nsGkAtoms::checked, commandValue, true);
    1702                 :         }
    1703                 :       }
    1704                 :     }
    1705                 :   }
    1706               0 : }
    1707                 : 
    1708                 : // Notify
    1709                 : //
    1710                 : // The item selection timer has fired, we might have to readjust the 
    1711                 : // selected item. There are two cases here that we are trying to deal with:
    1712                 : //   (1) diagonal movement from a parent menu to a submenu passing briefly over
    1713                 : //       other items, and
    1714                 : //   (2) moving out from a submenu to a parent or grandparent menu.
    1715                 : // In both cases, |mTimerMenu| is the menu item that might have an open submenu and
    1716                 : // the first item in |mPopups| is the item the mouse is currently over, which could be
    1717                 : // none of them.
    1718                 : //
    1719                 : // case (1):
    1720                 : //  As the mouse moves from the parent item of a submenu (we'll call 'A') diagonally into the
    1721                 : //  submenu, it probably passes through one or more sibilings (B). As the mouse passes
    1722                 : //  through B, it becomes the current menu item and the timer is set and mTimerMenu is 
    1723                 : //  set to A. Before the timer fires, the mouse leaves the menu containing A and B and
    1724                 : //  enters the submenus. Now when the timer fires, |mPopups| is null (!= |mTimerMenu|)
    1725                 : //  so we have to see if anything in A's children is selected (recall that even disabled
    1726                 : //  items are selected, the style just doesn't show it). If that is the case, we need to
    1727                 : //  set the selected item back to A.
    1728                 : //
    1729                 : // case (2);
    1730                 : //  Item A has an open submenu, and in it there is an item (B) which also has an open
    1731                 : //  submenu (so there are 3 menus displayed right now). The mouse then leaves B's child
    1732                 : //  submenu and selects an item that is a sibling of A, call it C. When the mouse enters C,
    1733                 : //  the timer is set and |mTimerMenu| is A and |mPopups| is C. As the timer fires,
    1734                 : //  the mouse is still within C. The correct behavior is to set the current item to C
    1735                 : //  and close up the chain parented at A.
    1736                 : //
    1737                 : //  This brings up the question of is the logic of case (1) enough? The answer is no,
    1738                 : //  and is discussed in bugzilla bug 29400. Case (1) asks if A's submenu has a selected
    1739                 : //  child, and if it does, set the selected item to A. Because B has a submenu open, it
    1740                 : //  is selected and as a result, A is set to be the selected item even though the mouse
    1741                 : //  rests in C -- very wrong. 
    1742                 : //
    1743                 : //  The solution is to use the same idea, but instead of only checking one level, 
    1744                 : //  drill all the way down to the deepest open submenu and check if it has something 
    1745                 : //  selected. Since the mouse is in a grandparent, it won't, and we know that we can
    1746                 : //  safely close up A and all its children.
    1747                 : //
    1748                 : // The code below melds the two cases together.
    1749                 : //
    1750                 : nsresult
    1751               0 : nsXULPopupManager::Notify(nsITimer* aTimer)
    1752                 : {
    1753               0 :   if (aTimer == mCloseTimer)
    1754               0 :     KillMenuTimer();
    1755                 : 
    1756               0 :   return NS_OK;
    1757                 : }
    1758                 : 
    1759                 : void
    1760               0 : nsXULPopupManager::KillMenuTimer()
    1761                 : {
    1762               0 :   if (mCloseTimer && mTimerMenu) {
    1763               0 :     mCloseTimer->Cancel();
    1764               0 :     mCloseTimer = nsnull;
    1765                 : 
    1766               0 :     if (mTimerMenu->IsOpen())
    1767               0 :       HidePopup(mTimerMenu->GetContent(), false, false, true);
    1768                 :   }
    1769                 : 
    1770               0 :   mTimerMenu = nsnull;
    1771               0 : }
    1772                 : 
    1773                 : void
    1774               0 : nsXULPopupManager::CancelMenuTimer(nsMenuParent* aMenuParent)
    1775                 : {
    1776               0 :   if (mCloseTimer && mTimerMenu == aMenuParent) {
    1777               0 :     mCloseTimer->Cancel();
    1778               0 :     mCloseTimer = nsnull;
    1779               0 :     mTimerMenu = nsnull;
    1780                 :   }
    1781               0 : }
    1782                 : 
    1783               0 : static nsGUIEvent* DOMKeyEventToGUIEvent(nsIDOMEvent* aEvent)
    1784                 : {
    1785               0 :   nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(aEvent));
    1786               0 :   nsEvent* evt = privateEvent ? privateEvent->GetInternalNSEvent() : nsnull;
    1787               0 :   return (evt->eventStructType == NS_KEY_EVENT) ? static_cast<nsGUIEvent *>(evt) : nsnull;
    1788                 : }
    1789                 : 
    1790                 : bool
    1791               0 : nsXULPopupManager::HandleShortcutNavigation(nsIDOMKeyEvent* aKeyEvent,
    1792                 :                                             nsMenuPopupFrame* aFrame)
    1793                 : {
    1794               0 :   nsMenuChainItem* item = GetTopVisibleMenu();
    1795               0 :   if (!aFrame && item)
    1796               0 :     aFrame = item->Frame();
    1797                 : 
    1798               0 :   if (aFrame) {
    1799                 :     bool action;
    1800               0 :     nsMenuFrame* result = aFrame->FindMenuWithShortcut(aKeyEvent, action);
    1801               0 :     if (result) {
    1802               0 :       aFrame->ChangeMenuItem(result, false);
    1803               0 :       if (action) {
    1804               0 :         nsGUIEvent* evt = DOMKeyEventToGUIEvent(aKeyEvent);
    1805               0 :         nsMenuFrame* menuToOpen = result->Enter(evt);
    1806               0 :         if (menuToOpen) {
    1807               0 :           nsCOMPtr<nsIContent> content = menuToOpen->GetContent();
    1808               0 :           ShowMenu(content, true, false);
    1809                 :         }
    1810                 :       }
    1811               0 :       return true;
    1812                 :     }
    1813                 : 
    1814               0 :     return false;
    1815                 :   }
    1816                 : 
    1817               0 :   if (mActiveMenuBar) {
    1818               0 :     nsMenuFrame* result = mActiveMenuBar->FindMenuWithShortcut(aKeyEvent);
    1819               0 :     if (result) {
    1820               0 :       mActiveMenuBar->SetActive(true);
    1821               0 :       result->OpenMenu(true);
    1822               0 :       return true;
    1823                 :     }
    1824                 :   }
    1825                 : 
    1826               0 :   return false;
    1827                 : }
    1828                 : 
    1829                 : 
    1830                 : bool
    1831               0 : nsXULPopupManager::HandleKeyboardNavigation(PRUint32 aKeyCode)
    1832                 : {
    1833                 :   // navigate up through the open menus, looking for the topmost one
    1834                 :   // in the same hierarchy
    1835               0 :   nsMenuChainItem* item = nsnull;
    1836               0 :   nsMenuChainItem* nextitem = GetTopVisibleMenu();
    1837                 : 
    1838               0 :   while (nextitem) {
    1839               0 :     item = nextitem;
    1840               0 :     nextitem = item->GetParent();
    1841                 : 
    1842               0 :     if (nextitem) {
    1843                 :       // stop if the parent isn't a menu
    1844               0 :       if (!nextitem->IsMenu())
    1845               0 :         break;
    1846                 : 
    1847                 :       // check to make sure that the parent is actually the parent menu. It won't
    1848                 :       // be if the parent is in a different frame hierarchy, for example, for a
    1849                 :       // context menu opened on another menu.
    1850               0 :       nsMenuParent* expectedParent = static_cast<nsMenuParent *>(nextitem->Frame());
    1851               0 :       nsMenuFrame* menuFrame = item->Frame()->GetParentMenu();
    1852               0 :       if (!menuFrame || menuFrame->GetMenuParent() != expectedParent) {
    1853               0 :         break;
    1854                 :       }
    1855                 :     }
    1856                 :   }
    1857                 : 
    1858                 :   nsIFrame* itemFrame;
    1859               0 :   if (item)
    1860               0 :     itemFrame = item->Frame();
    1861               0 :   else if (mActiveMenuBar)
    1862               0 :     itemFrame = mActiveMenuBar;
    1863                 :   else
    1864               0 :     return false;
    1865                 : 
    1866                 :   nsNavigationDirection theDirection;
    1867               0 :   NS_ASSERTION(aKeyCode >= NS_VK_END && aKeyCode <= NS_VK_DOWN, "Illegal key code");
    1868               0 :   theDirection = NS_DIRECTION_FROM_KEY_CODE(itemFrame, aKeyCode);
    1869                 : 
    1870                 :   // if a popup is open, first check for navigation within the popup
    1871               0 :   if (item && HandleKeyboardNavigationInPopup(item, theDirection))
    1872               0 :     return true;
    1873                 : 
    1874                 :   // no popup handled the key, so check the active menubar, if any
    1875               0 :   if (mActiveMenuBar) {
    1876               0 :     nsMenuFrame* currentMenu = mActiveMenuBar->GetCurrentMenuItem();
    1877                 :   
    1878               0 :     if (NS_DIRECTION_IS_INLINE(theDirection)) {
    1879                 :       nsMenuFrame* nextItem = (theDirection == eNavigationDirection_End) ?
    1880               0 :                               GetNextMenuItem(mActiveMenuBar, currentMenu, false) : 
    1881               0 :                               GetPreviousMenuItem(mActiveMenuBar, currentMenu, false);
    1882               0 :       mActiveMenuBar->ChangeMenuItem(nextItem, true);
    1883               0 :       return true;
    1884                 :     }
    1885               0 :     else if (NS_DIRECTION_IS_BLOCK(theDirection)) {
    1886                 :       // Open the menu and select its first item.
    1887               0 :       if (currentMenu) {
    1888               0 :         nsCOMPtr<nsIContent> content = currentMenu->GetContent();
    1889               0 :         ShowMenu(content, true, false);
    1890                 :       }
    1891               0 :       return true;
    1892                 :     }
    1893                 :   }
    1894                 : 
    1895               0 :   return false;
    1896                 : }
    1897                 : 
    1898                 : bool
    1899               0 : nsXULPopupManager::HandleKeyboardNavigationInPopup(nsMenuChainItem* item,
    1900                 :                                                    nsMenuPopupFrame* aFrame,
    1901                 :                                                    nsNavigationDirection aDir)
    1902                 : {
    1903               0 :   NS_ASSERTION(aFrame, "aFrame is null");
    1904               0 :   NS_ASSERTION(!item || item->Frame() == aFrame,
    1905                 :                "aFrame is expected to be equal to item->Frame()");
    1906                 : 
    1907               0 :   nsMenuFrame* currentMenu = aFrame->GetCurrentMenuItem();
    1908                 : 
    1909               0 :   aFrame->ClearIncrementalString();
    1910                 : 
    1911                 :   // This method only gets called if we're open.
    1912               0 :   if (!currentMenu && NS_DIRECTION_IS_INLINE(aDir)) {
    1913                 :     // We've been opened, but we haven't had anything selected.
    1914                 :     // We can handle End, but our parent handles Start.
    1915               0 :     if (aDir == eNavigationDirection_End) {
    1916               0 :       nsMenuFrame* nextItem = GetNextMenuItem(aFrame, nsnull, true);
    1917               0 :       if (nextItem) {
    1918               0 :         aFrame->ChangeMenuItem(nextItem, false);
    1919               0 :         return true;
    1920                 :       }
    1921                 :     }
    1922               0 :     return false;
    1923                 :   }
    1924                 : 
    1925               0 :   bool isContainer = false;
    1926               0 :   bool isOpen = false;
    1927               0 :   if (currentMenu) {
    1928               0 :     isOpen = currentMenu->IsOpen();
    1929               0 :     isContainer = currentMenu->IsMenu();
    1930               0 :     if (isOpen) {
    1931                 :       // for an open popup, have the child process the event
    1932               0 :       nsMenuChainItem* child = item ? item->GetChild() : nsnull;
    1933               0 :       if (child && HandleKeyboardNavigationInPopup(child, aDir))
    1934               0 :         return true;
    1935                 :     }
    1936               0 :     else if (aDir == eNavigationDirection_End &&
    1937               0 :              isContainer && !currentMenu->IsDisabled()) {
    1938                 :       // The menu is not yet open. Open it and select the first item.
    1939               0 :       nsCOMPtr<nsIContent> content = currentMenu->GetContent();
    1940               0 :       ShowMenu(content, true, false);
    1941               0 :       return true;
    1942                 :     }
    1943                 :   }
    1944                 : 
    1945                 :   // For block progression, we can move in either direction
    1946               0 :   if (NS_DIRECTION_IS_BLOCK(aDir) ||
    1947                 :       NS_DIRECTION_IS_BLOCK_TO_EDGE(aDir)) {
    1948                 :     nsMenuFrame* nextItem;
    1949                 : 
    1950               0 :     if (aDir == eNavigationDirection_Before)
    1951               0 :       nextItem = GetPreviousMenuItem(aFrame, currentMenu, true);
    1952               0 :     else if (aDir == eNavigationDirection_After)
    1953               0 :       nextItem = GetNextMenuItem(aFrame, currentMenu, true);
    1954               0 :     else if (aDir == eNavigationDirection_First)
    1955               0 :       nextItem = GetNextMenuItem(aFrame, nsnull, true);
    1956                 :     else
    1957               0 :       nextItem = GetPreviousMenuItem(aFrame, nsnull, true);
    1958                 : 
    1959               0 :     if (nextItem) {
    1960               0 :       aFrame->ChangeMenuItem(nextItem, false);
    1961               0 :       return true;
    1962               0 :     }
    1963                 :   }
    1964               0 :   else if (currentMenu && isContainer && isOpen) {
    1965               0 :     if (aDir == eNavigationDirection_Start) {
    1966                 :       // close a submenu when Left is pressed
    1967               0 :       nsMenuPopupFrame* popupFrame = currentMenu->GetPopup();
    1968               0 :       if (popupFrame)
    1969               0 :         HidePopup(popupFrame->GetContent(), false, false, false);
    1970               0 :       return true;
    1971                 :     }
    1972                 :   }
    1973                 : 
    1974               0 :   return false;
    1975                 : }
    1976                 : 
    1977                 : nsMenuFrame*
    1978               0 : nsXULPopupManager::GetNextMenuItem(nsIFrame* aParent,
    1979                 :                                    nsMenuFrame* aStart,
    1980                 :                                    bool aIsPopup)
    1981                 : {
    1982               0 :   nsIFrame* immediateParent = nsnull;
    1983               0 :   nsPresContext* presContext = aParent->PresContext();
    1984                 :   presContext->PresShell()->
    1985               0 :     FrameConstructor()->GetInsertionPoint(aParent, nsnull, &immediateParent);
    1986               0 :   if (!immediateParent)
    1987               0 :     immediateParent = aParent;
    1988                 : 
    1989               0 :   nsIFrame* currFrame = nsnull;
    1990               0 :   if (aStart)
    1991               0 :     currFrame = aStart->GetNextSibling();
    1992                 :   else 
    1993               0 :     currFrame = immediateParent->GetFirstPrincipalChild();
    1994                 :   
    1995               0 :   while (currFrame) {
    1996                 :     // See if it's a menu item.
    1997               0 :     if (IsValidMenuItem(presContext, currFrame->GetContent(), aIsPopup)) {
    1998               0 :       return (currFrame->GetType() == nsGkAtoms::menuFrame) ?
    1999               0 :              static_cast<nsMenuFrame *>(currFrame) : nsnull;
    2000                 :     }
    2001               0 :     currFrame = currFrame->GetNextSibling();
    2002                 :   }
    2003                 : 
    2004               0 :   currFrame = immediateParent->GetFirstPrincipalChild();
    2005                 : 
    2006                 :   // Still don't have anything. Try cycling from the beginning.
    2007               0 :   while (currFrame && currFrame != aStart) {
    2008                 :     // See if it's a menu item.
    2009               0 :     if (IsValidMenuItem(presContext, currFrame->GetContent(), aIsPopup)) {
    2010               0 :       return (currFrame->GetType() == nsGkAtoms::menuFrame) ?
    2011               0 :              static_cast<nsMenuFrame *>(currFrame) : nsnull;
    2012                 :     }
    2013                 : 
    2014               0 :     currFrame = currFrame->GetNextSibling();
    2015                 :   }
    2016                 : 
    2017                 :   // No luck. Just return our start value.
    2018               0 :   return aStart;
    2019                 : }
    2020                 : 
    2021                 : nsMenuFrame*
    2022               0 : nsXULPopupManager::GetPreviousMenuItem(nsIFrame* aParent,
    2023                 :                                        nsMenuFrame* aStart,
    2024                 :                                        bool aIsPopup)
    2025                 : {
    2026               0 :   nsIFrame* immediateParent = nsnull;
    2027               0 :   nsPresContext* presContext = aParent->PresContext();
    2028                 :   presContext->PresShell()->
    2029               0 :     FrameConstructor()->GetInsertionPoint(aParent, nsnull, &immediateParent);
    2030               0 :   if (!immediateParent)
    2031               0 :     immediateParent = aParent;
    2032                 : 
    2033               0 :   const nsFrameList& frames(immediateParent->PrincipalChildList());
    2034                 : 
    2035               0 :   nsIFrame* currFrame = nsnull;
    2036               0 :   if (aStart)
    2037               0 :     currFrame = aStart->GetPrevSibling();
    2038                 :   else
    2039               0 :     currFrame = frames.LastChild();
    2040                 : 
    2041               0 :   while (currFrame) {
    2042                 :     // See if it's a menu item.
    2043               0 :     if (IsValidMenuItem(presContext, currFrame->GetContent(), aIsPopup)) {
    2044               0 :       return (currFrame->GetType() == nsGkAtoms::menuFrame) ?
    2045               0 :              static_cast<nsMenuFrame *>(currFrame) : nsnull;
    2046                 :     }
    2047               0 :     currFrame = currFrame->GetPrevSibling();
    2048                 :   }
    2049                 : 
    2050               0 :   currFrame = frames.LastChild();
    2051                 : 
    2052                 :   // Still don't have anything. Try cycling from the end.
    2053               0 :   while (currFrame && currFrame != aStart) {
    2054                 :     // See if it's a menu item.
    2055               0 :     if (IsValidMenuItem(presContext, currFrame->GetContent(), aIsPopup)) {
    2056               0 :       return (currFrame->GetType() == nsGkAtoms::menuFrame) ?
    2057               0 :              static_cast<nsMenuFrame *>(currFrame) : nsnull;
    2058                 :     }
    2059                 : 
    2060               0 :     currFrame = currFrame->GetPrevSibling();
    2061                 :   }
    2062                 : 
    2063                 :   // No luck. Just return our start value.
    2064               0 :   return aStart;
    2065                 : }
    2066                 : 
    2067                 : bool
    2068               0 : nsXULPopupManager::IsValidMenuItem(nsPresContext* aPresContext,
    2069                 :                                    nsIContent* aContent,
    2070                 :                                    bool aOnPopup)
    2071                 : {
    2072               0 :   PRInt32 ns = aContent->GetNameSpaceID();
    2073               0 :   nsIAtom *tag = aContent->Tag();
    2074               0 :   if (ns == kNameSpaceID_XUL) {
    2075               0 :     if (tag != nsGkAtoms::menu && tag != nsGkAtoms::menuitem)
    2076               0 :       return false;
    2077                 :   }
    2078               0 :   else if (ns != kNameSpaceID_XHTML || !aOnPopup || tag != nsGkAtoms::option) {
    2079               0 :     return false;
    2080                 :   }
    2081                 : 
    2082               0 :   bool skipNavigatingDisabledMenuItem = true;
    2083               0 :   if (aOnPopup) {
    2084                 :     skipNavigatingDisabledMenuItem =
    2085                 :       LookAndFeel::GetInt(LookAndFeel::eIntID_SkipNavigatingDisabledMenuItem,
    2086               0 :                           0) != 0;
    2087                 :   }
    2088                 : 
    2089                 :   return !(skipNavigatingDisabledMenuItem &&
    2090                 :            aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
    2091               0 :                                  nsGkAtoms::_true, eCaseMatters));
    2092                 : }
    2093                 : 
    2094                 : nsresult
    2095               0 : nsXULPopupManager::HandleEvent(nsIDOMEvent* aEvent)
    2096                 : {
    2097               0 :   nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
    2098               0 :   NS_ENSURE_TRUE(keyEvent, NS_ERROR_UNEXPECTED);
    2099                 : 
    2100               0 :   nsAutoString eventType;
    2101               0 :   keyEvent->GetType(eventType);
    2102               0 :   if (eventType.EqualsLiteral("keyup")) {
    2103               0 :     return KeyUp(keyEvent);
    2104                 :   }
    2105               0 :   if (eventType.EqualsLiteral("keydown")) {
    2106               0 :     return KeyDown(keyEvent);
    2107                 :   }
    2108               0 :   if (eventType.EqualsLiteral("keypress")) {
    2109               0 :     return KeyPress(keyEvent);
    2110                 :   }
    2111                 : 
    2112               0 :   NS_ABORT();
    2113                 : 
    2114               0 :   return NS_OK;
    2115                 : }
    2116                 : 
    2117                 : nsresult
    2118               0 : nsXULPopupManager::KeyUp(nsIDOMKeyEvent* aKeyEvent)
    2119                 : {
    2120                 :   // don't do anything if a menu isn't open or a menubar isn't active
    2121               0 :   if (!mActiveMenuBar) {
    2122               0 :     nsMenuChainItem* item = GetTopVisibleMenu();
    2123               0 :     if (!item || item->PopupType() != ePopupTypeMenu)
    2124               0 :       return NS_OK;
    2125                 :   }
    2126                 : 
    2127               0 :   aKeyEvent->StopPropagation();
    2128               0 :   aKeyEvent->PreventDefault();
    2129                 : 
    2130               0 :   return NS_OK; // I am consuming event
    2131                 : }
    2132                 : 
    2133                 : nsresult
    2134               0 : nsXULPopupManager::KeyDown(nsIDOMKeyEvent* aKeyEvent)
    2135                 : {
    2136               0 :   nsMenuChainItem* item = GetTopVisibleMenu();
    2137               0 :   if (item && item->Frame()->IsMenuLocked())
    2138               0 :     return NS_OK;
    2139                 : 
    2140                 :   // don't do anything if a menu isn't open or a menubar isn't active
    2141               0 :   if (!mActiveMenuBar && (!item || item->PopupType() != ePopupTypeMenu))
    2142               0 :     return NS_OK;
    2143                 : 
    2144               0 :   PRInt32 menuAccessKey = -1;
    2145                 : 
    2146                 :   // If the key just pressed is the access key (usually Alt),
    2147                 :   // dismiss and unfocus the menu.
    2148                 : 
    2149               0 :   nsMenuBarListener::GetMenuAccessKey(&menuAccessKey);
    2150               0 :   if (menuAccessKey) {
    2151                 :     PRUint32 theChar;
    2152               0 :     aKeyEvent->GetKeyCode(&theChar);
    2153                 : 
    2154               0 :     if (theChar == (PRUint32)menuAccessKey) {
    2155               0 :       bool ctrl = false;
    2156               0 :       if (menuAccessKey != nsIDOMKeyEvent::DOM_VK_CONTROL)
    2157               0 :         aKeyEvent->GetCtrlKey(&ctrl);
    2158               0 :       bool alt=false;
    2159               0 :       if (menuAccessKey != nsIDOMKeyEvent::DOM_VK_ALT)
    2160               0 :         aKeyEvent->GetAltKey(&alt);
    2161               0 :       bool shift=false;
    2162               0 :       if (menuAccessKey != nsIDOMKeyEvent::DOM_VK_SHIFT)
    2163               0 :         aKeyEvent->GetShiftKey(&shift);
    2164               0 :       bool meta=false;
    2165               0 :       if (menuAccessKey != nsIDOMKeyEvent::DOM_VK_META)
    2166               0 :         aKeyEvent->GetMetaKey(&meta);
    2167               0 :       if (!(ctrl || alt || shift || meta)) {
    2168                 :         // The access key just went down and no other
    2169                 :         // modifiers are already down.
    2170               0 :         if (mPopups)
    2171               0 :           Rollup(0);
    2172               0 :         else if (mActiveMenuBar)
    2173               0 :           mActiveMenuBar->MenuClosed();
    2174                 :       }
    2175                 :     }
    2176                 :   }
    2177                 : 
    2178                 :   // Since a menu was open, eat the event to keep other event
    2179                 :   // listeners from becoming confused.
    2180               0 :   aKeyEvent->StopPropagation();
    2181               0 :   aKeyEvent->PreventDefault();
    2182               0 :   return NS_OK; // I am consuming event
    2183                 : }
    2184                 : 
    2185                 : nsresult
    2186               0 : nsXULPopupManager::KeyPress(nsIDOMKeyEvent* aKeyEvent)
    2187                 : {
    2188                 :   // Don't check prevent default flag -- menus always get first shot at key events.
    2189                 :   // When a menu is open, the prevent default flag on a keypress is always set, so
    2190                 :   // that no one else uses the key event.
    2191                 : 
    2192               0 :   nsMenuChainItem* item = GetTopVisibleMenu();
    2193               0 :   if (item && item->Frame()->IsMenuLocked())
    2194               0 :     return NS_OK;
    2195                 : 
    2196                 :   //handlers shouldn't be triggered by non-trusted events.
    2197               0 :   nsCOMPtr<nsIDOMNSEvent> domNSEvent = do_QueryInterface(aKeyEvent);
    2198               0 :   bool trustedEvent = false;
    2199                 : 
    2200               0 :   if (domNSEvent) {
    2201               0 :     domNSEvent->GetIsTrusted(&trustedEvent);
    2202                 :   }
    2203                 : 
    2204               0 :   if (!trustedEvent)
    2205               0 :     return NS_OK;
    2206                 : 
    2207               0 :   nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
    2208               0 :   NS_ENSURE_TRUE(keyEvent, NS_ERROR_UNEXPECTED);
    2209                 :   PRUint32 theChar;
    2210               0 :   keyEvent->GetKeyCode(&theChar);
    2211                 : 
    2212                 :   // Escape should close panels, but the other keys should have no effect.
    2213               0 :   if (item && item->PopupType() != ePopupTypeMenu) {
    2214               0 :     if (theChar == NS_VK_ESCAPE) {
    2215               0 :       HidePopup(item->Content(), false, false, false);
    2216               0 :       aKeyEvent->StopPropagation();
    2217               0 :       aKeyEvent->PreventDefault();
    2218                 :     }
    2219               0 :     return NS_OK;
    2220                 :   }
    2221                 : 
    2222                 :   // if a menu is open or a menubar is active, it consumes the key event
    2223               0 :   bool consume = (mPopups || mActiveMenuBar);
    2224                 : 
    2225               0 :   if (theChar == NS_VK_LEFT ||
    2226                 :       theChar == NS_VK_RIGHT ||
    2227                 :       theChar == NS_VK_UP ||
    2228                 :       theChar == NS_VK_DOWN ||
    2229                 :       theChar == NS_VK_HOME ||
    2230                 :       theChar == NS_VK_END) {
    2231               0 :     HandleKeyboardNavigation(theChar);
    2232                 :   }
    2233               0 :   else if (theChar == NS_VK_ESCAPE) {
    2234                 :     // Pressing Escape hides one level of menus only. If no menu is open,
    2235                 :     // check if a menubar is active and inform it that a menu closed. Even
    2236                 :     // though in this latter case, a menu didn't actually close, the effect
    2237                 :     // ends up being the same. Similar for the tab key below.
    2238               0 :     if (item)
    2239               0 :       HidePopup(item->Content(), false, false, false);
    2240               0 :     else if (mActiveMenuBar)
    2241               0 :       mActiveMenuBar->MenuClosed();
    2242                 :   }
    2243               0 :   else if (theChar == NS_VK_TAB
    2244                 : #ifndef XP_MACOSX
    2245                 :            || theChar == NS_VK_F10
    2246                 : #endif
    2247                 :   ) {
    2248                 :     // close popups or deactivate menubar when Tab or F10 are pressed
    2249               0 :     if (item)
    2250               0 :       Rollup(0);
    2251               0 :     else if (mActiveMenuBar)
    2252               0 :       mActiveMenuBar->MenuClosed();
    2253                 :   }
    2254               0 :   else if (theChar == NS_VK_ENTER ||
    2255                 :            theChar == NS_VK_RETURN) {
    2256                 :     // If there is a popup open, check if the current item needs to be opened.
    2257                 :     // Otherwise, tell the active menubar, if any, to activate the menu. The
    2258                 :     // Enter method will return a menu if one needs to be opened as a result.
    2259               0 :     nsMenuFrame* menuToOpen = nsnull;
    2260               0 :     nsMenuChainItem* item = GetTopVisibleMenu();
    2261               0 :     nsGUIEvent* evt = DOMKeyEventToGUIEvent(aKeyEvent);
    2262               0 :     if (item)
    2263               0 :       menuToOpen = item->Frame()->Enter(evt);
    2264               0 :     else if (mActiveMenuBar)
    2265               0 :       menuToOpen = mActiveMenuBar->Enter(evt);
    2266               0 :     if (menuToOpen) {
    2267               0 :       nsCOMPtr<nsIContent> content = menuToOpen->GetContent();
    2268               0 :       ShowMenu(content, true, false);
    2269               0 :     }
    2270                 :   }
    2271                 :   else {
    2272               0 :     HandleShortcutNavigation(keyEvent, nsnull);
    2273                 :   }
    2274                 : 
    2275               0 :   if (consume) {
    2276               0 :     aKeyEvent->StopPropagation();
    2277               0 :     aKeyEvent->PreventDefault();
    2278                 :   }
    2279                 : 
    2280               0 :   return NS_OK; // I am consuming event
    2281                 : }
    2282                 : 
    2283                 : NS_IMETHODIMP
    2284               0 : nsXULPopupShowingEvent::Run()
    2285                 : {
    2286               0 :   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    2287               0 :   if (pm) {
    2288               0 :     pm->FirePopupShowingEvent(mPopup, mIsContextMenu, mSelectFirstItem);
    2289                 :   }
    2290                 : 
    2291               0 :   return NS_OK;
    2292                 : }
    2293                 : 
    2294                 : NS_IMETHODIMP
    2295               0 : nsXULPopupHidingEvent::Run()
    2296                 : {
    2297               0 :   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    2298                 : 
    2299               0 :   nsIDocument *document = mPopup->GetCurrentDoc();
    2300               0 :   if (pm && document) {
    2301               0 :     nsIPresShell* presShell = document->GetShell();
    2302               0 :     if (presShell) {
    2303               0 :       nsPresContext* context = presShell->GetPresContext();
    2304               0 :       if (context) {
    2305                 :         pm->FirePopupHidingEvent(mPopup, mNextPopup, mLastPopup,
    2306               0 :                                  context, mPopupType, mDeselectMenu);
    2307                 :       }
    2308                 :     }
    2309                 :   }
    2310                 : 
    2311               0 :   return NS_OK;
    2312                 : }
    2313                 : 
    2314                 : NS_IMETHODIMP
    2315               0 : nsXULMenuCommandEvent::Run()
    2316                 : {
    2317               0 :   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    2318               0 :   if (!pm)
    2319               0 :     return NS_OK;
    2320                 : 
    2321                 :   // The order of the nsIViewManager and nsIPresShell COM pointers is
    2322                 :   // important below.  We want the pres shell to get released before the
    2323                 :   // associated view manager on exit from this function.
    2324                 :   // See bug 54233.
    2325                 :   // XXXndeakin is this still needed?
    2326                 : 
    2327               0 :   nsCOMPtr<nsIContent> popup;
    2328               0 :   nsMenuFrame* menuFrame = pm->GetMenuFrameForContent(mMenu);
    2329               0 :   nsWeakFrame weakFrame(menuFrame);
    2330               0 :   if (menuFrame && mFlipChecked) {
    2331               0 :     if (menuFrame->IsChecked()) {
    2332               0 :       mMenu->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked, true);
    2333                 :     } else {
    2334                 :       mMenu->SetAttr(kNameSpaceID_None, nsGkAtoms::checked,
    2335               0 :                      NS_LITERAL_STRING("true"), true);
    2336                 :     }
    2337                 :   }
    2338                 : 
    2339               0 :   if (menuFrame && weakFrame.IsAlive()) {
    2340                 :     // Find the popup that the menu is inside. Below, this popup will
    2341                 :     // need to be hidden.
    2342               0 :     nsIFrame* popupFrame = menuFrame->GetParent();
    2343               0 :     while (popupFrame) {
    2344               0 :       if (popupFrame->GetType() == nsGkAtoms::menuPopupFrame) {
    2345               0 :         popup = popupFrame->GetContent();
    2346               0 :         break;
    2347                 :       }
    2348               0 :       popupFrame = popupFrame->GetParent();
    2349                 :     }
    2350                 : 
    2351               0 :     nsPresContext* presContext = menuFrame->PresContext();
    2352               0 :     nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
    2353               0 :     nsCOMPtr<nsIViewManager> kungFuDeathGrip = shell->GetViewManager();
    2354                 : 
    2355                 :     // Deselect ourselves.
    2356               0 :     if (mCloseMenuMode != CloseMenuMode_None)
    2357               0 :       menuFrame->SelectMenu(false);
    2358                 : 
    2359                 :     nsAutoHandlingUserInputStatePusher userInpStatePusher(mUserInput, nsnull,
    2360               0 :                                                           shell->GetDocument());
    2361                 :     nsContentUtils::DispatchXULCommand(mMenu, mIsTrusted, nsnull, shell,
    2362               0 :                                        mControl, mAlt, mShift, mMeta);
    2363                 :   }
    2364                 : 
    2365               0 :   if (popup && mCloseMenuMode != CloseMenuMode_None)
    2366               0 :     pm->HidePopup(popup, mCloseMenuMode == CloseMenuMode_Auto, true, false);
    2367                 : 
    2368               0 :   return NS_OK;
    2369                 : }

Generated by: LCOV version 1.7