2007-07-04 15:49:38 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is Neil Deakin
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2006
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#include "nsGkAtoms.h"
|
|
|
|
#include "nsXULPopupManager.h"
|
|
|
|
#include "nsMenuFrame.h"
|
|
|
|
#include "nsMenuPopupFrame.h"
|
|
|
|
#include "nsMenuBarFrame.h"
|
|
|
|
#include "nsIPopupBoxObject.h"
|
|
|
|
#include "nsMenuBarListener.h"
|
|
|
|
#include "nsContentUtils.h"
|
|
|
|
#include "nsIDOMDocument.h"
|
|
|
|
#include "nsIDOMNSEvent.h"
|
|
|
|
#include "nsIDOMNSUIEvent.h"
|
2008-02-21 17:40:12 +00:00
|
|
|
#include "nsIDOMXULElement.h"
|
2010-08-09 16:17:19 +00:00
|
|
|
#include "nsIXULDocument.h"
|
2008-02-21 17:40:12 +00:00
|
|
|
#include "nsIXULTemplateBuilder.h"
|
2007-07-17 12:21:53 +00:00
|
|
|
#include "nsIPrivateDOMEvent.h"
|
2007-07-04 15:49:38 +00:00
|
|
|
#include "nsEventDispatcher.h"
|
2007-07-24 00:55:52 +00:00
|
|
|
#include "nsEventStateManager.h"
|
2007-07-04 15:49:38 +00:00
|
|
|
#include "nsCSSFrameConstructor.h"
|
|
|
|
#include "nsLayoutUtils.h"
|
|
|
|
#include "nsIViewManager.h"
|
|
|
|
#include "nsILookAndFeel.h"
|
|
|
|
#include "nsIComponentManager.h"
|
|
|
|
#include "nsITimer.h"
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 18:00:39 +00:00
|
|
|
#include "nsFocusManager.h"
|
2007-07-04 15:49:38 +00:00
|
|
|
#include "nsIDocShellTreeItem.h"
|
|
|
|
#include "nsIDocShell.h"
|
|
|
|
#include "nsPIDOMWindow.h"
|
|
|
|
#include "nsIInterfaceRequestorUtils.h"
|
|
|
|
#include "nsIBaseWindow.h"
|
2008-02-08 20:23:05 +00:00
|
|
|
#include "nsIDocShellTreeItem.h"
|
2008-02-20 20:16:55 +00:00
|
|
|
#include "nsIDOMMouseEvent.h"
|
2008-07-16 10:52:01 +00:00
|
|
|
#include "nsCaret.h"
|
2008-04-01 08:35:11 +00:00
|
|
|
#include "nsIDocument.h"
|
|
|
|
#include "nsPIDOMWindow.h"
|
2010-08-09 16:17:19 +00:00
|
|
|
#include "nsPIWindowRoot.h"
|
2009-07-22 00:45:06 +00:00
|
|
|
#include "nsFrameManager.h"
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2008-12-30 13:30:51 +00:00
|
|
|
const nsNavigationDirection DirectionFromKeyCodeTable[2][6] = {
|
|
|
|
{
|
|
|
|
eNavigationDirection_Last, // NS_VK_END
|
|
|
|
eNavigationDirection_First, // NS_VK_HOME
|
|
|
|
eNavigationDirection_Start, // NS_VK_LEFT
|
|
|
|
eNavigationDirection_Before, // NS_VK_UP
|
|
|
|
eNavigationDirection_End, // NS_VK_RIGHT
|
|
|
|
eNavigationDirection_After // NS_VK_DOWN
|
|
|
|
},
|
|
|
|
{
|
|
|
|
eNavigationDirection_Last, // NS_VK_END
|
|
|
|
eNavigationDirection_First, // NS_VK_HOME
|
|
|
|
eNavigationDirection_End, // NS_VK_LEFT
|
|
|
|
eNavigationDirection_Before, // NS_VK_UP
|
|
|
|
eNavigationDirection_Start, // NS_VK_RIGHT
|
|
|
|
eNavigationDirection_After // NS_VK_DOWN
|
|
|
|
}
|
2007-07-16 22:25:29 +00:00
|
|
|
};
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
nsXULPopupManager* nsXULPopupManager::sInstance = nsnull;
|
|
|
|
|
|
|
|
nsIContent* nsMenuChainItem::Content()
|
|
|
|
{
|
|
|
|
return mFrame->GetContent();
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsMenuChainItem::SetParent(nsMenuChainItem* aParent)
|
|
|
|
{
|
|
|
|
if (mParent) {
|
|
|
|
NS_ASSERTION(mParent->mChild == this, "Unexpected - parent's child not set to this");
|
|
|
|
mParent->mChild = nsnull;
|
|
|
|
}
|
|
|
|
mParent = aParent;
|
|
|
|
if (mParent) {
|
|
|
|
if (mParent->mChild)
|
|
|
|
mParent->mChild->mParent = nsnull;
|
|
|
|
mParent->mChild = this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsMenuChainItem::Detach(nsMenuChainItem** aRoot)
|
|
|
|
{
|
|
|
|
// If the item has a child, set the child's parent to this item's parent,
|
|
|
|
// effectively removing the item from the chain. If the item has no child,
|
|
|
|
// just set the parent to null.
|
|
|
|
if (mChild) {
|
|
|
|
NS_ASSERTION(this != *aRoot, "Unexpected - popup with child at end of chain");
|
|
|
|
mChild->SetParent(mParent);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// An item without a child should be the first item in the chain, so set
|
|
|
|
// the first item pointer, pointed to by aRoot, to the parent.
|
|
|
|
NS_ASSERTION(this == *aRoot, "Unexpected - popup with no child not at end of chain");
|
|
|
|
*aRoot = mParent;
|
|
|
|
SetParent(nsnull);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-22 23:49:33 +00:00
|
|
|
NS_IMPL_ISUPPORTS4(nsXULPopupManager,
|
2008-04-06 12:28:34 +00:00
|
|
|
nsIDOMKeyListener,
|
|
|
|
nsIDOMEventListener,
|
|
|
|
nsIMenuRollup,
|
|
|
|
nsITimerCallback)
|
2007-07-04 15:49:38 +00:00
|
|
|
|
|
|
|
nsXULPopupManager::nsXULPopupManager() :
|
|
|
|
mRangeOffset(0),
|
2009-07-22 00:45:06 +00:00
|
|
|
mCachedMousePoint(0, 0),
|
2007-07-04 15:49:38 +00:00
|
|
|
mActiveMenuBar(nsnull),
|
2008-12-05 16:37:30 +00:00
|
|
|
mPopups(nsnull),
|
|
|
|
mNoHidePanels(nsnull),
|
2007-07-04 15:49:38 +00:00
|
|
|
mTimerMenu(nsnull)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
nsXULPopupManager::~nsXULPopupManager()
|
|
|
|
{
|
2008-12-05 16:37:30 +00:00
|
|
|
NS_ASSERTION(!mPopups && !mNoHidePanels, "XUL popups still open");
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsXULPopupManager::Init()
|
|
|
|
{
|
|
|
|
sInstance = new nsXULPopupManager();
|
|
|
|
NS_ENSURE_TRUE(sInstance, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
NS_ADDREF(sInstance);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::Shutdown()
|
|
|
|
{
|
2007-07-12 13:57:01 +00:00
|
|
|
NS_IF_RELEASE(sInstance);
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsXULPopupManager*
|
|
|
|
nsXULPopupManager::GetInstance()
|
|
|
|
{
|
|
|
|
return sInstance;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2009-06-12 18:23:16 +00:00
|
|
|
nsXULPopupManager::Rollup(PRUint32 aCount, nsIContent** aLastRolledUp)
|
2007-07-04 15:49:38 +00:00
|
|
|
{
|
2007-12-03 16:33:42 +00:00
|
|
|
if (aLastRolledUp)
|
|
|
|
*aLastRolledUp = nsnull;
|
|
|
|
|
2007-09-18 15:00:43 +00:00
|
|
|
nsMenuChainItem* item = GetTopVisibleMenu();
|
2007-12-03 16:33:42 +00:00
|
|
|
if (item) {
|
|
|
|
if (aLastRolledUp) {
|
|
|
|
// we need to get the popup that will be closed last, so that
|
|
|
|
// widget can keep track of it so it doesn't reopen if a mouse
|
|
|
|
// down event is going to processed.
|
|
|
|
// Keep going up the menu chain to get the first level menu. This will
|
|
|
|
// be the one that closes up last. It's possible that this menu doesn't
|
|
|
|
// end up closing because the popuphiding event was cancelled, but in
|
|
|
|
// that case we don't need to deal with the menu reopening as it will
|
|
|
|
// already still be open.
|
|
|
|
nsMenuChainItem* first = item;
|
|
|
|
while (first->GetParent())
|
|
|
|
first = first->GetParent();
|
2009-07-22 22:31:02 +00:00
|
|
|
NS_ADDREF(*aLastRolledUp = first->Content());
|
2007-12-03 16:33:42 +00:00
|
|
|
}
|
2009-06-12 18:23:16 +00:00
|
|
|
|
|
|
|
// if a number of popups to close has been specified, determine the last
|
|
|
|
// popup to close
|
|
|
|
nsIContent* lastPopup = nsnull;
|
|
|
|
if (aCount != PR_UINT32_MAX) {
|
|
|
|
nsMenuChainItem* last = item;
|
|
|
|
while (--aCount && last->GetParent()) {
|
|
|
|
last = last->GetParent();
|
|
|
|
}
|
|
|
|
if (last) {
|
|
|
|
lastPopup = last->Content();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
HidePopup(item->Content(), PR_TRUE, PR_TRUE, PR_FALSE, lastPopup);
|
2007-12-03 16:33:42 +00:00
|
|
|
}
|
2007-07-04 15:49:38 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
NS_IMETHODIMP nsXULPopupManager::ShouldRollupOnMouseWheelEvent(PRBool *aShouldRollup)
|
|
|
|
{
|
|
|
|
// should rollup only for autocomplete widgets
|
|
|
|
// XXXndeakin this should really be something the popup has more control over
|
2007-08-15 14:03:21 +00:00
|
|
|
nsMenuChainItem* item = GetTopVisibleMenu();
|
|
|
|
*aShouldRollup = (item && !item->Frame()->IsMenu());
|
2007-07-04 15:49:38 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// a menu should not roll up if activated by a mouse activate message (eg. X-mouse)
|
|
|
|
NS_IMETHODIMP nsXULPopupManager::ShouldRollupOnMouseActivate(PRBool *aShouldRollup)
|
|
|
|
{
|
|
|
|
*aShouldRollup = PR_FALSE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2009-06-12 18:23:16 +00:00
|
|
|
PRUint32
|
|
|
|
nsXULPopupManager::GetSubmenuWidgetChain(nsTArray<nsIWidget*> *aWidgetChain)
|
2007-07-04 15:49:38 +00:00
|
|
|
{
|
2007-08-09 08:53:19 +00:00
|
|
|
// this method is used by the widget code to determine the list of popups
|
|
|
|
// that are open. If a mouse click occurs outside one of these popups, the
|
|
|
|
// panels will roll up. If the click is inside a popup, they will not roll up
|
2009-06-12 18:23:16 +00:00
|
|
|
PRUint32 count = 0, sameTypeCount = 0;
|
|
|
|
|
|
|
|
NS_ASSERTION(aWidgetChain, "null parameter");
|
2007-08-15 14:03:21 +00:00
|
|
|
nsMenuChainItem* item = GetTopVisibleMenu();
|
2007-07-04 15:49:38 +00:00
|
|
|
while (item) {
|
|
|
|
nsCOMPtr<nsIWidget> widget;
|
|
|
|
item->Frame()->GetWidget(getter_AddRefs(widget));
|
2007-09-18 14:32:22 +00:00
|
|
|
NS_ASSERTION(widget, "open popup has no widget");
|
2009-06-12 18:23:16 +00:00
|
|
|
aWidgetChain->AppendElement(widget.get());
|
2007-08-09 08:53:19 +00:00
|
|
|
// In the case when a menulist inside a panel is open, clicking in the
|
|
|
|
// panel should still roll up the menu, so if a different type is found,
|
|
|
|
// stop scanning.
|
2007-11-09 08:00:23 +00:00
|
|
|
nsMenuChainItem* parent = item->GetParent();
|
2009-06-12 18:23:16 +00:00
|
|
|
if (!sameTypeCount) {
|
|
|
|
count++;
|
|
|
|
if (!parent || item->Frame()->PopupType() != parent->Frame()->PopupType() ||
|
|
|
|
item->IsContextMenu() != parent->IsContextMenu()) {
|
|
|
|
sameTypeCount = count;
|
|
|
|
}
|
|
|
|
}
|
2007-08-09 08:53:19 +00:00
|
|
|
item = parent;
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
2009-06-12 18:23:16 +00:00
|
|
|
|
|
|
|
return sameTypeCount;
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
2008-01-17 07:00:31 +00:00
|
|
|
void
|
2010-07-27 13:38:02 +00:00
|
|
|
nsXULPopupManager::AdjustPopupsOnWindowChange(nsPIDOMWindow* aWindow)
|
2007-11-28 20:18:11 +00:00
|
|
|
{
|
2010-07-27 13:38:02 +00:00
|
|
|
// When the parent window is moved, adjust any child popups. Dismissable
|
|
|
|
// menus and panels are expected to roll up when a window is moved, so there
|
|
|
|
// is no need to check these popups, only the noautohide popups.
|
2008-12-05 16:37:30 +00:00
|
|
|
nsMenuChainItem* item = mNoHidePanels;
|
2007-11-28 20:18:11 +00:00
|
|
|
while (item) {
|
2010-07-27 13:38:02 +00:00
|
|
|
// only move popups that are within the same window and where auto
|
|
|
|
// positioning has not been disabled
|
|
|
|
nsMenuPopupFrame* frame= item->Frame();
|
|
|
|
if (frame->GetAutoPosition()) {
|
|
|
|
nsIContent* popup = frame->GetContent();
|
|
|
|
if (popup) {
|
|
|
|
nsIDocument* document = popup->GetCurrentDoc();
|
|
|
|
if (document) {
|
|
|
|
nsPIDOMWindow* window = document->GetWindow();
|
|
|
|
if (window) {
|
|
|
|
window = window->GetPrivateRoot();
|
|
|
|
if (window == aWindow) {
|
|
|
|
frame->SetPopupPosition(nsnull, PR_TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-28 20:18:11 +00:00
|
|
|
item = item->GetParent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-27 13:38:02 +00:00
|
|
|
static
|
|
|
|
nsMenuPopupFrame* GetPopupToMoveOrResize(nsIView* aView)
|
|
|
|
{
|
|
|
|
nsIFrame *frame = static_cast<nsIFrame *>(aView->GetClientData());
|
|
|
|
if (!frame || frame->GetType() != nsGkAtoms::menuPopupFrame)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
// no point moving or resizing hidden popups
|
|
|
|
nsMenuPopupFrame* menuPopupFrame = static_cast<nsMenuPopupFrame *>(frame);
|
|
|
|
if (menuPopupFrame->PopupState() != ePopupOpenAndVisible)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
return menuPopupFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::PopupMoved(nsIView* aView, nsIntPoint aPnt)
|
|
|
|
{
|
|
|
|
nsMenuPopupFrame* menuPopupFrame = GetPopupToMoveOrResize(aView);
|
|
|
|
if (!menuPopupFrame)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Don't do anything if the popup is already at the specified location. This
|
|
|
|
// prevents recursive calls when a popup is positioned.
|
|
|
|
nsIntPoint currentPnt = menuPopupFrame->ScreenPosition();
|
|
|
|
if (aPnt.x != currentPnt.x || aPnt.y != currentPnt.y) {
|
|
|
|
// Update the popup's position using SetPopupPosition if the popup is
|
|
|
|
// anchored and at the parent level as these maintain their position
|
|
|
|
// relative to the parent window. Otherwise, just update the popup to
|
|
|
|
// the specified screen coordinates.
|
2010-07-27 13:38:03 +00:00
|
|
|
if (menuPopupFrame->IsAnchored() &&
|
|
|
|
menuPopupFrame->PopupLevel() == ePopupLevelParent) {
|
2010-07-27 13:38:02 +00:00
|
|
|
menuPopupFrame->SetPopupPosition(nsnull, PR_TRUE);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
menuPopupFrame->MoveTo(aPnt.x, aPnt.y, PR_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::PopupResized(nsIView* aView, nsIntSize aSize)
|
|
|
|
{
|
|
|
|
nsMenuPopupFrame* menuPopupFrame = GetPopupToMoveOrResize(aView);
|
|
|
|
if (!menuPopupFrame)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsPresContext* presContext = menuPopupFrame->PresContext();
|
|
|
|
|
|
|
|
nsSize currentSize = menuPopupFrame->GetSize();
|
|
|
|
if (aSize.width != presContext->AppUnitsToDevPixels(currentSize.width) ||
|
|
|
|
aSize.height != presContext->AppUnitsToDevPixels(currentSize.height)) {
|
|
|
|
// for resizes, we just set the width and height attributes
|
|
|
|
nsIContent* popup = menuPopupFrame->GetContent();
|
|
|
|
nsAutoString width, height;
|
|
|
|
width.AppendInt(aSize.width);
|
|
|
|
height.AppendInt(aSize.height);
|
|
|
|
popup->SetAttr(kNameSpaceID_None, nsGkAtoms::width, width, PR_FALSE);
|
|
|
|
popup->SetAttr(kNameSpaceID_None, nsGkAtoms::height, height, PR_TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
nsIFrame*
|
|
|
|
nsXULPopupManager::GetFrameOfTypeForContent(nsIContent* aContent,
|
2007-10-09 12:11:14 +00:00
|
|
|
nsIAtom* aFrameType,
|
|
|
|
PRBool aShouldFlush)
|
2007-07-04 15:49:38 +00:00
|
|
|
{
|
2010-05-13 13:42:57 +00:00
|
|
|
if (aShouldFlush) {
|
|
|
|
nsIDocument *document = aContent->GetCurrentDoc();
|
|
|
|
if (document) {
|
2010-06-25 13:59:57 +00:00
|
|
|
nsCOMPtr<nsIPresShell> presShell = document->GetShell();
|
2010-05-13 13:42:57 +00:00
|
|
|
if (presShell)
|
2010-09-06 03:30:17 +00:00
|
|
|
presShell->FlushPendingNotifications(Flush_Layout);
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-13 13:42:57 +00:00
|
|
|
nsIFrame* frame = aContent->GetPrimaryFrame();
|
|
|
|
return (frame && frame->GetType() == aFrameType) ? frame : nsnull;
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsMenuFrame*
|
|
|
|
nsXULPopupManager::GetMenuFrameForContent(nsIContent* aContent)
|
|
|
|
{
|
2007-10-09 12:11:14 +00:00
|
|
|
// as ShowMenu is called from frames, don't flush to be safe.
|
2007-07-08 07:08:04 +00:00
|
|
|
return static_cast<nsMenuFrame *>
|
2007-10-09 12:11:14 +00:00
|
|
|
(GetFrameOfTypeForContent(aContent, nsGkAtoms::menuFrame, PR_FALSE));
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsMenuPopupFrame*
|
2010-08-09 16:17:19 +00:00
|
|
|
nsXULPopupManager::GetPopupFrameForContent(nsIContent* aContent, PRBool aShouldFlush)
|
2007-07-04 15:49:38 +00:00
|
|
|
{
|
2007-07-08 07:08:04 +00:00
|
|
|
return static_cast<nsMenuPopupFrame *>
|
2010-08-09 16:17:19 +00:00
|
|
|
(GetFrameOfTypeForContent(aContent, nsGkAtoms::menuPopupFrame, aShouldFlush));
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
2007-08-15 14:03:21 +00:00
|
|
|
nsMenuChainItem*
|
|
|
|
nsXULPopupManager::GetTopVisibleMenu()
|
|
|
|
{
|
2008-12-05 16:37:30 +00:00
|
|
|
nsMenuChainItem* item = mPopups;
|
2007-08-15 14:03:21 +00:00
|
|
|
while (item && item->Frame()->PopupState() == ePopupInvisible)
|
|
|
|
item = item->GetParent();
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
void
|
|
|
|
nsXULPopupManager::GetMouseLocation(nsIDOMNode** aNode, PRInt32* aOffset)
|
|
|
|
{
|
|
|
|
*aNode = mRangeParent;
|
|
|
|
NS_IF_ADDREF(*aNode);
|
|
|
|
*aOffset = mRangeOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-08-09 16:17:19 +00:00
|
|
|
nsXULPopupManager::InitTriggerEvent(nsIDOMEvent* aEvent, nsIContent* aPopup,
|
|
|
|
nsIContent** aTriggerContent)
|
2007-07-04 15:49:38 +00:00
|
|
|
{
|
2009-01-15 03:27:09 +00:00
|
|
|
mCachedMousePoint = nsIntPoint(0, 0);
|
2007-07-17 12:21:53 +00:00
|
|
|
|
2010-08-09 16:17:19 +00:00
|
|
|
if (aTriggerContent) {
|
|
|
|
*aTriggerContent = nsnull;
|
|
|
|
if (aEvent) {
|
|
|
|
// get the trigger content from the event
|
|
|
|
nsCOMPtr<nsIDOMEventTarget> target;
|
|
|
|
aEvent->GetTarget(getter_AddRefs(target));
|
|
|
|
if (target) {
|
|
|
|
CallQueryInterface(target, aTriggerContent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
nsCOMPtr<nsIDOMNSUIEvent> uiEvent = do_QueryInterface(aEvent);
|
|
|
|
if (uiEvent) {
|
|
|
|
uiEvent->GetRangeParent(getter_AddRefs(mRangeParent));
|
|
|
|
uiEvent->GetRangeOffset(&mRangeOffset);
|
2007-07-17 12:21:53 +00:00
|
|
|
|
|
|
|
// get the event coordinates relative to the root frame of the document
|
|
|
|
// containing the popup.
|
|
|
|
nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(aEvent));
|
|
|
|
if (privateEvent) {
|
|
|
|
NS_ASSERTION(aPopup, "Expected a popup node");
|
|
|
|
nsEvent* event;
|
2008-10-09 23:23:07 +00:00
|
|
|
event = privateEvent->GetInternalNSEvent();
|
|
|
|
if (event) {
|
2007-07-17 12:21:53 +00:00
|
|
|
nsIDocument* doc = aPopup->GetCurrentDoc();
|
|
|
|
if (doc) {
|
2010-06-25 13:59:57 +00:00
|
|
|
nsIPresShell* presShell = doc->GetShell();
|
2010-07-19 02:23:48 +00:00
|
|
|
nsPresContext* presContext;
|
|
|
|
if (presShell && (presContext = presShell->GetPresContext())) {
|
2009-07-22 00:45:06 +00:00
|
|
|
nsPresContext* rootDocPresContext =
|
2010-07-19 02:23:48 +00:00
|
|
|
presContext->GetRootPresContext();
|
2010-01-26 13:10:12 +00:00
|
|
|
if (!rootDocPresContext)
|
|
|
|
return;
|
2009-07-22 00:45:06 +00:00
|
|
|
nsIFrame* rootDocumentRootFrame = rootDocPresContext->
|
|
|
|
PresShell()->FrameManager()->GetRootFrame();
|
2008-02-20 20:16:55 +00:00
|
|
|
if ((event->eventStructType == NS_MOUSE_EVENT ||
|
|
|
|
event->eventStructType == NS_MOUSE_SCROLL_EVENT) &&
|
|
|
|
!(static_cast<nsGUIEvent *>(event))->widget) {
|
|
|
|
// no widget, so just use the client point if available
|
|
|
|
nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
|
2009-07-22 00:45:06 +00:00
|
|
|
nsIntPoint clientPt;
|
|
|
|
mouseEvent->GetClientX(&clientPt.x);
|
|
|
|
mouseEvent->GetClientY(&clientPt.y);
|
2008-02-20 20:16:55 +00:00
|
|
|
|
2009-07-22 00:45:06 +00:00
|
|
|
// XXX this doesn't handle IFRAMEs in transforms
|
2010-07-19 02:23:48 +00:00
|
|
|
nsPoint thisDocToRootDocOffset = presShell->FrameManager()->
|
|
|
|
GetRootFrame()->GetOffsetToCrossDoc(rootDocumentRootFrame);
|
2008-02-20 20:16:55 +00:00
|
|
|
// convert to device pixels
|
2010-07-19 02:23:48 +00:00
|
|
|
mCachedMousePoint.x = presContext->AppUnitsToDevPixels(
|
2009-07-22 00:45:06 +00:00
|
|
|
nsPresContext::CSSPixelsToAppUnits(clientPt.x) + thisDocToRootDocOffset.x);
|
2010-07-19 02:23:48 +00:00
|
|
|
mCachedMousePoint.y = presContext->AppUnitsToDevPixels(
|
2009-07-22 00:45:06 +00:00
|
|
|
nsPresContext::CSSPixelsToAppUnits(clientPt.y) + thisDocToRootDocOffset.y);
|
2008-02-20 20:16:55 +00:00
|
|
|
}
|
2009-07-22 00:45:06 +00:00
|
|
|
else if (rootDocumentRootFrame) {
|
2007-07-17 12:21:53 +00:00
|
|
|
nsPoint pnt =
|
2009-07-22 00:45:06 +00:00
|
|
|
nsLayoutUtils::GetEventCoordinatesRelativeTo(event, rootDocumentRootFrame);
|
|
|
|
mCachedMousePoint = nsIntPoint(rootDocPresContext->AppUnitsToDevPixels(pnt.x),
|
|
|
|
rootDocPresContext->AppUnitsToDevPixels(pnt.y));
|
2007-07-17 12:21:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
mRangeParent = nsnull;
|
|
|
|
mRangeOffset = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::SetActiveMenuBar(nsMenuBarFrame* aMenuBar, PRBool aActivate)
|
|
|
|
{
|
|
|
|
if (aActivate)
|
|
|
|
mActiveMenuBar = aMenuBar;
|
|
|
|
else if (mActiveMenuBar == aMenuBar)
|
|
|
|
mActiveMenuBar = nsnull;
|
|
|
|
|
|
|
|
UpdateKeyboardListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::ShowMenu(nsIContent *aMenu,
|
|
|
|
PRBool aSelectFirstItem,
|
|
|
|
PRBool aAsynchronous)
|
|
|
|
{
|
2008-02-21 17:40:12 +00:00
|
|
|
// generate any template content first. Otherwise, the menupopup may not
|
|
|
|
// have been created yet.
|
|
|
|
if (aMenu) {
|
|
|
|
nsIContent* element = aMenu;
|
|
|
|
do {
|
|
|
|
nsCOMPtr<nsIDOMXULElement> xulelem = do_QueryInterface(element);
|
|
|
|
if (xulelem) {
|
|
|
|
nsCOMPtr<nsIXULTemplateBuilder> builder;
|
|
|
|
xulelem->GetBuilder(getter_AddRefs(builder));
|
|
|
|
if (builder) {
|
|
|
|
builder->CreateContents(aMenu, PR_TRUE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
element = element->GetParent();
|
|
|
|
} while (element);
|
|
|
|
}
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
nsMenuFrame* menuFrame = GetMenuFrameForContent(aMenu);
|
|
|
|
if (!menuFrame || !menuFrame->IsMenu())
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsMenuPopupFrame* popupFrame = menuFrame->GetPopup();
|
|
|
|
if (!popupFrame || !MayShowPopup(popupFrame))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// inherit whether or not we're a context menu from the parent
|
|
|
|
PRBool parentIsContextMenu = PR_FALSE;
|
|
|
|
PRBool onMenuBar = PR_FALSE;
|
|
|
|
PRBool onmenu = menuFrame->IsOnMenu();
|
|
|
|
|
2008-12-05 16:37:31 +00:00
|
|
|
nsMenuParent* parent = menuFrame->GetMenuParent();
|
2007-07-04 15:49:38 +00:00
|
|
|
if (parent && onmenu) {
|
|
|
|
parentIsContextMenu = parent->IsContextMenu();
|
|
|
|
onMenuBar = parent->IsMenuBar();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsAutoString position;
|
|
|
|
if (onMenuBar || !onmenu)
|
|
|
|
position.AssignLiteral("after_start");
|
|
|
|
else
|
|
|
|
position.AssignLiteral("end_before");
|
2010-08-09 16:17:19 +00:00
|
|
|
|
2010-09-06 03:30:17 +00:00
|
|
|
// there is no trigger event for menus
|
|
|
|
InitTriggerEvent(nsnull, nsnull, nsnull);
|
2010-08-09 16:17:19 +00:00
|
|
|
popupFrame->InitializePopup(aMenu, nsnull, position, 0, 0, PR_TRUE);
|
2007-07-04 15:49:38 +00:00
|
|
|
|
|
|
|
if (aAsynchronous) {
|
|
|
|
nsCOMPtr<nsIRunnable> event =
|
2010-09-06 03:30:17 +00:00
|
|
|
new nsXULPopupShowingEvent(popupFrame->GetContent(),
|
2007-07-04 15:49:38 +00:00
|
|
|
parentIsContextMenu, aSelectFirstItem);
|
|
|
|
NS_DispatchToCurrentThread(event);
|
|
|
|
}
|
|
|
|
else {
|
2007-08-03 14:05:07 +00:00
|
|
|
nsCOMPtr<nsIContent> popupContent = popupFrame->GetContent();
|
2010-09-06 03:30:17 +00:00
|
|
|
FirePopupShowingEvent(popupContent, parentIsContextMenu, aSelectFirstItem);
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::ShowPopup(nsIContent* aPopup,
|
|
|
|
nsIContent* aAnchorContent,
|
|
|
|
const nsAString& aPosition,
|
|
|
|
PRInt32 aXPos, PRInt32 aYPos,
|
|
|
|
PRBool aIsContextMenu,
|
|
|
|
PRBool aAttributesOverride,
|
2007-11-17 15:47:38 +00:00
|
|
|
PRBool aSelectFirstItem,
|
|
|
|
nsIDOMEvent* aTriggerEvent)
|
2007-07-04 15:49:38 +00:00
|
|
|
{
|
2010-08-09 16:17:19 +00:00
|
|
|
nsMenuPopupFrame* popupFrame = GetPopupFrameForContent(aPopup, PR_TRUE);
|
2007-07-04 15:49:38 +00:00
|
|
|
if (!popupFrame || !MayShowPopup(popupFrame))
|
|
|
|
return;
|
|
|
|
|
2010-08-09 16:17:19 +00:00
|
|
|
nsCOMPtr<nsIContent> triggerContent;
|
|
|
|
InitTriggerEvent(aTriggerEvent, aPopup, getter_AddRefs(triggerContent));
|
2007-11-17 15:47:38 +00:00
|
|
|
|
2010-08-09 16:17:19 +00:00
|
|
|
popupFrame->InitializePopup(aAnchorContent, triggerContent, aPosition,
|
|
|
|
aXPos, aYPos, aAttributesOverride);
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2010-09-06 03:30:17 +00:00
|
|
|
FirePopupShowingEvent(aPopup, aIsContextMenu, aSelectFirstItem);
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::ShowPopupAtScreen(nsIContent* aPopup,
|
|
|
|
PRInt32 aXPos, PRInt32 aYPos,
|
2007-11-17 15:47:38 +00:00
|
|
|
PRBool aIsContextMenu,
|
|
|
|
nsIDOMEvent* aTriggerEvent)
|
2007-07-04 15:49:38 +00:00
|
|
|
{
|
2010-08-09 16:17:19 +00:00
|
|
|
nsMenuPopupFrame* popupFrame = GetPopupFrameForContent(aPopup, PR_TRUE);
|
2007-07-04 15:49:38 +00:00
|
|
|
if (!popupFrame || !MayShowPopup(popupFrame))
|
|
|
|
return;
|
|
|
|
|
2010-08-09 16:17:19 +00:00
|
|
|
nsCOMPtr<nsIContent> triggerContent;
|
|
|
|
InitTriggerEvent(aTriggerEvent, aPopup, getter_AddRefs(triggerContent));
|
2007-11-17 15:47:38 +00:00
|
|
|
|
2010-08-09 16:17:19 +00:00
|
|
|
popupFrame->InitializePopupAtScreen(triggerContent, aXPos, aYPos, aIsContextMenu);
|
2010-09-06 03:30:17 +00:00
|
|
|
FirePopupShowingEvent(aPopup, aIsContextMenu, PR_FALSE);
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::ShowPopupWithAnchorAlign(nsIContent* aPopup,
|
|
|
|
nsIContent* aAnchorContent,
|
|
|
|
nsAString& aAnchor,
|
|
|
|
nsAString& aAlign,
|
|
|
|
PRInt32 aXPos, PRInt32 aYPos,
|
|
|
|
PRBool aIsContextMenu)
|
|
|
|
{
|
2010-08-09 16:17:19 +00:00
|
|
|
nsMenuPopupFrame* popupFrame = GetPopupFrameForContent(aPopup, PR_TRUE);
|
2007-07-04 15:49:38 +00:00
|
|
|
if (!popupFrame || !MayShowPopup(popupFrame))
|
|
|
|
return;
|
|
|
|
|
2010-08-09 16:17:19 +00:00
|
|
|
InitTriggerEvent(nsnull, aPopup, nsnull);
|
2007-11-17 15:47:38 +00:00
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
popupFrame->InitializePopupWithAnchorAlign(aAnchorContent, aAnchor,
|
|
|
|
aAlign, aXPos, aYPos);
|
2010-09-06 03:30:17 +00:00
|
|
|
FirePopupShowingEvent(aPopup, aIsContextMenu, PR_FALSE);
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
2008-04-01 08:35:11 +00:00
|
|
|
static void
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 18:00:39 +00:00
|
|
|
CheckCaretDrawingState() {
|
2008-04-01 08:35:11 +00:00
|
|
|
|
|
|
|
// There is 1 caret per document, we need to find the focused
|
|
|
|
// document and erase its caret.
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 18:00:39 +00:00
|
|
|
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
|
|
|
if (fm) {
|
|
|
|
nsCOMPtr<nsIDOMWindow> window;
|
|
|
|
fm->GetFocusedWindow(getter_AddRefs(window));
|
|
|
|
if (!window)
|
|
|
|
return;
|
2008-04-01 08:35:11 +00:00
|
|
|
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 18:00:39 +00:00
|
|
|
nsCOMPtr<nsIDOMWindowInternal> windowInternal = do_QueryInterface(window);
|
2008-04-01 08:35:11 +00:00
|
|
|
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 18:00:39 +00:00
|
|
|
nsCOMPtr<nsIDOMDocument> domDoc;
|
|
|
|
nsCOMPtr<nsIDocument> focusedDoc;
|
|
|
|
windowInternal->GetDocument(getter_AddRefs(domDoc));
|
|
|
|
focusedDoc = do_QueryInterface(domDoc);
|
|
|
|
if (!focusedDoc)
|
|
|
|
return;
|
2008-04-01 08:35:11 +00:00
|
|
|
|
2010-06-25 13:59:57 +00:00
|
|
|
nsIPresShell* presShell = focusedDoc->GetShell();
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 18:00:39 +00:00
|
|
|
if (!presShell)
|
|
|
|
return;
|
2008-04-01 08:35:11 +00:00
|
|
|
|
2010-03-31 12:39:31 +00:00
|
|
|
nsRefPtr<nsCaret> caret = presShell->GetCaret();
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 18:00:39 +00:00
|
|
|
if (!caret)
|
|
|
|
return;
|
|
|
|
caret->CheckCaretDrawingState();
|
|
|
|
}
|
2008-04-01 08:35:11 +00:00
|
|
|
}
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
void
|
|
|
|
nsXULPopupManager::ShowPopupCallback(nsIContent* aPopup,
|
|
|
|
nsMenuPopupFrame* aPopupFrame,
|
|
|
|
PRBool aIsContextMenu,
|
|
|
|
PRBool aSelectFirstItem)
|
|
|
|
{
|
2007-08-16 01:09:58 +00:00
|
|
|
nsPopupType popupType = aPopupFrame->PopupType();
|
2007-07-04 15:49:38 +00:00
|
|
|
PRBool ismenu = (popupType == ePopupTypeMenu);
|
|
|
|
|
|
|
|
nsMenuChainItem* item =
|
2007-08-16 01:09:58 +00:00
|
|
|
new nsMenuChainItem(aPopupFrame, aIsContextMenu, popupType);
|
2007-07-04 15:49:38 +00:00
|
|
|
if (!item)
|
|
|
|
return;
|
|
|
|
|
2007-11-12 21:52:13 +00:00
|
|
|
// install keyboard event listeners for navigating menus. For panels, the
|
|
|
|
// escape key may be used to close the panel. However, the ignorekeys
|
|
|
|
// attribute may be used to disable adding these event listeners for popups
|
|
|
|
// that want to handle their own keyboard events.
|
|
|
|
if (aPopup->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ignorekeys,
|
|
|
|
nsGkAtoms::_true, eCaseMatters))
|
|
|
|
item->SetIgnoreKeys(PR_TRUE);
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2007-11-12 21:52:13 +00:00
|
|
|
if (ismenu) {
|
2007-07-04 15:49:38 +00:00
|
|
|
// if the menu is on a menubar, use the menubar's listener instead
|
2010-08-09 16:17:19 +00:00
|
|
|
nsMenuFrame* menuFrame = aPopupFrame->GetParentMenu();
|
|
|
|
if (menuFrame) {
|
2007-07-04 15:49:38 +00:00
|
|
|
item->SetOnMenuBar(menuFrame->IsOnMenuBar());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// use a weak frame as the popup will set an open attribute if it is a menu
|
|
|
|
nsWeakFrame weakFrame(aPopupFrame);
|
2010-09-06 03:30:17 +00:00
|
|
|
aPopupFrame->ShowPopup(aIsContextMenu, aSelectFirstItem);
|
2007-07-04 15:49:38 +00:00
|
|
|
ENSURE_TRUE(weakFrame.IsAlive());
|
|
|
|
|
|
|
|
// popups normally hide when an outside click occurs. Panels may use
|
|
|
|
// the noautohide attribute to disable this behaviour. It is expected
|
|
|
|
// that the application will hide these popups manually. The tooltip
|
|
|
|
// listener will handle closing the tooltip also.
|
2007-11-28 20:18:11 +00:00
|
|
|
if (aPopupFrame->IsNoAutoHide() || popupType == ePopupTypeTooltip) {
|
2008-12-05 16:37:30 +00:00
|
|
|
item->SetParent(mNoHidePanels);
|
|
|
|
mNoHidePanels = item;
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
nsIContent* oldmenu = nsnull;
|
2008-12-05 16:37:30 +00:00
|
|
|
if (mPopups)
|
|
|
|
oldmenu = mPopups->Content();
|
|
|
|
item->SetParent(mPopups);
|
|
|
|
mPopups = item;
|
2007-07-04 15:49:38 +00:00
|
|
|
SetCaptureState(oldmenu);
|
|
|
|
}
|
|
|
|
|
2010-09-06 03:30:17 +00:00
|
|
|
if (aSelectFirstItem) {
|
|
|
|
nsMenuFrame* next = GetNextMenuItem(aPopupFrame, nsnull, PR_TRUE);
|
|
|
|
aPopupFrame->SetCurrentMenuItem(next);
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
2008-04-01 08:35:11 +00:00
|
|
|
|
2010-09-06 03:30:17 +00:00
|
|
|
if (ismenu)
|
|
|
|
UpdateMenuItems(aPopup);
|
|
|
|
|
2008-04-01 08:35:11 +00:00
|
|
|
// Caret visibility may have been affected, ensure that
|
|
|
|
// the caret isn't now drawn when it shouldn't be.
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 18:00:39 +00:00
|
|
|
CheckCaretDrawingState();
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::HidePopup(nsIContent* aPopup,
|
|
|
|
PRBool aHideChain,
|
|
|
|
PRBool aDeselectMenu,
|
2009-06-12 18:23:16 +00:00
|
|
|
PRBool aAsynchronous,
|
|
|
|
nsIContent* aLastPopup)
|
2007-07-04 15:49:38 +00:00
|
|
|
{
|
2008-12-05 16:37:30 +00:00
|
|
|
// if the popup is on the nohide panels list, remove it but don't close any
|
|
|
|
// other panels
|
2007-07-04 15:49:38 +00:00
|
|
|
nsMenuPopupFrame* popupFrame = nsnull;
|
|
|
|
PRBool foundPanel = PR_FALSE;
|
2008-12-05 16:37:30 +00:00
|
|
|
nsMenuChainItem* item = mNoHidePanels;
|
2007-07-04 15:49:38 +00:00
|
|
|
while (item) {
|
|
|
|
if (item->Content() == aPopup) {
|
|
|
|
foundPanel = PR_TRUE;
|
|
|
|
popupFrame = item->Frame();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
item = item->GetParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
// when removing a menu, all of the child popups must be closed
|
|
|
|
nsMenuChainItem* foundMenu = nsnull;
|
2008-12-05 16:37:30 +00:00
|
|
|
item = mPopups;
|
2007-07-04 15:49:38 +00:00
|
|
|
while (item) {
|
|
|
|
if (item->Content() == aPopup) {
|
|
|
|
foundMenu = item;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
item = item->GetParent();
|
|
|
|
}
|
|
|
|
|
2007-08-16 01:09:58 +00:00
|
|
|
nsPopupType type = ePopupTypePanel;
|
2007-07-04 15:49:38 +00:00
|
|
|
PRBool deselectMenu = PR_FALSE;
|
|
|
|
nsCOMPtr<nsIContent> popupToHide, nextPopup, lastPopup;
|
|
|
|
if (foundMenu) {
|
2007-08-23 22:13:42 +00:00
|
|
|
// at this point, foundMenu will be set to the found item in the list. If
|
|
|
|
// foundMenu is the topmost menu, the one to remove, then there are no other
|
|
|
|
// popups to hide. If foundMenu is not the topmost menu, then there may be
|
2007-07-04 15:49:38 +00:00
|
|
|
// open submenus below it. In this case, we need to make sure that those
|
2007-08-23 22:13:42 +00:00
|
|
|
// submenus are closed up first. To do this, we scan up the menu list to
|
|
|
|
// find the topmost popup with only menus between it and foundMenu and
|
|
|
|
// close that menu first. In synchronous mode, the FirePopupHidingEvent
|
|
|
|
// method will be called which in turn calls HidePopupCallback to close up
|
|
|
|
// the next popup in the chain. These two methods will be called in
|
|
|
|
// sequence recursively to close up all the necessary popups. In
|
|
|
|
// asynchronous mode, a similar process occurs except that the
|
2007-11-17 15:47:38 +00:00
|
|
|
// FirePopupHidingEvent method is called asynchronously. In either case,
|
2007-08-23 22:13:42 +00:00
|
|
|
// nextPopup is set to the content node of the next popup to close, and
|
|
|
|
// lastPopup is set to the last popup in the chain to close, which will be
|
|
|
|
// aPopup, or null to close up all menus.
|
|
|
|
|
|
|
|
nsMenuChainItem* topMenu = foundMenu;
|
|
|
|
// Use IsMenu to ensure that foundMenu is a menu and scan down the child
|
|
|
|
// list until a non-menu is found. If foundMenu isn't a menu at all, don't
|
|
|
|
// scan and just close up this menu.
|
|
|
|
if (foundMenu->IsMenu()) {
|
|
|
|
item = topMenu->GetChild();
|
|
|
|
while (item && item->IsMenu()) {
|
|
|
|
topMenu = item;
|
|
|
|
item = item->GetChild();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
deselectMenu = aDeselectMenu;
|
2007-08-23 22:13:42 +00:00
|
|
|
popupToHide = topMenu->Content();
|
|
|
|
popupFrame = topMenu->Frame();
|
2007-08-16 01:09:58 +00:00
|
|
|
type = popupFrame->PopupType();
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2007-08-23 22:13:42 +00:00
|
|
|
nsMenuChainItem* parent = topMenu->GetParent();
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2007-08-03 14:05:07 +00:00
|
|
|
// close up another popup if there is one, and we are either hiding the
|
|
|
|
// entire chain or the item to hide isn't the topmost popup.
|
2007-08-23 22:13:42 +00:00
|
|
|
if (parent && (aHideChain || topMenu != foundMenu))
|
2007-08-03 14:05:07 +00:00
|
|
|
nextPopup = parent->Content();
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2009-06-12 18:23:16 +00:00
|
|
|
lastPopup = aLastPopup ? aLastPopup : (aHideChain ? nsnull : aPopup);
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
else if (foundPanel) {
|
|
|
|
popupToHide = aPopup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (popupFrame) {
|
2007-08-03 14:05:07 +00:00
|
|
|
nsPopupState state = popupFrame->PopupState();
|
|
|
|
// if the popup is already being hidden, don't attempt to hide it again
|
|
|
|
if (state == ePopupHiding)
|
|
|
|
return;
|
|
|
|
// change the popup state to hiding. Don't set the hiding state if the
|
|
|
|
// popup is invisible, otherwise nsMenuPopupFrame::HidePopup will
|
|
|
|
// run again. In the invisible state, we just want the events to fire.
|
|
|
|
if (state != ePopupInvisible)
|
|
|
|
popupFrame->SetPopupState(ePopupHiding);
|
|
|
|
|
2010-08-09 16:17:19 +00:00
|
|
|
// for menus, popupToHide is always the frontmost item in the list to hide.
|
2007-07-04 15:49:38 +00:00
|
|
|
if (aAsynchronous) {
|
|
|
|
nsCOMPtr<nsIRunnable> event =
|
|
|
|
new nsXULPopupHidingEvent(popupToHide, nextPopup, lastPopup,
|
2007-08-16 01:09:58 +00:00
|
|
|
type, deselectMenu);
|
2007-07-04 15:49:38 +00:00
|
|
|
NS_DispatchToCurrentThread(event);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
FirePopupHidingEvent(popupToHide, nextPopup, lastPopup,
|
2007-08-16 01:09:58 +00:00
|
|
|
popupFrame->PresContext(), type, deselectMenu);
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::HidePopupCallback(nsIContent* aPopup,
|
|
|
|
nsMenuPopupFrame* aPopupFrame,
|
|
|
|
nsIContent* aNextPopup,
|
|
|
|
nsIContent* aLastPopup,
|
2007-08-16 01:09:58 +00:00
|
|
|
nsPopupType aPopupType,
|
2007-07-04 15:49:38 +00:00
|
|
|
PRBool aDeselectMenu)
|
|
|
|
{
|
2007-10-29 17:26:19 +00:00
|
|
|
if (mCloseTimer && mTimerMenu == aPopupFrame) {
|
2007-07-04 15:49:38 +00:00
|
|
|
mCloseTimer->Cancel();
|
|
|
|
mCloseTimer = nsnull;
|
|
|
|
mTimerMenu = nsnull;
|
|
|
|
}
|
|
|
|
|
2007-08-03 14:05:07 +00:00
|
|
|
// The popup to hide is aPopup. Search the list again to find the item that
|
|
|
|
// corresponds to the popup to hide aPopup. This is done because it's
|
|
|
|
// possible someone added another item (attempted to open another popup)
|
|
|
|
// or removed a popup frame during the event processing so the item isn't at
|
|
|
|
// the front anymore.
|
2008-12-05 16:37:30 +00:00
|
|
|
nsMenuChainItem* item = mNoHidePanels;
|
2007-08-03 14:05:07 +00:00
|
|
|
while (item) {
|
|
|
|
if (item->Content() == aPopup) {
|
2008-12-05 16:37:30 +00:00
|
|
|
item->Detach(&mNoHidePanels);
|
2007-08-03 14:05:07 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
item = item->GetParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!item) {
|
2008-12-05 16:37:30 +00:00
|
|
|
item = mPopups;
|
2007-08-03 14:05:07 +00:00
|
|
|
while (item) {
|
|
|
|
if (item->Content() == aPopup) {
|
2008-12-05 16:37:30 +00:00
|
|
|
item->Detach(&mPopups);
|
2007-08-03 14:05:07 +00:00
|
|
|
SetCaptureState(aPopup);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
item = item->GetParent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
delete item;
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
nsWeakFrame weakFrame(aPopupFrame);
|
2007-08-03 14:05:07 +00:00
|
|
|
aPopupFrame->HidePopup(aDeselectMenu, ePopupClosed);
|
2007-07-04 15:49:38 +00:00
|
|
|
ENSURE_TRUE(weakFrame.IsAlive());
|
|
|
|
|
|
|
|
// send the popuphidden event synchronously. This event has no default behaviour.
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2007-07-17 12:21:53 +00:00
|
|
|
nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_HIDDEN, nsnull, nsMouseEvent::eReal);
|
2007-07-04 15:49:38 +00:00
|
|
|
nsEventDispatcher::Dispatch(aPopup, aPopupFrame->PresContext(),
|
|
|
|
&event, nsnull, &status);
|
|
|
|
|
|
|
|
// if there are more popups to close, look for the next one
|
|
|
|
if (aNextPopup && aPopup != aLastPopup) {
|
|
|
|
nsMenuChainItem* foundMenu = nsnull;
|
2008-12-05 16:37:30 +00:00
|
|
|
nsMenuChainItem* item = mPopups;
|
2007-07-04 15:49:38 +00:00
|
|
|
while (item) {
|
|
|
|
if (item->Content() == aNextPopup) {
|
|
|
|
foundMenu = item;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
item = item->GetParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
// continue hiding the chain of popups until the last popup aLastPopup
|
|
|
|
// is reached, or until a popup of a different type is reached. This
|
|
|
|
// last check is needed so that a menulist inside a non-menu panel only
|
|
|
|
// closes the menu and not the panel as well.
|
2007-08-16 01:09:58 +00:00
|
|
|
if (foundMenu &&
|
|
|
|
(aLastPopup || aPopupType == foundMenu->PopupType())) {
|
2009-06-12 18:23:16 +00:00
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
nsCOMPtr<nsIContent> popupToHide = item->Content();
|
2007-07-10 17:03:32 +00:00
|
|
|
nsMenuChainItem* parent = item->GetParent();
|
2007-07-04 15:49:38 +00:00
|
|
|
|
|
|
|
nsCOMPtr<nsIContent> nextPopup;
|
2007-07-10 17:03:32 +00:00
|
|
|
if (parent && popupToHide != aLastPopup)
|
|
|
|
nextPopup = parent->Content();
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2007-08-03 14:05:07 +00:00
|
|
|
nsMenuPopupFrame* popupFrame = item->Frame();
|
|
|
|
nsPopupState state = popupFrame->PopupState();
|
|
|
|
if (state == ePopupHiding)
|
|
|
|
return;
|
|
|
|
if (state != ePopupInvisible)
|
|
|
|
popupFrame->SetPopupState(ePopupHiding);
|
2007-07-04 15:49:38 +00:00
|
|
|
|
|
|
|
FirePopupHidingEvent(popupToHide, nextPopup, aLastPopup,
|
2007-08-16 01:09:58 +00:00
|
|
|
popupFrame->PresContext(),
|
|
|
|
foundMenu->PopupType(), aDeselectMenu);
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-27 13:38:04 +00:00
|
|
|
void
|
|
|
|
nsXULPopupManager::HidePopup(nsIView* aView)
|
|
|
|
{
|
|
|
|
nsIFrame *frame = static_cast<nsIFrame *>(aView->GetClientData());
|
|
|
|
if (!frame || frame->GetType() != nsGkAtoms::menuPopupFrame)
|
|
|
|
return;
|
|
|
|
|
|
|
|
HidePopup(frame->GetContent(), PR_FALSE, PR_TRUE, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
void
|
|
|
|
nsXULPopupManager::HidePopupAfterDelay(nsMenuPopupFrame* aPopup)
|
|
|
|
{
|
|
|
|
// Don't close up immediately.
|
|
|
|
// Kick off a close timer.
|
|
|
|
KillMenuTimer();
|
|
|
|
|
|
|
|
PRInt32 menuDelay = 300; // ms
|
|
|
|
aPopup->PresContext()->LookAndFeel()->
|
|
|
|
GetMetric(nsILookAndFeel::eMetric_SubmenuDelay, menuDelay);
|
|
|
|
|
|
|
|
// Kick off the timer.
|
|
|
|
mCloseTimer = do_CreateInstance("@mozilla.org/timer;1");
|
|
|
|
mCloseTimer->InitWithCallback(this, menuDelay, nsITimer::TYPE_ONE_SHOT);
|
|
|
|
|
|
|
|
// the popup will call PopupDestroyed if it is destroyed, which checks if it
|
|
|
|
// is set to mTimerMenu, so it should be safe to keep a reference to it
|
|
|
|
mTimerMenu = aPopup;
|
|
|
|
}
|
|
|
|
|
2007-11-17 15:47:38 +00:00
|
|
|
void
|
|
|
|
nsXULPopupManager::HidePopupsInList(const nsTArray<nsMenuPopupFrame *> &aFrames,
|
|
|
|
PRBool aDeselectMenu)
|
|
|
|
{
|
|
|
|
// Create a weak frame list. This is done in a separate array with the
|
|
|
|
// right capacity predetermined, otherwise the array would get resized and
|
|
|
|
// move the weak frame pointers around.
|
|
|
|
nsTArray<nsWeakFrame> weakPopups(aFrames.Length());
|
|
|
|
PRUint32 f;
|
|
|
|
for (f = 0; f < aFrames.Length(); f++) {
|
|
|
|
nsWeakFrame* wframe = weakPopups.AppendElement();
|
|
|
|
if (wframe)
|
|
|
|
*wframe = aFrames[f];
|
|
|
|
}
|
|
|
|
|
|
|
|
for (f = 0; f < weakPopups.Length(); f++) {
|
|
|
|
// check to ensure that the frame is still alive before hiding it.
|
|
|
|
if (weakPopups[f].IsAlive()) {
|
|
|
|
nsMenuPopupFrame* frame =
|
|
|
|
static_cast<nsMenuPopupFrame *>(weakPopups[f].GetFrame());
|
|
|
|
frame->HidePopup(PR_TRUE, ePopupInvisible);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SetCaptureState(nsnull);
|
|
|
|
}
|
|
|
|
|
2008-02-08 20:23:05 +00:00
|
|
|
PRBool
|
|
|
|
nsXULPopupManager::IsChildOfDocShell(nsIDocument* aDoc, nsIDocShellTreeItem* aExpected)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsISupports> doc = aDoc->GetContainer();
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> docShellItem(do_QueryInterface(doc));
|
|
|
|
while(docShellItem) {
|
|
|
|
if (docShellItem == aExpected)
|
|
|
|
return PR_TRUE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> parent;
|
|
|
|
docShellItem->GetParent(getter_AddRefs(parent));
|
|
|
|
docShellItem = parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
void
|
2008-02-08 20:23:05 +00:00
|
|
|
nsXULPopupManager::HidePopupsInDocShell(nsIDocShellTreeItem* aDocShellToHide)
|
2007-07-04 15:49:38 +00:00
|
|
|
{
|
2007-11-17 15:47:38 +00:00
|
|
|
nsTArray<nsMenuPopupFrame *> popupsToHide;
|
|
|
|
|
|
|
|
// iterate to get the set of popup frames to hide
|
2008-12-05 16:37:30 +00:00
|
|
|
nsMenuChainItem* item = mPopups;
|
2007-07-04 15:49:38 +00:00
|
|
|
while (item) {
|
2007-08-03 14:05:07 +00:00
|
|
|
nsMenuChainItem* parent = item->GetParent();
|
2007-11-17 15:47:38 +00:00
|
|
|
if (item->Frame()->PopupState() != ePopupInvisible &&
|
2008-02-08 20:23:05 +00:00
|
|
|
IsChildOfDocShell(item->Content()->GetOwnerDoc(), aDocShellToHide)) {
|
2007-11-17 15:47:38 +00:00
|
|
|
nsMenuPopupFrame* frame = item->Frame();
|
2008-12-05 16:37:30 +00:00
|
|
|
item->Detach(&mPopups);
|
2007-08-03 14:05:07 +00:00
|
|
|
delete item;
|
2007-11-17 15:47:38 +00:00
|
|
|
popupsToHide.AppendElement(frame);
|
2007-08-03 14:05:07 +00:00
|
|
|
}
|
|
|
|
item = parent;
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
2007-11-17 15:47:38 +00:00
|
|
|
// now look for panels to hide
|
2008-12-05 16:37:30 +00:00
|
|
|
item = mNoHidePanels;
|
2007-07-04 15:49:38 +00:00
|
|
|
while (item) {
|
2007-08-03 14:05:07 +00:00
|
|
|
nsMenuChainItem* parent = item->GetParent();
|
2007-11-17 15:47:38 +00:00
|
|
|
if (item->Frame()->PopupState() != ePopupInvisible &&
|
2008-02-08 20:23:05 +00:00
|
|
|
IsChildOfDocShell(item->Content()->GetOwnerDoc(), aDocShellToHide)) {
|
2007-11-17 15:47:38 +00:00
|
|
|
nsMenuPopupFrame* frame = item->Frame();
|
2008-12-05 16:37:30 +00:00
|
|
|
item->Detach(&mNoHidePanels);
|
2007-08-03 14:05:07 +00:00
|
|
|
delete item;
|
2007-11-17 15:47:38 +00:00
|
|
|
popupsToHide.AppendElement(frame);
|
2007-08-03 14:05:07 +00:00
|
|
|
}
|
|
|
|
item = parent;
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
2007-08-15 14:03:21 +00:00
|
|
|
|
2007-11-17 15:47:38 +00:00
|
|
|
HidePopupsInList(popupsToHide, PR_TRUE);
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-04-19 14:12:58 +00:00
|
|
|
nsXULPopupManager::ExecuteMenu(nsIContent* aMenu, nsXULMenuCommandEvent* aEvent)
|
2007-07-04 15:49:38 +00:00
|
|
|
{
|
2007-08-30 15:43:10 +00:00
|
|
|
CloseMenuMode cmm = CloseMenuMode_Auto;
|
|
|
|
|
|
|
|
static nsIContent::AttrValuesArray strings[] =
|
|
|
|
{&nsGkAtoms::none, &nsGkAtoms::single, nsnull};
|
|
|
|
|
|
|
|
switch (aMenu->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::closemenu,
|
|
|
|
strings, eCaseMatters)) {
|
|
|
|
case 0:
|
|
|
|
cmm = CloseMenuMode_None;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
cmm = CloseMenuMode_Single;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
// When a menuitem is selected to be executed, first hide all the open
|
|
|
|
// popups, but don't remove them yet. This is needed when a menu command
|
|
|
|
// opens a modal dialog. The views associated with the popups needed to be
|
|
|
|
// hidden and the accesibility events fired before the command executes, but
|
|
|
|
// the popuphiding/popuphidden events are fired afterwards.
|
2007-11-17 15:47:38 +00:00
|
|
|
nsTArray<nsMenuPopupFrame *> popupsToHide;
|
2007-08-15 14:03:21 +00:00
|
|
|
nsMenuChainItem* item = GetTopVisibleMenu();
|
2007-08-30 15:43:10 +00:00
|
|
|
if (cmm != CloseMenuMode_None) {
|
|
|
|
while (item) {
|
|
|
|
// if it isn't a <menupopup>, don't close it automatically
|
|
|
|
if (!item->IsMenu())
|
|
|
|
break;
|
|
|
|
nsMenuChainItem* next = item->GetParent();
|
2007-11-17 15:47:38 +00:00
|
|
|
popupsToHide.AppendElement(item->Frame());
|
2007-08-30 15:43:10 +00:00
|
|
|
if (cmm == CloseMenuMode_Single) // only close one level of menu
|
|
|
|
break;
|
|
|
|
item = next;
|
|
|
|
}
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2007-11-17 15:47:38 +00:00
|
|
|
// Now hide the popups. If the closemenu mode is auto, deselect the menu,
|
|
|
|
// otherwise only one popup is closing, so keep the parent menu selected.
|
|
|
|
HidePopupsInList(popupsToHide, cmm == CloseMenuMode_Auto);
|
|
|
|
}
|
2007-08-15 14:03:21 +00:00
|
|
|
|
2010-04-19 14:12:58 +00:00
|
|
|
aEvent->SetCloseMenuMode(cmm);
|
|
|
|
nsCOMPtr<nsIRunnable> event = aEvent;
|
2007-07-04 15:49:38 +00:00
|
|
|
NS_DispatchToCurrentThread(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::FirePopupShowingEvent(nsIContent* aPopup,
|
|
|
|
PRBool aIsContextMenu,
|
|
|
|
PRBool aSelectFirstItem)
|
|
|
|
{
|
2010-09-06 03:30:17 +00:00
|
|
|
nsCOMPtr<nsIContent> popup = aPopup; // keep a strong reference to the popup
|
|
|
|
|
|
|
|
nsIFrame* frame = aPopup->GetPrimaryFrame();
|
|
|
|
if (!frame || frame->GetType() != nsGkAtoms::menuPopupFrame)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsMenuPopupFrame* popupFrame = static_cast<nsMenuPopupFrame *>(frame);
|
|
|
|
nsPresContext *presContext = popupFrame->PresContext();
|
|
|
|
nsCOMPtr<nsIPresShell> presShell = presContext->PresShell();
|
|
|
|
nsPopupType popupType = popupFrame->PopupType();
|
|
|
|
|
|
|
|
// generate the child frames if they have not already been generated
|
|
|
|
if (!popupFrame->HasGeneratedChildren()) {
|
|
|
|
popupFrame->SetGeneratedChildren();
|
|
|
|
presShell->FrameConstructor()->GenerateChildFrames(popupFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the frame again
|
|
|
|
frame = aPopup->GetPrimaryFrame();
|
|
|
|
if (!frame)
|
|
|
|
return;
|
|
|
|
|
|
|
|
presShell->FrameNeedsReflow(frame, nsIPresShell::eTreeChange,
|
|
|
|
NS_FRAME_HAS_DIRTY_CHILDREN);
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2010-08-09 16:17:19 +00:00
|
|
|
// cache the popup so that document.popupNode can retrieve the trigger node
|
|
|
|
// during the popupshowing event. It will be cleared below after the event
|
|
|
|
// has fired.
|
|
|
|
mOpeningPopup = aPopup;
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2007-07-17 12:21:53 +00:00
|
|
|
nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_SHOWING, nsnull, nsMouseEvent::eReal);
|
2009-09-03 19:30:07 +00:00
|
|
|
|
|
|
|
// coordinates are relative to the root widget
|
|
|
|
nsPresContext* rootPresContext =
|
2010-01-26 13:10:12 +00:00
|
|
|
presShell->GetPresContext()->GetRootPresContext();
|
|
|
|
if (rootPresContext) {
|
|
|
|
rootPresContext->PresShell()->GetViewManager()->
|
|
|
|
GetRootWidget(getter_AddRefs(event.widget));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
event.widget = nsnull;
|
|
|
|
}
|
2009-09-03 19:30:07 +00:00
|
|
|
|
2007-07-17 12:21:53 +00:00
|
|
|
event.refPoint = mCachedMousePoint;
|
2010-09-06 03:30:17 +00:00
|
|
|
nsEventDispatcher::Dispatch(popup, presContext, &event, nsnull, &status);
|
2009-01-15 03:27:09 +00:00
|
|
|
mCachedMousePoint = nsIntPoint(0, 0);
|
2010-08-09 16:17:19 +00:00
|
|
|
mOpeningPopup = nsnull;
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2007-08-16 01:09:58 +00:00
|
|
|
// if a panel, blur whatever has focus so that the panel can take the focus.
|
|
|
|
// This is done after the popupshowing event in case that event is cancelled.
|
|
|
|
// Using noautofocus="true" will disable this behaviour, which is needed for
|
|
|
|
// the autocomplete widget as it manages focus itself.
|
2010-09-06 03:30:17 +00:00
|
|
|
if (popupType == ePopupTypePanel &&
|
|
|
|
!popup->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautofocus,
|
2007-08-16 01:09:58 +00:00
|
|
|
nsGkAtoms::_true, eCaseMatters)) {
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 18:00:39 +00:00
|
|
|
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
|
|
|
if (fm) {
|
2010-09-06 03:30:17 +00:00
|
|
|
nsIDocument* doc = popup->GetCurrentDoc();
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 18:00:39 +00:00
|
|
|
|
|
|
|
// Only remove the focus if the currently focused item is ouside the
|
|
|
|
// popup. It isn't a big deal if the current focus is in a child popup
|
|
|
|
// inside the popup as that shouldn't be visible. This check ensures that
|
|
|
|
// a node inside the popup that is focused during a popupshowing event
|
|
|
|
// remains focused.
|
|
|
|
nsCOMPtr<nsIDOMElement> currentFocusElement;
|
|
|
|
fm->GetFocusedElement(getter_AddRefs(currentFocusElement));
|
|
|
|
nsCOMPtr<nsIContent> currentFocus = do_QueryInterface(currentFocusElement);
|
|
|
|
if (doc && currentFocus &&
|
2010-09-06 03:30:17 +00:00
|
|
|
!nsContentUtils::ContentIsCrossDocDescendantOf(currentFocus, popup)) {
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 18:00:39 +00:00
|
|
|
fm->ClearFocus(doc->GetWindow());
|
|
|
|
}
|
2007-08-16 01:09:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-09 16:17:19 +00:00
|
|
|
// clear these as they are no longer valid
|
|
|
|
mRangeParent = nsnull;
|
|
|
|
mRangeOffset = 0;
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
// get the frame again in case it went away
|
2010-09-06 03:30:17 +00:00
|
|
|
frame = aPopup->GetPrimaryFrame();
|
2007-07-04 15:49:38 +00:00
|
|
|
if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) {
|
2007-07-08 07:08:04 +00:00
|
|
|
nsMenuPopupFrame* popupFrame = static_cast<nsMenuPopupFrame *>(frame);
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2010-08-09 16:17:19 +00:00
|
|
|
// if the event was cancelled, don't open the popup, reset its state back
|
|
|
|
// to closed and clear its trigger content.
|
2007-08-03 14:05:07 +00:00
|
|
|
if (status == nsEventStatus_eConsumeNoDefault) {
|
|
|
|
popupFrame->SetPopupState(ePopupClosed);
|
2010-08-09 16:17:19 +00:00
|
|
|
popupFrame->SetTriggerContent(nsnull);
|
2007-08-03 14:05:07 +00:00
|
|
|
}
|
|
|
|
else {
|
2007-07-04 15:49:38 +00:00
|
|
|
ShowPopupCallback(aPopup, popupFrame, aIsContextMenu, aSelectFirstItem);
|
2007-08-03 14:05:07 +00:00
|
|
|
}
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::FirePopupHidingEvent(nsIContent* aPopup,
|
|
|
|
nsIContent* aNextPopup,
|
|
|
|
nsIContent* aLastPopup,
|
|
|
|
nsPresContext *aPresContext,
|
2007-08-16 01:09:58 +00:00
|
|
|
nsPopupType aPopupType,
|
2007-07-04 15:49:38 +00:00
|
|
|
PRBool aDeselectMenu)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIPresShell> presShell = aPresContext->PresShell();
|
|
|
|
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
2007-07-17 12:21:53 +00:00
|
|
|
nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_HIDING, nsnull, nsMouseEvent::eReal);
|
2007-07-04 15:49:38 +00:00
|
|
|
nsEventDispatcher::Dispatch(aPopup, aPresContext, &event, nsnull, &status);
|
|
|
|
|
2007-08-16 01:09:58 +00:00
|
|
|
// when a panel is closed, blur whatever has focus inside the popup
|
|
|
|
if (aPopupType == ePopupTypePanel &&
|
|
|
|
!aPopup->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautofocus,
|
|
|
|
nsGkAtoms::_true, eCaseMatters)) {
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 18:00:39 +00:00
|
|
|
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
|
|
|
if (fm) {
|
|
|
|
nsIDocument* doc = aPopup->GetCurrentDoc();
|
|
|
|
|
|
|
|
// Remove the focus from the focused node only if it is inside the popup.
|
|
|
|
nsCOMPtr<nsIDOMElement> currentFocusElement;
|
|
|
|
fm->GetFocusedElement(getter_AddRefs(currentFocusElement));
|
|
|
|
nsCOMPtr<nsIContent> currentFocus = do_QueryInterface(currentFocusElement);
|
|
|
|
if (doc && currentFocus &&
|
2009-08-27 15:51:41 +00:00
|
|
|
nsContentUtils::ContentIsCrossDocDescendantOf(currentFocus, aPopup)) {
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 18:00:39 +00:00
|
|
|
fm->ClearFocus(doc->GetWindow());
|
|
|
|
}
|
2007-08-16 01:09:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
// get frame again in case it went away
|
2009-12-24 21:20:06 +00:00
|
|
|
nsIFrame* frame = aPopup->GetPrimaryFrame();
|
2007-07-04 15:49:38 +00:00
|
|
|
if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) {
|
2007-07-08 07:08:04 +00:00
|
|
|
nsMenuPopupFrame* popupFrame = static_cast<nsMenuPopupFrame *>(frame);
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2010-08-09 16:17:19 +00:00
|
|
|
// if the event was cancelled, don't hide the popup, and reset its
|
2007-08-03 14:05:07 +00:00
|
|
|
// state back to open. Only popups in chrome shells can prevent a popup
|
|
|
|
// from hiding.
|
|
|
|
if (status == nsEventStatus_eConsumeNoDefault &&
|
|
|
|
!popupFrame->IsInContentShell()) {
|
|
|
|
popupFrame->SetPopupState(ePopupOpenAndVisible);
|
|
|
|
}
|
|
|
|
else {
|
2007-07-04 15:49:38 +00:00
|
|
|
HidePopupCallback(aPopup, popupFrame, aNextPopup, aLastPopup,
|
2007-08-16 01:09:58 +00:00
|
|
|
aPopupType, aDeselectMenu);
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-11 17:23:30 +00:00
|
|
|
PRBool
|
|
|
|
nsXULPopupManager::IsPopupOpen(nsIContent* aPopup)
|
|
|
|
{
|
2007-08-03 14:05:07 +00:00
|
|
|
// a popup is open if it is in the open list. The assertions ensure that the
|
|
|
|
// frame is in the correct state. If the popup is in the hiding or invisible
|
|
|
|
// state, it will still be in the open popup list until it is closed.
|
2008-12-05 16:37:30 +00:00
|
|
|
nsMenuChainItem* item = mPopups;
|
2007-07-11 17:23:30 +00:00
|
|
|
while (item) {
|
2007-08-03 14:05:07 +00:00
|
|
|
if (item->Content() == aPopup) {
|
|
|
|
NS_ASSERTION(item->Frame()->IsOpen() ||
|
|
|
|
item->Frame()->PopupState() == ePopupHiding ||
|
|
|
|
item->Frame()->PopupState() == ePopupInvisible,
|
2007-08-30 15:43:10 +00:00
|
|
|
"popup in open list not actually open");
|
2007-07-11 17:23:30 +00:00
|
|
|
return PR_TRUE;
|
2007-08-03 14:05:07 +00:00
|
|
|
}
|
2007-07-11 17:23:30 +00:00
|
|
|
item = item->GetParent();
|
|
|
|
}
|
|
|
|
|
2008-12-05 16:37:30 +00:00
|
|
|
item = mNoHidePanels;
|
2007-07-11 17:23:30 +00:00
|
|
|
while (item) {
|
2007-08-03 14:05:07 +00:00
|
|
|
if (item->Content() == aPopup) {
|
|
|
|
NS_ASSERTION(item->Frame()->IsOpen() ||
|
|
|
|
item->Frame()->PopupState() == ePopupHiding ||
|
|
|
|
item->Frame()->PopupState() == ePopupInvisible,
|
2007-08-30 15:43:10 +00:00
|
|
|
"popup in open list not actually open");
|
2007-07-11 17:23:30 +00:00
|
|
|
return PR_TRUE;
|
2007-08-03 14:05:07 +00:00
|
|
|
}
|
2007-07-11 17:23:30 +00:00
|
|
|
item = item->GetParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
PRBool
|
2008-12-05 16:37:31 +00:00
|
|
|
nsXULPopupManager::IsPopupOpenForMenuParent(nsMenuParent* aMenuParent)
|
2007-07-04 15:49:38 +00:00
|
|
|
{
|
2007-08-15 14:03:21 +00:00
|
|
|
nsMenuChainItem* item = GetTopVisibleMenu();
|
2007-07-04 15:49:38 +00:00
|
|
|
while (item) {
|
2007-08-03 14:05:07 +00:00
|
|
|
nsMenuPopupFrame* popup = item->Frame();
|
|
|
|
if (popup && popup->IsOpen()) {
|
2010-08-09 16:17:19 +00:00
|
|
|
nsMenuFrame* menuFrame = popup->GetParentMenu();
|
|
|
|
if (menuFrame && menuFrame->GetMenuParent() == aMenuParent) {
|
|
|
|
return PR_TRUE;
|
2007-08-03 14:05:07 +00:00
|
|
|
}
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
item = item->GetParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
2007-08-16 01:09:58 +00:00
|
|
|
nsIFrame*
|
|
|
|
nsXULPopupManager::GetTopPopup(nsPopupType aType)
|
|
|
|
{
|
2010-08-09 16:17:19 +00:00
|
|
|
if ((aType == ePopupTypePanel || aType == ePopupTypeTooltip) && mNoHidePanels)
|
2008-12-05 16:37:30 +00:00
|
|
|
return mNoHidePanels->Frame();
|
2007-08-16 01:09:58 +00:00
|
|
|
|
|
|
|
nsMenuChainItem* item = GetTopVisibleMenu();
|
|
|
|
while (item) {
|
2007-09-18 15:00:43 +00:00
|
|
|
if (item->PopupType() == aType || aType == ePopupTypeAny)
|
2007-08-16 01:09:58 +00:00
|
|
|
return item->Frame();
|
|
|
|
item = item->GetParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
2007-07-16 14:53:32 +00:00
|
|
|
nsTArray<nsIFrame *>
|
2009-02-24 08:25:25 +00:00
|
|
|
nsXULPopupManager::GetVisiblePopups()
|
2007-07-16 14:53:32 +00:00
|
|
|
{
|
|
|
|
nsTArray<nsIFrame *> popups;
|
|
|
|
|
2008-12-05 16:37:30 +00:00
|
|
|
nsMenuChainItem* item = mPopups;
|
2007-07-16 14:53:32 +00:00
|
|
|
while (item) {
|
2009-02-24 08:25:25 +00:00
|
|
|
if (item->Frame()->PopupState() == ePopupOpenAndVisible)
|
2007-08-15 14:03:21 +00:00
|
|
|
popups.AppendElement(static_cast<nsIFrame*>(item->Frame()));
|
2007-07-16 14:53:32 +00:00
|
|
|
item = item->GetParent();
|
2010-07-27 13:38:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
item = mNoHidePanels;
|
|
|
|
while (item) {
|
|
|
|
if (item->Frame()->PopupState() == ePopupOpenAndVisible)
|
|
|
|
popups.AppendElement(static_cast<nsIFrame*>(item->Frame()));
|
|
|
|
item = item->GetParent();
|
2007-07-16 14:53:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return popups;
|
|
|
|
}
|
|
|
|
|
2010-08-09 16:17:19 +00:00
|
|
|
already_AddRefed<nsIDOMNode>
|
|
|
|
nsXULPopupManager::GetLastTriggerNode(nsIDocument* aDocument, PRBool aIsTooltip)
|
|
|
|
{
|
|
|
|
if (!aDocument)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
|
|
|
|
// if mOpeningPopup is set, it means that a popupshowing event is being
|
|
|
|
// fired. In this case, just use the cached node, as the popup is not yet in
|
|
|
|
// the list of open popups.
|
|
|
|
if (mOpeningPopup && mOpeningPopup->GetCurrentDoc() == aDocument &&
|
|
|
|
aIsTooltip == (mOpeningPopup->Tag() == nsGkAtoms::tooltip)) {
|
|
|
|
nsMenuPopupFrame* popupFrame = GetPopupFrameForContent(mOpeningPopup, PR_FALSE);
|
|
|
|
if (popupFrame)
|
|
|
|
node = do_QueryInterface(popupFrame->GetTriggerContent());
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nsMenuChainItem* item = aIsTooltip ? mNoHidePanels : mPopups;
|
|
|
|
while (item) {
|
|
|
|
// look for a popup of the same type and document.
|
|
|
|
if ((item->PopupType() == ePopupTypeTooltip) == aIsTooltip &&
|
|
|
|
item->Content()->GetCurrentDoc() == aDocument) {
|
|
|
|
node = do_QueryInterface(item->Frame()->GetTriggerContent());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
item = item->GetParent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return node.forget();
|
|
|
|
}
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
PRBool
|
|
|
|
nsXULPopupManager::MayShowPopup(nsMenuPopupFrame* aPopup)
|
|
|
|
{
|
2007-08-03 14:05:07 +00:00
|
|
|
// if a popup's IsOpen method returns true, then the popup must always be in
|
|
|
|
// the popup chain scanned in IsPopupOpen.
|
|
|
|
NS_ASSERTION(!aPopup->IsOpen() || IsPopupOpen(aPopup->GetContent()),
|
|
|
|
"popup frame state doesn't match XULPopupManager open state");
|
|
|
|
|
|
|
|
nsPopupState state = aPopup->PopupState();
|
|
|
|
|
|
|
|
// if the popup is not in the open popup chain, then it must have a state that
|
|
|
|
// is either closed, in the process of being shown, or invisible.
|
|
|
|
NS_ASSERTION(IsPopupOpen(aPopup->GetContent()) || state == ePopupClosed ||
|
|
|
|
state == ePopupShowing || state == ePopupInvisible,
|
|
|
|
"popup not in XULPopupManager open list is open");
|
|
|
|
|
|
|
|
// don't show popups unless they are closed or invisible
|
|
|
|
if (state != ePopupClosed && state != ePopupInvisible)
|
2007-07-04 15:49:38 +00:00
|
|
|
return PR_FALSE;
|
|
|
|
|
2009-12-04 04:01:39 +00:00
|
|
|
// Don't show popups that we already have in our popup chain
|
|
|
|
if (IsPopupOpen(aPopup->GetContent())) {
|
|
|
|
NS_WARNING("Refusing to show duplicate popup");
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
2007-12-03 16:33:42 +00:00
|
|
|
// if the popup was just rolled up, don't reopen it
|
|
|
|
nsCOMPtr<nsIWidget> widget;
|
|
|
|
aPopup->GetWidget(getter_AddRefs(widget));
|
|
|
|
if (widget && widget->GetLastRollup() == aPopup->GetContent())
|
|
|
|
return PR_FALSE;
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
nsCOMPtr<nsISupports> cont = aPopup->PresContext()->GetContainer();
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(cont);
|
2008-09-10 16:57:57 +00:00
|
|
|
nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(dsti);
|
|
|
|
if (!baseWin)
|
2007-07-04 15:49:38 +00:00
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
PRInt32 type = -1;
|
|
|
|
if (NS_FAILED(dsti->GetItemType(&type)))
|
|
|
|
return PR_FALSE;
|
|
|
|
|
2008-09-10 16:57:57 +00:00
|
|
|
// chrome shells can always open popups, but other types of shells can only
|
|
|
|
// open popups when they are focused and visible
|
2007-07-04 15:49:38 +00:00
|
|
|
if (type != nsIDocShellTreeItem::typeChrome) {
|
2008-09-10 16:57:57 +00:00
|
|
|
// only allow popups in active windows
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 18:00:39 +00:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> root;
|
|
|
|
dsti->GetRootTreeItem(getter_AddRefs(root));
|
|
|
|
nsCOMPtr<nsIDOMWindow> rootWin = do_GetInterface(root);
|
|
|
|
|
|
|
|
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
|
|
|
if (!fm || !rootWin)
|
2007-07-04 15:49:38 +00:00
|
|
|
return PR_FALSE;
|
|
|
|
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 18:00:39 +00:00
|
|
|
nsCOMPtr<nsIDOMWindow> activeWindow;
|
|
|
|
fm->GetActiveWindow(getter_AddRefs(activeWindow));
|
|
|
|
if (activeWindow != rootWin)
|
2007-07-04 15:49:38 +00:00
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
// only allow popups in visible frames
|
|
|
|
PRBool visible;
|
|
|
|
baseWin->GetVisibility(&visible);
|
|
|
|
if (!visible)
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
2008-09-10 16:57:57 +00:00
|
|
|
// platforms respond differently when an popup is opened in a minimized
|
|
|
|
// window, so this is always disabled.
|
|
|
|
nsCOMPtr<nsIWidget> mainWidget;
|
|
|
|
baseWin->GetMainWidget(getter_AddRefs(mainWidget));
|
|
|
|
if (mainWidget) {
|
|
|
|
PRInt32 sizeMode;
|
|
|
|
mainWidget->GetSizeMode(&sizeMode);
|
|
|
|
if (sizeMode == nsSizeMode_Minimized)
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
// cannot open a popup that is a submenu of a menupopup that isn't open.
|
2010-08-09 16:17:19 +00:00
|
|
|
nsMenuFrame* menuFrame = aPopup->GetParentMenu();
|
|
|
|
if (menuFrame) {
|
2008-12-05 16:37:31 +00:00
|
|
|
nsMenuParent* parentPopup = menuFrame->GetMenuParent();
|
2007-07-04 15:49:38 +00:00
|
|
|
if (parentPopup && !parentPopup->IsOpen())
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::PopupDestroyed(nsMenuPopupFrame* aPopup)
|
|
|
|
{
|
|
|
|
// when a popup frame is destroyed, just unhook it from the list of popups
|
|
|
|
if (mTimerMenu == aPopup) {
|
|
|
|
if (mCloseTimer) {
|
|
|
|
mCloseTimer->Cancel();
|
|
|
|
mCloseTimer = nsnull;
|
|
|
|
}
|
|
|
|
mTimerMenu = nsnull;
|
|
|
|
}
|
|
|
|
|
2008-12-05 16:37:30 +00:00
|
|
|
nsMenuChainItem* item = mNoHidePanels;
|
2007-07-04 15:49:38 +00:00
|
|
|
while (item) {
|
|
|
|
if (item->Frame() == aPopup) {
|
2008-12-05 16:37:30 +00:00
|
|
|
item->Detach(&mNoHidePanels);
|
2007-07-04 15:49:38 +00:00
|
|
|
delete item;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
item = item->GetParent();
|
|
|
|
}
|
|
|
|
|
2007-11-17 15:47:38 +00:00
|
|
|
nsTArray<nsMenuPopupFrame *> popupsToHide;
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2008-12-05 16:37:30 +00:00
|
|
|
item = mPopups;
|
2007-07-04 15:49:38 +00:00
|
|
|
while (item) {
|
2007-11-17 15:47:38 +00:00
|
|
|
nsMenuPopupFrame* frame = item->Frame();
|
|
|
|
if (frame == aPopup) {
|
|
|
|
if (frame->PopupState() != ePopupInvisible) {
|
|
|
|
// Iterate through any child menus and hide them as well, since the
|
|
|
|
// parent is going away. We won't remove them from the list yet, just
|
|
|
|
// hide them, as they will be removed from the list when this function
|
|
|
|
// gets called for that child frame.
|
|
|
|
nsMenuChainItem* child = item->GetChild();
|
|
|
|
while (child) {
|
|
|
|
// if the popup is a child frame of the menu that was destroyed, add
|
|
|
|
// it to the list of popups to hide. Don't bother with the events
|
|
|
|
// since the frames are going away. If the child menu is not a child
|
|
|
|
// frame, for example, a context menu, use HidePopup instead, but call
|
|
|
|
// it asynchronously since we are in the middle of frame destruction.
|
|
|
|
nsMenuPopupFrame* childframe = child->Frame();
|
|
|
|
if (nsLayoutUtils::IsProperAncestorFrame(frame, childframe)) {
|
|
|
|
popupsToHide.AppendElement(childframe);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// HidePopup will take care of hiding any of its children, so
|
|
|
|
// break out afterwards
|
|
|
|
HidePopup(child->Content(), PR_FALSE, PR_FALSE, PR_TRUE);
|
|
|
|
break;
|
|
|
|
}
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2007-11-17 15:47:38 +00:00
|
|
|
child = child->GetChild();
|
|
|
|
}
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
2008-12-05 16:37:30 +00:00
|
|
|
item->Detach(&mPopups);
|
2007-07-04 15:49:38 +00:00
|
|
|
delete item;
|
2007-11-17 15:47:38 +00:00
|
|
|
break;
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
2007-11-17 15:47:38 +00:00
|
|
|
item = item->GetParent();
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
2007-11-17 15:47:38 +00:00
|
|
|
HidePopupsInList(popupsToHide, PR_FALSE);
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsXULPopupManager::HasContextMenu(nsMenuPopupFrame* aPopup)
|
|
|
|
{
|
2007-08-15 14:03:21 +00:00
|
|
|
nsMenuChainItem* item = GetTopVisibleMenu();
|
2007-07-04 15:49:38 +00:00
|
|
|
while (item && item->Frame() != aPopup) {
|
|
|
|
if (item->IsContextMenu())
|
|
|
|
return PR_TRUE;
|
|
|
|
item = item->GetParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::SetCaptureState(nsIContent* aOldPopup)
|
|
|
|
{
|
2007-08-15 14:03:21 +00:00
|
|
|
nsMenuChainItem* item = GetTopVisibleMenu();
|
|
|
|
if (item && aOldPopup == item->Content())
|
2007-07-04 15:49:38 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (mWidget) {
|
2009-12-22 23:49:33 +00:00
|
|
|
mWidget->CaptureRollupEvents(this, this, PR_FALSE, PR_FALSE);
|
2007-07-04 15:49:38 +00:00
|
|
|
mWidget = nsnull;
|
|
|
|
}
|
|
|
|
|
2007-08-15 14:03:21 +00:00
|
|
|
if (item) {
|
|
|
|
nsMenuPopupFrame* popup = item->Frame();
|
2007-07-04 15:49:38 +00:00
|
|
|
nsCOMPtr<nsIWidget> widget;
|
|
|
|
popup->GetWidget(getter_AddRefs(widget));
|
|
|
|
if (widget) {
|
2009-12-22 23:49:33 +00:00
|
|
|
widget->CaptureRollupEvents(this, this, PR_TRUE,
|
|
|
|
popup->ConsumeOutsideClicks());
|
2007-07-04 15:49:38 +00:00
|
|
|
mWidget = widget;
|
|
|
|
popup->AttachedDismissalListener();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateKeyboardListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::UpdateKeyboardListeners()
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMEventTarget> newTarget;
|
2007-12-04 04:07:12 +00:00
|
|
|
PRBool isForMenu = PR_FALSE;
|
2007-08-15 14:03:21 +00:00
|
|
|
nsMenuChainItem* item = GetTopVisibleMenu();
|
|
|
|
if (item) {
|
|
|
|
if (!item->IgnoreKeys())
|
|
|
|
newTarget = do_QueryInterface(item->Content()->GetDocument());
|
2007-12-04 04:07:12 +00:00
|
|
|
isForMenu = item->PopupType() == ePopupTypeMenu;
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
else if (mActiveMenuBar) {
|
|
|
|
newTarget = do_QueryInterface(mActiveMenuBar->GetContent()->GetDocument());
|
2007-12-04 04:07:12 +00:00
|
|
|
isForMenu = PR_TRUE;
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mKeyListener != newTarget) {
|
|
|
|
if (mKeyListener) {
|
|
|
|
mKeyListener->RemoveEventListener(NS_LITERAL_STRING("keypress"), this, PR_TRUE);
|
|
|
|
mKeyListener->RemoveEventListener(NS_LITERAL_STRING("keydown"), this, PR_TRUE);
|
|
|
|
mKeyListener->RemoveEventListener(NS_LITERAL_STRING("keyup"), this, PR_TRUE);
|
|
|
|
mKeyListener = nsnull;
|
|
|
|
nsContentUtils::NotifyInstalledMenuKeyboardListener(PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newTarget) {
|
|
|
|
newTarget->AddEventListener(NS_LITERAL_STRING("keypress"), this, PR_TRUE);
|
|
|
|
newTarget->AddEventListener(NS_LITERAL_STRING("keydown"), this, PR_TRUE);
|
|
|
|
newTarget->AddEventListener(NS_LITERAL_STRING("keyup"), this, PR_TRUE);
|
2007-12-04 04:07:12 +00:00
|
|
|
nsContentUtils::NotifyInstalledMenuKeyboardListener(isForMenu);
|
2007-07-04 15:49:38 +00:00
|
|
|
mKeyListener = newTarget;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::UpdateMenuItems(nsIContent* aPopup)
|
|
|
|
{
|
|
|
|
// Walk all of the menu's children, checking to see if any of them has a
|
|
|
|
// command attribute. If so, then several attributes must potentially be updated.
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(aPopup->GetDocument()));
|
|
|
|
PRUint32 count = aPopup->GetChildCount();
|
|
|
|
for (PRUint32 i = 0; i < count; i++) {
|
|
|
|
nsCOMPtr<nsIContent> grandChild = aPopup->GetChildAt(i);
|
|
|
|
|
|
|
|
if (grandChild->NodeInfo()->Equals(nsGkAtoms::menuitem, kNameSpaceID_XUL)) {
|
|
|
|
// See if we have a command attribute.
|
|
|
|
nsAutoString command;
|
|
|
|
grandChild->GetAttr(kNameSpaceID_None, nsGkAtoms::command, command);
|
|
|
|
if (!command.IsEmpty()) {
|
|
|
|
// We do! Look it up in our document
|
|
|
|
nsCOMPtr<nsIDOMElement> commandElt;
|
|
|
|
domDoc->GetElementById(command, getter_AddRefs(commandElt));
|
|
|
|
nsCOMPtr<nsIContent> commandContent(do_QueryInterface(commandElt));
|
|
|
|
if (commandContent) {
|
|
|
|
nsAutoString commandValue;
|
|
|
|
// The menu's disabled state needs to be updated to match the command.
|
|
|
|
if (commandContent->GetAttr(kNameSpaceID_None, nsGkAtoms::disabled, commandValue))
|
|
|
|
grandChild->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, commandValue, PR_TRUE);
|
|
|
|
else
|
|
|
|
grandChild->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, PR_TRUE);
|
|
|
|
|
|
|
|
// The menu's label, accesskey and checked states need to be updated
|
|
|
|
// to match the command. Note that unlike the disabled state if the
|
|
|
|
// command has *no* value, we assume the menu is supplying its own.
|
|
|
|
if (commandContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, commandValue))
|
|
|
|
grandChild->SetAttr(kNameSpaceID_None, nsGkAtoms::label, commandValue, PR_TRUE);
|
|
|
|
|
|
|
|
if (commandContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, commandValue))
|
|
|
|
grandChild->SetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, commandValue, PR_TRUE);
|
|
|
|
|
|
|
|
if (commandContent->GetAttr(kNameSpaceID_None, nsGkAtoms::checked, commandValue))
|
|
|
|
grandChild->SetAttr(kNameSpaceID_None, nsGkAtoms::checked, commandValue, PR_TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Notify
|
|
|
|
//
|
|
|
|
// The item selection timer has fired, we might have to readjust the
|
|
|
|
// selected item. There are two cases here that we are trying to deal with:
|
|
|
|
// (1) diagonal movement from a parent menu to a submenu passing briefly over
|
|
|
|
// other items, and
|
|
|
|
// (2) moving out from a submenu to a parent or grandparent menu.
|
|
|
|
// In both cases, |mTimerMenu| is the menu item that might have an open submenu and
|
2008-12-05 16:37:30 +00:00
|
|
|
// the first item in |mPopups| is the item the mouse is currently over, which could be
|
|
|
|
// none of them.
|
2007-07-04 15:49:38 +00:00
|
|
|
//
|
|
|
|
// case (1):
|
|
|
|
// As the mouse moves from the parent item of a submenu (we'll call 'A') diagonally into the
|
|
|
|
// submenu, it probably passes through one or more sibilings (B). As the mouse passes
|
|
|
|
// through B, it becomes the current menu item and the timer is set and mTimerMenu is
|
|
|
|
// set to A. Before the timer fires, the mouse leaves the menu containing A and B and
|
2008-12-05 16:37:30 +00:00
|
|
|
// enters the submenus. Now when the timer fires, |mPopups| is null (!= |mTimerMenu|)
|
2007-07-04 15:49:38 +00:00
|
|
|
// so we have to see if anything in A's children is selected (recall that even disabled
|
|
|
|
// items are selected, the style just doesn't show it). If that is the case, we need to
|
|
|
|
// set the selected item back to A.
|
|
|
|
//
|
|
|
|
// case (2);
|
|
|
|
// Item A has an open submenu, and in it there is an item (B) which also has an open
|
|
|
|
// submenu (so there are 3 menus displayed right now). The mouse then leaves B's child
|
|
|
|
// submenu and selects an item that is a sibling of A, call it C. When the mouse enters C,
|
2008-12-05 16:37:30 +00:00
|
|
|
// the timer is set and |mTimerMenu| is A and |mPopups| is C. As the timer fires,
|
2007-07-04 15:49:38 +00:00
|
|
|
// the mouse is still within C. The correct behavior is to set the current item to C
|
|
|
|
// and close up the chain parented at A.
|
|
|
|
//
|
|
|
|
// This brings up the question of is the logic of case (1) enough? The answer is no,
|
|
|
|
// and is discussed in bugzilla bug 29400. Case (1) asks if A's submenu has a selected
|
|
|
|
// child, and if it does, set the selected item to A. Because B has a submenu open, it
|
|
|
|
// is selected and as a result, A is set to be the selected item even though the mouse
|
|
|
|
// rests in C -- very wrong.
|
|
|
|
//
|
|
|
|
// The solution is to use the same idea, but instead of only checking one level,
|
|
|
|
// drill all the way down to the deepest open submenu and check if it has something
|
|
|
|
// selected. Since the mouse is in a grandparent, it won't, and we know that we can
|
|
|
|
// safely close up A and all its children.
|
|
|
|
//
|
|
|
|
// The code below melds the two cases together.
|
|
|
|
//
|
|
|
|
nsresult
|
|
|
|
nsXULPopupManager::Notify(nsITimer* aTimer)
|
|
|
|
{
|
|
|
|
if (aTimer == mCloseTimer)
|
|
|
|
KillMenuTimer();
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsXULPopupManager::KillMenuTimer()
|
|
|
|
{
|
|
|
|
if (mCloseTimer && mTimerMenu) {
|
|
|
|
mCloseTimer->Cancel();
|
|
|
|
mCloseTimer = nsnull;
|
|
|
|
|
|
|
|
if (mTimerMenu->IsOpen())
|
|
|
|
HidePopup(mTimerMenu->GetContent(), PR_FALSE, PR_FALSE, PR_TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
mTimerMenu = nsnull;
|
|
|
|
}
|
|
|
|
|
2007-07-11 12:05:40 +00:00
|
|
|
void
|
2008-12-05 16:37:31 +00:00
|
|
|
nsXULPopupManager::CancelMenuTimer(nsMenuParent* aMenuParent)
|
2007-07-11 12:05:40 +00:00
|
|
|
{
|
|
|
|
if (mCloseTimer && mTimerMenu == aMenuParent) {
|
|
|
|
mCloseTimer->Cancel();
|
|
|
|
mCloseTimer = nsnull;
|
|
|
|
mTimerMenu = nsnull;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
PRBool
|
2007-07-23 17:08:10 +00:00
|
|
|
nsXULPopupManager::HandleShortcutNavigation(nsIDOMKeyEvent* aKeyEvent,
|
|
|
|
nsMenuPopupFrame* aFrame)
|
2007-07-04 15:49:38 +00:00
|
|
|
{
|
2007-08-15 14:03:21 +00:00
|
|
|
nsMenuChainItem* item = GetTopVisibleMenu();
|
|
|
|
if (!aFrame && item)
|
|
|
|
aFrame = item->Frame();
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2007-07-23 17:08:10 +00:00
|
|
|
if (aFrame) {
|
2007-07-04 15:49:38 +00:00
|
|
|
PRBool action;
|
2007-07-23 17:08:10 +00:00
|
|
|
nsMenuFrame* result = aFrame->FindMenuWithShortcut(aKeyEvent, action);
|
2007-07-04 15:49:38 +00:00
|
|
|
if (result) {
|
2007-07-23 17:08:10 +00:00
|
|
|
aFrame->ChangeMenuItem(result, PR_FALSE);
|
2007-07-07 08:40:54 +00:00
|
|
|
if (action) {
|
|
|
|
nsMenuFrame* menuToOpen = result->Enter();
|
|
|
|
if (menuToOpen) {
|
|
|
|
nsCOMPtr<nsIContent> content = menuToOpen->GetContent();
|
|
|
|
ShowMenu(content, PR_TRUE, PR_FALSE);
|
|
|
|
}
|
|
|
|
}
|
2007-07-04 15:49:38 +00:00
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mActiveMenuBar) {
|
|
|
|
nsMenuFrame* result = mActiveMenuBar->FindMenuWithShortcut(aKeyEvent);
|
|
|
|
if (result) {
|
|
|
|
mActiveMenuBar->SetActive(PR_TRUE);
|
|
|
|
result->OpenMenu(PR_TRUE);
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsXULPopupManager::HandleKeyboardNavigation(PRUint32 aKeyCode)
|
|
|
|
{
|
|
|
|
// navigate up through the open menus, looking for the topmost one
|
|
|
|
// in the same hierarchy
|
|
|
|
nsMenuChainItem* item = nsnull;
|
2007-08-15 14:03:21 +00:00
|
|
|
nsMenuChainItem* nextitem = GetTopVisibleMenu();
|
2007-07-04 15:49:38 +00:00
|
|
|
|
|
|
|
while (nextitem) {
|
|
|
|
item = nextitem;
|
|
|
|
nextitem = item->GetParent();
|
|
|
|
|
|
|
|
if (nextitem) {
|
|
|
|
// stop if the parent isn't a menu
|
|
|
|
if (!nextitem->IsMenu())
|
|
|
|
break;
|
|
|
|
|
|
|
|
// check to make sure that the parent is actually the parent menu. It won't
|
|
|
|
// be if the parent is in a different frame hierarchy, for example, for a
|
|
|
|
// context menu opened on another menu.
|
2008-12-05 16:37:31 +00:00
|
|
|
nsMenuParent* expectedParent = static_cast<nsMenuParent *>(nextitem->Frame());
|
2010-08-09 16:17:19 +00:00
|
|
|
nsMenuFrame* menuFrame = item->Frame()->GetParentMenu();
|
|
|
|
if (!menuFrame || menuFrame->GetMenuParent() != expectedParent) {
|
2007-07-04 15:49:38 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIFrame* itemFrame;
|
|
|
|
if (item)
|
|
|
|
itemFrame = item->Frame();
|
|
|
|
else if (mActiveMenuBar)
|
|
|
|
itemFrame = mActiveMenuBar;
|
|
|
|
else
|
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
nsNavigationDirection theDirection;
|
2008-12-30 13:30:51 +00:00
|
|
|
NS_ASSERTION(aKeyCode >= NS_VK_END && aKeyCode <= NS_VK_DOWN, "Illegal key code");
|
|
|
|
theDirection = NS_DIRECTION_FROM_KEY_CODE(itemFrame, aKeyCode);
|
2007-07-04 15:49:38 +00:00
|
|
|
|
|
|
|
// if a popup is open, first check for navigation within the popup
|
|
|
|
if (item && HandleKeyboardNavigationInPopup(item, theDirection))
|
|
|
|
return PR_TRUE;
|
|
|
|
|
|
|
|
// no popup handled the key, so check the active menubar, if any
|
|
|
|
if (mActiveMenuBar) {
|
|
|
|
nsMenuFrame* currentMenu = mActiveMenuBar->GetCurrentMenuItem();
|
|
|
|
|
|
|
|
if (NS_DIRECTION_IS_INLINE(theDirection)) {
|
|
|
|
nsMenuFrame* nextItem = (theDirection == eNavigationDirection_End) ?
|
|
|
|
GetNextMenuItem(mActiveMenuBar, currentMenu, PR_FALSE) :
|
|
|
|
GetPreviousMenuItem(mActiveMenuBar, currentMenu, PR_FALSE);
|
|
|
|
mActiveMenuBar->ChangeMenuItem(nextItem, PR_TRUE);
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
2008-03-12 21:47:11 +00:00
|
|
|
else if (NS_DIRECTION_IS_BLOCK(theDirection)) {
|
2007-07-04 15:49:38 +00:00
|
|
|
// Open the menu and select its first item.
|
2007-12-17 18:17:03 +00:00
|
|
|
if (currentMenu) {
|
|
|
|
nsCOMPtr<nsIContent> content = currentMenu->GetContent();
|
|
|
|
ShowMenu(content, PR_TRUE, PR_FALSE);
|
|
|
|
}
|
2007-07-04 15:49:38 +00:00
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsXULPopupManager::HandleKeyboardNavigationInPopup(nsMenuChainItem* item,
|
2007-07-23 17:08:10 +00:00
|
|
|
nsMenuPopupFrame* aFrame,
|
2007-07-04 15:49:38 +00:00
|
|
|
nsNavigationDirection aDir)
|
|
|
|
{
|
2007-07-23 17:08:10 +00:00
|
|
|
NS_ASSERTION(aFrame, "aFrame is null");
|
|
|
|
NS_ASSERTION(!item || item->Frame() == aFrame,
|
|
|
|
"aFrame is expected to be equal to item->Frame()");
|
|
|
|
|
|
|
|
nsMenuFrame* currentMenu = aFrame->GetCurrentMenuItem();
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2007-07-23 17:08:10 +00:00
|
|
|
aFrame->ClearIncrementalString();
|
2007-07-04 15:49:38 +00:00
|
|
|
|
|
|
|
// This method only gets called if we're open.
|
|
|
|
if (!currentMenu && NS_DIRECTION_IS_INLINE(aDir)) {
|
|
|
|
// We've been opened, but we haven't had anything selected.
|
|
|
|
// We can handle End, but our parent handles Start.
|
|
|
|
if (aDir == eNavigationDirection_End) {
|
2007-07-23 17:08:10 +00:00
|
|
|
nsMenuFrame* nextItem = GetNextMenuItem(aFrame, nsnull, PR_TRUE);
|
2007-07-04 15:49:38 +00:00
|
|
|
if (nextItem) {
|
2007-07-23 17:08:10 +00:00
|
|
|
aFrame->ChangeMenuItem(nextItem, PR_FALSE);
|
2007-07-04 15:49:38 +00:00
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool isContainer = PR_FALSE;
|
|
|
|
PRBool isOpen = PR_FALSE;
|
|
|
|
if (currentMenu) {
|
|
|
|
isOpen = currentMenu->IsOpen();
|
|
|
|
isContainer = currentMenu->IsMenu();
|
|
|
|
if (isOpen) {
|
|
|
|
// for an open popup, have the child process the event
|
2007-07-23 17:08:10 +00:00
|
|
|
nsMenuChainItem* child = item ? item->GetChild() : nsnull;
|
2007-07-04 15:49:38 +00:00
|
|
|
if (child && HandleKeyboardNavigationInPopup(child, aDir))
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
else if (aDir == eNavigationDirection_End &&
|
|
|
|
isContainer && !currentMenu->IsDisabled()) {
|
|
|
|
// The menu is not yet open. Open it and select the first item.
|
|
|
|
nsCOMPtr<nsIContent> content = currentMenu->GetContent();
|
|
|
|
ShowMenu(content, PR_TRUE, PR_FALSE);
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// For block progression, we can move in either direction
|
|
|
|
if (NS_DIRECTION_IS_BLOCK(aDir) ||
|
|
|
|
NS_DIRECTION_IS_BLOCK_TO_EDGE(aDir)) {
|
|
|
|
nsMenuFrame* nextItem;
|
|
|
|
|
|
|
|
if (aDir == eNavigationDirection_Before)
|
2007-07-23 17:08:10 +00:00
|
|
|
nextItem = GetPreviousMenuItem(aFrame, currentMenu, PR_TRUE);
|
2007-07-04 15:49:38 +00:00
|
|
|
else if (aDir == eNavigationDirection_After)
|
2007-07-23 17:08:10 +00:00
|
|
|
nextItem = GetNextMenuItem(aFrame, currentMenu, PR_TRUE);
|
2007-07-04 15:49:38 +00:00
|
|
|
else if (aDir == eNavigationDirection_First)
|
2007-07-23 17:08:10 +00:00
|
|
|
nextItem = GetNextMenuItem(aFrame, nsnull, PR_TRUE);
|
2007-07-04 15:49:38 +00:00
|
|
|
else
|
2007-07-23 17:08:10 +00:00
|
|
|
nextItem = GetPreviousMenuItem(aFrame, nsnull, PR_TRUE);
|
2007-07-04 15:49:38 +00:00
|
|
|
|
|
|
|
if (nextItem) {
|
2007-07-23 17:08:10 +00:00
|
|
|
aFrame->ChangeMenuItem(nextItem, PR_FALSE);
|
2007-07-04 15:49:38 +00:00
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (currentMenu && isContainer && isOpen) {
|
|
|
|
if (aDir == eNavigationDirection_Start) {
|
|
|
|
// close a submenu when Left is pressed
|
|
|
|
nsMenuPopupFrame* popupFrame = currentMenu->GetPopup();
|
|
|
|
if (popupFrame)
|
|
|
|
HidePopup(popupFrame->GetContent(), PR_FALSE, PR_FALSE, PR_FALSE);
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsMenuFrame*
|
|
|
|
nsXULPopupManager::GetNextMenuItem(nsIFrame* aParent,
|
|
|
|
nsMenuFrame* aStart,
|
|
|
|
PRBool aIsPopup)
|
|
|
|
{
|
|
|
|
nsIFrame* immediateParent = nsnull;
|
|
|
|
nsPresContext* presContext = aParent->PresContext();
|
|
|
|
presContext->PresShell()->
|
|
|
|
FrameConstructor()->GetInsertionPoint(aParent, nsnull, &immediateParent);
|
|
|
|
if (!immediateParent)
|
|
|
|
immediateParent = aParent;
|
|
|
|
|
|
|
|
nsIFrame* currFrame = nsnull;
|
|
|
|
if (aStart)
|
|
|
|
currFrame = aStart->GetNextSibling();
|
|
|
|
else
|
|
|
|
currFrame = immediateParent->GetFirstChild(nsnull);
|
|
|
|
|
|
|
|
while (currFrame) {
|
|
|
|
// See if it's a menu item.
|
|
|
|
if (IsValidMenuItem(presContext, currFrame->GetContent(), aIsPopup)) {
|
|
|
|
return (currFrame->GetType() == nsGkAtoms::menuFrame) ?
|
2007-07-08 07:08:04 +00:00
|
|
|
static_cast<nsMenuFrame *>(currFrame) : nsnull;
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
currFrame = currFrame->GetNextSibling();
|
|
|
|
}
|
|
|
|
|
|
|
|
currFrame = immediateParent->GetFirstChild(nsnull);
|
|
|
|
|
|
|
|
// Still don't have anything. Try cycling from the beginning.
|
|
|
|
while (currFrame && currFrame != aStart) {
|
|
|
|
// See if it's a menu item.
|
|
|
|
if (IsValidMenuItem(presContext, currFrame->GetContent(), aIsPopup)) {
|
|
|
|
return (currFrame->GetType() == nsGkAtoms::menuFrame) ?
|
2007-07-08 07:08:04 +00:00
|
|
|
static_cast<nsMenuFrame *>(currFrame) : nsnull;
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
currFrame = currFrame->GetNextSibling();
|
|
|
|
}
|
|
|
|
|
|
|
|
// No luck. Just return our start value.
|
|
|
|
return aStart;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsMenuFrame*
|
|
|
|
nsXULPopupManager::GetPreviousMenuItem(nsIFrame* aParent,
|
|
|
|
nsMenuFrame* aStart,
|
|
|
|
PRBool aIsPopup)
|
|
|
|
{
|
|
|
|
nsIFrame* immediateParent = nsnull;
|
|
|
|
nsPresContext* presContext = aParent->PresContext();
|
|
|
|
presContext->PresShell()->
|
|
|
|
FrameConstructor()->GetInsertionPoint(aParent, nsnull, &immediateParent);
|
|
|
|
if (!immediateParent)
|
|
|
|
immediateParent = aParent;
|
|
|
|
|
2009-09-18 11:09:36 +00:00
|
|
|
const nsFrameList& frames(immediateParent->GetChildList(nsnull));
|
2007-07-04 15:49:38 +00:00
|
|
|
|
|
|
|
nsIFrame* currFrame = nsnull;
|
|
|
|
if (aStart)
|
2009-10-02 16:27:37 +00:00
|
|
|
currFrame = aStart->GetPrevSibling();
|
2007-07-04 15:49:38 +00:00
|
|
|
else
|
|
|
|
currFrame = frames.LastChild();
|
|
|
|
|
|
|
|
while (currFrame) {
|
|
|
|
// See if it's a menu item.
|
|
|
|
if (IsValidMenuItem(presContext, currFrame->GetContent(), aIsPopup)) {
|
|
|
|
return (currFrame->GetType() == nsGkAtoms::menuFrame) ?
|
2007-07-08 07:08:04 +00:00
|
|
|
static_cast<nsMenuFrame *>(currFrame) : nsnull;
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
2009-10-02 16:27:37 +00:00
|
|
|
currFrame = currFrame->GetPrevSibling();
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
currFrame = frames.LastChild();
|
|
|
|
|
|
|
|
// Still don't have anything. Try cycling from the end.
|
|
|
|
while (currFrame && currFrame != aStart) {
|
|
|
|
// See if it's a menu item.
|
|
|
|
if (IsValidMenuItem(presContext, currFrame->GetContent(), aIsPopup)) {
|
|
|
|
return (currFrame->GetType() == nsGkAtoms::menuFrame) ?
|
2007-07-08 07:08:04 +00:00
|
|
|
static_cast<nsMenuFrame *>(currFrame) : nsnull;
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
2009-10-02 16:27:37 +00:00
|
|
|
currFrame = currFrame->GetPrevSibling();
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// No luck. Just return our start value.
|
|
|
|
return aStart;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsXULPopupManager::IsValidMenuItem(nsPresContext* aPresContext,
|
|
|
|
nsIContent* aContent,
|
|
|
|
PRBool aOnPopup)
|
|
|
|
{
|
|
|
|
PRInt32 ns = aContent->GetNameSpaceID();
|
|
|
|
nsIAtom *tag = aContent->Tag();
|
2007-11-17 15:47:38 +00:00
|
|
|
if (ns == kNameSpaceID_XUL) {
|
|
|
|
if (tag != nsGkAtoms::menu && tag != nsGkAtoms::menuitem)
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
else if (ns != kNameSpaceID_XHTML || !aOnPopup || tag != nsGkAtoms::option) {
|
2007-07-04 15:49:38 +00:00
|
|
|
return PR_FALSE;
|
2007-11-17 15:47:38 +00:00
|
|
|
}
|
2007-07-04 15:49:38 +00:00
|
|
|
|
|
|
|
PRBool skipNavigatingDisabledMenuItem = PR_TRUE;
|
|
|
|
if (aOnPopup) {
|
|
|
|
aPresContext->LookAndFeel()->
|
|
|
|
GetMetric(nsILookAndFeel::eMetric_SkipNavigatingDisabledMenuItem,
|
|
|
|
skipNavigatingDisabledMenuItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
return !(skipNavigatingDisabledMenuItem &&
|
|
|
|
aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
|
|
|
|
nsGkAtoms::_true, eCaseMatters));
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsXULPopupManager::KeyUp(nsIDOMEvent* aKeyEvent)
|
|
|
|
{
|
2007-11-21 20:47:11 +00:00
|
|
|
// don't do anything if a menu isn't open or a menubar isn't active
|
|
|
|
if (!mActiveMenuBar) {
|
|
|
|
nsMenuChainItem* item = GetTopVisibleMenu();
|
|
|
|
if (!item || item->PopupType() != ePopupTypeMenu)
|
|
|
|
return NS_OK;
|
2007-08-23 21:21:27 +00:00
|
|
|
}
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2007-11-21 20:47:11 +00:00
|
|
|
aKeyEvent->StopPropagation();
|
|
|
|
aKeyEvent->PreventDefault();
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
return NS_OK; // I am consuming event
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsXULPopupManager::KeyDown(nsIDOMEvent* aKeyEvent)
|
|
|
|
{
|
2010-04-19 14:12:58 +00:00
|
|
|
nsMenuChainItem* item = GetTopVisibleMenu();
|
|
|
|
if (item && item->Frame()->IsMenuLocked())
|
|
|
|
return NS_OK;
|
|
|
|
|
2007-11-21 20:47:11 +00:00
|
|
|
// don't do anything if a menu isn't open or a menubar isn't active
|
2010-04-19 14:12:58 +00:00
|
|
|
if (!mActiveMenuBar && (!item || item->PopupType() != ePopupTypeMenu))
|
|
|
|
return NS_OK;
|
2007-08-23 21:21:27 +00:00
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
PRInt32 menuAccessKey = -1;
|
|
|
|
|
|
|
|
// If the key just pressed is the access key (usually Alt),
|
|
|
|
// dismiss and unfocus the menu.
|
|
|
|
|
|
|
|
nsMenuBarListener::GetMenuAccessKey(&menuAccessKey);
|
|
|
|
if (menuAccessKey) {
|
|
|
|
PRUint32 theChar;
|
|
|
|
nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
|
|
|
|
keyEvent->GetKeyCode(&theChar);
|
|
|
|
|
|
|
|
if (theChar == (PRUint32)menuAccessKey) {
|
|
|
|
PRBool ctrl = PR_FALSE;
|
|
|
|
if (menuAccessKey != nsIDOMKeyEvent::DOM_VK_CONTROL)
|
|
|
|
keyEvent->GetCtrlKey(&ctrl);
|
|
|
|
PRBool alt=PR_FALSE;
|
|
|
|
if (menuAccessKey != nsIDOMKeyEvent::DOM_VK_ALT)
|
|
|
|
keyEvent->GetAltKey(&alt);
|
|
|
|
PRBool shift=PR_FALSE;
|
|
|
|
if (menuAccessKey != nsIDOMKeyEvent::DOM_VK_SHIFT)
|
|
|
|
keyEvent->GetShiftKey(&shift);
|
|
|
|
PRBool meta=PR_FALSE;
|
|
|
|
if (menuAccessKey != nsIDOMKeyEvent::DOM_VK_META)
|
|
|
|
keyEvent->GetMetaKey(&meta);
|
|
|
|
if (!(ctrl || alt || shift || meta)) {
|
|
|
|
// The access key just went down and no other
|
|
|
|
// modifiers are already down.
|
2008-12-05 16:37:30 +00:00
|
|
|
if (mPopups)
|
2009-06-12 18:23:16 +00:00
|
|
|
Rollup(nsnull, nsnull);
|
2007-11-21 20:47:11 +00:00
|
|
|
else if (mActiveMenuBar)
|
|
|
|
mActiveMenuBar->MenuClosed();
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Since a menu was open, eat the event to keep other event
|
|
|
|
// listeners from becoming confused.
|
|
|
|
aKeyEvent->StopPropagation();
|
|
|
|
aKeyEvent->PreventDefault();
|
|
|
|
return NS_OK; // I am consuming event
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
nsXULPopupManager::KeyPress(nsIDOMEvent* aKeyEvent)
|
|
|
|
{
|
|
|
|
// Don't check prevent default flag -- menus always get first shot at key events.
|
|
|
|
// When a menu is open, the prevent default flag on a keypress is always set, so
|
|
|
|
// that no one else uses the key event.
|
|
|
|
|
2010-04-19 14:12:58 +00:00
|
|
|
nsMenuChainItem* item = GetTopVisibleMenu();
|
|
|
|
if (item && item->Frame()->IsMenuLocked())
|
|
|
|
return NS_OK;
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
//handlers shouldn't be triggered by non-trusted events.
|
|
|
|
nsCOMPtr<nsIDOMNSEvent> domNSEvent = do_QueryInterface(aKeyEvent);
|
|
|
|
PRBool trustedEvent = PR_FALSE;
|
|
|
|
|
|
|
|
if (domNSEvent) {
|
|
|
|
domNSEvent->GetIsTrusted(&trustedEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!trustedEvent)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
|
|
|
|
PRUint32 theChar;
|
|
|
|
keyEvent->GetKeyCode(&theChar);
|
|
|
|
|
2007-11-12 21:52:13 +00:00
|
|
|
// Escape should close panels, but the other keys should have no effect.
|
|
|
|
if (item && item->PopupType() != ePopupTypeMenu) {
|
|
|
|
if (theChar == NS_VK_ESCAPE) {
|
|
|
|
HidePopup(item->Content(), PR_FALSE, PR_FALSE, PR_FALSE);
|
|
|
|
aKeyEvent->StopPropagation();
|
|
|
|
aKeyEvent->PreventDefault();
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2007-11-21 20:47:11 +00:00
|
|
|
// if a menu is open or a menubar is active, it consumes the key event
|
2008-12-05 16:37:30 +00:00
|
|
|
PRBool consume = (mPopups || mActiveMenuBar);
|
2007-11-21 20:47:11 +00:00
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
if (theChar == NS_VK_LEFT ||
|
|
|
|
theChar == NS_VK_RIGHT ||
|
|
|
|
theChar == NS_VK_UP ||
|
|
|
|
theChar == NS_VK_DOWN ||
|
|
|
|
theChar == NS_VK_HOME ||
|
|
|
|
theChar == NS_VK_END) {
|
|
|
|
HandleKeyboardNavigation(theChar);
|
|
|
|
}
|
|
|
|
else if (theChar == NS_VK_ESCAPE) {
|
2007-08-15 14:35:02 +00:00
|
|
|
// Pressing Escape hides one level of menus only. If no menu is open,
|
|
|
|
// check if a menubar is active and inform it that a menu closed. Even
|
|
|
|
// though in this latter case, a menu didn't actually close, the effect
|
|
|
|
// ends up being the same. Similar for the tab key below.
|
2007-08-15 14:03:21 +00:00
|
|
|
if (item)
|
|
|
|
HidePopup(item->Content(), PR_FALSE, PR_FALSE, PR_FALSE);
|
2007-08-15 14:35:02 +00:00
|
|
|
else if (mActiveMenuBar)
|
|
|
|
mActiveMenuBar->MenuClosed();
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
2007-11-21 20:47:11 +00:00
|
|
|
else if (theChar == NS_VK_TAB
|
|
|
|
#ifndef XP_MACOSX
|
|
|
|
|| theChar == NS_VK_F10
|
|
|
|
#endif
|
|
|
|
) {
|
|
|
|
// close popups or deactivate menubar when Tab or F10 are pressed
|
|
|
|
if (item)
|
2009-06-12 18:23:16 +00:00
|
|
|
Rollup(nsnull, nsnull);
|
2007-08-15 14:35:02 +00:00
|
|
|
else if (mActiveMenuBar)
|
|
|
|
mActiveMenuBar->MenuClosed();
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
else if (theChar == NS_VK_ENTER ||
|
|
|
|
theChar == NS_VK_RETURN) {
|
|
|
|
// If there is a popup open, check if the current item needs to be opened.
|
|
|
|
// Otherwise, tell the active menubar, if any, to activate the menu. The
|
|
|
|
// Enter method will return a menu if one needs to be opened as a result.
|
|
|
|
nsMenuFrame* menuToOpen = nsnull;
|
2007-08-15 14:03:21 +00:00
|
|
|
nsMenuChainItem* item = GetTopVisibleMenu();
|
|
|
|
if (item)
|
|
|
|
menuToOpen = item->Frame()->Enter();
|
2007-07-04 15:49:38 +00:00
|
|
|
else if (mActiveMenuBar)
|
|
|
|
menuToOpen = mActiveMenuBar->Enter();
|
|
|
|
if (menuToOpen) {
|
|
|
|
nsCOMPtr<nsIContent> content = menuToOpen->GetContent();
|
|
|
|
ShowMenu(content, PR_TRUE, PR_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2007-07-23 17:08:10 +00:00
|
|
|
HandleShortcutNavigation(keyEvent, nsnull);
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
2007-11-21 20:47:11 +00:00
|
|
|
if (consume) {
|
2007-08-23 21:21:27 +00:00
|
|
|
aKeyEvent->StopPropagation();
|
|
|
|
aKeyEvent->PreventDefault();
|
|
|
|
}
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
return NS_OK; // I am consuming event
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsXULPopupShowingEvent::Run()
|
|
|
|
{
|
|
|
|
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
2010-09-06 03:30:17 +00:00
|
|
|
if (pm) {
|
|
|
|
pm->FirePopupShowingEvent(mPopup, mIsContextMenu, mSelectFirstItem);
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsXULPopupHidingEvent::Run()
|
|
|
|
{
|
|
|
|
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
2010-09-06 03:30:17 +00:00
|
|
|
|
|
|
|
nsIDocument *document = mPopup->GetCurrentDoc();
|
|
|
|
if (pm && document) {
|
|
|
|
nsIPresShell* presShell = document->GetShell();
|
|
|
|
if (presShell) {
|
|
|
|
nsPresContext* context = presShell->GetPresContext();
|
|
|
|
if (context) {
|
|
|
|
pm->FirePopupHidingEvent(mPopup, mNextPopup, mLastPopup,
|
|
|
|
context, mPopupType, mDeselectMenu);
|
|
|
|
}
|
|
|
|
}
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsXULMenuCommandEvent::Run()
|
|
|
|
{
|
|
|
|
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
|
|
|
if (!pm)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
// The order of the nsIViewManager and nsIPresShell COM pointers is
|
|
|
|
// important below. We want the pres shell to get released before the
|
|
|
|
// associated view manager on exit from this function.
|
|
|
|
// See bug 54233.
|
|
|
|
// XXXndeakin is this still needed?
|
2007-08-23 22:13:42 +00:00
|
|
|
|
|
|
|
nsCOMPtr<nsIContent> popup;
|
2007-07-04 15:49:38 +00:00
|
|
|
nsMenuFrame* menuFrame = pm->GetMenuFrameForContent(mMenu);
|
2010-04-19 14:12:58 +00:00
|
|
|
nsWeakFrame weakFrame(menuFrame);
|
|
|
|
if (menuFrame && mFlipChecked) {
|
|
|
|
if (menuFrame->IsChecked()) {
|
|
|
|
mMenu->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked, PR_TRUE);
|
|
|
|
} else {
|
|
|
|
mMenu->SetAttr(kNameSpaceID_None, nsGkAtoms::checked,
|
|
|
|
NS_LITERAL_STRING("true"), PR_TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (menuFrame && weakFrame.IsAlive()) {
|
2007-08-23 22:13:42 +00:00
|
|
|
// Find the popup that the menu is inside. Below, this popup will
|
|
|
|
// need to be hidden.
|
|
|
|
nsIFrame* popupFrame = menuFrame->GetParent();
|
|
|
|
while (popupFrame) {
|
2007-12-03 16:33:42 +00:00
|
|
|
if (popupFrame->GetType() == nsGkAtoms::menuPopupFrame) {
|
2007-10-26 17:24:35 +00:00
|
|
|
popup = popupFrame->GetContent();
|
2007-12-03 16:33:42 +00:00
|
|
|
break;
|
2007-10-26 17:24:35 +00:00
|
|
|
}
|
2007-08-23 22:13:42 +00:00
|
|
|
popupFrame = popupFrame->GetParent();
|
|
|
|
}
|
|
|
|
|
2007-07-04 15:49:38 +00:00
|
|
|
nsPresContext* presContext = menuFrame->PresContext();
|
|
|
|
nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
|
2009-03-11 15:43:08 +00:00
|
|
|
nsCOMPtr<nsIViewManager> kungFuDeathGrip = shell->GetViewManager();
|
2007-07-04 15:49:38 +00:00
|
|
|
|
|
|
|
// Deselect ourselves.
|
2007-08-30 15:43:10 +00:00
|
|
|
if (mCloseMenuMode != CloseMenuMode_None)
|
|
|
|
menuFrame->SelectMenu(PR_FALSE);
|
2007-07-04 15:49:38 +00:00
|
|
|
|
2009-09-13 13:13:16 +00:00
|
|
|
nsAutoHandlingUserInputStatePusher userInpStatePusher(mUserInput, PR_FALSE);
|
2009-06-30 07:56:40 +00:00
|
|
|
nsContentUtils::DispatchXULCommand(mMenu, mIsTrusted, nsnull, shell,
|
|
|
|
mControl, mAlt, mShift, mMeta);
|
2007-07-04 15:49:38 +00:00
|
|
|
}
|
|
|
|
|
2007-08-30 15:43:10 +00:00
|
|
|
if (popup && mCloseMenuMode != CloseMenuMode_None)
|
2007-09-18 15:00:43 +00:00
|
|
|
pm->HidePopup(popup, mCloseMenuMode == CloseMenuMode_Auto, PR_TRUE, PR_FALSE);
|
2007-07-04 15:49:38 +00:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2007-11-28 20:18:11 +00:00
|
|
|
|
|
|
|
nsresult
|
|
|
|
NS_NewXULPopupManager(nsISupports** aResult)
|
|
|
|
{
|
|
|
|
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
|
|
|
NS_IF_ADDREF(pm);
|
|
|
|
*aResult = static_cast<nsIMenuRollup *>(pm);
|
|
|
|
return NS_OK;
|
|
|
|
}
|