LCOV - app.info - layout/xul/base/src/nsXULPopupManager.cpp

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-07-07 | 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