From 66f6fed78b447caebce382fb6e9b5188fccf9d96 Mon Sep 17 00:00:00 2001 From: "enndeakin%sympatico.ca" Date: Wed, 4 Jul 2007 15:49:46 +0000 Subject: [PATCH] Bug 279703, rework XUL popups to use asynchronous opening, plus many other fixes, attempt 2 with creating widgets later, r=bz,neil,roc,sr=bz git-svn-id: svn://10.0.0.236/trunk@229322 18797224-902f-48f8-a5cc-f745e15eee43 --- .../accessible/src/base/nsRootAccessible.cpp | 24 +- mozilla/browser/base/content/browser.xul | 2 +- mozilla/content/base/src/nsDocument.cpp | 1 + mozilla/content/base/src/nsGkAtomList.h | 6 +- mozilla/content/xul/content/Makefile.in | 2 +- .../content/xul/content/public/Makefile.in | 54 - .../xul/content/public/nsIXULPopupListener.h | 66 - .../content/xul/content/src/nsXULElement.cpp | 54 +- .../xul/content/src/nsXULPopupListener.cpp | 353 ++-- .../xul/content/src/nsXULPopupListener.h | 123 ++ .../xul/document/src/nsXULDocument.cpp | 74 +- .../dom/public/idl/xul/nsIDOMXULDocument.idl | 5 - mozilla/dom/src/base/nsGlobalWindow.cpp | 17 +- mozilla/layout/base/nsCSSFrameConstructor.cpp | 90 +- mozilla/layout/base/nsCSSFrameConstructor.h | 35 + mozilla/layout/base/nsDocumentViewer.cpp | 8 +- mozilla/layout/base/nsIPresShell.h | 8 +- mozilla/layout/base/nsPresShell.cpp | 44 +- mozilla/layout/build/nsLayoutModule.cpp | 7 - mozilla/layout/build/nsLayoutStatics.cpp | 8 + mozilla/layout/generic/nsContainerFrame.cpp | 14 +- mozilla/layout/xul/base/public/Makefile.in | 2 +- mozilla/layout/xul/base/public/nsIMenuFrame.h | 52 +- .../xul/base/public/nsIPopupBoxObject.idl | 75 +- .../layout/xul/base/public/nsIPopupSetFrame.h | 68 - .../xul/base/public/nsXULPopupManager.h | 636 ++++++ mozilla/layout/xul/base/src/Makefile.in | 3 +- mozilla/layout/xul/base/src/nsBoxFrame.cpp | 28 +- mozilla/layout/xul/base/src/nsBoxFrame.h | 7 +- mozilla/layout/xul/base/src/nsIMenuParent.h | 162 +- mozilla/layout/xul/base/src/nsIRootBox.h | 12 +- .../layout/xul/base/src/nsMenuBarFrame.cpp | 576 +----- mozilla/layout/xul/base/src/nsMenuBarFrame.h | 88 +- .../layout/xul/base/src/nsMenuBarListener.cpp | 51 +- .../layout/xul/base/src/nsMenuBarListener.h | 4 + .../layout/xul/base/src/nsMenuBoxObject.cpp | 70 +- .../xul/base/src/nsMenuDismissalListener.cpp | 227 --- .../xul/base/src/nsMenuDismissalListener.h | 103 - mozilla/layout/xul/base/src/nsMenuFrame.cpp | 1417 +++----------- mozilla/layout/xul/base/src/nsMenuFrame.h | 134 +- .../layout/xul/base/src/nsMenuListener.cpp | 281 --- mozilla/layout/xul/base/src/nsMenuListener.h | 81 - .../layout/xul/base/src/nsMenuPopupFrame.cpp | 1714 +++++++---------- .../layout/xul/base/src/nsMenuPopupFrame.h | 248 ++- .../layout/xul/base/src/nsPopupBoxObject.cpp | 131 +- .../layout/xul/base/src/nsPopupSetFrame.cpp | 629 +----- mozilla/layout/xul/base/src/nsPopupSetFrame.h | 62 +- .../layout/xul/base/src/nsRootBoxFrame.cpp | 10 +- .../layout/xul/base/src/nsXULPopupManager.cpp | 1530 +++++++++++++++ .../xul/base/src/nsXULTooltipListener.cpp | 87 +- mozilla/themes/classic/global/mac/popup.css | 3 +- mozilla/themes/classic/global/win/popup.css | 3 +- .../public/nsIAutoCompleteController.idl | 8 +- .../public/nsIAutoCompletePopup.idl | 4 +- .../src/nsAutoCompleteController.cpp | 21 - .../satchel/src/nsFormFillController.cpp | 4 +- .../toolkit/content/widgets/autocomplete.xml | 17 +- mozilla/toolkit/content/widgets/popup.xml | 52 +- mozilla/toolkit/content/xul.css | 18 +- .../themes/gnomestripe/global/popup.css | 4 +- .../themes/pinstripe/global/autocomplete.css | 2 +- .../toolkit/themes/pinstripe/global/popup.css | 3 +- .../themes/winstripe/global/autocomplete.css | 2 +- .../toolkit/themes/winstripe/global/popup.css | 3 +- mozilla/widget/src/gtk2/nsNativeThemeGTK.cpp | 9 +- .../widget/src/windows/nsNativeThemeWin.cpp | 21 +- .../widget/src/xpwidgets/nsWidgetAtomList.h | 1 + .../xpfe/appshell/src/nsWebShellWindow.cpp | 2 - .../resources/content/autocomplete.xml | 2 +- .../resources/content/bindings/popup.xml | 36 +- mozilla/xpfe/global/resources/content/xul.css | 13 +- 71 files changed, 4323 insertions(+), 5388 deletions(-) delete mode 100644 mozilla/content/xul/content/public/Makefile.in delete mode 100644 mozilla/content/xul/content/public/nsIXULPopupListener.h create mode 100644 mozilla/content/xul/content/src/nsXULPopupListener.h delete mode 100644 mozilla/layout/xul/base/public/nsIPopupSetFrame.h create mode 100644 mozilla/layout/xul/base/public/nsXULPopupManager.h delete mode 100644 mozilla/layout/xul/base/src/nsMenuDismissalListener.cpp delete mode 100644 mozilla/layout/xul/base/src/nsMenuDismissalListener.h delete mode 100644 mozilla/layout/xul/base/src/nsMenuListener.cpp delete mode 100644 mozilla/layout/xul/base/src/nsMenuListener.h create mode 100644 mozilla/layout/xul/base/src/nsXULPopupManager.cpp diff --git a/mozilla/accessible/src/base/nsRootAccessible.cpp b/mozilla/accessible/src/base/nsRootAccessible.cpp index 34f5a44968d..c8692a3f835 100644 --- a/mozilla/accessible/src/base/nsRootAccessible.cpp +++ b/mozilla/accessible/src/base/nsRootAccessible.cpp @@ -62,6 +62,7 @@ #include "nsIEventListenerManager.h" #include "nsIFocusController.h" #include "nsIFrame.h" +#include "nsIMenuFrame.h" #include "nsIHTMLDocument.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIMenuParent.h" @@ -801,22 +802,23 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent, } else if (eventType.EqualsLiteral("DOMMenuItemActive")) { if (!treeItemAccessible) { - nsCOMPtr containerAccessible; - accessible->GetParent(getter_AddRefs(containerAccessible)); - NS_ENSURE_TRUE(containerAccessible, NS_OK); - if (Role(containerAccessible) == nsIAccessibleRole::ROLE_MENUBAR) { - nsCOMPtr menuBarAccessNode(do_QueryInterface(containerAccessible)); - NS_ENSURE_TRUE(menuBarAccessNode, NS_ERROR_FAILURE); - nsCOMPtr menuParent = do_QueryInterface(menuBarAccessNode->GetFrame()); - NS_ENSURE_TRUE(menuParent, NS_ERROR_FAILURE); - PRBool isActive; - menuParent->GetIsActive(isActive); - if (!isActive) { + nsCOMPtr menuAccessNode = do_QueryInterface(accessible); + NS_ENSURE_TRUE(menuAccessNode, NS_ERROR_FAILURE); + nsIFrame* menuFrame = menuAccessNode->GetFrame(); + NS_ENSURE_TRUE(menuFrame, NS_ERROR_FAILURE); + nsIMenuFrame* imenuFrame; + CallQueryInterface(menuFrame, &imenuFrame); + NS_ENSURE_TRUE(imenuFrame, NS_ERROR_FAILURE); + if (imenuFrame->IsOnMenuBar()) { + if (!imenuFrame->IsOnActiveMenuBar()) { // It is a top level menuitem. Only fire a focus event when the menu bar // is active. return NS_OK; } } else { + nsCOMPtr containerAccessible; + accessible->GetParent(getter_AddRefs(containerAccessible)); + NS_ENSURE_TRUE(containerAccessible, NS_ERROR_FAILURE); // It is not top level menuitem // Only fire focus event if it is not inside collapsed popup if (State(containerAccessible) & nsIAccessibleStates::STATE_COLLAPSED) diff --git a/mozilla/browser/base/content/browser.xul b/mozilla/browser/base/content/browser.xul index bc91b23d286..ab66a74dcfa 100644 --- a/mozilla/browser/base/content/browser.xul +++ b/mozilla/browser/base/content/browser.xul @@ -107,7 +107,7 @@ - + diff --git a/mozilla/content/base/src/nsDocument.cpp b/mozilla/content/base/src/nsDocument.cpp index 61e40db7edc..06d04c23de1 100644 --- a/mozilla/content/base/src/nsDocument.cpp +++ b/mozilla/content/base/src/nsDocument.cpp @@ -3682,6 +3682,7 @@ nsDocument::GetBoxObjectFor(nsIDOMElement* aElement, nsIBoxObject** aResult) contractID += "-menu"; else if (tag == nsGkAtoms::popup || tag == nsGkAtoms::menupopup || + tag == nsGkAtoms::panel || tag == nsGkAtoms::tooltip) contractID += "-popup"; else if (tag == nsGkAtoms::tree) diff --git a/mozilla/content/base/src/nsGkAtomList.h b/mozilla/content/base/src/nsGkAtomList.h index 7d9e93d9dee..cb5da8502be 100755 --- a/mozilla/content/base/src/nsGkAtomList.h +++ b/mozilla/content/base/src/nsGkAtomList.h @@ -488,7 +488,6 @@ GK_ATOM(menugenerated, "menugenerated") GK_ATOM(menuitem, "menuitem") GK_ATOM(menulist, "menulist") GK_ATOM(menupopup, "menupopup") -GK_ATOM(menutobedisplayed, "menutobedisplayed") GK_ATOM(message, "message") GK_ATOM(meta, "meta") GK_ATOM(method, "method") @@ -520,6 +519,7 @@ GK_ATOM(never, "never") GK_ATOM(_new, "new") GK_ATOM(nextBidi, "NextBidi") GK_ATOM(no, "no") +GK_ATOM(noautohide, "noautohide") GK_ATOM(nobr, "nobr") GK_ATOM(node, "node") GK_ATOM(nodeSet, "node-set") @@ -634,6 +634,7 @@ GK_ATOM(pageincrement, "pageincrement") GK_ATOM(pagex, "pagex") GK_ATOM(pagey, "pagey") GK_ATOM(palettename, "palettename") +GK_ATOM(panel, "panel") GK_ATOM(param, "param") GK_ATOM(parameter, "parameter") GK_ATOM(parent, "parent") @@ -1391,12 +1392,15 @@ GK_ATOM(legendFrame, "LegendFrame") GK_ATOM(letterFrame, "LetterFrame") GK_ATOM(lineFrame, "LineFrame") GK_ATOM(listControlFrame,"ListControlFrame") +GK_ATOM(menuBarFrame,"MenuBarFrame") +GK_ATOM(menuFrame,"MenuFrame") GK_ATOM(menuPopupFrame,"MenuPopupFrame") GK_ATOM(objectFrame, "ObjectFrame") GK_ATOM(pageFrame, "PageFrame") GK_ATOM(pageBreakFrame, "PageBreakFrame") GK_ATOM(pageContentFrame, "PageContentFrame") GK_ATOM(placeholderFrame, "PlaceholderFrame") +GK_ATOM(popupSetFrame, "PopupSetFrame") GK_ATOM(positionedInlineFrame, "PositionedInlineFrame") GK_ATOM(canvasFrame, "CanvasFrame") GK_ATOM(rootFrame, "RootFrame") diff --git a/mozilla/content/xul/content/Makefile.in b/mozilla/content/xul/content/Makefile.in index deed0d222d3..4281407d982 100644 --- a/mozilla/content/xul/content/Makefile.in +++ b/mozilla/content/xul/content/Makefile.in @@ -43,7 +43,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk MODULE = xul -DIRS = public src test +DIRS = src test include $(topsrcdir)/config/rules.mk diff --git a/mozilla/content/xul/content/public/Makefile.in b/mozilla/content/xul/content/public/Makefile.in deleted file mode 100644 index b52da718d4d..00000000000 --- a/mozilla/content/xul/content/public/Makefile.in +++ /dev/null @@ -1,54 +0,0 @@ -# -# ***** 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 -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# 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 ***** - -DEPTH = ../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -MODULE = xul - -ifdef MOZ_XUL -EXPORTS = \ - nsIXULPopupListener.h \ - $(NULL) -endif - -include $(topsrcdir)/config/rules.mk - diff --git a/mozilla/content/xul/content/public/nsIXULPopupListener.h b/mozilla/content/xul/content/public/nsIXULPopupListener.h deleted file mode 100644 index c6412571504..00000000000 --- a/mozilla/content/xul/content/public/nsIXULPopupListener.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** 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 - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * 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 ***** */ - -#ifndef nsIXULPopupListener_h__ -#define nsIXULPopupListener_h__ - -// {2C453161-0942-11d3-BF87-00105A1B0627} -#define NS_IXULPOPUPLISTENER_IID \ -{ 0x2c453161, 0x942, 0x11d3, { 0xbf, 0x87, 0x0, 0x10, 0x5a, 0x1b, 0x6, 0x27 } } - -class nsIDOMElement; - -typedef enum { - eXULPopupType_popup, - eXULPopupType_context, - eXULPopupType_tooltip, - eXULPopupType_blur -} XULPopupType; - -class nsIXULPopupListener: public nsISupports { -public: - NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXULPOPUPLISTENER_IID) - - NS_IMETHOD Init(nsIDOMElement* anElement, const XULPopupType& aPopupType) = 0; -}; - -NS_DEFINE_STATIC_IID_ACCESSOR(nsIXULPopupListener, NS_IXULPOPUPLISTENER_IID) - -nsresult -NS_NewXULPopupListener(nsIXULPopupListener** result); - -#endif // nsIXULPopupListener_h__ diff --git a/mozilla/content/xul/content/src/nsXULElement.cpp b/mozilla/content/xul/content/src/nsXULElement.cpp index f1e0887dd5f..d0831ac1512 100644 --- a/mozilla/content/xul/content/src/nsXULElement.cpp +++ b/mozilla/content/xul/content/src/nsXULElement.cpp @@ -107,7 +107,6 @@ #include "nsIViewManager.h" #include "nsIWidget.h" #include "nsIXULDocument.h" -#include "nsIXULPopupListener.h" #include "nsIXULTemplateBuilder.h" #include "nsIXBLService.h" #include "nsLayoutCID.h" @@ -119,6 +118,7 @@ #include "nsIBoxObject.h" #include "nsPIBoxObject.h" #include "nsXULDocument.h" +#include "nsXULPopupListener.h" #include "nsRuleWalker.h" #include "nsIDOMViewCSS.h" #include "nsIDOMCSSStyleDeclaration.h" @@ -2046,18 +2046,17 @@ static void PopupListenerPropertyDtor(void* aObject, nsIAtom* aPropertyName, void* aPropertyValue, void* aData) { - nsIXULPopupListener* listener = - NS_STATIC_CAST(nsIXULPopupListener*, aPropertyValue); + nsIDOMEventListener* listener = + NS_STATIC_CAST(nsIDOMEventListener*, aPropertyValue); if (!listener) { return; } - nsCOMPtr eventListener = do_QueryInterface(listener); nsCOMPtr target = do_QueryInterface(NS_STATIC_CAST(nsINode*, aObject)); if (target) { - target->RemoveEventListener(NS_LITERAL_STRING("mousedown"), eventListener, + target->RemoveEventListener(NS_LITERAL_STRING("mousedown"), listener, PR_FALSE); - target->RemoveEventListener(NS_LITERAL_STRING("contextmenu"), eventListener, + target->RemoveEventListener(NS_LITERAL_STRING("contextmenu"), listener, PR_FALSE); } NS_RELEASE(listener); @@ -2066,43 +2065,38 @@ PopupListenerPropertyDtor(void* aObject, nsIAtom* aPropertyName, nsresult nsXULElement::AddPopupListener(nsIAtom* aName) { - XULPopupType popupType; - nsCOMPtr listenerAtom; - if (aName == nsGkAtoms::context || aName == nsGkAtoms::contextmenu) { - popupType = eXULPopupType_context; - listenerAtom = nsGkAtoms::contextmenulistener; - } else { - popupType = eXULPopupType_popup; - listenerAtom = nsGkAtoms::popuplistener; - } + // Add a popup listener to the element + PRBool isContext = (aName == nsGkAtoms::context || + aName == nsGkAtoms::contextmenu); + nsIAtom* listenerAtom = isContext ? + nsGkAtoms::contextmenulistener : + nsGkAtoms::popuplistener; - nsCOMPtr popupListener = - NS_STATIC_CAST(nsIXULPopupListener*, GetProperty(listenerAtom)); + nsCOMPtr popupListener = + NS_STATIC_CAST(nsIDOMEventListener*, GetProperty(listenerAtom)); if (popupListener) { // Popup listener is already installed. return NS_OK; } - // Add a popup listener to the element - nsresult rv; - popupListener = do_CreateInstance(kXULPopupListenerCID, &rv); - NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to create an instance of the popup listener object."); - if (NS_FAILED(rv)) return rv; - - // Add a weak reference to the node. - popupListener->Init(this, popupType); + nsresult rv = NS_NewXULPopupListener(this, isContext, + getter_AddRefs(popupListener)); + if (NS_FAILED(rv)) + return rv; // Add the popup as a listener on this element. - nsCOMPtr eventListener = do_QueryInterface(popupListener); nsCOMPtr target(do_QueryInterface(NS_STATIC_CAST(nsIContent *, this))); NS_ENSURE_TRUE(target, NS_ERROR_FAILURE); rv = SetProperty(listenerAtom, popupListener, PopupListenerPropertyDtor, PR_TRUE); NS_ENSURE_SUCCESS(rv, rv); - nsIXULPopupListener* listener = popupListener; - NS_ADDREF(listener); - target->AddEventListener(NS_LITERAL_STRING("mousedown"), eventListener, PR_FALSE); - target->AddEventListener(NS_LITERAL_STRING("contextmenu"), eventListener, PR_FALSE); + // Want the property to have a reference to the listener. + nsIDOMEventListener* listener = nsnull; + popupListener.swap(listener); + if (isContext) + target->AddEventListener(NS_LITERAL_STRING("contextmenu"), listener, PR_FALSE); + else + target->AddEventListener(NS_LITERAL_STRING("mousedown"), listener, PR_FALSE); return NS_OK; } diff --git a/mozilla/content/xul/content/src/nsXULPopupListener.cpp b/mozilla/content/xul/content/src/nsXULPopupListener.cpp index 16a83928ab6..1d5b7d5672a 100644 --- a/mozilla/content/xul/content/src/nsXULPopupListener.cpp +++ b/mozilla/content/xul/content/src/nsXULPopupListener.cpp @@ -44,6 +44,7 @@ tracks xul popups and context menus */ +#include "nsXULPopupListener.h" #include "nsCOMPtr.h" #include "nsGkAtoms.h" #include "nsIDOMElement.h" @@ -51,27 +52,23 @@ #include "nsIDOMNodeList.h" #include "nsIDOMDocument.h" #include "nsIDOMDocumentXBL.h" -#include "nsIXULPopupListener.h" -#include "nsIDOMMouseListener.h" -#include "nsIDOMContextMenuListener.h" #include "nsContentCID.h" #include "nsContentUtils.h" +#include "nsXULPopupManager.h" #include "nsIScriptContext.h" #include "nsIDOMWindowInternal.h" #include "nsIDOMXULDocument.h" #include "nsIDocument.h" -#include "nsIContent.h" -#include "nsIDOMMouseEvent.h" #include "nsIDOMNSUIEvent.h" #include "nsIDOMEventTarget.h" #include "nsIDOMNSEvent.h" #include "nsServiceManagerUtils.h" #include "nsIPrincipal.h" #include "nsIScriptSecurityManager.h" - -#include "nsIBoxObject.h" -#include "nsIPopupBoxObject.h" +#include "nsLayoutUtils.h" +#include "nsFrameManager.h" +#include "nsHTMLReflowState.h" // for event firing in context menus #include "nsPresContext.h" @@ -79,9 +76,8 @@ #include "nsIEventStateManager.h" #include "nsIFocusController.h" #include "nsPIDOMWindow.h" +#include "nsIViewManager.h" #include "nsDOMError.h" - -#include "nsIFrame.h" #include "nsIMenuFrame.h" // on win32 and os/2, context menus come up on mouse up. On other platforms, @@ -90,124 +86,49 @@ #define NS_CONTEXT_MENU_IS_MOUSEUP 1 #endif - -//////////////////////////////////////////////////////////////////////// -// PopupListenerImpl -// -// This is the popup listener implementation for popup menus and context menus. -// -class XULPopupListenerImpl : public nsIXULPopupListener, - public nsIDOMMouseListener, - public nsIDOMContextMenuListener -{ -public: - XULPopupListenerImpl(void); - virtual ~XULPopupListenerImpl(void); - -public: - // nsISupports - NS_DECL_ISUPPORTS - - // nsIXULPopupListener - NS_IMETHOD Init(nsIDOMElement* aElement, const XULPopupType& popupType); - - // nsIDOMMouseListener - NS_IMETHOD MouseDown(nsIDOMEvent* aMouseEvent); - NS_IMETHOD MouseUp(nsIDOMEvent* aMouseEvent) { return NS_OK; } - NS_IMETHOD MouseClick(nsIDOMEvent* aMouseEvent) { return NS_OK; } - NS_IMETHOD MouseDblClick(nsIDOMEvent* aMouseEvent) { return NS_OK; } - NS_IMETHOD MouseOver(nsIDOMEvent* aMouseEvent) { return NS_OK; } - NS_IMETHOD MouseOut(nsIDOMEvent* aMouseEvent) { return NS_OK; } - - // nsIDOMContextMenuListener - NS_IMETHOD ContextMenu(nsIDOMEvent* aContextMenuEvent); - - // nsIDOMEventListener - NS_IMETHOD HandleEvent(nsIDOMEvent* anEvent) { return NS_OK; } - -protected: - - virtual nsresult LaunchPopup(nsIDOMEvent* anEvent); - virtual nsresult LaunchPopup(PRInt32 aClientX, PRInt32 aClientY) ; - -private: - - nsresult PreLaunchPopup(nsIDOMEvent* aMouseEvent); - nsresult FireFocusOnTargetContent(nsIDOMNode* aTargetNode); - - // |mElement| is the node to which this listener is attached. - nsIDOMElement* mElement; // Weak ref. The element will go away first. - - // The popup that is getting shown on top of mElement. - nsCOMPtr mPopup; - - // The type of the popup - XULPopupType popupType; - -}; - -//////////////////////////////////////////////////////////////////////// - -XULPopupListenerImpl::XULPopupListenerImpl(void) - : mElement(nsnull) +nsXULPopupListener::nsXULPopupListener(nsIDOMElement *aElement, PRBool aIsContext) + : mElement(aElement), mPopupContent(nsnull), mIsContext(aIsContext) { } -XULPopupListenerImpl::~XULPopupListenerImpl(void) +nsXULPopupListener::~nsXULPopupListener(void) { - if (mPopup) { - mPopup->HidePopup(); - } - -#ifdef DEBUG_REFS - --gInstanceCount; - fprintf(stdout, "%d - RDF: XULPopupListenerImpl\n", gInstanceCount); -#endif + ClosePopup(); } -NS_IMPL_ADDREF(XULPopupListenerImpl) -NS_IMPL_RELEASE(XULPopupListenerImpl) +NS_IMPL_ADDREF(nsXULPopupListener) +NS_IMPL_RELEASE(nsXULPopupListener) -NS_INTERFACE_MAP_BEGIN(XULPopupListenerImpl) - NS_INTERFACE_MAP_ENTRY(nsIXULPopupListener) +NS_INTERFACE_MAP_BEGIN(nsXULPopupListener) NS_INTERFACE_MAP_ENTRY(nsIDOMMouseListener) NS_INTERFACE_MAP_ENTRY(nsIDOMContextMenuListener) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMMouseListener) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULPopupListener) NS_INTERFACE_MAP_END -NS_IMETHODIMP -XULPopupListenerImpl::Init(nsIDOMElement* aElement, const XULPopupType& popup) -{ - mElement = aElement; // Weak reference. Don't addref it. - popupType = popup; - return NS_OK; -} - //////////////////////////////////////////////////////////////// // nsIDOMMouseListener nsresult -XULPopupListenerImpl::MouseDown(nsIDOMEvent* aMouseEvent) +nsXULPopupListener::MouseDown(nsIDOMEvent* aMouseEvent) { - if(popupType != eXULPopupType_context) + if(!mIsContext) return PreLaunchPopup(aMouseEvent); else return NS_OK; } nsresult -XULPopupListenerImpl::ContextMenu(nsIDOMEvent* aMouseEvent) +nsXULPopupListener::ContextMenu(nsIDOMEvent* aMouseEvent) { - if(popupType == eXULPopupType_context) + if(mIsContext) return PreLaunchPopup(aMouseEvent); else return NS_OK; } nsresult -XULPopupListenerImpl::PreLaunchPopup(nsIDOMEvent* aMouseEvent) +nsXULPopupListener::PreLaunchPopup(nsIDOMEvent* aMouseEvent) { PRUint16 button; @@ -230,7 +151,7 @@ XULPopupListenerImpl::PreLaunchPopup(nsIDOMEvent* aMouseEvent) mouseEvent->GetTarget(getter_AddRefs(target)); nsCOMPtr targetNode = do_QueryInterface(target); - if (!targetNode && popupType == eXULPopupType_context) { + if (!targetNode && mIsContext) { // Not a DOM node, see if it's the DOM window (bug 380818). nsCOMPtr domWin = do_QueryInterface(target); if (!domWin) { @@ -250,7 +171,7 @@ XULPopupListenerImpl::PreLaunchPopup(nsIDOMEvent* aMouseEvent) PRBool preventDefault; nsUIEvent->GetPreventDefault(&preventDefault); - if (preventDefault && targetNode && popupType == eXULPopupType_context) { + if (preventDefault && targetNode && mIsContext) { // Someone called preventDefault on a context menu. // Let's make sure they are allowed to do so. PRBool eventEnabled = @@ -278,13 +199,13 @@ XULPopupListenerImpl::PreLaunchPopup(nsIDOMEvent* aMouseEvent) return NS_OK; } - // This is a gross hack to deal with a recursive popup situation happening in AIM code. - // See http://bugzilla.mozilla.org/show_bug.cgi?id=96920. + // prevent popups on menu and menuitems as they handle their own popups + // This was added for bug 96920. // If a menu item child was clicked on that leads to a popup needing // to show, we know (guaranteed) that we're dealing with a menu or // submenu of an already-showing popup. We don't need to do anything at all. - if (popupType == eXULPopupType_popup) { - nsCOMPtr targetContent = do_QueryInterface(target); + nsCOMPtr targetContent = do_QueryInterface(target); + if (!mIsContext) { nsIAtom *tag = targetContent ? targetContent->Tag() : nsnull; if (tag == nsGkAtoms::menu || tag == nsGkAtoms::menuitem) return NS_OK; @@ -295,48 +216,38 @@ XULPopupListenerImpl::PreLaunchPopup(nsIDOMEvent* aMouseEvent) // Turn the document into a XUL document so we can use SetPopupNode. nsCOMPtr xulDocument = do_QueryInterface(content->GetDocument()); - if (!xulDocument) { - NS_ERROR("Popup attached to an element that isn't in XUL!"); + if (!xulDocument) return NS_ERROR_FAILURE; - } // Store clicked-on node in xul document for context menus and menu popups. - // CLEAR THE POPUP EVENT BEFORE THIS FUNCTION EXITS - xulDocument->SetPopupNode( targetNode ); - xulDocument->SetTrustedPopupEvent( aMouseEvent ); + xulDocument->SetPopupNode(targetNode); nsCOMPtr nsevent(do_QueryInterface(aMouseEvent)); - switch (popupType) { - case eXULPopupType_popup: - // Check for left mouse button down - mouseEvent->GetButton(&button); - if (button == 0) { - // Time to launch a popup menu. - LaunchPopup(aMouseEvent); - aMouseEvent->StopPropagation(); - aMouseEvent->PreventDefault(); - } - break; - case eXULPopupType_context: - - // Time to launch a context menu + if (mIsContext) { #ifndef NS_CONTEXT_MENU_IS_MOUSEUP - // If the context menu launches on mousedown, - // we have to fire focus on the content we clicked on - FireFocusOnTargetContent(targetNode); + // If the context menu launches on mousedown, + // we have to fire focus on the content we clicked on + FireFocusOnTargetContent(targetNode); #endif - LaunchPopup(aMouseEvent); - aMouseEvent->StopPropagation(); - aMouseEvent->PreventDefault(); - break; } - xulDocument->SetTrustedPopupEvent(nsnull); + else { + // Only open popups when the left mouse button is down. + mouseEvent->GetButton(&button); + if (button != 0) + return NS_OK; + } + + // Open the popup and cancel the default handling of the event. + LaunchPopup(aMouseEvent, targetContent); + aMouseEvent->StopPropagation(); + aMouseEvent->PreventDefault(); + return NS_OK; } nsresult -XULPopupListenerImpl::FireFocusOnTargetContent(nsIDOMNode* aTargetNode) +nsXULPopupListener::FireFocusOnTargetContent(nsIDOMNode* aTargetNode) { nsresult rv; nsCOMPtr domDoc; @@ -408,26 +319,25 @@ XULPopupListenerImpl::FireFocusOnTargetContent(nsIDOMNode* aTargetNode) return rv; } +// ClosePopup // -// LaunchPopup +// Do everything needed to shut down the popup. // -nsresult -XULPopupListenerImpl::LaunchPopup ( nsIDOMEvent* anEvent ) +// NOTE: This routine is safe to call even if the popup is already closed. +// +void +nsXULPopupListener::ClosePopup() { - // Retrieve our x and y position. - nsCOMPtr mouseEvent ( do_QueryInterface(anEvent) ); - if (!mouseEvent) { - //non-ui event passed in. bad things. - return NS_OK; + if (mPopupContent) { + // this is called when the listener is going away, so make sure that the + // popup is hidden. Use asynchronous hiding just to be safe so we don't + // fire events during destruction. + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) + pm->HidePopup(mPopupContent, PR_FALSE, PR_TRUE, PR_TRUE); + mPopupContent = nsnull; // release the popup } - - PRInt32 xPos, yPos; - mouseEvent->GetClientX(&xPos); - mouseEvent->GetClientY(&yPos); - - return LaunchPopup(xPos, yPos); -} - +} // ClosePopup static void GetImmediateChild(nsIContent* aContent, nsIAtom *aTag, nsIContent** aResult) @@ -446,53 +356,6 @@ GetImmediateChild(nsIContent* aContent, nsIAtom *aTag, nsIContent** aResult) return; } -static void ConvertPosition(nsIDOMElement* aPopupElt, nsString& aAnchor, nsString& aAlign, PRInt32& aY) -{ - nsAutoString position; - aPopupElt->GetAttribute(NS_LITERAL_STRING("position"), position); - if (position.IsEmpty()) - return; - - if (position.EqualsLiteral("before_start")) { - aAnchor.AssignLiteral("topleft"); - aAlign.AssignLiteral("bottomleft"); - } - else if (position.EqualsLiteral("before_end")) { - aAnchor.AssignLiteral("topright"); - aAlign.AssignLiteral("bottomright"); - } - else if (position.EqualsLiteral("after_start")) { - aAnchor.AssignLiteral("bottomleft"); - aAlign.AssignLiteral("topleft"); - } - else if (position.EqualsLiteral("after_end")) { - aAnchor.AssignLiteral("bottomright"); - aAlign.AssignLiteral("topright"); - } - else if (position.EqualsLiteral("start_before")) { - aAnchor.AssignLiteral("topleft"); - aAlign.AssignLiteral("topright"); - } - else if (position.EqualsLiteral("start_after")) { - aAnchor.AssignLiteral("bottomleft"); - aAlign.AssignLiteral("bottomright"); - } - else if (position.EqualsLiteral("end_before")) { - aAnchor.AssignLiteral("topright"); - aAlign.AssignLiteral("topleft"); - } - else if (position.EqualsLiteral("end_after")) { - aAnchor.AssignLiteral("bottomright"); - aAlign.AssignLiteral("bottomleft"); - } - else if (position.EqualsLiteral("overlap")) { - aAnchor.AssignLiteral("topleft"); - aAlign.AssignLiteral("topleft"); - } - else if (position.EqualsLiteral("after_pointer")) - aY += 21; -} - // // LaunchPopup // @@ -500,25 +363,22 @@ static void ConvertPosition(nsIDOMElement* aPopupElt, nsString& aAnchor, nsStrin // Client and widget coordinates, popup a new window showing the appropriate // content. // -// This looks for an attribute on |aElement| of the appropriate popup type +// aTargetContent is the target of the mouse event aEvent that triggered the +// popup. mElement is the element that the popup menu is attached to. The +// former may be equal to mElement or it may be a descendant. +// +// This looks for an attribute on |mElement| of the appropriate popup type // (popup, context) and uses that attribute's value as an ID for // the popup content in the document. // nsresult -XULPopupListenerImpl::LaunchPopup(PRInt32 aClientX, PRInt32 aClientY) +nsXULPopupListener::LaunchPopup(nsIDOMEvent* aEvent, nsIContent* aTargetContent) { nsresult rv = NS_OK; nsAutoString type(NS_LITERAL_STRING("popup")); - if ( popupType == eXULPopupType_context ) { + if (mIsContext) type.AssignLiteral("context"); - - // position the menu two pixels down and to the right from the current - // mouse position. This makes it easier to dismiss the menu by just - // clicking. - aClientX += 2; - aClientY += 2; - } nsAutoString identifier; mElement->GetAttribute(type, identifier); @@ -544,14 +404,14 @@ XULPopupListenerImpl::LaunchPopup(PRInt32 aClientX, PRInt32 aClientY) } // Handle the _child case for popups and context menus - nsCOMPtr popupContent; + nsCOMPtr popupElement; if (identifier.EqualsLiteral("_child")) { nsCOMPtr popup; GetImmediateChild(content, nsGkAtoms::menupopup, getter_AddRefs(popup)); if (popup) - popupContent = do_QueryInterface(popup); + popupElement = do_QueryInterface(popup); else { nsCOMPtr nsDoc(do_QueryInterface(domDocument)); nsCOMPtr list; @@ -566,7 +426,7 @@ XULPopupListenerImpl::LaunchPopup(PRInt32 aClientX, PRInt32 aClientY) if (childContent->NodeInfo()->Equals(nsGkAtoms::menupopup, kNameSpaceID_XUL)) { - popupContent = do_QueryInterface(childContent); + popupElement = do_QueryInterface(childContent); break; } } @@ -574,58 +434,60 @@ XULPopupListenerImpl::LaunchPopup(PRInt32 aClientX, PRInt32 aClientY) } } else if (NS_FAILED(rv = domDocument->GetElementById(identifier, - getter_AddRefs(popupContent)))) { + getter_AddRefs(popupElement)))) { // Use getElementById to obtain the popup content and gracefully fail if // we didn't find any popup content in the document. NS_ERROR("GetElementById had some kind of spasm."); return rv; } - if (!popupContent || mElement == popupContent) + // return if no popup was found or the popup is the element itself. + if ( !popupElement || popupElement == mElement) return NS_OK; // Submenus can't be used as context menus or popups, bug 288763. // Similar code also in nsXULTooltipListener::GetTooltipFor. - nsCOMPtr popup = do_QueryInterface(popupContent); + nsCOMPtr popup = do_QueryInterface(popupElement); nsIContent* parent = popup->GetParent(); if (parent) { nsIDocument* doc = parent->GetCurrentDoc(); nsIPresShell* presShell = doc ? doc->GetPrimaryShell() : nsnull; nsIFrame* frame = presShell ? presShell->GetPrimaryFrameFor(parent) : nsnull; - if (frame) { - nsIMenuFrame* menu = nsnull; - CallQueryInterface(frame, &menu); - NS_ENSURE_FALSE(menu, NS_OK); - } + if (frame && frame->GetType() == nsGkAtoms::menuFrame) + return NS_OK; } - // We have some popup content. Obtain our window. - nsPIDOMWindow *domWindow = document->GetWindow(); + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (!pm) + return NS_OK; - if (domWindow) { - // Find out if we're anchored. - nsAutoString anchorAlignment; - popupContent->GetAttribute(NS_LITERAL_STRING("popupanchor"), anchorAlignment); + // XXXndeakin this is temporary. It is needed to grab the mouse location details + // used by the spellchecking popup. See bug 383930. + pm->SetMouseLocation(aEvent); - nsAutoString popupAlignment; - popupContent->GetAttribute(NS_LITERAL_STRING("popupalign"), popupAlignment); + // if the popup has an anchoring attribute, anchor it to the element, + // otherwise just open it at the screen position where the mouse was clicked. + mPopupContent = popup; + if (mPopupContent->HasAttr(kNameSpaceID_None, nsGkAtoms::position) || + mPopupContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popupanchor) || + mPopupContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popupalign)) { + pm->ShowPopup(mPopupContent, content, EmptyString(), 0, 0, + mIsContext, PR_TRUE, PR_FALSE); + } + else { + PRInt32 xPos = 0, yPos = 0; + nsCOMPtr mouseEvent = do_QueryInterface(aEvent); + mouseEvent->GetScreenX(&xPos); + mouseEvent->GetScreenY(&yPos); - PRInt32 xPos = aClientX, yPos = aClientY; - - ConvertPosition(popupContent, anchorAlignment, popupAlignment, yPos); - if (!anchorAlignment.IsEmpty() && !popupAlignment.IsEmpty()) - xPos = yPos = -1; - - nsCOMPtr popupBox; - nsCOMPtr xulPopupElt(do_QueryInterface(popupContent)); - xulPopupElt->GetBoxObject(getter_AddRefs(popupBox)); - nsCOMPtr popupBoxObject(do_QueryInterface(popupBox)); - if (popupBoxObject) { - mPopup = popupBoxObject; - popupBoxObject->ShowPopup(mElement, popupContent, xPos, yPos, - type.get(), anchorAlignment.get(), - popupAlignment.get()); + if (mIsContext) { + // position the menu two pixels down and to the right from the current + // mouse position. This makes it easier to dismiss the menu by just clicking + xPos += 2; + yPos += 2; } + + pm->ShowPopupAtScreen(mPopupContent, xPos, yPos, mIsContext); } return NS_OK; @@ -633,13 +495,14 @@ XULPopupListenerImpl::LaunchPopup(PRInt32 aClientX, PRInt32 aClientY) //////////////////////////////////////////////////////////////// nsresult -NS_NewXULPopupListener(nsIXULPopupListener** pop) +NS_NewXULPopupListener(nsIDOMElement* aElement, PRBool aIsContext, + nsIDOMEventListener** aListener) { - XULPopupListenerImpl* popup = new XULPopupListenerImpl(); - if (!popup) + nsXULPopupListener* pl = new nsXULPopupListener(aElement, aIsContext); + if (!pl) return NS_ERROR_OUT_OF_MEMORY; - - NS_ADDREF(popup); - *pop = popup; + + *aListener = NS_STATIC_CAST(nsIDOMMouseListener *, pl); + NS_ADDREF(*aListener); return NS_OK; } diff --git a/mozilla/content/xul/content/src/nsXULPopupListener.h b/mozilla/content/xul/content/src/nsXULPopupListener.h new file mode 100644 index 00000000000..07e24b81c3a --- /dev/null +++ b/mozilla/content/xul/content/src/nsXULPopupListener.h @@ -0,0 +1,123 @@ +/* -*- 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 + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Original Author: David W. Hyatt (hyatt@netscape.com) + * Dean Tessman + * Pierre Phaneuf + * Robert O'Callahan + * + * 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 ***** */ + +/** + * This is the popup listener implementation for popup menus and context menus. + */ + +#ifndef nsXULPopupListener_h___ +#define nsXULPopupListener_h___ + +#include "nsCOMPtr.h" + +#include "nsIContent.h" +#include "nsIDOMElement.h" +#include "nsIDOMMouseEvent.h" +#include "nsIFrame.h" +#include "nsIDOMMouseListener.h" +#include "nsIDOMContextMenuListener.h" + +class nsXULPopupListener : public nsIDOMMouseListener, + public nsIDOMContextMenuListener +{ +public: + // aElement is the element that the popup is attached to. If aIsContext is + // false, the popup opens on left click on aElement or a descendant. If + // aIsContext is true, the popup is a context menu which opens on a + // context menu event. + nsXULPopupListener(nsIDOMElement *aElement, PRBool aIsContext); + virtual ~nsXULPopupListener(void); + +public: + // nsISupports + NS_DECL_ISUPPORTS + + // nsIDOMMouseListener + NS_IMETHOD MouseDown(nsIDOMEvent* aMouseEvent); + NS_IMETHOD MouseUp(nsIDOMEvent* aMouseEvent) { return NS_OK; } + NS_IMETHOD MouseClick(nsIDOMEvent* aMouseEvent) { return NS_OK; } + NS_IMETHOD MouseDblClick(nsIDOMEvent* aMouseEvent) { return NS_OK; } + NS_IMETHOD MouseOver(nsIDOMEvent* aMouseEvent) { return NS_OK; } + NS_IMETHOD MouseOut(nsIDOMEvent* aMouseEvent) { return NS_OK; } + + // nsIDOMContextMenuListener + NS_IMETHOD ContextMenu(nsIDOMEvent* aContextMenuEvent); + + // nsIDOMEventListener + NS_IMETHOD HandleEvent(nsIDOMEvent* anEvent) { return NS_OK; } + +protected: + + // open the popup. aEvent is the event that triggered the popup such as + // a mouse click and aTargetContent is the target of this event. + virtual nsresult LaunchPopup(nsIDOMEvent* aEvent, nsIContent* aTargetContent); + + // close the popup when the listener goes away + virtual void ClosePopup(); + +private: + + // PreLaunchPopup is called before LaunchPopup to ensure that the event is + // suitable and to initialize the XUL document's popupNode to the event + // target. + nsresult PreLaunchPopup(nsIDOMEvent* aMouseEvent); + + // When a context menu is opened, focus the target of the contextmenu event. + nsresult FireFocusOnTargetContent(nsIDOMNode* aTargetNode); + + // |mElement| is the node to which this listener is attached. + nsIDOMElement* mElement; // Weak ref. The element will go away first. + + // The popup that is getting shown on top of mElement. + nsCOMPtr mPopupContent; + + // true if a context popup + PRBool mIsContext; +}; + +// Construct a new nsXULPopupListener and return in aListener. See the +// nsXULPopupListener constructor for details about the aElement and +// aIsContext arguments. +nsresult +NS_NewXULPopupListener(nsIDOMElement* aElement, PRBool aIsContext, + nsIDOMEventListener** aListener); + +#endif // nsXULPopupListener_h___ diff --git a/mozilla/content/xul/document/src/nsXULDocument.cpp b/mozilla/content/xul/document/src/nsXULDocument.cpp index 4240f766fd0..596d1d01efc 100644 --- a/mozilla/content/xul/document/src/nsXULDocument.cpp +++ b/mozilla/content/xul/document/src/nsXULDocument.cpp @@ -128,6 +128,7 @@ #include "nsIDocShellTreeItem.h" #include "nsIDocShellTreeOwner.h" #include "nsIXULWindow.h" +#include "nsXULPopupManager.h" //---------------------------------------------------------------------- // @@ -1456,84 +1457,49 @@ nsXULDocument::SetPopupNode(nsIDOMNode* aNode) return rv; } -NS_IMETHODIMP -nsXULDocument::GetTrustedPopupEvent(nsIDOMEvent** aEvent) -{ - nsresult rv; - - nsCOMPtr focusController; - GetFocusController(getter_AddRefs(focusController)); - NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE); - - rv = focusController->GetPopupEvent(aEvent); - - return rv; -} - -NS_IMETHODIMP -nsXULDocument::SetTrustedPopupEvent(nsIDOMEvent* aEvent) -{ - nsresult rv; - - nsCOMPtr focusController; - GetFocusController(getter_AddRefs(focusController)); - NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE); - - rv = focusController->SetPopupEvent(aEvent); - - return rv; -} - -// Returns the rangeOffset element from the popupEvent. This is for chrome -// callers only. +// Returns the rangeOffset element from the XUL Popup Manager. This is for +// chrome callers only. NS_IMETHODIMP nsXULDocument::GetPopupRangeParent(nsIDOMNode** aRangeParent) { NS_ENSURE_ARG_POINTER(aRangeParent); *aRangeParent = nsnull; - nsCOMPtr event; - nsresult rv = GetTrustedPopupEvent(getter_AddRefs(event)); - NS_ENSURE_SUCCESS(rv, rv); - if (! event) - return NS_ERROR_UNEXPECTED; // no event active + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (!pm) + return NS_ERROR_FAILURE; - nsCOMPtr uiEvent = do_QueryInterface(event, &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = uiEvent->GetRangeParent(aRangeParent); // addrefs + PRInt32 offset; + pm->GetMouseLocation(aRangeParent, &offset); - if (NS_SUCCEEDED(rv) && *aRangeParent && - !nsContentUtils::CanCallerAccess(*aRangeParent)) { + if (*aRangeParent && !nsContentUtils::CanCallerAccess(*aRangeParent)) { NS_RELEASE(*aRangeParent); return NS_ERROR_DOM_SECURITY_ERR; } - return rv; + + return NS_OK; } -// Returns the rangeOffset element from the popupEvent. We check the rangeParent -// to determine if the caller has rights to access to the data. +// Returns the rangeOffset element from the XUL Popup Manager. We check the +// rangeParent to determine if the caller has rights to access to the data. NS_IMETHODIMP nsXULDocument::GetPopupRangeOffset(PRInt32* aRangeOffset) { NS_ENSURE_ARG_POINTER(aRangeOffset); - nsCOMPtr event; - nsresult rv = GetTrustedPopupEvent(getter_AddRefs(event)); - NS_ENSURE_SUCCESS(rv, rv); - if (! event) - return NS_ERROR_UNEXPECTED; // no event active - - nsCOMPtr uiEvent = do_QueryInterface(event, &rv); - NS_ENSURE_SUCCESS(rv, rv); + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (!pm) + return NS_ERROR_FAILURE; + PRInt32 offset; nsCOMPtr parent; - rv = uiEvent->GetRangeParent(getter_AddRefs(parent)); - NS_ENSURE_SUCCESS(rv, rv); + pm->GetMouseLocation(getter_AddRefs(parent), &offset); if (parent && !nsContentUtils::CanCallerAccess(parent)) return NS_ERROR_DOM_SECURITY_ERR; - return uiEvent->GetRangeOffset(aRangeOffset); + *aRangeOffset = offset; + return NS_OK; } NS_IMETHODIMP diff --git a/mozilla/dom/public/idl/xul/nsIDOMXULDocument.idl b/mozilla/dom/public/idl/xul/nsIDOMXULDocument.idl index 46da2faa03a..ed8be7f8d02 100644 --- a/mozilla/dom/public/idl/xul/nsIDOMXULDocument.idl +++ b/mozilla/dom/public/idl/xul/nsIDOMXULDocument.idl @@ -110,9 +110,4 @@ interface nsIDOMXULDocument : nsISupports * Like trustedGetPopupNode, but gets the tooltip node instead. */ [noscript] nsIDOMNode trustedGetTooltipNode(); - - /** - * Like trustedGetPopupNode, but gets the - */ - [noscript] attribute nsIDOMEvent trustedPopupEvent; }; diff --git a/mozilla/dom/src/base/nsGlobalWindow.cpp b/mozilla/dom/src/base/nsGlobalWindow.cpp index 41c4b9c8f56..c60a70d5620 100644 --- a/mozilla/dom/src/base/nsGlobalWindow.cpp +++ b/mozilla/dom/src/base/nsGlobalWindow.cpp @@ -159,6 +159,7 @@ #include "nsIObserverService.h" #include "nsIXULAppInfo.h" #include "nsNetUtil.h" +#include "nsXULPopupManager.h" #include "plbase64.h" @@ -3037,10 +3038,10 @@ nsGlobalWindow::CheckSecurityWidthAndHeight(PRInt32* aWidth, PRInt32* aHeight) { if (!nsContentUtils::IsCallerTrustedForWrite()) { // if attempting to resize the window, hide any open popups - nsCOMPtr presShell; - mDocShell->GetPresShell(getter_AddRefs(presShell)); - if (presShell) - presShell->HidePopups(); + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + nsCOMPtr doc(do_QueryInterface(mDocument)); + if (pm && doc) + pm->HidePopupsInDocument(doc); } // This one is easy. Just ensure the variable is greater than 100; @@ -3070,10 +3071,10 @@ nsGlobalWindow::CheckSecurityLeftAndTop(PRInt32* aLeft, PRInt32* aTop) if (!nsContentUtils::IsCallerTrustedForWrite()) { // if attempting to move the window, hide any open popups - nsCOMPtr presShell; - mDocShell->GetPresShell(getter_AddRefs(presShell)); - if (presShell) - presShell->HidePopups(); + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + nsCOMPtr doc(do_QueryInterface(mDocument)); + if (pm && doc) + pm->HidePopupsInDocument(doc); PRInt32 screenLeft, screenTop, screenWidth, screenHeight; PRInt32 winLeft, winTop, winWidth, winHeight; diff --git a/mozilla/layout/base/nsCSSFrameConstructor.cpp b/mozilla/layout/base/nsCSSFrameConstructor.cpp index f2d00a25a30..7b6db481265 100644 --- a/mozilla/layout/base/nsCSSFrameConstructor.cpp +++ b/mozilla/layout/base/nsCSSFrameConstructor.cpp @@ -119,7 +119,6 @@ #include "nsContentErrors.h" #include "nsIDOMWindowInternal.h" -#include "nsIMenuFrame.h" #include "nsBox.h" @@ -1706,11 +1705,8 @@ GetChildListNameFor(nsIFrame* aChildFrame) // Out-of-flows that are DISPLAY_POPUP must be kids of the root popup set #ifdef DEBUG nsIFrame* parent = aChildFrame->GetParent(); - if (parent) { - nsIPopupSetFrame* popupSet; - CallQueryInterface(parent, &popupSet); - NS_ASSERTION(popupSet, "Unexpected parent"); - } + NS_ASSERTION(parent && parent->GetType() == nsGkAtoms::popupSetFrame, + "Unexpected parent"); #endif // DEBUG // XXX FIXME: Bug 350740 @@ -5956,9 +5952,7 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, // If, however, the parent is *not* a menu frame, then we need to create // a placeholder frame for the popup, and then we add the popup frame to the // root popup set (that manages all such "detached" popups). - nsIMenuFrame* menuFrame; - CallQueryInterface(aParentFrame, &menuFrame); - if (!menuFrame) { + if (aParentFrame->GetType() != nsGkAtoms::menuFrame) { if (!aState.mPopupItems.containingBlock) { // Just don't create a frame for this popup; we can't do // anything with it, since there is no root popup set. @@ -5968,9 +5962,9 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, } #ifdef NS_DEBUG - nsIPopupSetFrame* popupSet; - CallQueryInterface(aState.mPopupItems.containingBlock, &popupSet); - NS_ASSERTION(popupSet, "Popup containing block isn't a nsIPopupSetFrame"); + NS_ASSERTION(aState.mPopupItems.containingBlock->GetType() == + nsGkAtoms::popupSetFrame, + "Popup containing block isn't a nsIPopupSetFrame"); #endif isPopup = PR_TRUE; } @@ -6145,6 +6139,16 @@ nsCSSFrameConstructor::ConstructXULFrame(nsFrameConstructorState& aState, return rv; } +nsresult +nsCSSFrameConstructor::AddLazyChildren(nsIContent* aContent, + nsLazyFrameConstructionCallback* aCallback, + void* aArg) +{ + nsCOMPtr event = + new LazyGenerateChildrenEvent(aContent, mPresShell, aCallback, aArg); + return NS_DispatchToCurrentThread(event); +} + already_AddRefed nsCSSFrameConstructor::BeginBuildingScrollFrame(nsFrameConstructorState& aState, nsIContent* aContent, @@ -10133,29 +10137,6 @@ nsCSSFrameConstructor::AttributeChanged(nsIContent* aContent, aAttribute, aModType); - // Menus and such can't deal with asynchronous changes of display - // when the menugenerated or menuactive attribute changes, so make - // sure to process that immediately - if (aNameSpaceID == kNameSpaceID_None && - ((aAttribute == nsGkAtoms::menugenerated && - aModType != nsIDOMMutationEvent::REMOVAL) || - aAttribute == nsGkAtoms::menuactive)) { - PRInt32 namespaceID; - nsIAtom* tag = - mDocument->BindingManager()->ResolveTag(aContent, &namespaceID); - - if (namespaceID == kNameSpaceID_XUL && - (tag == nsGkAtoms::menupopup || tag == nsGkAtoms::popup || - tag == nsGkAtoms::tooltip || tag == nsGkAtoms::menu)) { - nsIViewManager* viewManager = mPresShell->GetViewManager(); - viewManager->BeginUpdateViewBatch(); - ProcessOneRestyle(aContent, rshint, hint); - viewManager->EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC); - - return result; - } - } - PostRestyleEvent(aContent, rshint, hint); return result; @@ -13025,3 +13006,42 @@ NS_IMETHODIMP nsCSSFrameConstructor::RestyleEvent::Run() { return NS_OK; } +NS_IMETHODIMP +nsCSSFrameConstructor::LazyGenerateChildrenEvent::Run() +{ + mPresShell->GetDocument()->FlushPendingNotifications(Flush_Layout); + + // this is hard-coded to handle only menu popup frames + nsIFrame* frame = mPresShell->GetPrimaryFrameFor(mContent); + if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) { + // it is possible that the frame is different than the one that requested + // the lazy generation, but as long as it's a popup frame that hasn't + // generated its children yet, that's OK. + nsMenuPopupFrame* menuPopupFrame = NS_STATIC_CAST(nsMenuPopupFrame *, frame); + if (menuPopupFrame->HasGeneratedChildren()) + return NS_OK; + + // indicate that the children have been generated + menuPopupFrame->SetGeneratedChildren(); + + nsFrameItems childItems; + nsFrameConstructorState state(mPresShell, nsnull, nsnull, nsnull); + nsCSSFrameConstructor* fc = mPresShell->FrameConstructor(); + nsresult rv = fc->ProcessChildren(state, mContent, frame, PR_FALSE, + childItems, PR_FALSE); + if (NS_FAILED(rv)) + return rv; + + fc->CreateAnonymousFrames(mContent->Tag(), state, mContent, frame, + PR_FALSE, childItems); + frame->SetInitialChildList(nsnull, childItems.childList); + + if (mCallback) + mCallback(mContent, frame, mArg); + + // call XBL constructors after the frames are created + mPresShell->GetDocument()->BindingManager()->ProcessAttachedQueue(); + } + + return NS_OK; +} diff --git a/mozilla/layout/base/nsCSSFrameConstructor.h b/mozilla/layout/base/nsCSSFrameConstructor.h index 7f67d74b216..e479992c900 100644 --- a/mozilla/layout/base/nsCSSFrameConstructor.h +++ b/mozilla/layout/base/nsCSSFrameConstructor.h @@ -72,6 +72,9 @@ struct nsFindFrameHint nsFindFrameHint() : mPrimaryFrameForPrevSibling(nsnull) { } }; +typedef void (PR_CALLBACK nsLazyFrameConstructionCallback) + (nsIContent* aContent, nsIFrame* aFrame, void* aArg); + class nsFrameConstructorState; class nsFrameConstructorSaveState; @@ -122,6 +125,17 @@ public: nsIContent* aContent2, PRInt32 aStateMask); + // Process the children of aContent and indicate that frames should be + // created for them. This is used for lazily built content such as that + // inside popups so that it is only created when the popup is opened. + // This method constructs the frames asynchronously. + // aCallback will be called with three arguments, the first is the value + // of aContent, the second is aContent's primary frame, and the third is + // the value of aArg. + nsresult AddLazyChildren(nsIContent* aContent, + nsLazyFrameConstructionCallback* aCallback, + void* aArg); + // Should be called when a frame is going to be destroyed and // WillDestroyFrameTree hasn't been called yet. void NotifyDestroyingFrame(nsIFrame* aFrame); @@ -1003,6 +1017,27 @@ public: friend class nsFrameConstructorState; private: + + class LazyGenerateChildrenEvent; + friend class LazyGenerateChildrenEvent; + + class LazyGenerateChildrenEvent : public nsRunnable { + public: + NS_DECL_NSIRUNNABLE + LazyGenerateChildrenEvent(nsIContent *aContent, + nsIPresShell *aPresShell, + nsLazyFrameConstructionCallback* aCallback, + void* aArg) + : mContent(aContent), mPresShell(aPresShell), mCallback(aCallback), mArg(aArg) + {} + + private: + nsCOMPtr mContent; + nsCOMPtr mPresShell; + nsLazyFrameConstructionCallback* mCallback; + void* mArg; + }; + nsIDocument* mDocument; // Weak ref nsIPresShell* mPresShell; // Weak ref diff --git a/mozilla/layout/base/nsDocumentViewer.cpp b/mozilla/layout/base/nsDocumentViewer.cpp index 272f036534a..8f8f8866da7 100644 --- a/mozilla/layout/base/nsDocumentViewer.cpp +++ b/mozilla/layout/base/nsDocumentViewer.cpp @@ -111,6 +111,7 @@ #ifdef MOZ_XUL #include "nsIXULDocument.h" #endif +#include "nsXULPopupManager.h" #include "nsPrintfCString.h" #include "nsIClipboardHelper.h" @@ -1153,10 +1154,9 @@ DocumentViewerImpl::PageHide(PRBool aIsUnload) // look for open menupopups and close them after the unload event, in case // the unload event listeners open any new popups - if (mPresShell) { - nsCOMPtr kungFuDeathGrip = mPresShell; - mPresShell->HidePopups(); - } + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm && mDocument) + pm->HidePopupsInDocument(mDocument); return NS_OK; } diff --git a/mozilla/layout/base/nsIPresShell.h b/mozilla/layout/base/nsIPresShell.h index 6b955deba1b..049e055d989 100644 --- a/mozilla/layout/base/nsIPresShell.h +++ b/mozilla/layout/base/nsIPresShell.h @@ -102,10 +102,10 @@ class gfxContext; typedef short SelectionType; typedef PRUint32 nsFrameState; -// 9562bb2b-990c-4875-aafd-bd46fc9a4fc1 +// D93B931B-D5EF-4D3C-AB99-444176963464 #define NS_IPRESSHELL_IID \ -{ 0x9562bb2b, 0x990c, 0x4875, \ - { 0xaa, 0xfd, 0xbd, 0x46, 0xfc, 0x9a, 0x4f, 0xc1 } } +{ 0xd93b931b, 0xd5ef, 0x4d3c, \ + { 0xab, 0x99, 0x44, 0x41, 0x76, 0x96, 0x34, 0x64 } } // Constants for ScrollContentIntoView() function #define NS_PRESSHELL_SCROLL_TOP 0 @@ -725,8 +725,6 @@ public: nsPoint& aPoint, nsRect* aScreenRect) = 0; - virtual void HidePopups() = 0; - void AddWeakFrame(nsWeakFrame* aWeakFrame); void RemoveWeakFrame(nsWeakFrame* aWeakFrame); diff --git a/mozilla/layout/base/nsPresShell.cpp b/mozilla/layout/base/nsPresShell.cpp index d0b0d3450f3..fc63f9b5452 100644 --- a/mozilla/layout/base/nsPresShell.cpp +++ b/mozilla/layout/base/nsPresShell.cpp @@ -189,7 +189,7 @@ #include "nsStyleChangeList.h" #include "nsCSSFrameConstructor.h" #ifdef MOZ_XUL -#include "nsIMenuFrame.h" +#include "nsMenuFrame.h" #include "nsITreeBoxObject.h" #endif #include "nsIMenuParent.h" @@ -888,8 +888,6 @@ public: nsPoint& aPoint, nsRect* aScreenRect); - virtual void HidePopups(); - //nsIViewObserver interface NS_IMETHOD Paint(nsIView *aView, @@ -5840,18 +5838,6 @@ PresShell::Thaw() UnsuppressPainting(); } -void -PresShell::HidePopups() -{ - nsIViewManager *vm = GetViewManager(); - if (vm) { - nsIView *rootView = nsnull; - vm->GetRootView(rootView); - if (rootView) - HideViewIfPopup(rootView); - } -} - //-------------------------------------------------------- // Start of protected and private methods on the PresShell //-------------------------------------------------------- @@ -6186,11 +6172,8 @@ ReResolveMenusAndTrees(nsIFrame *aFrame, void *aClosure) // sub-content, since doing so slows menus to a crawl. That means we // have to special-case them on a skin switch, and ensure that the // popup frames just get destroyed completely. - nsCOMPtr menuFrame(do_QueryInterface(aFrame)); - if (menuFrame) { - menuFrame->UngenerateMenu(); - menuFrame->OpenMenu(PR_FALSE); - } + if (aFrame && aFrame->GetType() == nsGkAtoms::menuFrame) + (NS_STATIC_CAST(nsMenuFrame *, aFrame))->CloseMenu(PR_TRUE); return PR_TRUE; } @@ -6328,27 +6311,6 @@ PresShell::EnumeratePlugins(nsIDOMDocument *aDocument, } } -void -PresShell::HideViewIfPopup(nsIView* aView) -{ - nsIFrame* frame = NS_STATIC_CAST(nsIFrame*, aView->GetClientData()); - if (frame) { - nsIMenuParent* parent; - CallQueryInterface(frame, &parent); - if (parent) { - parent->HideChain(); - // really make sure the view is hidden - mViewManager->SetViewVisibility(aView, nsViewVisibility_kHide); - } - } - - nsIView* child = aView->GetFirstChild(); - while (child) { - HideViewIfPopup(child); - child = child->GetNextSibling(); - } -} - //------------------------------------------------------ // End of protected and private methods on the PresShell //------------------------------------------------------ diff --git a/mozilla/layout/build/nsLayoutModule.cpp b/mozilla/layout/build/nsLayoutModule.cpp index 04ea85c1034..fb855f1063b 100644 --- a/mozilla/layout/build/nsLayoutModule.cpp +++ b/mozilla/layout/build/nsLayoutModule.cpp @@ -235,7 +235,6 @@ static NS_DEFINE_CID(kWindowCommandTableCID, NS_WINDOWCOMMANDTABLE_CID); #ifdef MOZ_XUL #include "nsIBoxObject.h" #include "nsIXULDocument.h" -#include "nsIXULPopupListener.h" #include "nsIXULPrototypeCache.h" #include "nsIXULSortService.h" @@ -508,7 +507,6 @@ MAKE_CTOR(CreateXULSortService, nsIXULSortService, NS_NewXUL // NS_NewXULContentBuilder // NS_NewXULTreeBuilder MAKE_CTOR(CreateXULDocument, nsIXULDocument, NS_NewXULDocument) -MAKE_CTOR(CreateXULPopupListener, nsIXULPopupListener, NS_NewXULPopupListener) // NS_NewXULControllers // NS_NewXULPrototypeCache #endif @@ -1192,11 +1190,6 @@ static const nsModuleComponentInfo gComponents[] = { "@mozilla.org/xul/xul-document;1", CreateXULDocument }, - { "XUL PopupListener", - NS_XULPOPUPLISTENER_CID, - "@mozilla.org/xul/xul-popup-listener;1", - CreateXULPopupListener }, - { "XUL Prototype Cache", NS_XULPROTOTYPECACHE_CID, "@mozilla.org/xul/xul-prototype-cache;1", diff --git a/mozilla/layout/build/nsLayoutStatics.cpp b/mozilla/layout/build/nsLayoutStatics.cpp index daf98c96350..b79fdf6637a 100644 --- a/mozilla/layout/build/nsLayoutStatics.cpp +++ b/mozilla/layout/build/nsLayoutStatics.cpp @@ -79,6 +79,7 @@ #include "nsCellMap.h" #include "nsTextFrameTextRunCache.h" #include "nsCCUncollectableMarker.h" +#include "nsXULPopupManager.h" #include "nsTextFragment.h" #ifdef MOZ_XUL @@ -218,12 +219,19 @@ nsLayoutStatics::Initialize() return rv; } + rv = nsXULPopupManager::Init(); + if (NS_FAILED(rv)) { + NS_ERROR("Could not initialize nsXULPopupManager"); + return rv; + } + return NS_OK; } void nsLayoutStatics::Shutdown() { + nsXULPopupManager::Shutdown(); nsDOMStorageManager::Shutdown(); txMozillaXSLTProcessor::Shutdown(); nsDOMAttribute::Shutdown(); diff --git a/mozilla/layout/generic/nsContainerFrame.cpp b/mozilla/layout/generic/nsContainerFrame.cpp index ceeb62d2d46..47ee56beb03 100644 --- a/mozilla/layout/generic/nsContainerFrame.cpp +++ b/mozilla/layout/generic/nsContainerFrame.cpp @@ -497,7 +497,7 @@ nsContainerFrame::SyncFrameViewProperties(nsPresContext* aPresContext, // visible in all cases because the scrollbars will be showing // XXXldb Does the view system really enforce this correctly? viewIsVisible = PR_FALSE; - } else { + } else if (aFrame->GetType() == nsGkAtoms::menuPopupFrame) { // if the view is for a popup, don't show the view if the popup is closed nsIWidget* widget = aView->GetWidget(); if (widget) { @@ -507,6 +507,11 @@ nsContainerFrame::SyncFrameViewProperties(nsPresContext* aPresContext, widget->IsVisible(viewIsVisible); } } + else { + // widgets for popups can be created later when the popup is opened, + // so if there is no widget, the popup won't be open. + viewIsVisible = PR_FALSE; + } } vm->SetViewVisibility(aView, viewIsVisible ? nsViewVisibility_kShow : @@ -716,7 +721,12 @@ nsContainerFrame::PositionChildViews(nsIFrame* aFrame) childFrame = childFrame->GetNextSibling(); } - childListName = aFrame->GetAdditionalChildListName(childListIndex++); + // also process the additional child lists, but skip the popup list as the + // view for popups is managed by the parent. Currently only nsMenuFrame + // has a popupList and during layout will call nsMenuPopupFrame::AdjustView. + do { + childListName = aFrame->GetAdditionalChildListName(childListIndex++); + } while (childListName == nsGkAtoms::popupList); } while (childListName); } diff --git a/mozilla/layout/xul/base/public/Makefile.in b/mozilla/layout/xul/base/public/Makefile.in index 401c97ba859..56793820011 100644 --- a/mozilla/layout/xul/base/public/Makefile.in +++ b/mozilla/layout/xul/base/public/Makefile.in @@ -49,8 +49,8 @@ GRE_MODULE = 1 EXPORTS = \ nsPIBoxObject.h \ nsIMenuFrame.h \ - nsIPopupSetFrame.h \ nsIScrollbarMediator.h \ + nsXULPopupManager.h \ $(NULL) XPIDLSRCS= nsIBoxObject.idl \ diff --git a/mozilla/layout/xul/base/public/nsIMenuFrame.h b/mozilla/layout/xul/base/public/nsIMenuFrame.h index 5c8d6d24b83..c17ff03e780 100644 --- a/mozilla/layout/xul/base/public/nsIMenuFrame.h +++ b/mozilla/layout/xul/base/public/nsIMenuFrame.h @@ -38,53 +38,25 @@ #ifndef nsIMenuFrame_h___ #define nsIMenuFrame_h___ -// {2281EFC8-A8BA-4a73-8CF7-DB4EECA5EAEC} +#include "nsISupports.h" + +// this interface exists solely because native themes need to call into it. +// Only menu frames should implement it + +// {212521C8-1509-4F41-ADDB-6A0B9356770F} #define NS_IMENUFRAME_IID \ -{ 0x2281efc8, 0xa8ba, 0x4a73, { 0x8c, 0xf7, 0xdb, 0x4e, 0xec, 0xa5, 0xea, 0xec } } - -class nsIMenuParent; -class nsIDOMElement; -class nsIDOMKeyEvent; - -enum nsMenuType { - eMenuType_Normal = 0, - eMenuType_Checkbox = 1, - eMenuType_Radio = 2 -}; +{ 0x212521C8, 0x1509, 0x4F41, { 0xAD, 0xDB, 0x6A, 0x0B, 0x93, 0x56, 0x77, 0x0F } } class nsIMenuFrame : public nsISupports { public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMENUFRAME_IID) - NS_IMETHOD ActivateMenu(PRBool aActivate) = 0; - NS_IMETHOD SelectMenu(PRBool aFlag) = 0; - NS_IMETHOD OpenMenu(PRBool aFlag) = 0; - - NS_IMETHOD MenuIsOpen(PRBool& aResult) = 0; - NS_IMETHOD MenuIsContainer(PRBool& aResult) = 0; - NS_IMETHOD MenuIsChecked(PRBool& aResult) = 0; - NS_IMETHOD MenuIsDisabled(PRBool& aResult) = 0; - - NS_IMETHOD SelectFirstItem() = 0; - - NS_IMETHOD Escape(PRBool& aHandledFlag) = 0; - NS_IMETHOD Enter() = 0; - NS_IMETHOD ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag) = 0; - NS_IMETHOD KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag) = 0; - - virtual nsIMenuParent *GetMenuParent() = 0; - virtual nsIFrame *GetMenuChild() = 0; - - NS_IMETHOD GetRadioGroupName(nsString &aName) = 0; - NS_IMETHOD GetMenuType(nsMenuType &aType) = 0; - - NS_IMETHOD MarkAsGenerated() = 0; - - NS_IMETHOD UngenerateMenu() = 0; - - NS_IMETHOD GetActiveChild(nsIDOMElement** aResult)=0; - NS_IMETHOD SetActiveChild(nsIDOMElement* aChild)=0; + virtual PRBool IsOpen() = 0; + virtual PRBool IsMenu() = 0; + virtual PRBool IsOnMenuBar() = 0; + virtual PRBool IsOnActiveMenuBar() = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIMenuFrame, NS_IMENUFRAME_IID) diff --git a/mozilla/layout/xul/base/public/nsIPopupBoxObject.idl b/mozilla/layout/xul/base/public/nsIPopupBoxObject.idl index 22133f6aa74..8bf70f961d1 100644 --- a/mozilla/layout/xul/base/public/nsIPopupBoxObject.idl +++ b/mozilla/layout/xul/base/public/nsIPopupBoxObject.idl @@ -41,16 +41,21 @@ interface nsIDOMElement; - -[scriptable, uuid(116ffbea-336d-4ff1-a978-7335f54d11da)] +[scriptable, uuid(8714441F-0E24-4EB5-BE58-905F2854B4EB)] interface nsIPopupBoxObject : nsISupports { + /** + * This method is deprecated. Use openPopup or openPopupAtScreen instead. + */ void showPopup(in nsIDOMElement srcContent, in nsIDOMElement popupContent, in long xpos, in long ypos, in wstring popupType, in wstring anchorAlignment, in wstring popupAlignment); - void hidePopup(); + /** + * Hide the popup if it is open. + */ + void hidePopup(); /** * Allow the popup to automatically position itself. @@ -58,7 +63,15 @@ interface nsIPopupBoxObject : nsISupports attribute boolean autoPosition; /** - * Allow the popup to eat all key events + * If keyboard navigation is enabled, the keyboard may be used to navigate + * the menuitems on the popup. Enabling keyboard navigation is the default + * behaviour and will install capturing key event listeners on the popup + * that do not propagate key events to the contents. If you wish to place + * elements in a popup which accept key events, such as textboxes, keyboard + * navigation should be disabled. + * + * Setting ignorekeys="true" on the popup element also disables keyboard + * navigation, and is recommended over calling this method. */ void enableKeyboardNavigator(in boolean enableKeyboardNavigator); @@ -85,10 +98,62 @@ interface nsIPopupBoxObject : nsISupports void sizeTo(in long width, in long height); /** - * Move the popup to a point on screen + * Move the popup to a point on screen in CSS pixels. */ void moveTo(in long left, in long top); + /** + * Open the popup relative to a specified node at a specific location. + * + * The popup may be either anchored to another node or opened freely. + * To anchor a popup to a node, supply an anchor node and set the position + * to a string indicating the manner in which the popup should be anchored. + * Possible values for position are: + * before_start, before_end, after_start, after_end, + * start_before, start_after, end_before, end_after, + * overlap, after_pointer + * + * The anchor node does not need to be in the same document as the popup. + * + * If the attributesOverride argument is true, the popupanchor, popupalign + * and position attributes on the popup node override the position value + * argument. If attributesOverride is false, the attributes are only used + * if position is empty. + * + * For an anchored popup, the x and y arguments may be used to offset the + * popup from its anchored position by some number, measured in CSS pixels. + * + * Unanchored popups may be created by supplying null as the anchor node. + * An unanchored popup appears at the position specified by x and y, + * relative to the viewport of the document containing the popup node. In + * this case, position and attributesOverride are ignored. + * + * @param anchorElement the node to anchor the popup to, may be null + * @param position manner is which to anchor the popup to node + * @param x horizontal offset + * @param y vertical offset + * @param isContextMenu true for context menus, false for other popups + * @param attributesOverride true if popup node attributes override position + */ + void openPopup(in nsIDOMElement anchorElement, + in AString position, + in long x, in long y, + in boolean isContextMenu, + in boolean attributesOverride); + + /** + * Open the popup at a specific screen position specified by x and y. This + * position may be adjusted if it would cause the popup to be off of the + * screen. The x and y coordinates are measured in CSS pixels. The monitor + * selected is determined within the platform specific widget code, but + * in general, the coordinates are relative to the screen the window + * containing the popup is on. + * + * @param isContextMenu true for context menus, false for other popups + * @param x horizontal screen position + * @param y vertical screen position + */ + void openPopupAtScreen(in long x, in long y, in boolean isContextMenu); }; %{C++ diff --git a/mozilla/layout/xul/base/public/nsIPopupSetFrame.h b/mozilla/layout/xul/base/public/nsIPopupSetFrame.h deleted file mode 100644 index c82a367fd5f..00000000000 --- a/mozilla/layout/xul/base/public/nsIPopupSetFrame.h +++ /dev/null @@ -1,68 +0,0 @@ -/* -*- 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 Communicator client code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * 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 ***** */ - -#ifndef nsIPopupSetFrame_h___ -#define nsIPopupSetFrame_h___ - -// 043ecc8e-469f-40e1-9569-0529ac0c3039 -#define NS_IPOPUPSETFRAME_IID \ -{ 0x043ecc8e, 0x469f, 0x40e1, \ - { 0x95, 0x69, 0x05, 0x29, 0xac, 0x0c, 0x30, 0x39 } } - -class nsIFrame; -class nsIContent; -class nsIDOMElement; - -#include "nsString.h" - -class nsIPopupSetFrame : public nsISupports { - -public: - NS_DECLARE_STATIC_IID_ACCESSOR(NS_IPOPUPSETFRAME_IID) - - NS_IMETHOD ShowPopup(nsIContent* aElementContent, nsIContent* aPopupContent, - PRInt32 aXPos, PRInt32 aYPos, - const nsString& aPopupType, const nsString& anAnchorAlignment, - const nsString& aPopupAlignment) = 0; - NS_IMETHOD HidePopup(nsIFrame* aPopup) = 0; - NS_IMETHOD DestroyPopup(nsIFrame* aPopup, PRBool aDestroyEntireChain) = 0; -}; - -NS_DEFINE_STATIC_IID_ACCESSOR(nsIPopupSetFrame, NS_IPOPUPSETFRAME_IID) - -#endif - diff --git a/mozilla/layout/xul/base/public/nsXULPopupManager.h b/mozilla/layout/xul/base/public/nsXULPopupManager.h new file mode 100644 index 00000000000..921e0aebbd9 --- /dev/null +++ b/mozilla/layout/xul/base/public/nsXULPopupManager.h @@ -0,0 +1,636 @@ +/* -*- 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 ***** */ + +/** + * The XUL Popup Manager keeps track of all open popups. + */ + +#ifndef nsXULPopupManager_h__ +#define nsXULPopupManager_h__ + +#include "nsIContent.h" +#include "nsIWidget.h" +#include "nsIRollupListener.h" +#include "nsIMenuRollup.h" +#include "nsIDOMKeyListener.h" +#include "nsCOMPtr.h" +#include "nsITimer.h" +#include "nsThreadUtils.h" + +/** + * There are two types that are used: + * - dismissable popups such as menus, which should close up when there is a + * click outside the popup. In this situation, the entire chain of menus + * above should also be closed. + * - panels, which stay open until a request is made to close them. This + * type is used by tooltips. + * XXXndeakin note that panels don't work too well currently due to widget + * changes needed to handle activation events properly. + * + * When a new popup is opened, it is appended to the popup chain, stored in a + * linked list in mCurrentMenu for dismissable menus or mPanels for panels. + * Popups are stored in this list linked from newest to oldest. When a click + * occurs outside one of the open dismissable popups, the chain is closed by + * calling Rollup. + */ + +class nsIPresShell; +class nsMenuFrame; +class nsMenuPopupFrame; +class nsMenuBarFrame; +class nsIMenuParent; +class nsIDOMKeyEvent; + +/** + * nsNavigationDirection: an enum expressing navigation through the menus in + * terms which are independent of the directionality of the chrome. The + * terminology, derived from XSL-FO and CSS3 (e.g. + * http://www.w3.org/TR/css3-text/#TextLayout), is BASE (Before, After, Start, + * End), with the addition of First and Last (mapped to Home and End + * respectively). + * + * In languages such as English where the inline progression is left-to-right + * and the block progression is top-to-bottom (lr-tb), these terms will map out + * as in the following diagram + * + * --- inline progression ---> + * + * First | + * ... | + * Before | + * +--------+ block + * Start | | End progression + * +--------+ | + * After | + * ... | + * Last V + * + */ + +enum nsNavigationDirection { + eNavigationDirection_Last, + eNavigationDirection_First, + eNavigationDirection_Start, + eNavigationDirection_Before, + eNavigationDirection_End, + eNavigationDirection_After +}; + +#define NS_DIRECTION_IS_INLINE(dir) (dir == eNavigationDirection_Start || \ + dir == eNavigationDirection_End) +#define NS_DIRECTION_IS_BLOCK(dir) (dir == eNavigationDirection_Before || \ + dir == eNavigationDirection_After) +#define NS_DIRECTION_IS_BLOCK_TO_EDGE(dir) (dir == eNavigationDirection_First || \ + dir == eNavigationDirection_Last) + +/** + * DirectionFromKeyCode_lr_tb: an array that maps keycodes to values of + * nsNavigationDirection for left-to-right and top-to-bottom flow orientation + */ +static nsNavigationDirection DirectionFromKeyCode_lr_tb [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 +}; + +/** + * DirectionFromKeyCode_rl_tb: an array that maps keycodes to values of + * nsNavigationDirection for right-to-left and top-to-bottom flow orientation + */ +static nsNavigationDirection DirectionFromKeyCode_rl_tb [6] = { + 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 +}; + +#define NS_DIRECTION_FROM_KEY_CODE(frame, direction, keycode) \ + NS_ASSERTION(NS_VK_HOME == NS_VK_END + 1, "Broken ordering"); \ + NS_ASSERTION(NS_VK_LEFT == NS_VK_END + 2, "Broken ordering"); \ + NS_ASSERTION(NS_VK_UP == NS_VK_END + 3, "Broken ordering"); \ + NS_ASSERTION(NS_VK_RIGHT == NS_VK_END + 4, "Broken ordering"); \ + NS_ASSERTION(NS_VK_DOWN == NS_VK_END + 5, "Broken ordering"); \ + NS_ASSERTION(keycode >= NS_VK_END && keycode <= NS_VK_DOWN, \ + "Illegal key code"); \ + const nsStyleVisibility* vis = frame->GetStyleVisibility(); \ + if (vis->mDirection == NS_STYLE_DIRECTION_RTL) \ + direction = DirectionFromKeyCode_rl_tb[keycode - NS_VK_END]; \ + else \ + direction = DirectionFromKeyCode_lr_tb[keycode - NS_VK_END]; + +// nsMenuChainItem holds info about an open popup. Items are stored in a +// doubly linked list. Note that the linked list is stored beginning from +// the lowest child in a chain of menus, as this is the active submenu. +class nsMenuChainItem +{ +private: + nsMenuPopupFrame* mFrame; // the popup frame + PRPackedBool mIsMenu; // true if the popup is a menu, false for a panel + PRPackedBool mIsContext; // true for context menus + PRPackedBool mOnMenuBar; // true if the menu is on a menu bar + PRPackedBool mIgnoreKeys; // true if keyboard listeners should not be used + + nsMenuChainItem* mParent; + nsMenuChainItem* mChild; + +public: + nsMenuChainItem(nsMenuPopupFrame* aFrame, PRBool aIsContext, PRBool aIsMenu) + : mFrame(aFrame), + mIsMenu(aIsMenu), + mIsContext(aIsContext), + mOnMenuBar(PR_FALSE), + mIgnoreKeys(!aIsMenu), // always ignore keys on non-menus + mParent(nsnull), + mChild(nsnull) + { + NS_ASSERTION(aFrame, "null frame passed to nsMenuChainItem constructor"); + MOZ_COUNT_CTOR(nsMenuChainItem); + } + + ~nsMenuChainItem() + { + MOZ_COUNT_DTOR(nsMenuChainItem); + } + + nsIContent* Content(); + nsMenuPopupFrame* Frame() { return mFrame; } + PRBool IsMenu() { return mIsMenu; } + PRBool IsContextMenu() { return mIsContext; } + PRBool IgnoreKeys() { return mIgnoreKeys; } + PRBool IsOnMenuBar() { return mOnMenuBar; } + void SetIgnoreKeys(PRBool aIgnoreKeys) { mIgnoreKeys = aIgnoreKeys; } + void SetOnMenuBar(PRBool aOnMenuBar) { mOnMenuBar = aOnMenuBar; } + nsMenuChainItem* GetParent() { return mParent; } + nsMenuChainItem* GetChild() { return mChild; } + + // set the parent of this item to aParent, also changing the parent + // to have this as a child. + void SetParent(nsMenuChainItem* aParent); + + // removes an item from the chain. The root pointer must be supplied in case + // the item is the first item in the chain in which case the pointer will be + // set to the next item, or null if there isn't another item. After detaching, + // this item will not have a parent or a child. + void Detach(nsMenuChainItem** aRoot); +}; + +// this class is used for dispatching popupshowing events asynchronously. +class nsXULPopupShowingEvent : public nsRunnable +{ +public: + nsXULPopupShowingEvent(nsIContent *aPopup, + nsIContent *aMenu, + PRBool aIsContextMenu, + PRBool aSelectFirstItem) + : mPopup(aPopup), + mMenu(aMenu), + mIsContextMenu(aIsContextMenu), + mSelectFirstItem(aSelectFirstItem) + { + NS_ASSERTION(aPopup, "null popup supplied to nsXULPopupShowingEvent constructor"); + NS_ASSERTION(aMenu, "null menu supplied to nsXULPopupShowingEvent constructor"); + } + + NS_IMETHOD Run(); + +private: + nsCOMPtr mPopup; + nsCOMPtr mMenu; + PRBool mIsContextMenu; + PRBool mSelectFirstItem; +}; + +// this class is used for dispatching popuphiding events asynchronously. +class nsXULPopupHidingEvent : public nsRunnable +{ +public: + nsXULPopupHidingEvent(nsIContent *aPopup, + nsIContent* aNextPopup, + nsIContent* aLastPopup, + PRBool aIsMenu, + PRBool aDeselectMenu) + : mPopup(aPopup), + mNextPopup(aNextPopup), + mLastPopup(aLastPopup), + mIsMenu(aIsMenu), + mDeselectMenu(aDeselectMenu) + { + NS_ASSERTION(aPopup, "null popup supplied to nsXULPopupHidingEvent constructor"); + // aNextPopup and aLastPopup may be null + } + + NS_IMETHOD Run(); + +private: + nsCOMPtr mPopup; + nsCOMPtr mNextPopup; + nsCOMPtr mLastPopup; + PRBool mIsMenu; + PRBool mDeselectMenu; +}; + +// this class is used for dispatching menu command events asynchronously. +class nsXULMenuCommandEvent : public nsRunnable +{ +public: + nsXULMenuCommandEvent(nsIContent *aMenu, + PRBool aIsTrusted, + PRBool aShift, + PRBool aControl, + PRBool aAlt, + PRBool aMeta) + : mMenu(aMenu), + mIsTrusted(aIsTrusted), + mShift(aShift), + mControl(aControl), + mAlt(aAlt), + mMeta(aMeta) + { + NS_ASSERTION(aMenu, "null menu supplied to nsXULMenuCommandEvent constructor"); + } + + NS_IMETHOD Run(); + +private: + nsCOMPtr mMenu; + PRBool mIsTrusted; + PRBool mShift; + PRBool mControl; + PRBool mAlt; + PRBool mMeta; +}; + +class nsXULPopupManager : public nsIDOMKeyListener, + public nsIMenuRollup, + public nsIRollupListener, + public nsITimerCallback +{ + +public: + friend class nsXULPopupShowingEvent; + friend class nsXULPopupHidingEvent; + friend class nsXULMenuCommandEvent; + + NS_DECL_ISUPPORTS + NS_DECL_NSIROLLUPLISTENER + NS_DECL_NSIMENUROLLUP + NS_DECL_NSITIMERCALLBACK + + static nsXULPopupManager* sInstance; + + // initialize and shutdown methods called by nsLayoutStatics + static nsresult Init(); + static void Shutdown(); + + // returns a weak reference to the popup manager instance, could return null + // if a popup manager could not be allocated + static nsXULPopupManager* GetInstance(); + + // given a menu frame, find the prevous or next menu frame. If aPopup is + // true then navigate a menupopup, from one item on the menu to the previous + // or next one. This is used for cursor navigation between items in a popup + // menu. If aIsPopup is false, the navigation is on a menubar, so navigate + // between menus on the menubar. This is used for left/right cursor navigation. + // + // Items that not valid, such as non-menu or menuitem elements are skipped, + // and the next or previous item after that is checked. + // + // If aStart is null, the first valid item is retrieved for GetNextMenuItem + // or the last valid item for GetPreviousMenuItem is used. + // + // aParent - the parent menubar or menupopup + // aStart - the menu/menuitem to start navigation from. GetPreviousMenuItem + // returns the item before it, while GetNextMenuItem returns the + // next item. + // aIsPopup - true for menupopups, false for menubars + static nsMenuFrame* GetPreviousMenuItem(nsIFrame* aParent, + nsMenuFrame* aStart, + PRBool aIsPopup); + static nsMenuFrame* GetNextMenuItem(nsIFrame* aParent, + nsMenuFrame* aStart, + PRBool aIsPopup); + + // returns true if the menu item aContent is a valid menuitem which may + // be navigated to. aIsPopup should be true for items on a popup, or false + // for items on a menubar. + static PRBool IsValidMenuItem(nsPresContext* aPresContext, + nsIContent* aContent, + PRBool aOnPopup); + + // inform the popup manager that a menu bar has been activated or deactivated, + // either because one of its menus has opened or closed, or that the menubar + // has been focused such that its menus may be navigated with the keyboard. + // aActivate should be true when the menubar should be focused, and false + // when the active menu bar should be defocused. In the latter case, if + // aMenuBar isn't currently active, yet another menu bar is, that menu bar + // will remain active. + void SetActiveMenuBar(nsMenuBarFrame* aMenuBar, PRBool aActivate); + + // retrieve the node and offset of the last mouse event used to open a + // context menu. This information is determined from the rangeParent and + // the rangeOffset of the event supplied from the last call to SetMouseLocation. + // This is used by the implementation of nsIDOMXULDocument::GetPopupRangeParent + // and nsIDOMXULDocument::GetPopupRangeOffset. + void GetMouseLocation(nsIDOMNode** aNode, PRInt32* aOffset); + // set the mouse event that was used to activate the next popup to be opened. + void SetMouseLocation(nsIDOMEvent* aEvent); + + /** + * Open a given its content node. If aSelectFirstItem is + * set to true, the first item on the menu will automatically be + * selected. If aAsynchronous is true, the event will be dispatched + * asynchronously. This should be true when called from frame code. + */ + void ShowMenu(nsIContent *aMenu, PRBool aSelectFirstItem, PRBool aAsynchronous); + + /** + * Open a popup, either anchored or unanchored. If aSelectFirstItem is + * true, then the first item in the menu is selected. The arguments are + * similar to those for nsIPopupBoxObject::OpenPopup. + * + * This fires the popupshowing event synchronously. + */ + void ShowPopup(nsIContent* aPopup, + nsIContent* aAnchorContent, + const nsAString& aPosition, + PRInt32 aXPos, PRInt32 aYPos, + PRBool aIsContextMenu, + PRBool aAttributesOverride, + PRBool aSelectFirstItem); + + /** + * Open a popup at a specific screen position specified by aXPos and aYPos, + * measured in CSS pixels. + * + * This fires the popupshowing event synchronously. + */ + void ShowPopupAtScreen(nsIContent* aPopup, + PRInt32 aXPos, PRInt32 aYPos, + PRBool aIsContextMenu); + + /** + * This method is provided only for compatibility with an older popup API. + * New code should not call this function and should call ShowPopup instead. + * + * This fires the popupshowing event synchronously. + */ + void ShowPopupWithAnchorAlign(nsIContent* aPopup, + nsIContent* aAnchorContent, + nsAString& aAnchor, + nsAString& aAlign, + PRInt32 aXPos, PRInt32 aYPos, + PRBool aIsContextMenu); + + /* + * Hide a popup aPopup. If the popup is in a , then also inform the + * menu that the popup is being hidden. + * + * aHideChain - true if the entire chain of menus should be closed. If false, + * only this popup is closed. + * aDeselectMenu - true if the parent of the popup should be deselected. + * This will be false when the menu is closed by pressing the + * Escape key. + * aAsynchronous - true if the first popuphiding event should be sent + * asynchrously. This should be true if HidePopup is called + * from a frame. + */ + void HidePopup(nsIContent* aPopup, + PRBool aHideChain, + PRBool aDeselectMenu, + PRBool aAsynchronous); + + /** + * Hide a popup after a short delay. This is used when rolling over menu items. + * This timer is stored in mCloseTimer. The timer may be cancelled and the popup + * closed by calling KillMenuTimer. + */ + void HidePopupAfterDelay(nsMenuPopupFrame* aPopup); + + /** + * Hide all of the popups from a given document. This should be called when the + * document is hidden. + */ + void HidePopupsInDocument(nsIDocument* aDocument); + + /** + * Execute a menu command from the triggering event aEvent. + * + * aMenu - a menuitem to execute + * aEvent - the mouse event which triggered the menu to be executed, + * may be null + */ + void ExecuteMenu(nsIContent* aMenu, nsEvent* aEvent); + + /** + * Return true if the popup for the supplied menu parent is open. + */ + PRBool IsPopupOpenForMenuParent(nsIMenuParent* aMenuParent); + + /** + * Return false if a popup may not be opened. This will return false if the + * popup is already open, if the popup is in a content shell that is not + * focused, or if it is a submenu of another menu that isn't open. + */ + PRBool MayShowPopup(nsMenuPopupFrame* aFrame); + + /** + * Called when a popup frame is destroyed. In this case, just remove the + * item and later popups from the list. No point going through HidePopup as + * the frames have gone away. + */ + void PopupDestroyed(nsMenuPopupFrame* aFrame); + + /** + * Returns true if there is a context menu open. If aPopup is specified, + * then the context menu must be later in the chain than aPopup. If aPopup + * is null, returns true if any context menu at all is open. + */ + PRBool HasContextMenu(nsMenuPopupFrame* aPopup); + + /** + * Update the commands for the menus within the menu popup for a given + * content node. aPopup should be a XUL menupopup element. This method + * changes attributes on the children of aPopup, and deals only with the + * content of the popup, not the frames. + */ + void UpdateMenuItems(nsIContent* aPopup); + + /** + * Stop the timer which hides a popup after a delay, started by a previous + * call to HidePopupAfterDelay. In addition, the popup awaiting to be hidden + * is closed asynchronously. + */ + void KillMenuTimer(); + + /** + * Handles navigation for menu accelkeys. Returns true if the key has + * been handled. + */ + PRBool HandleShortcutNavigation(nsIDOMKeyEvent* aKeyEvent); + + /** + * Handles cursor navigation within a menu. Returns true if the key has + * been handled. + */ + PRBool HandleKeyboardNavigation(PRUint32 aKeyCode); + + NS_IMETHODIMP HandleEvent(nsIDOMEvent* aEvent) { return NS_OK; } + + NS_IMETHOD KeyUp(nsIDOMEvent* aKeyEvent); + NS_IMETHOD KeyDown(nsIDOMEvent* aKeyEvent); + NS_IMETHOD KeyPress(nsIDOMEvent* aKeyEvent); + +protected: + nsXULPopupManager(); + ~nsXULPopupManager(); + + // get the frame for a content node aContent if the frame's type + // matches aFrameType. Otherwise, return null. + nsIFrame* GetFrameOfTypeForContent(nsIContent* aContent, nsIAtom* aFrameType); + + // get the nsMenuFrame, if any, for the given content node + nsMenuFrame* GetMenuFrameForContent(nsIContent* aContent); + + // get the nsMenuPopupFrame, if any, for the given content node + nsMenuPopupFrame* GetPopupFrameForContent(nsIContent* aContent); + + // callbacks for ShowPopup and HidePopup as events may be done asynchronously + void ShowPopupCallback(nsIContent* aPopup, + nsMenuPopupFrame* aPopupFrame, + PRBool aIsContextMenu, + PRBool aSelectFirstItem); + void HidePopupCallback(nsIContent* aPopup, + nsMenuPopupFrame* aPopupFrame, + nsIContent* aNextPopup, + nsIContent* aLastPopup, + PRBool aIsMenu, + PRBool aDeselectMenu); + + /** + * Fire a popupshowing event on the popup aPopup and then open the popup. + * + * aPopup - the popup node to open + * aMenu - should be set to the parent menu if this is a popup associated + * with a menu. Otherwise, should be null. + * aPresContext - the prescontext + * aIsContextMenu - true for context menus + * aSelectFirstItem - true to select the first item in the menu + */ + void FirePopupShowingEvent(nsIContent* aPopup, + nsIContent* aMenu, + nsPresContext* aPresContext, + PRBool aIsContextMenu, + PRBool aSelectFirstItem); + + /** + * Fire a popuphiding event and then hide the popup. This will be called + * recursively if aNextPopup and aLastPopup are set in order to hide a chain + * of open menus. If these are not set, only one popup is closed. However, + * if aIsMenu is true, yet the next popup is not a menu, then this ends the + * closing of popups. This allows a menulist inside a non-menu to close up + * the menu but not close up the panel it is contained within. + * + * aPopup - the popup to hide + * aNextPopup - the next popup to hide + * aLastPopup - the last popup in the chain to hide + * aPresContext - nsPresContext for the popup's frame + * aIsMenu - true if aPopup is a menu. + * aDeselectMenu - true to unhighlight the menu when hiding it + */ + void FirePopupHidingEvent(nsIContent* aPopup, + nsIContent* aNextPopup, + nsIContent* aLastPopup, + nsPresContext *aPresContext, + PRBool aIsMenu, + PRBool aDeselectMenu); + + // handle keyboard navigation within a menu popup. Returns true if the + // key was handled and that other default handling should not occur. + PRBool HandleKeyboardNavigationInPopup(nsMenuChainItem* item, + nsNavigationDirection aDir); + + /** + * Set mouse capturing for the current popup. This traps mouse clicks that + * occur outside the popup so that it can be closed up. aOldPopup should be + * set to the popup that was previously the current popup. + */ + void SetCaptureState(nsIContent *aOldPopup); + + /** + * Key event listeners are attached to the document containing the current + * menu for menu and shortcut navigation. Only one listener is needed at a + * time, stored in mKeyListener, so switch it only if the document changes. + * Having menus in different documents is very rare, so the listeners will + * usually only be attached when the first menu opens and removed when all + * menus have closed. + * + * This is also used when only a menubar is active without any open menus, + * so that keyboard navigation between menus on the menubar may be done. + */ + void UpdateKeyboardListeners(); + + // the document the key event listener is attached to + nsCOMPtr mKeyListener; + + // widget that is currently listening to rollup events + nsCOMPtr mWidget; + + // range parent and offset set in SetMouseLocation + nsCOMPtr mRangeParent; + PRInt32 mRangeOffset; + + // set to the currently active menu bar, if any + nsMenuBarFrame* mActiveMenuBar; + + // linked list of dismissable menus. + nsMenuChainItem* mCurrentMenu; + + // linked list of panels + nsMenuChainItem* mPanels; + + // timer used for HidePopupAfterDelay + nsCOMPtr mCloseTimer; + + // a popup that is waiting on the timer + nsMenuPopupFrame* mTimerMenu; +}; + +#endif diff --git a/mozilla/layout/xul/base/src/Makefile.in b/mozilla/layout/xul/base/src/Makefile.in index 1882b27fddf..1303b60ddde 100644 --- a/mozilla/layout/xul/base/src/Makefile.in +++ b/mozilla/layout/xul/base/src/Makefile.in @@ -119,14 +119,13 @@ CPPSRCS += \ nsMenuFrame.cpp \ nsMenuBarFrame.cpp \ nsMenuBarListener.cpp \ - nsMenuListener.cpp \ - nsMenuDismissalListener.cpp \ nsPopupSetFrame.cpp \ nsTitleBarFrame.cpp \ nsResizerFrame.cpp \ nsListBoxBodyFrame.cpp \ nsListItemFrame.cpp \ nsListBoxLayout.cpp \ + nsXULPopupManager.cpp \ $(NULL) endif diff --git a/mozilla/layout/xul/base/src/nsBoxFrame.cpp b/mozilla/layout/xul/base/src/nsBoxFrame.cpp index ae3bd4847da..6350e95e088 100644 --- a/mozilla/layout/xul/base/src/nsBoxFrame.cpp +++ b/mozilla/layout/xul/base/src/nsBoxFrame.cpp @@ -1793,25 +1793,37 @@ nsresult nsBoxFrame::CreateViewForFrame(nsPresContext* aPresContext, nsIFrame* aFrame, nsStyleContext* aStyleContext, - PRBool aForce) + PRBool aForce, + PRBool aIsPopup) { NS_ASSERTION(aForce, "We only get called to force view creation now"); // If we don't yet have a view, see if we need a view if (!aFrame->HasView()) { + nsViewVisibility visibility = nsViewVisibility_kShow; PRInt32 zIndex = 0; PRBool autoZIndex = PR_FALSE; if (aForce) { - // Create a view - nsIFrame* parent = aFrame->GetAncestorWithView(); - NS_ASSERTION(parent, "GetAncestorWithView failed"); - nsIView* parentView = parent->GetView(); - NS_ASSERTION(parentView, "no parent with view"); - nsIViewManager* viewManager = parentView->GetViewManager(); + nsIView* parentView; + nsIViewManager* viewManager = aPresContext->GetViewManager(); NS_ASSERTION(nsnull != viewManager, "null view manager"); // Create a view - nsIView *view = viewManager->CreateView(aFrame->GetRect(), parentView); + if (aIsPopup) { + viewManager->GetRootView(parentView); + visibility = nsViewVisibility_kHide; + zIndex = PR_INT32_MAX; + } + else { + nsIFrame* parent = aFrame->GetAncestorWithView(); + NS_ASSERTION(parent, "GetAncestorWithView failed"); + parentView = parent->GetView(); + } + + NS_ASSERTION(parentView, "no parent view"); + + // Create a view + nsIView *view = viewManager->CreateView(aFrame->GetRect(), parentView, visibility); if (view) { // Insert the view into the view hierarchy. If the parent view is a // scrolling view we need to do this differently diff --git a/mozilla/layout/xul/base/src/nsBoxFrame.h b/mozilla/layout/xul/base/src/nsBoxFrame.h index 13818202a1f..3c2ae5d7467 100644 --- a/mozilla/layout/xul/base/src/nsBoxFrame.h +++ b/mozilla/layout/xul/base/src/nsBoxFrame.h @@ -162,11 +162,14 @@ public: virtual ~nsBoxFrame(); nsBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, PRBool aIsRoot = nsnull, nsIBoxLayout* aLayoutManager = nsnull); - + + // if aIsPopup is true, then the view is for a popup. In this case, + // the view is added a child of the root view, and is initially hidden static nsresult CreateViewForFrame(nsPresContext* aPresContext, nsIFrame* aChild, nsStyleContext* aStyleContext, - PRBool aForce); + PRBool aForce, + PRBool aIsPopup = PR_FALSE); // virtual so nsStackFrame, nsButtonBoxFrame, nsSliderFrame and nsMenuFrame // can override it diff --git a/mozilla/layout/xul/base/src/nsIMenuParent.h b/mozilla/layout/xul/base/src/nsIMenuParent.h index 991d2313bc1..70741c243d1 100644 --- a/mozilla/layout/xul/base/src/nsIMenuParent.h +++ b/mozilla/layout/xul/base/src/nsIMenuParent.h @@ -39,145 +39,53 @@ #ifndef nsIMenuParent_h___ #define nsIMenuParent_h___ - -// {33f700c8-976a-4cdb-8f6c-d9f4cfee8366} -#define NS_IMENUPARENT_IID \ -{ 0x33f700c8, 0x976a, 0x4cdb, { 0x8f, 0x6c, 0xd9, 0xf4, 0xcf, 0xee, 0x83, 0x66 } } - -class nsIMenuFrame; -class nsIDOMKeyEvent; +class nsMenuFrame; /* - * nsIMenuParent is implemented on frames and thus should not be - * refcounted. Eventually it should not inherit from nsISupports. - */ - -/** - * nsNavigationDirection: an enum expressing navigation through the menus in - * terms which are independent of the directionality of the chrome. The - * terminology, derived from XSL-FO and CSS3 (e.g. - * http://www.w3.org/TR/css3-text/#TextLayout), is BASE (Before, After, Start, - * End), with the addition of First and Last (mapped to Home and End - * respectively). + * nsIMenuParent is an interface implemented by nsMenuBarFrame and nsMenuPopupFrame + * as both serve as parent frames to nsMenuFrame. * - * In languages such as English where the inline progression is left-to-right - * and the block progression is top-to-bottom (lr-tb), these terms will map out - * as in the following diagram - * - * --- inline progression ---> - * - * First | - * ... | - * Before | - * +--------+ block - * Start | | End progression - * +--------+ | - * After | - * ... | - * Last V - * + * Don't implement this interface on other classes unless you also fix up references, + * as this interface is directly cast to and from nsMenuBarFrame and nsMenuPopupFrame. */ -enum nsNavigationDirection { - eNavigationDirection_Last, - eNavigationDirection_First, - eNavigationDirection_Start, - eNavigationDirection_Before, - eNavigationDirection_End, - eNavigationDirection_After -}; - -#define NS_DIRECTION_IS_INLINE(dir) (dir == eNavigationDirection_Start || \ - dir == eNavigationDirection_End) -#define NS_DIRECTION_IS_BLOCK(dir) (dir == eNavigationDirection_Before || \ - dir == eNavigationDirection_After) -#define NS_DIRECTION_IS_BLOCK_TO_EDGE(dir) (dir == eNavigationDirection_First || \ - dir == eNavigationDirection_Last) - -/** - * DirectionFromKeyCode_lr_tb: an array that maps keycodes to values of - * nsNavigationDirection for left-to-right and top-to-bottom flow orientation - */ -static nsNavigationDirection DirectionFromKeyCode_lr_tb [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 -}; - -/** - * DirectionFromKeyCode_rl_tb: an array that maps keycodes to values of - * nsNavigationDirection for right-to-left and top-to-bottom flow orientation - */ -static nsNavigationDirection DirectionFromKeyCode_rl_tb [6] = { - 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 -}; - -#ifdef IBMBIDI -#define NS_DIRECTION_FROM_KEY_CODE(direction, keycode) \ - NS_ASSERTION(keycode >= NS_VK_END && keycode <= NS_VK_DOWN, \ - "Illegal key code"); \ - const nsStyleVisibility* vis = GetStyleVisibility(); \ - if (vis->mDirection == NS_STYLE_DIRECTION_RTL) \ - direction = DirectionFromKeyCode_rl_tb[keycode - NS_VK_END]; \ - else \ - direction = DirectionFromKeyCode_lr_tb[keycode - NS_VK_END]; -#else -#define NS_DIRECTION_FROM_KEY_CODE(direction, keycode) \ - direction = DirectionFromKeyCode_lr_tb[keycode - NS_VK_END]; -#endif - -class nsIMenuParent : public nsISupports { +class nsIMenuParent { public: - NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMENUPARENT_IID) + // returns the menu frame of the currently active item within the menu + virtual nsMenuFrame *GetCurrentMenuItem() = 0; + // sets the currently active menu frame. + NS_IMETHOD SetCurrentMenuItem(nsMenuFrame* aMenuItem) = 0; + // indicate that the current menu frame is being destroyed, so clear the + // current menu item + virtual void CurrentMenuIsBeingDestroyed() = 0; + // deselects the current item and closes its popup if any, then selects the + // new item aMenuItem. For a menubar, if another menu is already open, the + // new menu aMenuItem is opened. In this case, if aSelectFirstItem is true, + // select the first item in it. For menupoups, the menu is not opened and + // the aSelectFirstItem argument is not used. + NS_IMETHOD ChangeMenuItem(nsMenuFrame* aMenuItem, PRBool aSelectFirstItem) = 0; - virtual nsIMenuFrame *GetCurrentMenuItem() = 0; - NS_IMETHOD SetCurrentMenuItem(nsIMenuFrame* aMenuItem) = 0; - virtual nsIMenuFrame *GetNextMenuItem(nsIMenuFrame* aStart) = 0; - virtual nsIMenuFrame *GetPreviousMenuItem(nsIMenuFrame* aStart) = 0; + // returns true if the menupopup is open. For menubars, returns false. + virtual PRBool IsOpen() = 0; + // returns true if the menubar is currently active. For menupopups, returns false. + virtual PRBool IsActive() = 0; + // returns true if this is a menubar. If false, it is a popup + virtual PRBool IsMenuBar() = 0; + // returns true if this is a menu, which has a tag of menupopup or popup. + // Otherwise, this returns false + virtual PRBool IsMenu() = 0; + // returns true if this is a context menu + virtual PRBool IsContextMenu() = 0; + // indicate that the menubar should become active or inactive NS_IMETHOD SetActive(PRBool aActiveFlag) = 0; - NS_IMETHOD GetIsActive(PRBool& isActive) = 0; - NS_IMETHOD GetWidget(nsIWidget **aWidget) = 0; - - NS_IMETHOD IsMenuBar(PRBool& isMenuBar) = 0; - NS_IMETHOD ConsumeOutsideClicks(PRBool& aConsumeOutsideClicks) = 0; - NS_IMETHOD ClearRecentlyRolledUp() = 0; - NS_IMETHOD RecentlyRolledUp(nsIMenuFrame *aMenuFrame, PRBool *aJustRolledUp) = 0; - NS_IMETHOD DismissChain() = 0; - NS_IMETHOD HideChain() = 0; - NS_IMETHOD KillPendingTimers() = 0; - NS_IMETHOD CancelPendingTimers() = 0; - - NS_IMETHOD AttachedDismissalListener() = 0; - - NS_IMETHOD InstallKeyboardNavigator() = 0; - NS_IMETHOD RemoveKeyboardNavigator() = 0; - - // Used to move up, down, left, and right in menus. - NS_IMETHOD KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag) = 0; - NS_IMETHOD ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag) = 0; - // Called when the ESC key is held down to close levels of menus. - NS_IMETHOD Escape(PRBool& aHandledFlag) = 0; - // Called to execute a menu item. - NS_IMETHOD Enter() = 0; - - NS_IMETHOD SetIsContextMenu(PRBool aIsContextMenu) = 0; - NS_IMETHOD GetIsContextMenu(PRBool& aIsContextMenu) = 0; - - NS_IMETHOD GetParentPopup(nsIMenuParent** aResult) = 0; + // notify that the menu has been closed and any active state should be + // cleared. This should return true if the menu should be deselected + // by the caller. + virtual PRBool MenuClosed() = 0; }; -NS_DEFINE_STATIC_IID_ACCESSOR(nsIMenuParent, NS_IMENUPARENT_IID) - #endif diff --git a/mozilla/layout/xul/base/src/nsIRootBox.h b/mozilla/layout/xul/base/src/nsIRootBox.h index 2ab183d4862..d6693b4ca23 100644 --- a/mozilla/layout/xul/base/src/nsIRootBox.h +++ b/mozilla/layout/xul/base/src/nsIRootBox.h @@ -41,14 +41,14 @@ #define nsIRootBox_h___ #include "nsISupports.h" -class nsIFrame; +class nsPopupSetFrame; class nsIContent; class nsIPresShell; -// {2256d568-3f5a-42ec-b932-3d0f78551a1a} +// {9777EC2A-9A46-4D01-8CEB-B9CEB2C262A5} #define NS_IROOTBOX_IID \ -{ 0x2256d568, 0x3f5a, 0x42ec, \ - { 0xb9, 0x32, 0x3d, 0x0f, 0x78, 0x55, 0x1a, 0x1a } } +{ 0x9777EC2A, 0x9A46, 0x4D01, \ + { 0x8C, 0xEB, 0xB9, 0xCE, 0xB2, 0xC2, 0x62, 0xA5 } } class nsIRootBox : public nsISupports { @@ -56,8 +56,8 @@ class nsIRootBox : public nsISupports { public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_IROOTBOX_IID) - virtual nsIFrame* GetPopupSetFrame() = 0; - virtual void SetPopupSetFrame(nsIFrame* aPopupSet)=0; + virtual nsPopupSetFrame* GetPopupSetFrame() = 0; + virtual void SetPopupSetFrame(nsPopupSetFrame* aPopupSet) = 0; virtual nsIContent* GetDefaultTooltip() = 0; virtual void SetDefaultTooltip(nsIContent* aTooltip) = 0; diff --git a/mozilla/layout/xul/base/src/nsMenuBarFrame.cpp b/mozilla/layout/xul/base/src/nsMenuBarFrame.cpp index 0faa9d239a7..63dd3d2eda7 100644 --- a/mozilla/layout/xul/base/src/nsMenuBarFrame.cpp +++ b/mozilla/layout/xul/base/src/nsMenuBarFrame.cpp @@ -37,11 +37,9 @@ * * ***** END LICENSE BLOCK ***** */ -#include "nsMenuListener.h" #include "nsMenuBarFrame.h" #include "nsIServiceManager.h" #include "nsIContent.h" -#include "nsContentUtils.h" #include "prtypes.h" #include "nsIAtom.h" #include "nsPresContext.h" @@ -79,51 +77,18 @@ NS_NewMenuBarFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) return new (aPresShell) nsMenuBarFrame (aPresShell, aContext); } -NS_IMETHODIMP_(nsrefcnt) -nsMenuBarFrame::AddRef(void) -{ - return NS_OK; -} - -NS_IMETHODIMP_(nsrefcnt) -nsMenuBarFrame::Release(void) -{ - return NS_OK; -} - - -// -// QueryInterface -// -NS_INTERFACE_MAP_BEGIN(nsMenuBarFrame) - NS_INTERFACE_MAP_ENTRY(nsIMenuParent) -NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame) - - // // nsMenuBarFrame cntr // nsMenuBarFrame::nsMenuBarFrame(nsIPresShell* aShell, nsStyleContext* aContext): nsBoxFrame(aShell, aContext), mMenuBarListener(nsnull), - mKeyboardNavigator(nsnull), mIsActive(PR_FALSE), mTarget(nsnull), mCaretWasVisible(PR_FALSE) { } // cntr -nsMenuBarFrame::~nsMenuBarFrame() -{ - /* The menubar can still be active at this point under unusual circumstances. - (say, while switching skins (which tears down all frames including - this one) after having made a menu selection (say, Edit->Preferences, - to get to the skin switching UI)). SetActive(PR_FALSE) releases - mKeyboardNavigator, which is by now pointing to a deleted frame. - */ - SetActive(PR_FALSE); -} - NS_IMETHODIMP nsMenuBarFrame::Init(nsIContent* aContent, nsIFrame* aParent, @@ -156,20 +121,6 @@ nsMenuBarFrame::Init(nsIContent* aContent, return rv; } -NS_IMETHODIMP -nsMenuBarFrame::IsOpen() -{ - PRBool isOpen = PR_FALSE; - if(mCurrentMenu) { - mCurrentMenu->MenuIsOpen(isOpen); - if (isOpen) { - return PR_TRUE; - } - } - return PR_FALSE; -} - - NS_IMETHODIMP nsMenuBarFrame::SetActive(PRBool aActiveFlag) { @@ -177,6 +128,15 @@ nsMenuBarFrame::SetActive(PRBool aActiveFlag) if (mIsActive == aActiveFlag) return NS_OK; + if (!aActiveFlag) { + // if there is a request to deactivate the menu bar, check to see whether + // there is a menu popup open for the menu bar. In this case, don't + // deactivate the menu bar. + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm && pm->IsPopupOpenForMenuParent(this)) + return NS_OK; + } + mIsActive = aActiveFlag; if (mIsActive) { InstallKeyboardNavigator(); @@ -239,22 +199,22 @@ nsMenuBarFrame::SetActive(PRBool aActiveFlag) NS_NAMED_LITERAL_STRING(active, "DOMMenuBarActive"); NS_NAMED_LITERAL_STRING(inactive, "DOMMenuBarInactive"); - FireDOMEventSynch(mIsActive ? active : inactive); + FireDOMEvent(mIsActive ? active : inactive, mContent); return NS_OK; } -void +nsMenuFrame* nsMenuBarFrame::ToggleMenuActiveState() { if (mIsActive) { // Deactivate the menu bar SetActive(PR_FALSE); if (mCurrentMenu) { - // Deactivate the menu. - mCurrentMenu->OpenMenu(PR_FALSE); - mCurrentMenu->SelectMenu(PR_FALSE); + nsMenuFrame* closeframe = mCurrentMenu; + closeframe->SelectMenu(PR_FALSE); mCurrentMenu = nsnull; + return closeframe; } } else { @@ -268,7 +228,7 @@ nsMenuBarFrame::ToggleMenuActiveState() // Set the active menu to be the top left item (e.g., the File menu). // We use an attribute called "menuactive" to track the current // active menu. - nsIMenuFrame* firstFrame = GetNextMenuItem(nsnull); + nsMenuFrame* firstFrame = nsXULPopupManager::GetNextMenuItem(this, nsnull, PR_FALSE); if (firstFrame) { firstFrame->SelectMenu(PR_TRUE); @@ -276,10 +236,13 @@ nsMenuBarFrame::ToggleMenuActiveState() mCurrentMenu = firstFrame; } } + + return nsnull; } -static void GetInsertionPoint(nsIPresShell* aShell, nsIFrame* aFrame, nsIFrame* aChild, - nsIFrame** aResult) +static void +GetInsertionPoint(nsIPresShell* aShell, nsIFrame* aFrame, nsIFrame* aChild, + nsIFrame** aResult) { nsIContent* child = nsnull; if (aChild) @@ -287,7 +250,7 @@ static void GetInsertionPoint(nsIPresShell* aShell, nsIFrame* aFrame, nsIFrame* aShell->FrameConstructor()->GetInsertionPoint(aFrame, child, aResult); } -nsIMenuFrame* +nsMenuFrame* nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent) { PRUint32 charCode; @@ -305,7 +268,7 @@ nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent) nsIContent* current = currFrame->GetContent(); // See if it's a menu item. - if (IsValidItem(current)) { + if (nsXULPopupManager::IsValidMenuItem(PresContext(), current, PR_FALSE)) { // Get the shortcut attribute. nsAutoString shortcutKey; current->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, shortcutKey); @@ -315,11 +278,8 @@ nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent) if ( shortcutKey.Equals(Substring(&letter, &letter+1), nsCaseInsensitiveStringComparator()) ) { // We match! - nsIMenuFrame *menuFrame; - if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame))) { - menuFrame = nsnull; - } - return menuFrame; + return (currFrame->GetType() == nsGkAtoms::menuFrame) ? + NS_STATIC_CAST(nsMenuFrame *, currFrame) : nsnull; } } } @@ -335,263 +295,36 @@ nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent) soundInterface->Beep(); } - DismissChain(); + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) + pm->Rollup(); + + SetCurrentMenuItem(nsnull); + SetActive(PR_FALSE); + #endif // #ifdef XP_WIN return nsnull; } -NS_IMETHODIMP -nsMenuBarFrame::ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag) -{ - if (mCurrentMenu) { - PRBool isOpen = PR_FALSE; - mCurrentMenu->MenuIsOpen(isOpen); - if (isOpen) { - // No way this applies to us. Give it to our child. - mCurrentMenu->ShortcutNavigation(aKeyEvent, aHandledFlag); - return NS_OK; - } - } - - // This applies to us. Let's see if one of the shortcuts applies - nsIMenuFrame* result = FindMenuWithShortcut(aKeyEvent); - if (result) { - // We got one! - nsWeakFrame weakFrame(this); - nsIFrame* frame = nsnull; - CallQueryInterface(result, &frame); - nsWeakFrame weakResult(frame); - aHandledFlag = PR_TRUE; - SetActive(PR_TRUE); - if (weakFrame.IsAlive()) { - SetCurrentMenuItem(result); - } - if (weakResult.IsAlive()) { - result->OpenMenu(PR_TRUE); - if (weakResult.IsAlive()) { - result->SelectFirstItem(); - } - } - } - - return NS_OK; -} - -NS_IMETHODIMP -nsMenuBarFrame::KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag) -{ - nsNavigationDirection theDirection; - NS_DIRECTION_FROM_KEY_CODE(theDirection, aKeyCode); - if (!mCurrentMenu) - return NS_OK; - - nsWeakFrame weakFrame(this); - PRBool isContainer = PR_FALSE; - PRBool isOpen = PR_FALSE; - mCurrentMenu->MenuIsContainer(isContainer); - mCurrentMenu->MenuIsOpen(isOpen); - - aHandledFlag = PR_FALSE; - - if (isOpen) { - // Let the child menu try to handle it. - mCurrentMenu->KeyboardNavigation(aKeyCode, aHandledFlag); - } - - if (aHandledFlag) - return NS_OK; - - if NS_DIRECTION_IS_INLINE(theDirection) { - - nsIMenuFrame* nextItem = (theDirection == eNavigationDirection_End) ? - GetNextMenuItem(mCurrentMenu) : - GetPreviousMenuItem(mCurrentMenu); - - nsIFrame* nextFrame = nsnull; - if (nextItem) { - CallQueryInterface(nextItem, &nextFrame); - } - nsWeakFrame weakNext(nextFrame); - SetCurrentMenuItem(nextItem); - if (weakNext.IsAlive()) { - PRBool nextIsOpen; - nextItem->MenuIsOpen(nextIsOpen); - if (nextIsOpen) { - // Select the first item. - nextItem->SelectFirstItem(); - } - } - } - else if NS_DIRECTION_IS_BLOCK(theDirection) { - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); - nsIFrame* frame = nsnull; - CallQueryInterface(mCurrentMenu, &frame); - nsWeakFrame weakCurrentMenu(frame); - nsIMenuFrame* currentMenu = mCurrentMenu; - // Open the menu and select its first item. - currentMenu->OpenMenu(PR_TRUE); - if (weakCurrentMenu.IsAlive()) { - currentMenu->SelectFirstItem(); - } - } - - return NS_OK; -} - -/* virtual */ nsIMenuFrame* -nsMenuBarFrame::GetNextMenuItem(nsIMenuFrame* aStart) -{ - nsIFrame* immediateParent = nsnull; - GetInsertionPoint(PresContext()->PresShell(), this, nsnull, &immediateParent); - if (!immediateParent) - immediateParent = this; - - nsIFrame* currFrame = nsnull; - nsIFrame* startFrame = nsnull; - if (aStart) { - aStart->QueryInterface(NS_GET_IID(nsIFrame), (void**)&currFrame); - if (currFrame) { - startFrame = currFrame; - currFrame = currFrame->GetNextSibling(); - } - } - else - currFrame = immediateParent->GetFirstChild(nsnull); - - while (currFrame) { - // See if it's a menu item. - if (IsValidItem(currFrame->GetContent())) { - nsIMenuFrame *menuFrame; - if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame))) - menuFrame = nsnull; - return menuFrame; - } - currFrame = currFrame->GetNextSibling(); - } - - currFrame = immediateParent->GetFirstChild(nsnull); - - // Still don't have anything. Try cycling from the beginning. - while (currFrame && currFrame != startFrame) { - // See if it's a menu item. - if (IsValidItem(currFrame->GetContent())) { - nsIMenuFrame *menuFrame; - if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame))) - menuFrame = nsnull; - return menuFrame; - } - - currFrame = currFrame->GetNextSibling(); - } - - // No luck. Just return our start value. - return aStart; -} - -/* virtual */ nsIMenuFrame* -nsMenuBarFrame::GetPreviousMenuItem(nsIMenuFrame* aStart) -{ - nsIFrame* immediateParent = nsnull; - GetInsertionPoint(PresContext()->PresShell(), this, nsnull, &immediateParent); - if (!immediateParent) - immediateParent = this; - - nsFrameList frames(immediateParent->GetFirstChild(nsnull)); - - nsIFrame* currFrame = nsnull; - nsIFrame* startFrame = nsnull; - if (aStart) { - aStart->QueryInterface(NS_GET_IID(nsIFrame), (void**)&currFrame); - if (currFrame) { - startFrame = currFrame; - currFrame = frames.GetPrevSiblingFor(currFrame); - } - } - else currFrame = frames.LastChild(); - - while (currFrame) { - // See if it's a menu item. - if (IsValidItem(currFrame->GetContent())) { - nsIMenuFrame *menuFrame; - if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame))) - menuFrame = nsnull; - return menuFrame; - } - currFrame = frames.GetPrevSiblingFor(currFrame); - } - - currFrame = frames.LastChild(); - - // Still don't have anything. Try cycling from the end. - while (currFrame && currFrame != startFrame) { - // See if it's a menu item. - if (IsValidItem(currFrame->GetContent())) { - nsIMenuFrame *menuFrame; - if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame))) - menuFrame = nsnull; - return menuFrame; - } - - currFrame = frames.GetPrevSiblingFor(currFrame); - } - - // No luck. Just return our start value. - return aStart; -} - -/* virtual */ nsIMenuFrame* +/* virtual */ nsMenuFrame* nsMenuBarFrame::GetCurrentMenuItem() { return mCurrentMenu; } - -NS_IMETHODIMP nsMenuBarFrame::SetCurrentMenuItem(nsIMenuFrame* aMenuItem) +NS_IMETHODIMP +nsMenuBarFrame::SetCurrentMenuItem(nsMenuFrame* aMenuItem) { if (mCurrentMenu == aMenuItem) return NS_OK; - PRBool wasOpen = PR_FALSE; - - // check if there's an open context menu, we ignore this - if (nsMenuFrame::GetContextMenu()) - return NS_OK; - nsWeakFrame weakFrame(this); + if (mCurrentMenu) + mCurrentMenu->SelectMenu(PR_FALSE); - // Unset the current child. - if (mCurrentMenu) { - nsIFrame* frame = nsnull; - CallQueryInterface(mCurrentMenu, &frame); - nsWeakFrame weakCurrentMenu(frame); - nsIMenuFrame* currentMenu = mCurrentMenu; - currentMenu->MenuIsOpen(wasOpen); - currentMenu->SelectMenu(PR_FALSE); - if (wasOpen && weakCurrentMenu.IsAlive()) { - currentMenu->OpenMenu(PR_FALSE); - } - } - - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); - - - // Set the new child. - if (aMenuItem) { - nsIFrame* newMenu = nsnull; - CallQueryInterface(aMenuItem, &newMenu); - nsWeakFrame weakNewMenu(newMenu); + if (aMenuItem) aMenuItem->SelectMenu(PR_TRUE); - NS_ENSURE_TRUE(weakNewMenu.IsAlive(), NS_OK); - aMenuItem->MarkAsGenerated(); // Have the menu building. Get it ready to be shown. - NS_ENSURE_TRUE(weakNewMenu.IsAlive(), NS_OK); - - PRBool isDisabled = PR_FALSE; - aMenuItem->MenuIsDisabled(isDisabled); - if (wasOpen&&!isDisabled) - aMenuItem->OpenMenu(PR_TRUE); - ClearRecentlyRolledUp(); - } NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); mCurrentMenu = aMenuItem; @@ -599,211 +332,103 @@ NS_IMETHODIMP nsMenuBarFrame::SetCurrentMenuItem(nsIMenuFrame* aMenuItem) return NS_OK; } - -NS_IMETHODIMP -nsMenuBarFrame::Escape(PRBool& aHandledFlag) +void +nsMenuBarFrame::CurrentMenuIsBeingDestroyed() { - if (!mCurrentMenu) + mCurrentMenu->SelectMenu(PR_FALSE); + mCurrentMenu = nsnull; +} + +NS_IMETHODIMP +nsMenuBarFrame::ChangeMenuItem(nsMenuFrame* aMenuItem, + PRBool aSelectFirstItem) +{ + if (mCurrentMenu == aMenuItem) return NS_OK; - nsWeakFrame weakFrame(this); - // See if our menu is open. - PRBool isOpen = PR_FALSE; - mCurrentMenu->MenuIsOpen(isOpen); - if (isOpen) { - // Let the child menu handle this. - aHandledFlag = PR_FALSE; - mCurrentMenu->Escape(aHandledFlag); - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); - if (!aHandledFlag) { - // Close up this menu but keep our current menu item - // designation. - mCurrentMenu->OpenMenu(PR_FALSE); - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); - } + // check if there's an open context menu, we ignore this + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm && pm->HasContextMenu(nsnull)) return NS_OK; + + // Unset the current child. + PRBool wasOpen = PR_FALSE; + if (mCurrentMenu) { + wasOpen = mCurrentMenu->IsOpen(); + mCurrentMenu->SelectMenu(PR_FALSE); + if (wasOpen) { + nsMenuPopupFrame* popupFrame = mCurrentMenu->GetPopup(); + if (popupFrame) + pm->HidePopup(popupFrame->GetContent(), PR_FALSE, PR_FALSE, PR_TRUE); + } } - // Clear our current menu item if we've got one. - SetCurrentMenuItem(nsnull); - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); + // set to null first in case the IsAlive check below returns false + mCurrentMenu = nsnull; - SetActive(PR_FALSE); + // Set the new child. + if (aMenuItem) { + nsCOMPtr content = aMenuItem->GetContent(); + nsWeakFrame weakNewMenu(aMenuItem); + aMenuItem->SelectMenu(PR_TRUE); + NS_ENSURE_TRUE(weakNewMenu.IsAlive(), NS_OK); + mCurrentMenu = aMenuItem; + if (wasOpen && !aMenuItem->IsDisabled()) + pm->ShowMenu(content, aSelectFirstItem, PR_TRUE); + } - // Clear out our dismissal listener - nsMenuDismissalListener::Shutdown(); return NS_OK; } -NS_IMETHODIMP +nsMenuFrame* nsMenuBarFrame::Enter() { if (!mCurrentMenu) - return NS_OK; + return nsnull; - ClearRecentlyRolledUp(); + if (mCurrentMenu->IsOpen()) + return mCurrentMenu->Enter(); - // See if our menu is open. - PRBool isOpen = PR_FALSE; - mCurrentMenu->MenuIsOpen(isOpen); - if (isOpen) { - // Let the child menu handle this. - mCurrentMenu->Enter(); - return NS_OK; - } - - // It's us. Open the current menu. - mCurrentMenu->OpenMenu(PR_TRUE); - mCurrentMenu->SelectFirstItem(); - - return NS_OK; + return mCurrentMenu; } -NS_IMETHODIMP -nsMenuBarFrame::ClearRecentlyRolledUp() +PRBool +nsMenuBarFrame::MenuClosed() { - // We're no longer in danger of popping down a menu from the same - // click on the menubar, which was supposed to toggle the menu closed - mRecentRollupMenu = nsnull; - - return NS_OK; -} - -NS_IMETHODIMP -nsMenuBarFrame::RecentlyRolledUp(nsIMenuFrame *aMenuFrame, PRBool *aJustRolledUp) -{ - // Don't let a click reopen a menu that was just rolled up - // from the same click. Otherwise, the user can't click on - // a menubar item to toggle its submenu closed. - *aJustRolledUp = (mRecentRollupMenu == aMenuFrame); - - return NS_OK; -} - -NS_IMETHODIMP -nsMenuBarFrame::HideChain() -{ - // XXX hack if a context menu is active, do an Escape, which is - // currently bugged and destroys everything. We need to close - // the context menu first, otherwise SetCurrentMenuItem above - // would get blocked. - if (nsMenuFrame::GetContextMenu()) { - PRBool dummy; - mCurrentMenu->Escape(dummy); - } - - // Stop capturing rollups - // (must do this during Hide, which happens before the menu item is executed, - // since this reinstates normal event handling.) - nsMenuDismissalListener::Shutdown(); - - ClearRecentlyRolledUp(); - if (mCurrentMenu) { - mCurrentMenu->ActivateMenu(PR_FALSE); + SetActive(PR_FALSE); + if (!mIsActive && mCurrentMenu) { mCurrentMenu->SelectMenu(PR_FALSE); - mRecentRollupMenu = mCurrentMenu; + mCurrentMenu = nsnull; + return PR_TRUE; } - - if (mIsActive) { - ToggleMenuActiveState(); - } - - return NS_OK; + return PR_FALSE; } -NS_IMETHODIMP -nsMenuBarFrame::DismissChain() -{ - // Stop capturing rollups - nsMenuDismissalListener::Shutdown(); - nsWeakFrame weakFrame(this); - SetCurrentMenuItem(nsnull); - if (weakFrame.IsAlive()) { - SetActive(PR_FALSE); - } - return NS_OK; -} - - -NS_IMETHODIMP -nsMenuBarFrame::KillPendingTimers ( ) -{ - return NS_OK; - -} // KillPendingTimers - - -NS_IMETHODIMP -nsMenuBarFrame::GetWidget(nsIWidget **aWidget) -{ - // (pinkerton/hyatt) - // since the menubar is a menuparent but not a menuItem, the win32 rollup code - // would erroneously add the entire top-level window to the widget list built up for - // determining if a click is in a submenu's menu chain. To get around this, we just - // don't let the menubar have a widget. Things seem to work because the dismissal - // listener is registered when a new menu is popped up, which is the only real reason - // why we need a widget at all. - *aWidget = nsnull; - return NS_OK; -} - -NS_IMETHODIMP +void nsMenuBarFrame::InstallKeyboardNavigator() { - if (mKeyboardNavigator) - return NS_OK; - - mKeyboardNavigator = new nsMenuListener(this); - NS_IF_ADDREF(mKeyboardNavigator); - - mTarget->AddEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); - mTarget->AddEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); - mTarget->AddEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); - - nsContentUtils::NotifyInstalledMenuKeyboardListener(PR_TRUE); - - return NS_OK; + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) + pm->SetActiveMenuBar(this, PR_TRUE); } -NS_IMETHODIMP +void nsMenuBarFrame::RemoveKeyboardNavigator() { - if (!mKeyboardNavigator || mIsActive) - return NS_OK; - - mTarget->RemoveEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); - mTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); - mTarget->RemoveEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); - - NS_IF_RELEASE(mKeyboardNavigator); - - nsContentUtils::NotifyInstalledMenuKeyboardListener(PR_FALSE); - - return NS_OK; -} - -// helpers /////////////////////////////////////////////////////////// - -PRBool -nsMenuBarFrame::IsValidItem(nsIContent* aContent) -{ - nsIAtom *tag = aContent->Tag(); - - return ((tag == nsGkAtoms::menu || - tag == nsGkAtoms::menuitem) && - !IsDisabled(aContent)); -} - -PRBool -nsMenuBarFrame::IsDisabled(nsIContent* aContent) -{ - return aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, - nsGkAtoms::_true, eCaseMatters); + if (!mIsActive) { + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) + pm->SetActiveMenuBar(this, PR_FALSE); + } } void nsMenuBarFrame::Destroy() { + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) + pm->SetActiveMenuBar(this, PR_FALSE); + mTarget->RemoveEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mMenuBarListener, PR_FALSE); mTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mMenuBarListener, PR_FALSE); mTarget->RemoveEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mMenuBarListener, PR_FALSE); @@ -815,4 +440,3 @@ nsMenuBarFrame::Destroy() nsBoxFrame::Destroy(); } - diff --git a/mozilla/layout/xul/base/src/nsMenuBarFrame.h b/mozilla/layout/xul/base/src/nsMenuBarFrame.h index 2089d1e2922..a70ab6c50db 100644 --- a/mozilla/layout/xul/base/src/nsMenuBarFrame.h +++ b/mozilla/layout/xul/base/src/nsMenuBarFrame.h @@ -49,13 +49,12 @@ #include "nsIAtom.h" #include "nsCOMPtr.h" #include "nsBoxFrame.h" +#include "nsMenuFrame.h" #include "nsMenuBarListener.h" -#include "nsMenuListener.h" #include "nsIMenuParent.h" #include "nsIWidget.h" class nsIContent; -class nsIMenuFrame; nsIFrame* NS_NewMenuBarFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); @@ -63,47 +62,25 @@ class nsMenuBarFrame : public nsBoxFrame, public nsIMenuParent { public: nsMenuBarFrame(nsIPresShell* aShell, nsStyleContext* aContext); - virtual ~nsMenuBarFrame(); - - NS_DECL_ISUPPORTS // nsIMenuParentInterface - virtual nsIMenuFrame* GetCurrentMenuItem(); - NS_IMETHOD SetCurrentMenuItem(nsIMenuFrame* aMenuItem); - virtual nsIMenuFrame* GetNextMenuItem(nsIMenuFrame* aStart); - virtual nsIMenuFrame* GetPreviousMenuItem(nsIMenuFrame* aStart); + virtual nsMenuFrame* GetCurrentMenuItem(); + NS_IMETHOD SetCurrentMenuItem(nsMenuFrame* aMenuItem); + virtual void CurrentMenuIsBeingDestroyed(); + NS_IMETHOD ChangeMenuItem(nsMenuFrame* aMenuItem, PRBool aSelectFirstItem); + NS_IMETHOD SetActive(PRBool aActiveFlag); - NS_IMETHOD GetIsActive(PRBool& isActive) { isActive = IsActive(); return NS_OK; } - NS_IMETHOD IsMenuBar(PRBool& isMenuBar) { isMenuBar = PR_TRUE; return NS_OK; } - NS_IMETHOD ConsumeOutsideClicks(PRBool& aConsumeOutsideClicks) \ - {aConsumeOutsideClicks = PR_FALSE; return NS_OK;} - NS_IMETHOD ClearRecentlyRolledUp(); - NS_IMETHOD RecentlyRolledUp(nsIMenuFrame *aMenuFrame, PRBool *aJustRolledUp); - NS_IMETHOD SetIsContextMenu(PRBool aIsContextMenu) { return NS_OK; } - NS_IMETHOD GetIsContextMenu(PRBool& aIsContextMenu) { aIsContextMenu = PR_FALSE; return NS_OK; } + virtual PRBool IsMenuBar() { return PR_TRUE; } + virtual PRBool IsContextMenu() { return PR_FALSE; } + virtual PRBool IsActive() { return mIsActive; } + virtual PRBool IsMenu() { return PR_FALSE; } + virtual PRBool IsOpen() { return PR_TRUE; } // menubars are considered always open - NS_IMETHOD GetParentPopup(nsIMenuParent** aResult) { *aResult = nsnull; - return NS_OK;} + PRBool IsMenuOpen() { return mCurrentMenu && mCurrentMenu->IsOpen(); } - NS_IMETHOD IsActive() { return mIsActive; } - - NS_IMETHOD IsOpen(); - NS_IMETHOD KillPendingTimers(); - NS_IMETHOD CancelPendingTimers() { return NS_OK; } - - // Closes up the chain of open cascaded menus. - NS_IMETHOD DismissChain(); - - // Hides the chain of cascaded menus without closing them up. - NS_IMETHOD HideChain(); - - NS_IMETHOD InstallKeyboardNavigator(); - NS_IMETHOD RemoveKeyboardNavigator(); - - NS_IMETHOD GetWidget(nsIWidget **aWidget); - // The dismissal listener gets created and attached to the window. - NS_IMETHOD AttachedDismissalListener() { return NS_OK; } + void InstallKeyboardNavigator(); + void RemoveKeyboardNavigator(); NS_IMETHOD Init(nsIContent* aContent, nsIFrame* aParent, @@ -111,24 +88,24 @@ public: virtual void Destroy(); + virtual nsIAtom* GetType() const { return nsGkAtoms::menuBarFrame; } + // Non-interface helpers - // Called when a menu on the menu bar is clicked on. - void ToggleMenuActiveState(); - - // Used to move up, down, left, and right in menus. - NS_IMETHOD KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag); - NS_IMETHOD ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag); - // Called when the ESC key is held down to close levels of menus. - NS_IMETHOD Escape(PRBool& aHandledFlag); - // Called to execute a menu item. - NS_IMETHOD Enter(); + // Called when a menu on the menu bar is clicked on. Returns a menu if one + // needs to be closed. + nsMenuFrame* ToggleMenuActiveState(); + + // indicate that a menu on the menubar was closed. Returns true if the caller + // may deselect the menuitem. + virtual PRBool MenuClosed(); + + // Called when Enter is pressed while the menubar is focused. If the current + // menu is open, let the child handle the key. + nsMenuFrame* Enter(); // Used to handle ALT+key combos - nsIMenuFrame* FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent); - - PRBool IsValidItem(nsIContent* aContent); - PRBool IsDisabled(nsIContent* aContent); + nsMenuFrame* FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent); virtual PRBool IsFrameOfType(PRUint32 aFlags) const { @@ -147,14 +124,11 @@ public: protected: nsMenuBarListener* mMenuBarListener; // The listener that tells us about key and mouse events. - nsMenuListener* mKeyboardNavigator; PRBool mIsActive; // Whether or not the menu bar is active (a menu item is highlighted or shown). - nsIMenuFrame* mCurrentMenu; // The current menu that is active. - - // Can contain a menu that was rolled up via nsIMenuDismissalListener::Rollup() - // if nothing has happened since the last click. Otherwise, contains nsnull. - nsIMenuFrame* mRecentRollupMenu; + // The current menu that is active (highlighted), which may not be open. This will + // be null if no menu is active. + nsMenuFrame* mCurrentMenu; nsIDOMEventTarget* mTarget; diff --git a/mozilla/layout/xul/base/src/nsMenuBarListener.cpp b/mozilla/layout/xul/base/src/nsMenuBarListener.cpp index 7e042f3a7b8..65bcec109ee 100644 --- a/mozilla/layout/xul/base/src/nsMenuBarListener.cpp +++ b/mozilla/layout/xul/base/src/nsMenuBarListener.cpp @@ -40,6 +40,7 @@ #include "nsMenuBarListener.h" #include "nsMenuBarFrame.h" +#include "nsMenuPopupFrame.h" #include "nsIDOMKeyListener.h" #include "nsIDOMEventTarget.h" #include "nsIDOMEventListener.h" @@ -131,6 +132,18 @@ void nsMenuBarListener::InitAccessKey() nsContentUtils::GetBoolPref("ui.key.menuAccessKeyFocuses"); } +void +nsMenuBarListener::ToggleMenuActiveState() +{ + nsMenuFrame* closemenu = mMenuBarFrame->ToggleMenuActiveState(); + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm && closemenu) { + nsMenuPopupFrame* popupFrame = closemenu->GetPopup(); + if (popupFrame) + pm->HidePopup(popupFrame->GetContent(), PR_FALSE, PR_FALSE, PR_TRUE); + } +} + //////////////////////////////////////////////////////////////////////// nsresult nsMenuBarListener::KeyUp(nsIDOMEvent* aKeyEvent) @@ -161,7 +174,7 @@ nsMenuBarListener::KeyUp(nsIDOMEvent* aKeyEvent) { // The access key was down and is now up, and no other // keys were pressed in between. - mMenuBarFrame->ToggleMenuActiveState(); + ToggleMenuActiveState(); } mAccessKeyDown = PR_FALSE; @@ -169,7 +182,7 @@ nsMenuBarListener::KeyUp(nsIDOMEvent* aKeyEvent) if (active) { aKeyEvent->StopPropagation(); aKeyEvent->PreventDefault(); - return NS_ERROR_BASE; // I am consuming event + return NS_OK; // I am consuming event } } @@ -180,8 +193,6 @@ nsMenuBarListener::KeyUp(nsIDOMEvent* aKeyEvent) nsresult nsMenuBarListener::KeyPress(nsIDOMEvent* aKeyEvent) { - mMenuBarFrame->ClearRecentlyRolledUp(); - // if event has already been handled, bail nsCOMPtr uiEvent ( do_QueryInterface(aKeyEvent) ); if ( uiEvent ) { @@ -229,14 +240,13 @@ nsMenuBarListener::KeyPress(nsIDOMEvent* aKeyEvent) // Do shortcut navigation. // A letter was pressed. We want to see if a shortcut gets matched. If // so, we'll know the menu got activated. - PRBool active = PR_FALSE; - mMenuBarFrame->ShortcutNavigation(keyEvent, active); - - if (active) { + nsMenuFrame* result = mMenuBarFrame->FindMenuWithShortcut(keyEvent); + if (result) { + mMenuBarFrame->SetActive(PR_TRUE); + result->OpenMenu(PR_TRUE); aKeyEvent->StopPropagation(); aKeyEvent->PreventDefault(); - - retVal = NS_ERROR_BASE; // I am consuming event + retVal = NS_OK; // I am consuming event } } #if !defined(XP_MAC) && !defined(XP_MACOSX) @@ -245,16 +255,17 @@ nsMenuBarListener::KeyPress(nsIDOMEvent* aKeyEvent) if ((GetModifiers(keyEvent) & ~MODIFIER_CONTROL) == 0) { // The F10 key just went down by itself or with ctrl pressed. // In Windows, both of these activate the menu bar. - mMenuBarFrame->ToggleMenuActiveState(); + ToggleMenuActiveState(); aKeyEvent->StopPropagation(); aKeyEvent->PreventDefault(); - return NS_ERROR_BASE; // consume the event + return NS_OK; // consume the event } } #endif // !XP_MAC && !XP_MACOSX } } + return retVal; } @@ -349,10 +360,8 @@ nsMenuBarListener::Focus(nsIDOMEvent* aEvent) nsresult nsMenuBarListener::Blur(nsIDOMEvent* aEvent) { - if (!mMenuBarFrame->IsOpen() && mMenuBarFrame->IsActive()) { - mMenuBarFrame->ToggleMenuActiveState(); - PRBool handled; - mMenuBarFrame->Escape(handled); + if (!mMenuBarFrame->IsMenuOpen() && mMenuBarFrame->IsActive()) { + ToggleMenuActiveState(); mAccessKeyDown = PR_FALSE; } return NS_OK; // means I am NOT consuming event @@ -362,12 +371,8 @@ nsMenuBarListener::Blur(nsIDOMEvent* aEvent) nsresult nsMenuBarListener::MouseDown(nsIDOMEvent* aMouseEvent) { - if (!mMenuBarFrame->IsOpen() && mMenuBarFrame->IsActive()) { - mMenuBarFrame->ToggleMenuActiveState(); - PRBool handled; - mMenuBarFrame->Escape(handled); - } - + if (!mMenuBarFrame->IsMenuOpen() && mMenuBarFrame->IsActive()) + ToggleMenuActiveState(); mAccessKeyDown = PR_FALSE; return NS_OK; // means I am NOT consuming event @@ -377,8 +382,6 @@ nsMenuBarListener::MouseDown(nsIDOMEvent* aMouseEvent) nsresult nsMenuBarListener::MouseUp(nsIDOMEvent* aMouseEvent) { - mMenuBarFrame->ClearRecentlyRolledUp(); - return NS_OK; // means I am NOT consuming event } diff --git a/mozilla/layout/xul/base/src/nsMenuBarListener.h b/mozilla/layout/xul/base/src/nsMenuBarListener.h index bce41f8c7b7..cf2b5a1bb6d 100644 --- a/mozilla/layout/xul/base/src/nsMenuBarListener.h +++ b/mozilla/layout/xul/base/src/nsMenuBarListener.h @@ -87,6 +87,10 @@ protected: static PRUint32 GetModifiers(nsIDOMKeyEvent* event); + // This should only be called by the nsMenuBarListener during event dispatch, + // thus ensuring that this doesn't get destroyed during the process. + void ToggleMenuActiveState(); + nsMenuBarFrame* mMenuBarFrame; // The menu bar object. PRBool mAccessKeyDown; // Whether or not the ALT key is currently down. static PRBool mAccessKeyFocuses; // Does the access key by itself focus the menubar? diff --git a/mozilla/layout/xul/base/src/nsMenuBoxObject.cpp b/mozilla/layout/xul/base/src/nsMenuBoxObject.cpp index a9581027b15..41daa3d5e09 100644 --- a/mozilla/layout/xul/base/src/nsMenuBoxObject.cpp +++ b/mozilla/layout/xul/base/src/nsMenuBoxObject.cpp @@ -39,11 +39,12 @@ #include "nsIMenuBoxObject.h" #include "nsBoxObject.h" #include "nsIPresShell.h" -#include "nsIMenuFrame.h" #include "nsIFrame.h" #include "nsGUIEvent.h" #include "nsIDOMNSUIEvent.h" #include "nsMenuBarListener.h" +#include "nsMenuFrame.h" +#include "nsMenuPopupFrame.h" #include "nsPopupSetFrame.h" class nsMenuBoxObject : public nsIMenuBoxObject, public nsBoxObject @@ -88,47 +89,41 @@ nsMenuBoxObject::~nsMenuBoxObject() /* void openMenu (in boolean openFlag); */ NS_IMETHODIMP nsMenuBoxObject::OpenMenu(PRBool aOpenFlag) { - nsIFrame* frame = GetFrame(PR_FALSE); - if (!frame) - return NS_OK; + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) { + nsIFrame* frame = GetFrame(PR_FALSE); + if (frame) { + if (aOpenFlag) { + nsCOMPtr content = mContent; + pm->ShowMenu(content, PR_FALSE, PR_FALSE); + } + else { + if (frame->GetType() == nsGkAtoms::menuFrame) { + nsMenuPopupFrame* popupFrame = (NS_STATIC_CAST(nsMenuFrame *, frame))->GetPopup(); + if (popupFrame) + pm->HidePopup(popupFrame->GetContent(), PR_FALSE, PR_TRUE, PR_FALSE); + } + } + } + } - if (!nsPopupSetFrame::MayOpenPopup(frame)) - return NS_OK; - - nsIMenuFrame* menuFrame; - CallQueryInterface(frame, &menuFrame); - if (!menuFrame) - return NS_OK; - - return menuFrame->OpenMenu(aOpenFlag); + return NS_OK; } NS_IMETHODIMP nsMenuBoxObject::GetActiveChild(nsIDOMElement** aResult) { *aResult = nsnull; nsIFrame* frame = GetFrame(PR_FALSE); - if (!frame) - return NS_OK; - - nsIMenuFrame* menuFrame; - CallQueryInterface(frame, &menuFrame); - if (menuFrame) - menuFrame->GetActiveChild(aResult); + if (frame && frame->GetType() == nsGkAtoms::menuFrame) + return NS_STATIC_CAST(nsMenuFrame *, frame)->GetActiveChild(aResult); return NS_OK; } NS_IMETHODIMP nsMenuBoxObject::SetActiveChild(nsIDOMElement* aResult) { nsIFrame* frame = GetFrame(PR_FALSE); - if (!frame) - return NS_OK; - - nsIMenuFrame* menuFrame; - CallQueryInterface(frame, &menuFrame); - if (menuFrame) { - menuFrame->MarkAsGenerated(); - menuFrame->SetActiveChild(aResult); - } + if (frame && frame->GetType() == nsGkAtoms::menuFrame) + return NS_STATIC_CAST(nsMenuFrame *, frame)->SetActiveChild(aResult); return NS_OK; } @@ -138,6 +133,10 @@ NS_IMETHODIMP nsMenuBoxObject::HandleKeyPress(nsIDOMKeyEvent* aKeyEvent, PRBool* *aHandledFlag = PR_FALSE; NS_ENSURE_ARG(aKeyEvent); + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (!pm) + return NS_OK; + // if event has already been handled, bail nsCOMPtr uiEvent(do_QueryInterface(aKeyEvent)); if (!uiEvent) @@ -152,12 +151,11 @@ NS_IMETHODIMP nsMenuBoxObject::HandleKeyPress(nsIDOMKeyEvent* aKeyEvent, PRBool* return NS_OK; nsIFrame* frame = GetFrame(PR_FALSE); - if (!frame) + if (!frame || frame->GetType() != nsGkAtoms::menuFrame) return NS_OK; - nsIMenuFrame* menuFrame; - CallQueryInterface(frame, &menuFrame); - if (!menuFrame) + nsMenuPopupFrame* popupFrame = NS_STATIC_CAST(nsMenuFrame *, frame)->GetPopup(); + if (!popupFrame) return NS_OK; PRUint32 keyCode; @@ -167,9 +165,11 @@ NS_IMETHODIMP nsMenuBoxObject::HandleKeyPress(nsIDOMKeyEvent* aKeyEvent, PRBool* case NS_VK_DOWN: case NS_VK_HOME: case NS_VK_END: - return menuFrame->KeyboardNavigation(keyCode, *aHandledFlag); + *aHandledFlag = pm->HandleKeyboardNavigation(keyCode); + return NS_OK; default: - return menuFrame->ShortcutNavigation(aKeyEvent, *aHandledFlag); + *aHandledFlag = pm->HandleShortcutNavigation(aKeyEvent); + return NS_OK; } } diff --git a/mozilla/layout/xul/base/src/nsMenuDismissalListener.cpp b/mozilla/layout/xul/base/src/nsMenuDismissalListener.cpp deleted file mode 100644 index 1760c124670..00000000000 --- a/mozilla/layout/xul/base/src/nsMenuDismissalListener.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/* -*- 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 Communicator client code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Dean Tessman - * - * 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 "nsMenuDismissalListener.h" -#include "nsIMenuParent.h" -#include "nsMenuFrame.h" -#include "nsIPopupBoxObject.h" -#include "nsContentUtils.h" - -nsMenuDismissalListener* nsMenuDismissalListener::sInstance = nsnull; - -/* - * nsMenuDismissalListener implementation - */ - -NS_IMPL_ADDREF(nsMenuDismissalListener) -NS_IMPL_RELEASE(nsMenuDismissalListener) -NS_INTERFACE_MAP_BEGIN(nsMenuDismissalListener) - NS_INTERFACE_MAP_ENTRY(nsIDOMMouseListener) - NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) - NS_INTERFACE_MAP_ENTRY(nsIMenuRollup) - NS_INTERFACE_MAP_ENTRY(nsIRollupListener) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMouseListener) -NS_INTERFACE_MAP_END - - -//////////////////////////////////////////////////////////////////////// - -nsMenuDismissalListener::nsMenuDismissalListener() : - mEnabled(PR_TRUE) -{ - mMenuParent = nsnull; -} - -nsMenuDismissalListener::~nsMenuDismissalListener() -{ -} - -nsMenuDismissalListener* -nsMenuDismissalListener::GetInstance() -{ - if (!sInstance) { - sInstance = new nsMenuDismissalListener(); - NS_IF_ADDREF(sInstance); - } - return sInstance; -} - -/* static */ void -nsMenuDismissalListener::Shutdown() -{ - if (sInstance) { - // XXX temporary code for bug 381426 until bug 279703 - nsContentUtils::NotifyInstalledMenuKeyboardListener(PR_FALSE); - - sInstance->Unregister(); - NS_RELEASE(sInstance); - } -} - - -nsIMenuParent* -nsMenuDismissalListener::GetCurrentMenuParent() -{ - return mMenuParent; -} - -void -nsMenuDismissalListener::SetCurrentMenuParent(nsIMenuParent* aMenuParent) -{ - if (aMenuParent == mMenuParent) - return; - - mMenuParent = aMenuParent; - - if (!aMenuParent) { - Shutdown(); - return; - } - - Unregister(); - Register(); -} - -NS_IMETHODIMP -nsMenuDismissalListener::Rollup() -{ - if (mEnabled) { - if (mMenuParent) { - AddRef(); - mMenuParent->HideChain(); - mMenuParent->DismissChain(); - Release(); - } - else - Shutdown(); - } - return NS_OK; -} - -//////////////////////////////////////////////////////////////////////// -NS_IMETHODIMP nsMenuDismissalListener::ShouldRollupOnMouseWheelEvent(PRBool *aShouldRollup) -{ - *aShouldRollup = PR_FALSE; - return NS_OK; -} - - -// uggggh. - -// a menu should not roll up if activated by a mouse activate message (eg. X-mouse) -NS_IMETHODIMP nsMenuDismissalListener::ShouldRollupOnMouseActivate(PRBool *aShouldRollup) -{ - *aShouldRollup = PR_FALSE; - return NS_OK; -} - -NS_IMETHODIMP -nsMenuDismissalListener::GetSubmenuWidgetChain(nsISupportsArray **_retval) -{ - NS_NewISupportsArray ( _retval ); - nsIMenuParent *curr = mMenuParent; - while ( curr ) { - nsCOMPtr widget; - curr->GetWidget ( getter_AddRefs(widget) ); - nsCOMPtr genericWidget ( do_QueryInterface(widget) ); - (**_retval).AppendElement ( genericWidget ); - - // move up the chain - nsIFrame* currAsFrame = nsnull; - if ( NS_SUCCEEDED(CallQueryInterface(curr, &currAsFrame)) ) { - nsIMenuFrame *menuFrame = nsnull; - nsIFrame *parentFrame = currAsFrame->GetParent(); - if (parentFrame) { - CallQueryInterface(parentFrame, &menuFrame); - } - if ( menuFrame ) { - curr = menuFrame->GetMenuParent (); // Advance to next parent - } - else { - // we are a menuParent but not a menuFrame. This is probably the case - // of the menu bar. Nothing to do here, really. - return NS_OK; - } - } - else { - // We've run into a menu parent that isn't a frame at all. Not good. - NS_WARNING ( "nsIMenuParent that is not a nsIFrame" ); - return NS_ERROR_FAILURE; - } - } // foreach parent menu - - return NS_OK; -} - - -void -nsMenuDismissalListener::Register() -{ - if (mWidget) - return; - - nsCOMPtr widget; - mMenuParent->GetWidget(getter_AddRefs(widget)); - if (!widget) { - Shutdown(); - return; - } - - PRBool consumeOutsideClicks = PR_FALSE; - mMenuParent->ConsumeOutsideClicks(consumeOutsideClicks); - widget->CaptureRollupEvents(this, PR_TRUE, consumeOutsideClicks); - mWidget = widget; - - mMenuParent->AttachedDismissalListener(); -} - -void -nsMenuDismissalListener::Unregister() -{ - if (mWidget) { - mWidget->CaptureRollupEvents(this, PR_FALSE, PR_FALSE); - mWidget = nsnull; - } -} - -void -nsMenuDismissalListener::EnableListener(PRBool aEnabled) -{ - mEnabled = aEnabled; -} - diff --git a/mozilla/layout/xul/base/src/nsMenuDismissalListener.h b/mozilla/layout/xul/base/src/nsMenuDismissalListener.h deleted file mode 100644 index b89b2ea17d8..00000000000 --- a/mozilla/layout/xul/base/src/nsMenuDismissalListener.h +++ /dev/null @@ -1,103 +0,0 @@ -/* -*- 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 Communicator client code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Original Author: David W. Hyatt (hyatt@netscape.com) - * - * 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 ***** */ -#ifndef nsMenuDismissalListener_h__ -#define nsMenuDismissalListener_h__ - -#include "nsIWidget.h" -#include "nsIDOMMouseListener.h" -#include "nsIRollupListener.h" -#include "nsIMenuRollup.h" -#include "nsIDOMEventTarget.h" -#include "nsCOMPtr.h" - -class nsIMenuParent; - -/** - * The object responsible for rolling up the open menu popups in cases when - * it's not done by menu code (for example, when clicking outside a popup - * on Windows). - * - * It is a singleton, which exists as long as there is a menu popup open. - */ -class nsMenuDismissalListener : public nsIDOMMouseListener, - public nsIMenuRollup, - public nsIRollupListener -{ - -public: - friend class nsMenuPopupFrame; - - NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) { return NS_OK; } - NS_IMETHOD MouseDown(nsIDOMEvent* aMouseEvent) { return NS_OK; } - NS_IMETHOD MouseUp(nsIDOMEvent* aMouseEvent) { return NS_OK; } - NS_IMETHOD MouseClick(nsIDOMEvent* aMouseEvent) { return NS_OK; } - NS_IMETHOD MouseDblClick(nsIDOMEvent* aMouseEvent) { return NS_OK; } - NS_IMETHOD MouseOver(nsIDOMEvent* aMouseEvent) { return NS_OK; } - NS_IMETHOD MouseOut(nsIDOMEvent* aMouseEvent) { return NS_OK; } - - NS_DECL_ISUPPORTS - NS_DECL_NSIROLLUPLISTENER - NS_DECL_NSIMENUROLLUP - - void EnableListener(PRBool aEnabled); - void SetCurrentMenuParent(nsIMenuParent* aMenuParent); - nsIMenuParent* GetCurrentMenuParent(); - - static nsMenuDismissalListener* GetInstance(); - static nsMenuDismissalListener* sInstance; - static void Shutdown(); - -protected: - nsMenuDismissalListener(); - ~nsMenuDismissalListener(); - - /** - * Registers itself as a rollup event listener for current mMenuParent's - * widget. mMenuParent must be non-null. - */ - void Register(); - - void Unregister(); - - nsIMenuParent* mMenuParent; - nsCOMPtr mWidget; - PRBool mEnabled; -}; - - -#endif diff --git a/mozilla/layout/xul/base/src/nsMenuFrame.cpp b/mozilla/layout/xul/base/src/nsMenuFrame.cpp index 274ffd91d58..3f1f4d30efd 100644 --- a/mozilla/layout/xul/base/src/nsMenuFrame.cpp +++ b/mozilla/layout/xul/base/src/nsMenuFrame.cpp @@ -71,18 +71,19 @@ #include "nsIServiceManager.h" #include "nsCSSFrameConstructor.h" #include "nsIDOMKeyEvent.h" +#include "nsEventDispatcher.h" +#include "nsIPrivateDOMEvent.h" #include "nsIScrollableView.h" #include "nsXPIDLString.h" #include "nsReadableUtils.h" #include "nsUnicharUtils.h" #include "nsIStringBundle.h" #include "nsGUIEvent.h" -#include "nsIEventStateManager.h" #include "nsContentUtils.h" #include "nsDisplayList.h" #include "nsIReflowCallback.h" -#define NS_MENU_POPUP_LIST_INDEX 0 +#define NS_MENU_POPUP_LIST_INDEX 0 #if defined(XP_WIN) || defined(XP_OS2) #define NSCONTEXTMENUISMOUSEUP 1 @@ -141,11 +142,10 @@ NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame) nsMenuFrame::nsMenuFrame(nsIPresShell* aShell, nsStyleContext* aContext): nsBoxFrame(aShell, aContext), mIsMenu(PR_FALSE), - mMenuOpen(PR_FALSE), - mCreateHandlerSucceeded(PR_FALSE), mChecked(PR_FALSE), mType(eMenuType_Normal), mMenuParent(nsnull), + mPopupFrame(nsnull), mLastPref(-1,-1) { @@ -155,17 +155,27 @@ NS_IMETHODIMP nsMenuFrame::SetParent(const nsIFrame* aParent) { nsBoxFrame::SetParent(aParent); - const nsIFrame* currFrame = aParent; - while (!mMenuParent && currFrame) { - // Set our menu parent. - CallQueryInterface(NS_CONST_CAST(nsIFrame*, currFrame), &mMenuParent); - - currFrame = currFrame->GetParent(); - } - + InitMenuParent(NS_CONST_CAST(nsIFrame *, aParent)); return NS_OK; } +void +nsMenuFrame::InitMenuParent(nsIFrame* aParent) +{ + while (aParent) { + nsIAtom* type = aParent->GetType(); + if (type == nsGkAtoms::menuPopupFrame) { + mMenuParent = NS_STATIC_CAST(nsMenuPopupFrame *, aParent); + break; + } + else if (type == nsGkAtoms::menuBarFrame) { + mMenuParent = NS_STATIC_CAST(nsMenuBarFrame *, aParent); + break; + } + aParent = aParent->GetParent(); + } +} + class nsASyncMenuInitialization : public nsIReflowCallback { public: @@ -177,10 +187,8 @@ public: virtual PRBool ReflowFinished() { PRBool shouldFlush = PR_FALSE; if (mWeakFrame.IsAlive()) { - nsIMenuFrame* imenu = nsnull; - CallQueryInterface(mWeakFrame.GetFrame(), &imenu); - if (imenu) { - nsMenuFrame* menu = NS_STATIC_CAST(nsMenuFrame*, imenu); + if (mWeakFrame.GetFrame()->GetType() == nsGkAtoms::menuFrame) { + nsMenuFrame* menu = NS_STATIC_CAST(nsMenuFrame*, mWeakFrame.GetFrame()); menu->UpdateMenuType(menu->PresContext()); shouldFlush = PR_TRUE; } @@ -204,13 +212,7 @@ nsMenuFrame::Init(nsIContent* aContent, if (NS_UNLIKELY(!mTimerMediator)) return NS_ERROR_OUT_OF_MEMORY; - nsIFrame* currFrame = aParent; - while (!mMenuParent && currFrame) { - // Set our menu parent. - CallQueryInterface(currFrame, &mMenuParent); - - currFrame = currFrame->GetParent(); - } + InitMenuParent(aParent); //load the display strings for the keyboard accelerators, but only once if (gRefCnt++ == 0) { @@ -276,7 +278,7 @@ nsIFrame* nsMenuFrame::GetFirstChild(nsIAtom* aListName) const { if (nsGkAtoms::popupList == aListName) { - return mPopupFrames.FirstChild(); + return mPopupFrame; } return nsBoxFrame::GetFirstChild(aListName); } @@ -285,38 +287,22 @@ NS_IMETHODIMP nsMenuFrame::SetInitialChildList(nsIAtom* aListName, nsIFrame* aChildList) { - nsresult rv = NS_OK; - if (nsGkAtoms::popupList == aListName) { - mPopupFrames.SetFrames(aChildList); - } else { - - nsFrameList frames(aChildList); - - // We may have a menupopup in here. Get it out, and move it into - // the popup frame list. - nsIFrame* frame = frames.FirstChild(); - while (frame) { - nsIMenuParent *menuPar; - CallQueryInterface(frame, &menuPar); - if (menuPar) { - PRBool isMenuBar; - menuPar->IsMenuBar(isMenuBar); - if (!isMenuBar) { - // Remove this frame from the list and place it in the other list. - frames.RemoveFrame(frame); - mPopupFrames.AppendFrame(this, frame); - nsIFrame* first = frames.FirstChild(); - rv = nsBoxFrame::SetInitialChildList(aListName, first); - return rv; - } - } - frame = frame->GetNextSibling(); + // Check for a menupopup and move it to mPopupFrame + nsFrameList frames(aChildList); + nsIFrame* frame = frames.FirstChild(); + while (frame) { + if (frame->GetType() == nsGkAtoms::menuPopupFrame) { + // Remove this frame from the list and set it as mPopupFrame + frames.RemoveFrame(frame); + mPopupFrame = (nsMenuPopupFrame *)frame; + aChildList = frames.FirstChild(); + break; } - - // Didn't find it. - rv = nsBoxFrame::SetInitialChildList(aListName, aChildList); + frame = frame->GetNextSibling(); } - return rv; + + // Didn't find it. + return nsBoxFrame::SetInitialChildList(aListName, aChildList); } nsIAtom* @@ -325,27 +311,9 @@ nsMenuFrame::GetAdditionalChildListName(PRInt32 aIndex) const if (NS_MENU_POPUP_LIST_INDEX == aIndex) { return nsGkAtoms::popupList; } - return nsnull; } -nsresult -nsMenuFrame::DestroyPopupFrames(nsPresContext* aPresContext) -{ - // Remove our frame mappings - nsCSSFrameConstructor* frameConstructor = - aPresContext->PresShell()->FrameConstructor(); - nsIFrame* curFrame = mPopupFrames.FirstChild(); - while (curFrame) { - frameConstructor->RemoveMappingsForFrameSubtree(curFrame); - curFrame = curFrame->GetNextSibling(); - } - - // Cleanup frames in popup child list - mPopupFrames.DestroyFrames(); - return NS_OK; -} - void nsMenuFrame::Destroy() { @@ -360,20 +328,15 @@ nsMenuFrame::Destroy() // doesn't try to interact with a deallocated frame. mTimerMediator->ClearFrame(); - nsWeakFrame weakFrame(this); // are we our menu parent's current menu item? - if (mMenuParent) { - nsIMenuFrame *curItem = mMenuParent->GetCurrentMenuItem(); - if (curItem == this) { - // yes; tell it that we're going away - mMenuParent->SetCurrentMenuItem(nsnull); - ENSURE_TRUE(weakFrame.IsAlive()); - } + if (mMenuParent && mMenuParent->GetCurrentMenuItem() == this) { + // yes; tell it that we're going away + mMenuParent->CurrentMenuIsBeingDestroyed(); } - UngenerateMenu(); - ENSURE_TRUE(weakFrame.IsAlive()); - DestroyPopupFrames(PresContext()); + if (mPopupFrame) + mPopupFrame->Destroy(); + nsBoxFrame::Destroy(); } @@ -392,16 +355,18 @@ nsMenuFrame::BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder, return WrapListsInRedirector(aBuilder, set, aLists); } -NS_IMETHODIMP +NS_IMETHODIMP nsMenuFrame::HandleEvent(nsPresContext* aPresContext, - nsGUIEvent* aEvent, - nsEventStatus* aEventStatus) + nsGUIEvent* aEvent, + nsEventStatus* aEventStatus) { NS_ENSURE_ARG_POINTER(aEventStatus); nsWeakFrame weakFrame(this); if (*aEventStatus == nsEventStatus_eIgnore) *aEventStatus = nsEventStatus_eConsumeDoDefault; - + + PRBool onmenu = IsOnMenu(); + if (aEvent->message == NS_KEY_PRESS && !IsDisabled()) { nsKeyEvent* keyEvent = (nsKeyEvent*)aEvent; PRUint32 keyCode = keyEvent->keyCode; @@ -409,46 +374,27 @@ nsMenuFrame::HandleEvent(nsPresContext* aPresContext, // On mac, open menulist on either up/down arrow or space (w/o Cmd pressed) if (!IsOpen() && ((keyEvent->charCode == NS_VK_SPACE && !keyEvent->isMeta) || (keyCode == NS_VK_UP || keyCode == NS_VK_DOWN))) - OpenMenu(PR_TRUE); + OpenMenu(PR_FALSE); #else // On other platforms, toggle menulist on unmodified F4 or Alt arrow if ((keyCode == NS_VK_F4 && !keyEvent->isAlt) || ((keyCode == NS_VK_UP || keyCode == NS_VK_DOWN) && keyEvent->isAlt)) - OpenMenu(!IsOpen()); + ToggleMenuState(); #endif } else if (aEvent->eventStructType == NS_MOUSE_EVENT && aEvent->message == NS_MOUSE_BUTTON_DOWN && NS_STATIC_CAST(nsMouseEvent*, aEvent)->button == nsMouseEvent::eLeftButton && !IsDisabled() && IsMenu()) { - PRBool isMenuBar = PR_FALSE; - if (mMenuParent) - mMenuParent->IsMenuBar(isMenuBar); - // The menu item was selected. Bring up the menu. // We have children. - if ( isMenuBar || !mMenuParent ) { + if (!mMenuParent || mMenuParent->IsMenuBar()) { ToggleMenuState(); - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); - - if (!IsOpen() && mMenuParent) { - // We closed up. The menu bar should always be - // deactivated when this happens. - mMenuParent->SetActive(PR_FALSE); - } } - else - if ( !IsOpen() ) { - // one of our siblings is probably open and even possibly waiting - // for its close timer to fire. Tell our parent to close it down. Not - // doing this before its timer fires will cause the rollup state to - // get very confused. - if ( mMenuParent ) - mMenuParent->KillPendingTimers(); - - // safe to open up - OpenMenu(PR_TRUE); - } + else { + if (!IsOpen()) + OpenMenu(PR_FALSE); + } } else if ( #ifndef NSCONTEXTMENUISMOUSEUP @@ -459,7 +405,7 @@ nsMenuFrame::HandleEvent(nsPresContext* aPresContext, #else aEvent->message == NS_CONTEXTMENU && #endif - mMenuParent && !IsMenu() && !IsDisabled()) { + onmenu && !IsMenu() && !IsDisabled()) { // if this menu is a context menu it accepts right-clicks...fire away! // Make sure we cancel default processing of the context menu event so // that it doesn't bubble and get seen again by the popuplistener and show @@ -470,9 +416,7 @@ nsMenuFrame::HandleEvent(nsPresContext* aPresContext, // on others we get it on a mouse down. For the ones where we get it on a // mouse down, we must continue listening for the right button up event to // dismiss the menu. - PRBool isContextMenu = PR_FALSE; - mMenuParent->GetIsContextMenu(isContextMenu); - if ( isContextMenu ) { + if (mMenuParent->IsContextMenu()) { *aEventStatus = nsEventStatus_eConsumeNoDefault; Execute(aEvent); } @@ -480,7 +424,7 @@ nsMenuFrame::HandleEvent(nsPresContext* aPresContext, else if (aEvent->eventStructType == NS_MOUSE_EVENT && aEvent->message == NS_MOUSE_BUTTON_UP && NS_STATIC_CAST(nsMouseEvent*, aEvent)->button == nsMouseEvent::eLeftButton && - !IsMenu() && mMenuParent && !IsDisabled()) { + !IsMenu() && !IsDisabled()) { // Execute the execute event handler. Execute(aEvent); } @@ -492,52 +436,42 @@ nsMenuFrame::HandleEvent(nsPresContext* aPresContext, } // Deactivate the menu. - PRBool isActive = PR_FALSE; - PRBool isMenuBar = PR_FALSE; if (mMenuParent) { - mMenuParent->IsMenuBar(isMenuBar); - PRBool cancel = PR_TRUE; - if (isMenuBar) { - mMenuParent->GetIsActive(isActive); - if (isActive) cancel = PR_FALSE; - } - - if (cancel) { - if (IsMenu() && !isMenuBar && mMenuOpen) { + PRBool onmenubar = mMenuParent->IsMenuBar(); + if (!(onmenubar && mMenuParent->IsActive())) { + if (IsMenu() && !onmenubar && IsOpen()) { // Submenus don't get closed up immediately. } - else mMenuParent->SetCurrentMenuItem(nsnull); + else + mMenuParent->ChangeMenuItem(nsnull, PR_FALSE); } } } - else if (aEvent->message == NS_MOUSE_MOVE && mMenuParent) { + else if (aEvent->message == NS_MOUSE_MOVE && + (onmenu || (mMenuParent && mMenuParent->IsMenuBar()))) { if (gEatMouseMove) { gEatMouseMove = PR_FALSE; return NS_OK; } - // we checked for mMenuParent right above - - PRBool isMenuBar = PR_FALSE; - mMenuParent->IsMenuBar(isMenuBar); - // Let the menu parent know we're the new item. - mMenuParent->SetCurrentMenuItem(this); + mMenuParent->ChangeMenuItem(this, PR_FALSE); NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); NS_ENSURE_TRUE(mMenuParent, NS_OK); - + // we need to check if we really became the current menu // item or not - nsIMenuFrame *realCurrentItem = mMenuParent->GetCurrentMenuItem(); + nsMenuFrame *realCurrentItem = mMenuParent->GetCurrentMenuItem(); if (realCurrentItem != this) { // we didn't (presumably because a context menu was active) return NS_OK; } - // If we're a menu (and not a menu item), - // kick off the timer. - if (!IsDisabled() && !isMenuBar && IsMenu() && !mMenuOpen && !mOpenTimer) { - + // Hovering over a menu in a popup should open it without a need for a click. + // A timer is used so that it doesn't open if the user moves the mouse quickly + // past the menu. This conditional check ensures that only menus have this + // behaviour + if (!IsDisabled() && IsMenu() && !IsOpen() && !mOpenTimer && !mMenuParent->IsMenuBar()) { PRInt32 menuDelay = 300; // ms nsCOMPtr lookAndFeel(do_GetService(kLookAndFeelCID)); @@ -547,203 +481,116 @@ nsMenuFrame::HandleEvent(nsPresContext* aPresContext, // We're a menu, we're built, we're closed, and no timer has been kicked off. mOpenTimer = do_CreateInstance("@mozilla.org/timer;1"); mOpenTimer->InitWithCallback(mTimerMediator, menuDelay, nsITimer::TYPE_ONE_SHOT); - } } return NS_OK; } -NS_IMETHODIMP +void nsMenuFrame::ToggleMenuState() { - nsWeakFrame weakFrame(this); - if (mMenuOpen) { + if (IsOpen()) + CloseMenu(PR_FALSE); + else OpenMenu(PR_FALSE); - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); - } - else { - PRBool justRolledUp = PR_FALSE; - if (mMenuParent) { - mMenuParent->RecentlyRolledUp(this, &justRolledUp); - } - if (justRolledUp) { - // Don't let a click reopen a menu that was just rolled up - // from the same click. Otherwise, the user can't click on - // a menubar item to toggle its submenu closed. - OpenMenu(PR_FALSE); - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); - SelectMenu(PR_TRUE); - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); - NS_ENSURE_TRUE(mMenuParent, NS_OK); - mMenuParent->SetActive(PR_FALSE); - } - else { - if (mMenuParent) { - mMenuParent->SetActive(PR_TRUE); - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); - } - OpenMenu(PR_TRUE); - } - } - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); +} + +void +nsMenuFrame::PopupOpened() +{ + nsWeakFrame weakFrame(this); + mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open, + NS_LITERAL_STRING("true"), PR_TRUE); + if (!weakFrame.IsAlive()) + return; if (mMenuParent) { + mMenuParent->SetActive(PR_TRUE); // Make sure the current menu which is being toggled on // the menubar is highlighted mMenuParent->SetCurrentMenuItem(this); - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); - NS_ENSURE_TRUE(mMenuParent, NS_OK); - // We've successfully prevented the same click from both - // dismissing and reopening this menu. - // Clear the recent rollup state so we don't prevent - // this menu from being opened by the next click. - mMenuParent->ClearRecentlyRolledUp(); + } +} + +void +nsMenuFrame::PopupClosed(PRBool aDeselectMenu) +{ + nsWeakFrame weakFrame(this); + mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::open, PR_TRUE); + if (!weakFrame.IsAlive()) + return; + + // if the popup is for a menu on a menubar, inform menubar to deactivate + if (mMenuParent && mMenuParent->MenuClosed()) { + if (aDeselectMenu) + SelectMenu(PR_FALSE); + } +} + +// this class is used for dispatching menu activation events asynchronously. +class nsMenuActivateEvent : public nsRunnable +{ +public: + nsMenuActivateEvent(nsIContent *aMenu, + nsPresContext* aPresContext, + PRBool aIsActivate) + : mMenu(aMenu), mPresContext(aPresContext), mIsActivate(aIsActivate) + { } - return NS_OK; -} + NS_IMETHOD Run() + { + nsAutoString domEventToFire; + + if (mIsActivate) { + // Highlight the menu. + mMenu->SetAttr(kNameSpaceID_None, nsGkAtoms::menuactive, + NS_LITERAL_STRING("true"), PR_TRUE); + // The menuactivated event is used by accessibility to track the user's + // movements through menus + domEventToFire.AssignLiteral("DOMMenuItemActive"); + } + else { + // Unhighlight the menu. + mMenu->UnsetAttr(kNameSpaceID_None, nsGkAtoms::menuactive, PR_TRUE); + domEventToFire.AssignLiteral("DOMMenuItemInactive"); + } + + nsCOMPtr event; + if (NS_SUCCEEDED(nsEventDispatcher::CreateEvent(mPresContext, nsnull, + NS_LITERAL_STRING("Events"), + getter_AddRefs(event)))) { + event->InitEvent(domEventToFire, PR_TRUE, PR_TRUE); + + nsCOMPtr privateEvent(do_QueryInterface(event)); + privateEvent->SetTrusted(PR_TRUE); + + nsEventDispatcher::DispatchDOMEvent(mMenu, nsnull, event, + mPresContext, nsnull); + } + + return NS_OK; + } + +private: + nsCOMPtr mMenu; + nsCOMPtr mPresContext; + PRBool mIsActivate; +}; NS_IMETHODIMP nsMenuFrame::SelectMenu(PRBool aActivateFlag) { - if (!mContent) { - return NS_OK; - } - - nsAutoString domEventToFire; - - nsWeakFrame weakFrame(this); - if (aActivateFlag) { - if (mMenuParent) { - nsIMenuParent* ancestor = nsnull; - nsresult rv = mMenuParent->GetParentPopup(&ancestor); - while (NS_SUCCEEDED(rv) && ancestor) { - ancestor->CancelPendingTimers(); - rv = ancestor->GetParentPopup(&ancestor); - } - } - // Highlight the menu. - mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::menuactive, NS_LITERAL_STRING("true"), PR_TRUE); - // The menuactivated event is used by accessibility to track the user's movements through menus - domEventToFire.AssignLiteral("DOMMenuItemActive"); - } - else { - // Unhighlight the menu. - mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::menuactive, PR_TRUE); - domEventToFire.AssignLiteral("DOMMenuItemInactive"); - } - - if (weakFrame.IsAlive()) { - FireDOMEventSynch(domEventToFire); - } - return NS_OK; -} - -PRBool nsMenuFrame::IsGenerated() -{ - nsCOMPtr child; - GetMenuChildrenElement(getter_AddRefs(child)); - - // Generate the menu if it hasn't been generated already. This - // takes it from display: none to display: block and gives us - // a menu forevermore. - if (child && - !nsContentUtils::HasNonEmptyAttr(child, kNameSpaceID_None, - nsGkAtoms::menugenerated)) { - return PR_FALSE; - } - - return PR_TRUE; -} - -NS_IMETHODIMP -nsMenuFrame::MarkAsGenerated() -{ - nsCOMPtr child; - GetMenuChildrenElement(getter_AddRefs(child)); - - // Generate the menu if it hasn't been generated already. This - // takes it from display: none to display: block and gives us - // a menu forevermore. - if (child && - !nsContentUtils::HasNonEmptyAttr(child, kNameSpaceID_None, - nsGkAtoms::menugenerated)) { - child->SetAttr(kNameSpaceID_None, nsGkAtoms::menugenerated, - NS_LITERAL_STRING("true"), PR_TRUE); + if (mContent) { + nsCOMPtr event = + new nsMenuActivateEvent(mContent, PresContext(), aActivateFlag); + NS_DispatchToCurrentThread(event); } return NS_OK; } -NS_IMETHODIMP -nsMenuFrame::UngenerateMenu() -{ - nsCOMPtr child; - GetMenuChildrenElement(getter_AddRefs(child)); - - if (child && - nsContentUtils::HasNonEmptyAttr(child, kNameSpaceID_None, - nsGkAtoms::menugenerated)) { - child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::menugenerated, PR_TRUE); - } - - return NS_OK; -} - -NS_IMETHODIMP -nsMenuFrame::ActivateMenu(PRBool aActivateFlag) -{ - nsIFrame* frame = mPopupFrames.FirstChild(); - nsMenuPopupFrame* menuPopup = (nsMenuPopupFrame*)frame; - - if (!menuPopup) - return NS_OK; - - if (aActivateFlag) { - nsRect rect = menuPopup->GetRect(); - nsIView* view = menuPopup->GetView(); - nsIViewManager* viewManager = view->GetViewManager(); - rect.x = rect.y = 0; - viewManager->ResizeView(view, rect); - - // make sure the scrolled window is at 0,0 - if (mLastPref.height <= rect.height) { - nsIBox* child = menuPopup->GetChildBox(); - - nsCOMPtr scrollframe(do_QueryInterface(child)); - if (scrollframe) { - scrollframe->ScrollTo(nsPoint(0,0)); - } - } - - viewManager->UpdateView(view, rect, NS_VMREFRESH_IMMEDIATE); - viewManager->SetViewVisibility(view, nsViewVisibility_kShow); - PresContext()->RootPresContext()->NotifyAddedActivePopupToTop(menuPopup); - } else { - if (mMenuOpen) { - nsWeakFrame weakFrame(this); - nsWeakFrame weakPopup(menuPopup); - FireDOMEventSynch(NS_LITERAL_STRING("DOMMenuInactive"), menuPopup->GetContent()); - NS_ENSURE_TRUE(weakFrame.IsAlive() && weakPopup.IsAlive(), NS_OK); - } - nsIView* view = menuPopup->GetView(); - NS_ASSERTION(view, "View is gone, looks like someone forgot to rollup the popup!"); - if (view) { - nsIViewManager* viewManager = view->GetViewManager(); - if (viewManager) { // the view manager can be null during widget teardown - viewManager->SetViewVisibility(view, nsViewVisibility_kHide); - viewManager->ResizeView(view, nsRect(0, 0, 0, 0)); - } - } - // set here so hide chain can close the menu as well. - mMenuOpen = PR_FALSE; - PresContext()->RootPresContext()->NotifyRemovedActivePopup(menuPopup); - } - - return NS_OK; -} - NS_IMETHODIMP nsMenuFrame::AttributeChanged(PRInt32 aNameSpaceID, nsIAtom* aAttribute, @@ -760,247 +607,37 @@ nsMenuFrame::AttributeChanged(PRInt32 aNameSpaceID, BuildAcceleratorText(); } else if (aAttribute == nsGkAtoms::key) { BuildAcceleratorText(); - } else if ( aAttribute == nsGkAtoms::type || aAttribute == nsGkAtoms::name ) + } else if (aAttribute == nsGkAtoms::type || aAttribute == nsGkAtoms::name) UpdateMenuType(PresContext()); return NS_OK; } -NS_IMETHODIMP -nsMenuFrame::OpenMenu(PRBool aActivateFlag) +void +nsMenuFrame::OpenMenu(PRBool aSelectFirstItem) { if (!mContent) - return NS_OK; - - nsWeakFrame weakFrame(this); - if (aActivateFlag) { - // Now that the menu is opened, we should have a menupopup child built. - // Mark it as generated, which ensures a frame gets built. - MarkAsGenerated(); - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); - - mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open, NS_LITERAL_STRING("true"), PR_TRUE); - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); - FireDOMEventSynch(NS_LITERAL_STRING("DOMMenuItemActive")); - } - else { - mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::open, PR_TRUE); - } - - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); - OpenMenuInternal(aActivateFlag); - - return NS_OK; -} - -void -nsMenuFrame::OpenMenuInternal(PRBool aActivateFlag) -{ - gEatMouseMove = PR_TRUE; - - if (!mIsMenu) return; - nsPresContext* presContext = PresContext(); - nsWeakFrame weakFrame(this); - - if (aActivateFlag) { - // Execute the oncreate handler - if (!OnCreate() || !weakFrame.IsAlive()) - return; - - mCreateHandlerSucceeded = PR_TRUE; - - // Set the focus back to our view's widget. - if (nsMenuDismissalListener::sInstance) - nsMenuDismissalListener::sInstance->EnableListener(PR_FALSE); - - // XXX Only have this here because of RDF-generated content. - MarkAsGenerated(); - ENSURE_TRUE(weakFrame.IsAlive()); - - nsIFrame* frame = mPopupFrames.FirstChild(); - nsMenuPopupFrame* menuPopup = (nsMenuPopupFrame*)frame; - - PRBool wasOpen = mMenuOpen; - mMenuOpen = PR_TRUE; - - if (menuPopup) { - nsWeakFrame weakMenuPopup(frame); - // inherit whether or not we're a context menu from the parent - if ( mMenuParent ) { - PRBool parentIsContextMenu = PR_FALSE; - mMenuParent->GetIsContextMenu(parentIsContextMenu); - menuPopup->SetIsContextMenu(parentIsContextMenu); - ENSURE_TRUE(weakFrame.IsAlive()); - } - - // Install a keyboard navigation listener if we're the root of the menu chain. - PRBool onMenuBar = PR_TRUE; - if (mMenuParent) - mMenuParent->IsMenuBar(onMenuBar); - - if (mMenuParent && onMenuBar) - mMenuParent->InstallKeyboardNavigator(); - else if (!mMenuParent) { - ENSURE_TRUE(weakMenuPopup.IsAlive()); - menuPopup->InstallKeyboardNavigator(); - } - - // Tell the menu bar we're active. - if (mMenuParent) { - mMenuParent->SetActive(PR_TRUE); - ENSURE_TRUE(weakFrame.IsAlive()); - } - - nsIContent* menuPopupContent = menuPopup->GetContent(); - - // Sync up the view. - nsAutoString popupAnchor, popupAlign; - - menuPopupContent->GetAttr(kNameSpaceID_None, nsGkAtoms::popupanchor, popupAnchor); - menuPopupContent->GetAttr(kNameSpaceID_None, nsGkAtoms::popupalign, popupAlign); - - ConvertPosition(menuPopupContent, popupAnchor, popupAlign); - - if (onMenuBar) { - if (popupAnchor.IsEmpty()) - popupAnchor.AssignLiteral("bottomleft"); - if (popupAlign.IsEmpty()) - popupAlign.AssignLiteral("topleft"); - } - else { - if (popupAnchor.IsEmpty()) - popupAnchor.AssignLiteral("topright"); - if (popupAlign.IsEmpty()) - popupAlign.AssignLiteral("topleft"); - } - - // If the menu popup was not open, do a reflow. This is either the - // initial reflow for a brand-new popup, or a subsequent reflow for - // a menu that was deactivated and needs to be brought back to its - // active dimensions. - if (!wasOpen) - { - presContext->PresShell()-> - FrameNeedsReflow(menuPopup, nsIPresShell::eStyleChange, - NS_FRAME_IS_DIRTY); - presContext->PresShell()->FlushPendingNotifications(Flush_OnlyReflow); - } - - nsRect curRect(menuPopup->GetRect()); - nsBoxLayoutState state(presContext); - menuPopup->SetBounds(state, nsRect(0,0,mLastPref.width, mLastPref.height)); - - nsIView* view = menuPopup->GetView(); - nsIViewManager* vm = view->GetViewManager(); - if (vm) { - vm->SetViewVisibility(view, nsViewVisibility_kHide); - } - menuPopup->SyncViewWithFrame(presContext, popupAnchor, popupAlign, this, -1, -1); - nscoord newHeight = menuPopup->GetRect().height; - - // if the height is different then reflow. It might need scrollbars force a reflow - if (curRect.height != newHeight || mLastPref.height != newHeight) - { - presContext->PresShell()-> - FrameNeedsReflow(menuPopup, nsIPresShell::eStyleChange, - NS_FRAME_IS_DIRTY); - presContext->PresShell()->FlushPendingNotifications(Flush_OnlyReflow); - } - - ActivateMenu(PR_TRUE); - ENSURE_TRUE(weakFrame.IsAlive()); - - nsIMenuParent *childPopup = nsnull; - CallQueryInterface(frame, &childPopup); - - nsMenuDismissalListener* listener = nsMenuDismissalListener::GetInstance(); - if (listener) - listener->SetCurrentMenuParent(childPopup); - - OnCreated(); - ENSURE_TRUE(weakFrame.IsAlive()); - } - - // Set the focus back to our view's widget. - if (nsMenuDismissalListener::sInstance) - nsMenuDismissalListener::sInstance->EnableListener(PR_TRUE); + gEatMouseMove = PR_TRUE; + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) { + pm->KillMenuTimer(); + // This opens the menu asynchronously + pm->ShowMenu(mContent, aSelectFirstItem, PR_TRUE); } - else { - - // Close the menu. - // Execute the ondestroy handler, but only if we're actually open - if ( !mCreateHandlerSucceeded || !OnDestroy() || !weakFrame.IsAlive()) - return; - - // Set the focus back to our view's widget. - if (nsMenuDismissalListener::sInstance) { - nsMenuDismissalListener::sInstance->EnableListener(PR_FALSE); - nsMenuDismissalListener::sInstance->SetCurrentMenuParent(mMenuParent); - } - - nsIFrame* frame = mPopupFrames.FirstChild(); - nsMenuPopupFrame* menuPopup = (nsMenuPopupFrame*)frame; - - // Make sure we clear out our own items. - if (menuPopup) { - menuPopup->SetCurrentMenuItem(nsnull); - ENSURE_TRUE(weakFrame.IsAlive()); - menuPopup->KillCloseTimer(); - - PRBool onMenuBar = PR_TRUE; - if (mMenuParent) - mMenuParent->IsMenuBar(onMenuBar); - - if (mMenuParent && onMenuBar) - mMenuParent->RemoveKeyboardNavigator(); - else if (!mMenuParent) - menuPopup->RemoveKeyboardNavigator(); - - // XXX, bug 137033, In Windows, if mouse is outside the window when the menupopup closes, no - // mouse_enter/mouse_exit event will be fired to clear current hover state, we should clear it manually. - // This code may not the best solution, but we can leave it here until we find the better approach. - - nsIEventStateManager *esm = presContext->EventStateManager(); - - PRInt32 state; - esm->GetContentState(menuPopup->GetContent(), state); - - if (state & NS_EVENT_STATE_HOVER) - esm->SetContentState(nsnull, NS_EVENT_STATE_HOVER); - } - - ActivateMenu(PR_FALSE); - ENSURE_TRUE(weakFrame.IsAlive()); - // XXX hack: ensure that mMenuOpen is set to false, in case where - // there is actually no popup. because ActivateMenu() will return - // early without setting it. It could be that mMenuOpen is true - // in that case, because OpenMenuInternal(true) gets called if - // the attribute open="true", whether there is a popup or not. - // We should not allow mMenuOpen unless there is a popup in the first place, - // in which case this line would not be necessary. - mMenuOpen = PR_FALSE; - - OnDestroyed(); - ENSURE_TRUE(weakFrame.IsAlive()); - - if (nsMenuDismissalListener::sInstance) - nsMenuDismissalListener::sInstance->EnableListener(PR_TRUE); - - mCreateHandlerSucceeded = PR_FALSE; - } - } void -nsMenuFrame::GetMenuChildrenElement(nsIContent** aResult) +nsMenuFrame::CloseMenu(PRBool aDeselectMenu) { - *aResult = nsContentUtils::FindFirstChildWithResolvedTag(mContent, - kNameSpaceID_XUL, - nsGkAtoms::menupopup); - NS_IF_ADDREF(*aResult); + gEatMouseMove = PR_TRUE; + + // Close the menu asynchronously + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm && mPopupFrame) + pm->HidePopup(mPopupFrame->GetContent(), PR_FALSE, aDeselectMenu, PR_TRUE); } PRBool @@ -1038,17 +675,12 @@ nsMenuFrame::DoLayout(nsBoxLayoutState& aState) nsresult rv = nsBoxFrame::DoLayout(aState); // layout the popup. First we need to get it. - nsIFrame* popupChild = mPopupFrames.FirstChild(); - - if (popupChild) { + if (mPopupFrame) { PRBool sizeToPopup = IsSizedToPopup(mContent, PR_FALSE); - - NS_ASSERTION(popupChild->IsBoxFrame(), "popupChild is not box!!"); - // then get its preferred size - nsSize prefSize = popupChild->GetPrefSize(aState); - nsSize minSize = popupChild->GetMinSize(aState); - nsSize maxSize = popupChild->GetMaxSize(aState); + nsSize prefSize = mPopupFrame->GetPrefSize(aState); + nsSize minSize = mPopupFrame->GetMinSize(aState); + nsSize maxSize = mPopupFrame->GetMaxSize(aState); BoundsCheck(minSize, prefSize, maxSize); @@ -1056,49 +688,44 @@ nsMenuFrame::DoLayout(nsBoxLayoutState& aState) prefSize.width = mRect.width; // if the pref size changed then set bounds to be the pref size - // and sync the view. And set new pref size. - if (mLastPref != prefSize) { - popupChild->SetBounds(aState, nsRect(0,0,prefSize.width, prefSize.height)); - RePositionPopup(aState); + PRBool sizeChanged = (mLastPref != prefSize); + if (sizeChanged) { + mPopupFrame->SetBounds(aState, nsRect(0,0,prefSize.width, prefSize.height)); mLastPref = prefSize; } - // is the new size too small? Make sure we handle scrollbars correctly - nsIBox* child = popupChild->GetChildBox(); + // if the menu has just been opened, or its size changed, position + // the popup. The flag that the popup checks in the HasOpenChanged + // method will get cleared in AdjustView which is called below. + if (IsOpen() && (sizeChanged || mPopupFrame->HasOpenChanged())) + mPopupFrame->SetPopupPosition(this); - nsRect bounds(popupChild->GetRect()); + // is the new size too small? Make sure we handle scrollbars correctly + nsIBox* child = mPopupFrame->GetChildBox(); + + nsRect bounds(mPopupFrame->GetRect()); nsCOMPtr scrollframe(do_QueryInterface(child)); if (scrollframe && scrollframe->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_AUTO) { if (bounds.height < prefSize.height) { // layout the child - popupChild->Layout(aState); + mPopupFrame->Layout(aState); nsMargin scrollbars = scrollframe->GetActualScrollbarSizes(); if (bounds.width < prefSize.width + scrollbars.left + scrollbars.right) { bounds.width += scrollbars.left + scrollbars.right; - //printf("Width=%d\n",width); - popupChild->SetBounds(aState, bounds); + mPopupFrame->SetBounds(aState, bounds); } } } - + // layout the child - popupChild->Layout(aState); - - // Only size the popups view if open. - if (mMenuOpen) { - nsIView* view = popupChild->GetView(); - nsRect r(0, 0, bounds.width, bounds.height); - view->GetViewManager()->ResizeView(view, r); - } - + mPopupFrame->Layout(aState); + mPopupFrame->AdjustView(); } - SyncLayout(aState); - return rv; } @@ -1114,7 +741,8 @@ nsMenuFrame::SetDebug(nsBoxLayoutState& aState, PRBool aDebug) if (debugChanged) { nsBoxFrame::SetDebug(aState, aDebug); - SetDebug(aState, mPopupFrames.FirstChild(), aDebug); + if (mPopupFrame) + SetDebug(aState, mPopupFrame, aDebug); } return NS_OK; @@ -1137,189 +765,44 @@ nsMenuFrame::SetDebug(nsBoxLayoutState& aState, nsIFrame* aList, PRBool aDebug) } #endif -void -nsMenuFrame::ConvertPosition(nsIContent* aPopupElt, nsString& aAnchor, nsString& aAlign) -{ - static nsIContent::AttrValuesArray strings[] = - {&nsGkAtoms::_empty, &nsGkAtoms::before_start, &nsGkAtoms::before_end, - &nsGkAtoms::after_start, &nsGkAtoms::after_end, &nsGkAtoms::start_before, - &nsGkAtoms::start_after, &nsGkAtoms::end_before, &nsGkAtoms::end_after, - &nsGkAtoms::overlap, nsnull}; - - switch (aPopupElt->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::position, - strings, eCaseMatters)) { - case nsIContent::ATTR_MISSING: - case 0: - return; - case 1: - aAnchor.AssignLiteral("topleft"); - aAlign.AssignLiteral("bottomleft"); - break; - case 2: - aAnchor.AssignLiteral("topright"); - aAlign.AssignLiteral("bottomright"); - break; - case 3: - aAnchor.AssignLiteral("bottomleft"); - aAlign.AssignLiteral("topleft"); - break; - case 4: - aAnchor.AssignLiteral("bottomright"); - aAlign.AssignLiteral("topright"); - break; - case 5: - aAnchor.AssignLiteral("topleft"); - aAlign.AssignLiteral("topright"); - break; - case 6: - aAnchor.AssignLiteral("bottomleft"); - aAlign.AssignLiteral("bottomright"); - break; - case 7: - aAnchor.AssignLiteral("topright"); - aAlign.AssignLiteral("topleft"); - break; - case 8: - aAnchor.AssignLiteral("bottomright"); - aAlign.AssignLiteral("bottomleft"); - break; - case 9: - aAnchor.AssignLiteral("topleft"); - aAlign.AssignLiteral("topleft"); - break; - } -} - -void -nsMenuFrame::RePositionPopup(nsBoxLayoutState& aState) -{ - nsPresContext* presContext = aState.PresContext(); - - // Sync up the view. - nsIFrame* frame = mPopupFrames.FirstChild(); - nsMenuPopupFrame* menuPopup = (nsMenuPopupFrame*)frame; - if (mMenuOpen && menuPopup) { - nsIContent* menuPopupContent = menuPopup->GetContent(); - nsAutoString popupAnchor, popupAlign; - - menuPopupContent->GetAttr(kNameSpaceID_None, nsGkAtoms::popupanchor, popupAnchor); - menuPopupContent->GetAttr(kNameSpaceID_None, nsGkAtoms::popupalign, popupAlign); - - ConvertPosition(menuPopupContent, popupAnchor, popupAlign); - - PRBool onMenuBar = PR_TRUE; - if (mMenuParent) - mMenuParent->IsMenuBar(onMenuBar); - - if (onMenuBar) { - if (popupAnchor.IsEmpty()) - popupAnchor.AssignLiteral("bottomleft"); - if (popupAlign.IsEmpty()) - popupAlign.AssignLiteral("topleft"); - } - else { - if (popupAnchor.IsEmpty()) - popupAnchor.AssignLiteral("topright"); - if (popupAlign.IsEmpty()) - popupAlign.AssignLiteral("topleft"); - } - - menuPopup->SyncViewWithFrame(presContext, popupAnchor, popupAlign, this, -1, -1); - } -} - -NS_IMETHODIMP -nsMenuFrame::ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag) -{ - nsIFrame* frame = mPopupFrames.FirstChild(); - if (frame) { - nsMenuPopupFrame* popup = (nsMenuPopupFrame*)frame; - popup->ShortcutNavigation(aKeyEvent, aHandledFlag); - } - - return NS_OK; -} - -NS_IMETHODIMP -nsMenuFrame::KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag) -{ - nsIFrame* frame = mPopupFrames.FirstChild(); - if (frame) { - nsMenuPopupFrame* popup = (nsMenuPopupFrame*)frame; - popup->KeyboardNavigation(aKeyCode, aHandledFlag); - } - - return NS_OK; -} - -NS_IMETHODIMP -nsMenuFrame::Escape(PRBool& aHandledFlag) -{ - if (mMenuParent) { - mMenuParent->ClearRecentlyRolledUp(); - } - nsIFrame* frame = mPopupFrames.FirstChild(); - if (frame) { - nsMenuPopupFrame* popup = (nsMenuPopupFrame*)frame; - popup->Escape(aHandledFlag); - } - - return NS_OK; -} - - // // Enter // // Called when the user hits the / keys or presses the // shortcut key. If this is a leaf item, the item's action will be executed. -// If it is a submenu parent, open the submenu and select the first time. // In either case, do nothing if the item is disabled. // -NS_IMETHODIMP +nsMenuFrame* nsMenuFrame::Enter() { if (IsDisabled()) { #ifdef XP_WIN // behavior on Windows - close the popup chain - if (mMenuParent) - mMenuParent->DismissChain(); + if (mMenuParent) { + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) + pm->Rollup(); + } #endif // #ifdef XP_WIN // this menu item was disabled - exit - return NS_OK; + return nsnull; } - - if (!mMenuOpen) { + + if (!IsOpen()) { // The enter key press applies to us. if (!IsMenu() && mMenuParent) Execute(0); // Execute our event handler - else { - OpenMenu(PR_TRUE); - SelectFirstItem(); - } - - return NS_OK; + else + return this; } - nsIFrame* frame = mPopupFrames.FirstChild(); - if (frame) { - nsMenuPopupFrame* popup = (nsMenuPopupFrame*)frame; - popup->Enter(); - } - - return NS_OK; + return nsnull; } -NS_IMETHODIMP -nsMenuFrame::SelectFirstItem() +PRBool +nsMenuFrame::IsOpen() { - nsIFrame* frame = mPopupFrames.FirstChild(); - if (frame) { - nsMenuPopupFrame* popup = (nsMenuPopupFrame*)frame; - popup->SetCurrentMenuItem(popup->GetNextMenuItem(nsnull)); - } - - return NS_OK; + return mPopupFrame && mPopupFrame->IsOpen(); } PRBool @@ -1333,32 +816,22 @@ nsMenuFrame::Notify(nsITimer* aTimer) { // Our timer has fired. if (aTimer == mOpenTimer.get()) { - if (!mMenuOpen && mMenuParent) { + mOpenTimer = nsnull; + + if (!IsOpen() && mMenuParent) { // make sure we didn't open a context menu in the meantime // (i.e. the user right-clicked while hovering over a submenu). - // However, also make sure that we're not the context menu itself, - // to allow context submenus to open. - nsIMenuParent *ctxMenu = nsMenuFrame::GetContextMenu(); - PRBool parentIsContextMenu = PR_FALSE; - - if (ctxMenu) - mMenuParent->GetIsContextMenu(parentIsContextMenu); - - if (ctxMenu == nsnull || parentIsContextMenu) { - if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menuactive, + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) { + if ((!pm->HasContextMenu(nsnull) || mMenuParent->IsContextMenu()) && + mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menuactive, nsGkAtoms::_true, eCaseMatters)) { - // We're still the active menu. Make sure all submenus/timers are closed - // before opening this one - mMenuParent->KillPendingTimers(); - OpenMenu(PR_TRUE); + OpenMenu(PR_FALSE); } } } - mOpenTimer->Cancel(); - mOpenTimer = nsnull; } - - mOpenTimer = nsnull; + return NS_OK; } @@ -1397,11 +870,11 @@ nsMenuFrame::UpdateMenuType(nsPresContext* aPresContext) /* update checked-ness for type="checkbox" and type="radio" */ void -nsMenuFrame::UpdateMenuSpecialState(nsPresContext* aPresContext) { +nsMenuFrame::UpdateMenuSpecialState(nsPresContext* aPresContext) +{ PRBool newChecked = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked, nsGkAtoms::_true, eCaseMatters); - if (newChecked == mChecked) { /* checked state didn't change */ @@ -1438,43 +911,27 @@ nsMenuFrame::UpdateMenuSpecialState(nsPresContext* aPresContext) { */ /* walk siblings, looking for the other checked item with the same name */ - nsIMenuFrame *sibMenu; - nsMenuType sibType; - nsAutoString sibGroup; - PRBool sibChecked; - // get the first sibling in this menu popup. This frame may be it, and if we're // being called at creation time, this frame isn't yet in the parent's child list. // All I'm saying is that this may fail, but it's most likely alright. nsIFrame* sib = GetParent()->GetFirstChild(nsnull); - if ( !sib ) - return; - // XXX - egcs 1.1.2 & gcc 2.95.x -Oy builds, where y > 1, - // are known to break if we declare nsCOMPtrs inside this loop. - // Moving the declaration out of the loop works around this problem. - // http://bugzilla.mozilla.org/show_bug.cgi?id=80988 - - do { - if (NS_FAILED(sib->QueryInterface(NS_GET_IID(nsIMenuFrame), - (void **)&sibMenu))) - continue; - - if (sibMenu != (nsIMenuFrame *)this && // correct way to check? - (sibMenu->GetMenuType(sibType), sibType == eMenuType_Radio) && - (sibMenu->MenuIsChecked(sibChecked), sibChecked) && - (sibMenu->GetRadioGroupName(sibGroup), sibGroup == mGroupName)) { - - /* uncheck the old item */ - sib->GetContent()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked, - PR_TRUE); - - /* XXX in DEBUG, check to make sure that there aren't two checked items */ - return; + while (sib) { + if (sib != this && sib->GetType() == nsGkAtoms::menuFrame) { + nsMenuFrame* menu = NS_STATIC_CAST(nsMenuFrame*, sib); + if (menu->GetMenuType() == eMenuType_Radio && + menu->IsChecked() && + (menu->GetRadioGroupName() == mGroupName)) { + /* uncheck the old item */ + sib->GetContent()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked, + PR_TRUE); + /* XXX in DEBUG, check to make sure that there aren't two checked items */ + return; + } } - } while ((sib = sib->GetNextSibling()) != nsnull); - + sib = sib->GetNextSibling(); + } } void @@ -1644,237 +1101,25 @@ nsMenuFrame::Execute(nsGUIEvent *aEvent) mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::checked, NS_LITERAL_STRING("true"), PR_TRUE); ENSURE_TRUE(weakFrame.IsAlive()); - } - /* the AttributeChanged code will update all the internal state */ - } - } - - // Temporarily disable rollup events on this menu. This is - // to suppress this menu getting removed in the case where - // the oncommand handler opens a dialog, etc. - if ( nsMenuDismissalListener::sInstance ) { - nsMenuDismissalListener::sInstance->EnableListener(PR_FALSE); - } - - // Get our own content node and hold on to it to keep it from going away. - nsCOMPtr content = mContent; - - // Deselect ourselves. - SelectMenu(PR_FALSE); - ENSURE_TRUE(weakFrame.IsAlive()); - - // Now hide all of the open menus. - if (mMenuParent) { - mMenuParent->HideChain(); - - // Since menu was not dismissed via click outside menu - // we don't want to keep track of this rollup. - // Otherwise, we keep track so that the same click - // won't both dismiss and then reopen a menu. - mMenuParent->ClearRecentlyRolledUp(); - } - - - nsEventStatus status = nsEventStatus_eIgnore; - // Create a trusted event if the triggering event was trusted, or if - // we're called from chrome code (since at least one of our caller - // passes in a null event). - nsXULCommandEvent event(aEvent ? NS_IS_TRUSTED_EVENT(aEvent) : - nsContentUtils::IsCallerChrome(), - NS_XUL_COMMAND, nsnull); - if (aEvent && (aEvent->eventStructType == NS_MOUSE_EVENT || - aEvent->eventStructType == NS_KEY_EVENT || - aEvent->eventStructType == NS_ACCESSIBLE_EVENT)) { - - event.isShift = NS_STATIC_CAST(nsInputEvent *, aEvent)->isShift; - event.isControl = NS_STATIC_CAST(nsInputEvent *, aEvent)->isControl; - event.isAlt = NS_STATIC_CAST(nsInputEvent *, aEvent)->isAlt; - event.isMeta = NS_STATIC_CAST(nsInputEvent *, aEvent)->isMeta; - } - - // 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. - nsPresContext* presContext = PresContext(); - nsCOMPtr kungFuDeathGrip = presContext->GetViewManager(); - nsCOMPtr shell = presContext->GetPresShell(); - if (shell) { - shell->HandleDOMEventWithTarget(mContent, &event, &status); - ENSURE_TRUE(weakFrame.IsAlive()); - } - - if (mMenuParent) { - mMenuParent->DismissChain(); - } - - // Re-enable rollup events on this menu. - if ( nsMenuDismissalListener::sInstance ) { - nsMenuDismissalListener::sInstance->EnableListener(PR_TRUE); - } -} - -PRBool -nsMenuFrame::OnCreate() -{ - nsEventStatus status = nsEventStatus_eIgnore; - nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_SHOWING, nsnull, - nsMouseEvent::eReal); - - nsCOMPtr child; - GetMenuChildrenElement(getter_AddRefs(child)); - - nsresult rv = NS_OK; - - nsCOMPtr shell = PresContext()->GetPresShell(); - if (shell) { - if (child) { - rv = shell->HandleDOMEventWithTarget(child, &event, &status); - } - else { - rv = shell->HandleDOMEventWithTarget(mContent, &event, &status); - } - } - - if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault ) - return PR_FALSE; - - // The menu is going to show, and the create handler has executed. - // We should now walk all of our menu item children, checking to see if any - // of them has a command attribute. If so, then several attributes must - // potentially be updated. - if (child) { - nsCOMPtr domDoc(do_QueryInterface(child->GetDocument())); - - PRUint32 count = child->GetChildCount(); - for (PRUint32 i = 0; i < count; i++) { - nsCOMPtr grandChild = child->GetChildAt(i); - - if (grandChild->Tag() == nsGkAtoms::menuitem) { - // 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 commandElt; - domDoc->GetElementById(command, getter_AddRefs(commandElt)); - nsCOMPtr commandContent(do_QueryInterface(commandElt)); - - if ( commandContent ) { - nsAutoString commandAttr; - // The menu's disabled state needs to be updated to match the command. - if (commandContent->GetAttr(kNameSpaceID_None, nsGkAtoms::disabled, commandAttr)) - grandChild->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, commandAttr, 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::checked, commandAttr)) - grandChild->SetAttr(kNameSpaceID_None, nsGkAtoms::checked, commandAttr, PR_TRUE); - - if (commandContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, commandAttr)) - grandChild->SetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, commandAttr, PR_TRUE); - - if (commandContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, commandAttr)) - grandChild->SetAttr(kNameSpaceID_None, nsGkAtoms::label, commandAttr, PR_TRUE); - } - } } } } - return PR_TRUE; -} - -PRBool -nsMenuFrame::OnCreated() -{ - nsEventStatus status = nsEventStatus_eIgnore; - nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_SHOWN, nsnull, - nsMouseEvent::eReal); - - nsCOMPtr child; - GetMenuChildrenElement(getter_AddRefs(child)); - - nsresult rv = NS_OK; - nsCOMPtr shell = PresContext()->GetPresShell(); - if (shell) { - if (child) { - rv = shell->HandleDOMEventWithTarget(child, &event, &status); - } - else { - rv = shell->HandleDOMEventWithTarget(mContent, &event, &status); - } - } - - if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault ) - return PR_FALSE; - return PR_TRUE; -} - -PRBool -nsMenuFrame::OnDestroy() -{ - nsEventStatus status = nsEventStatus_eIgnore; - nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_HIDING, nsnull, - nsMouseEvent::eReal); - - nsCOMPtr child; - GetMenuChildrenElement(getter_AddRefs(child)); - - nsresult rv = NS_OK; - nsCOMPtr shell = PresContext()->GetPresShell(); - if (shell) { - if (child) { - rv = shell->HandleDOMEventWithTarget(child, &event, &status); - } - else { - rv = shell->HandleDOMEventWithTarget(mContent, &event, &status); - } - } - - if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault ) - return PR_FALSE; - return PR_TRUE; -} - -PRBool -nsMenuFrame::OnDestroyed() -{ - nsEventStatus status = nsEventStatus_eIgnore; - nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_HIDDEN, nsnull, - nsMouseEvent::eReal); - - nsCOMPtr child; - GetMenuChildrenElement(getter_AddRefs(child)); - - nsresult rv = NS_OK; - nsCOMPtr shell = PresContext()->GetPresShell(); - if (shell) { - if (child) { - rv = shell->HandleDOMEventWithTarget(child, &event, &status); - } - else { - rv = shell->HandleDOMEventWithTarget(mContent, &event, &status); - } - } - - if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault ) - return PR_FALSE; - return PR_TRUE; + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm && mMenuParent) + pm->ExecuteMenu(mContent, aEvent); } NS_IMETHODIMP nsMenuFrame::RemoveFrame(nsIAtom* aListName, nsIFrame* aOldFrame) { - nsresult rv; + nsresult rv = NS_OK; - if (mPopupFrames.ContainsFrame(aOldFrame)) { + if (mPopupFrame == aOldFrame) { // Go ahead and remove this frame. - mPopupFrames.DestroyFrame(aOldFrame); + mPopupFrame->Destroy(); + mPopupFrame = nsnull; PresContext()->PresShell()-> FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_HAS_DIRTY_CHILDREN); @@ -1893,13 +1138,11 @@ nsMenuFrame::InsertFrames(nsIAtom* aListName, { nsresult rv; - nsIMenuParent *menuPar; - if (aFrameList && NS_SUCCEEDED(CallQueryInterface(aFrameList, &menuPar))) { - NS_ASSERTION(aFrameList->IsBoxFrame(),"Popup is not a box!!!"); - mPopupFrames.InsertFrames(nsnull, nsnull, aFrameList); + if (!mPopupFrame && aFrameList->GetType() == nsGkAtoms::menuPopupFrame) { + mPopupFrame = NS_STATIC_CAST(nsMenuPopupFrame *, aFrameList); #ifdef DEBUG_LAYOUT - nsBoxLayoutState state(GetPresContext()); + nsBoxLayoutState state(PresContext()); SetDebug(state, aFrameList, mState & NS_STATE_CURRENTLY_IN_DEBUG); #endif PresContext()->PresShell()-> @@ -1922,13 +1165,11 @@ nsMenuFrame::AppendFrames(nsIAtom* aListName, nsresult rv; - nsIMenuParent *menuPar; - if (aFrameList && NS_SUCCEEDED(CallQueryInterface(aFrameList, &menuPar))) { - NS_ASSERTION(aFrameList->IsBoxFrame(),"Popup is not a box!!!"); + if (!mPopupFrame && aFrameList->GetType() == nsGkAtoms::menuPopupFrame) { + mPopupFrame = NS_STATIC_CAST(nsMenuPopupFrame *, aFrameList); - mPopupFrames.AppendFrames(nsnull, aFrameList); #ifdef DEBUG_LAYOUT - nsBoxLayoutState state(GetPresContext()); + nsBoxLayoutState state(PresContext()); SetDebug(state, aFrameList, mState & NS_STATE_CURRENTLY_IN_DEBUG); #endif PresContext()->PresShell()-> @@ -1942,44 +1183,6 @@ nsMenuFrame::AppendFrames(nsIAtom* aListName, return rv; } -class nsASyncMenuGeneration : public nsIReflowCallback -{ -public: - nsASyncMenuGeneration(nsIFrame* aFrame) - : mWeakFrame(aFrame) - { - nsIContent* content = aFrame ? aFrame->GetContent() : nsnull; - mDocument = content ? content->GetCurrentDoc() : nsnull; - if (mDocument) { - mDocument->BlockOnload(); - } - } - - virtual PRBool ReflowFinished() { - PRBool shouldFlush = PR_FALSE; - nsIFrame* frame = mWeakFrame.GetFrame(); - if (frame) { - nsBoxLayoutState state(frame->PresContext()); - if (!frame->IsCollapsed(state)) { - nsIMenuFrame* imenu = nsnull; - CallQueryInterface(frame, &imenu); - if (imenu) { - imenu->MarkAsGenerated(); - shouldFlush = PR_TRUE; - } - } - } - if (mDocument) { - mDocument->UnblockOnload(PR_FALSE); - } - delete this; - return shouldFlush; - } - - nsWeakFrame mWeakFrame; - nsCOMPtr mDocument; -}; - PRBool nsMenuFrame::SizeToPopup(nsBoxLayoutState& aState, nsSize& aSize) { @@ -1987,24 +1190,9 @@ nsMenuFrame::SizeToPopup(nsBoxLayoutState& aState, nsSize& aSize) nsSize tmpSize(-1, 0); nsIBox::AddCSSPrefSize(aState, this, tmpSize); if (tmpSize.width == -1 && GetFlex(aState) == 0) { - nsIFrame* frame = mPopupFrames.FirstChild(); - if (!frame) { - nsCOMPtr child; - GetMenuChildrenElement(getter_AddRefs(child)); - if (child && - !nsContentUtils::HasNonEmptyAttr(child, kNameSpaceID_None, - nsGkAtoms::menugenerated)) { - nsIReflowCallback* cb = new nsASyncMenuGeneration(this); - if (cb) { - PresContext()->PresShell()->PostReflowCallback(cb); - } - } + if (!mPopupFrame) return PR_FALSE; - } - - NS_ASSERTION(frame->IsBoxFrame(), "popupChild is not box!!"); - - tmpSize = frame->GetPrefSize(aState); + tmpSize = mPopupFrame->GetPrefSize(aState); aSize.width = tmpSize.width; return PR_TRUE; } @@ -2036,20 +1224,15 @@ nsMenuFrame::GetPrefSize(nsBoxLayoutState& aState) NS_IMETHODIMP nsMenuFrame::GetActiveChild(nsIDOMElement** aResult) { - nsIFrame* frame = mPopupFrames.FirstChild(); - nsMenuPopupFrame* menuPopup = (nsMenuPopupFrame*)frame; - if (!frame) + if (!mPopupFrame) return NS_ERROR_FAILURE; - nsIMenuFrame* menuFrame = menuPopup->GetCurrentMenuItem(); - + nsMenuFrame* menuFrame = mPopupFrame->GetCurrentMenuItem(); if (!menuFrame) { *aResult = nsnull; } else { - nsIFrame* f; - menuFrame->QueryInterface(NS_GET_IID(nsIFrame), (void**)&f); - nsCOMPtr elt(do_QueryInterface(f->GetContent())); + nsCOMPtr elt(do_QueryInterface(menuFrame->GetContent())); *aResult = elt; NS_IF_ADDREF(*aResult); } @@ -2060,95 +1243,31 @@ nsMenuFrame::GetActiveChild(nsIDOMElement** aResult) NS_IMETHODIMP nsMenuFrame::SetActiveChild(nsIDOMElement* aChild) { - nsIFrame* frame = mPopupFrames.FirstChild(); - nsMenuPopupFrame* menuPopup = (nsMenuPopupFrame*)frame; - if (!frame) + if (!mPopupFrame) return NS_ERROR_FAILURE; if (!aChild) { // Remove the current selection - menuPopup->SetCurrentMenuItem(nsnull); + mPopupFrame->ChangeMenuItem(nsnull, PR_FALSE); return NS_OK; } nsCOMPtr child(do_QueryInterface(aChild)); - + nsIFrame* kid = PresContext()->PresShell()->GetPrimaryFrameFor(child); - if (!kid) - return NS_ERROR_FAILURE; - nsIMenuFrame *menuFrame; - nsresult rv = CallQueryInterface(kid, &menuFrame); - if (NS_FAILED(rv)) - return rv; - menuPopup->SetCurrentMenuItem(menuFrame); + if (kid && kid->GetType() == nsGkAtoms::menuFrame) + mPopupFrame->ChangeMenuItem(NS_STATIC_CAST(nsMenuFrame *, kid), PR_FALSE); return NS_OK; } nsIScrollableView* nsMenuFrame::GetScrollableView() { - if (!mPopupFrames.FirstChild()) + if (!mPopupFrame) return nsnull; - nsMenuPopupFrame* popup = (nsMenuPopupFrame*) mPopupFrames.FirstChild(); - nsIFrame* childFrame = popup->GetFirstChild(nsnull); - if (childFrame) { - return popup->GetScrollableView(childFrame); - } - return nsnull; -} - -/* Need to figure out what this does. -NS_IMETHODIMP -nsMenuFrame::GetBoxInfo(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsBoxInfo& aSize) -{ - nsresult rv = nsBoxFrame::GetBoxInfo(aPresContext, aReflowState, aSize); - nsCOMPtr menulist(do_QueryInterface(mContent)); - if (menulist) { - nsCalculatedBoxInfo boxInfo(this); - boxInfo.prefSize.width = NS_UNCONSTRAINEDSIZE; - boxInfo.prefSize.height = NS_UNCONSTRAINEDSIZE; - boxInfo.flex = 0; - GetRedefinedMinPrefMax(aPresContext, this, boxInfo); - if (boxInfo.prefSize.width == NS_UNCONSTRAINEDSIZE && - boxInfo.prefSize.height == NS_UNCONSTRAINEDSIZE && - boxInfo.flex == 0) { - nsIFrame* frame = mPopupFrames.FirstChild(); - if (!frame) { - MarkAsGenerated(); - frame = mPopupFrames.FirstChild(); - } - - nsCalculatedBoxInfo childInfo(frame); - frame->GetBoxInfo(aPresContext, aReflowState, childInfo); - GetRedefinedMinPrefMax(aPresContext, this, childInfo); - aSize.prefSize.width = childInfo.prefSize.width; - } - - // This retrieval guarantess that the selectedItem will - // be set before we lay out. - nsCOMPtr element; - menulist->GetSelectedItem(getter_AddRefs(element)); - } - return rv; -} -*/ - -nsIMenuParent* -nsMenuFrame::GetContextMenu() -{ - if (!nsMenuDismissalListener::sInstance) - return nsnull; - - nsIMenuParent *menuParent = - nsMenuDismissalListener::sInstance->GetCurrentMenuParent(); - if (!menuParent) - return nsnull; - - PRBool isContextMenu; - menuParent->GetIsContextMenu(isContextMenu); - if (isContextMenu) - return menuParent; - + nsIFrame* childFrame = mPopupFrame->GetFirstChild(nsnull); + if (childFrame) + return mPopupFrame->GetScrollableView(childFrame); return nsnull; } diff --git a/mozilla/layout/xul/base/src/nsMenuFrame.h b/mozilla/layout/xul/base/src/nsMenuFrame.h index 0bee50a141f..08b0fed02f6 100644 --- a/mozilla/layout/xul/base/src/nsMenuFrame.h +++ b/mozilla/layout/xul/base/src/nsMenuFrame.h @@ -49,9 +49,10 @@ #include "nsBoxFrame.h" #include "nsFrameList.h" +#include "nsGkAtoms.h" #include "nsIMenuParent.h" #include "nsIMenuFrame.h" -#include "nsMenuDismissalListener.h" +#include "nsXULPopupManager.h" #include "nsITimer.h" #include "nsISupportsArray.h" #include "nsIDOMText.h" @@ -61,11 +62,21 @@ nsIFrame* NS_NewMenuFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, PRUint32 aFlags); class nsMenuBarFrame; -class nsMenuPopupFrame; class nsIScrollableView; #define NS_STATE_ACCELTEXT_IS_DERIVED NS_STATE_BOX_CHILD_RESERVED +// the type of menuitem +enum nsMenuType { + // a normal menuitem where a command is carried out when activated + eMenuType_Normal = 0, + // a menuitem with a checkmark that toggles when activated + eMenuType_Checkbox = 1, + // a radio menuitem where only one of it and its siblings with the same + // name attribute can be checked at a time + eMenuType_Radio = 2 +}; + class nsMenuFrame; /** @@ -93,7 +104,7 @@ private: }; /** - * @note *** Methods marked with '@see comment ***' may cause the frame to be + * @note *** Methods marked with '@see comment above ***' may cause the frame to be * deleted during the method call. Be careful whenever using those * methods. */ @@ -122,14 +133,14 @@ public: NS_IMETHOD IsActive(PRBool& aResult) { aResult = PR_TRUE; return NS_OK; } - // The following four methods are all overridden so that the menu children - // can be stored in a separate list (so that they don't impact reflow of the - // actual menu item at all). + // The following methods are all overridden so that the menupopup + // can be stored in a separate list, so that it doesn't impact reflow of the + // actual menu item at all. virtual nsIFrame* GetFirstChild(nsIAtom* aListName) const; NS_IMETHOD SetInitialChildList(nsIAtom* aListName, nsIFrame* aChildList); virtual nsIAtom* GetAdditionalChildListName(PRInt32 aIndex) const; - virtual void Destroy(); // @see comment *** + virtual void Destroy(); // @see comment above *** // Overridden to prevent events from going to children of the menu. NS_IMETHOD BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder, @@ -138,7 +149,7 @@ public: NS_IMETHOD HandleEvent(nsPresContext* aPresContext, nsGUIEvent* aEvent, - nsEventStatus* aEventStatus); // @see comment *** + nsEventStatus* aEventStatus); // @see comment above *** NS_IMETHOD AppendFrames(nsIAtom* aListName, nsIFrame* aFrameList); @@ -150,36 +161,35 @@ public: NS_IMETHOD RemoveFrame(nsIAtom* aListName, nsIFrame* aOldFrame); - // nsIMenuFrame Interface + virtual nsIAtom* GetType() const { return nsGkAtoms::menuFrame; } - NS_IMETHOD ActivateMenu(PRBool aActivateFlag); // @see comment *** - NS_IMETHOD SelectMenu(PRBool aActivateFlag); // @see comment *** - NS_IMETHOD OpenMenu(PRBool aActivateFlag); // @see comment *** + NS_IMETHOD SelectMenu(PRBool aActivateFlag); // @see comment above *** + + /** + * NOTE: OpenMenu will open the menu synchronously. Don't call this if a frame + * is manipulated afterwards without checking to make sure it is still alive. + * All current calls to OpenMenu do not adjust the frame. + */ + void OpenMenu(PRBool aSelectFirstItem); + // CloseMenu closes the menu asynchronously + void CloseMenu(PRBool aDeselectMenu); + + PRBool IsChecked() { return mChecked; } - NS_IMETHOD MenuIsOpen(PRBool& aResult) { aResult = IsOpen(); return NS_OK; } - NS_IMETHOD MenuIsContainer(PRBool& aResult) { aResult = IsMenu(); return NS_OK; } - NS_IMETHOD MenuIsChecked(PRBool& aResult) { aResult = mChecked; return NS_OK; } - NS_IMETHOD MenuIsDisabled(PRBool& aResult) { aResult = IsDisabled(); return NS_OK; } - NS_IMETHOD GetActiveChild(nsIDOMElement** aResult); - NS_IMETHOD SetActiveChild(nsIDOMElement* aChild); // @see comment *** + NS_IMETHOD SetActiveChild(nsIDOMElement* aChild); // @see comment above *** - NS_IMETHOD UngenerateMenu(); // @see comment *** - - NS_IMETHOD SelectFirstItem(); // @see comment *** - - NS_IMETHOD Escape(PRBool& aHandledFlag); // @see comment *** - NS_IMETHOD Enter(); // @see comment *** - NS_IMETHOD ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag); // @see comment *** - NS_IMETHOD KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag); // @see comment *** + // called when the Enter key is pressed while the menuitem is the current + // one in its parent popup. This will carry out the command attached to + // the menuitem. + nsMenuFrame* Enter(); NS_IMETHOD SetParent(const nsIFrame* aParent); virtual nsIMenuParent *GetMenuParent() { return mMenuParent; } - virtual nsIFrame *GetMenuChild() { return mPopupFrames.FirstChild(); } - NS_IMETHOD GetRadioGroupName(nsString &aName) { aName = mGroupName; return NS_OK; } - NS_IMETHOD GetMenuType(nsMenuType &aType) { aType = mType; return NS_OK; } - NS_IMETHOD MarkAsGenerated(); + const nsAString& GetRadioGroupName() { return mGroupName; } + nsMenuType GetMenuType() { return mType; } + nsMenuPopupFrame* GetPopup() { return mPopupFrame; } // nsIScrollableViewProvider methods @@ -189,12 +199,26 @@ public: nsresult DestroyPopupFrames(nsPresContext* aPresContext); - PRBool IsOpen() { return mMenuOpen; } - PRBool IsMenu(); + virtual PRBool IsOnMenuBar() { return mMenuParent && mMenuParent->IsMenuBar(); } + virtual PRBool IsOnActiveMenuBar() { return IsOnMenuBar() && mMenuParent->IsActive(); } + virtual PRBool IsOpen(); + virtual PRBool IsMenu(); PRBool IsDisabled(); PRBool IsGenerated(); - NS_IMETHOD ToggleMenuState(); // @see comment *** + void ToggleMenuState(); + // indiciate that the menu's popup has just been opened, so that the menu + // can update its open state. This method modifies the open attribute on + // the menu, so the frames could be gone after this call + void PopupOpened(); + // indiciate that the menu's popup has just been closed, so that the menu + // can update its open state. The menu should be unhighlighted if + // aDeselectedMenu is true. + void PopupClosed(PRBool aDeselectMenu); + + // returns true if this is a menu on another menu popup. A menu is a submenu + // if it has a parent popup or menupopup. + PRBool IsOnMenu() { return mMenuParent && mMenuParent->IsMenu(); } void SetIsMenu(PRBool aIsMenu) { mIsMenu = aIsMenu; } #ifdef DEBUG @@ -206,44 +230,27 @@ public: static PRBool IsSizedToPopup(nsIContent* aContent, PRBool aRequireAlways); - static nsIMenuParent *GetContextMenu(); - protected: friend class nsMenuTimerMediator; - - virtual void RePositionPopup(nsBoxLayoutState& aState); - - void - ConvertPosition(nsIContent* aPopupElt, nsString& aAnchor, nsString& aAlign); - friend class nsASyncMenuInitialization; - void UpdateMenuType(nsPresContext* aPresContext); // @see comment *** - void UpdateMenuSpecialState(nsPresContext* aPresContext); // @see comment *** - void OpenMenuInternal(PRBool aActivateFlag); // @see comment *** - void GetMenuChildrenElement(nsIContent** aResult); + // set mMenuParent to the nearest enclosing menu bar or menupopup frame of + // aParent (or aParent itself). This is called when initializing the frame, + // so aParent should be the expected parent of this frame. + void InitMenuParent(nsIFrame* aParent); + + void UpdateMenuType(nsPresContext* aPresContext); // @see comment above *** + void UpdateMenuSpecialState(nsPresContext* aPresContext); // @see comment above *** // Examines the key node and builds the accelerator. void BuildAcceleratorText(); // Called to execute our command handler. - void Execute(nsGUIEvent *aEvent); // @see comment *** - - // Called as a hook just before the menu gets opened. - PRBool OnCreate(); // @see comment *** - - // Called as a hook just after the menu gets opened. - PRBool OnCreated(); // @see comment *** - - // Called as a hook just before the menu goes away. - PRBool OnDestroy(); // @see comment *** - - // Called as a hook just after the menu goes away. - PRBool OnDestroyed(); // @see comment *** + void Execute(nsGUIEvent *aEvent); // @see comment above *** NS_IMETHOD AttributeChanged(PRInt32 aNameSpaceID, nsIAtom* aAttribute, - PRInt32 aModType); // @see comment *** + PRInt32 aModType); // @see comment above *** virtual ~nsMenuFrame(); PRBool SizeToPopup(nsBoxLayoutState& aState, nsSize& aSize); @@ -254,22 +261,23 @@ protected: #endif NS_HIDDEN_(nsresult) Notify(nsITimer* aTimer); - nsFrameList mPopupFrames; PRPackedBool mIsMenu; // Whether or not we can even have children or not. - PRPackedBool mMenuOpen; - PRPackedBool mCreateHandlerSucceeded; // Did the create handler succeed? PRPackedBool mChecked; // are we checked? nsMenuType mType; nsIMenuParent* mMenuParent; // Our parent menu. + // the popup for this menu, owned + nsMenuPopupFrame* mPopupFrame; + + nsSize mLastPref; + // Reference to the mediator which wraps this frame. nsRefPtr mTimerMediator; nsCOMPtr mOpenTimer; nsString mGroupName; - nsSize mLastPref; //we load some display strings from platformKeys.properties only once static nsrefcnt gRefCnt; diff --git a/mozilla/layout/xul/base/src/nsMenuListener.cpp b/mozilla/layout/xul/base/src/nsMenuListener.cpp deleted file mode 100644 index ecb2f15cd1e..00000000000 --- a/mozilla/layout/xul/base/src/nsMenuListener.cpp +++ /dev/null @@ -1,281 +0,0 @@ -/* -*- 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 Communicator client code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Original Author: David W. Hyatt (hyatt@netscape.com) - * Dean Tessman - * Mark Hammond - * - * 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 "nsMenuListener.h" -#include "nsMenuBarFrame.h" -#include "nsIDOMKeyListener.h" -#include "nsIDOMEventTarget.h" -#include "nsIDOMEventListener.h" -#include "nsIDOMNSUIEvent.h" -#include "nsGUIEvent.h" - -// Drag & Drop, Clipboard -#include "nsIServiceManager.h" -#include "nsWidgetsCID.h" -#include "nsCOMPtr.h" -#include "nsIDOMKeyEvent.h" -#include "nsIDOMNSEvent.h" -#include "nsPresContext.h" -#include "nsIContent.h" -#include "nsIDOMNode.h" -#include "nsIDOMElement.h" - -#include "nsIEventStateManager.h" - -#include "nsISupportsArray.h" - -/* - * nsMenuListener implementation - */ - -NS_IMPL_ADDREF(nsMenuListener) -NS_IMPL_RELEASE(nsMenuListener) -NS_IMPL_QUERY_INTERFACE3(nsMenuListener, nsIDOMKeyListener, nsIDOMFocusListener, nsIDOMMouseListener) - - -//////////////////////////////////////////////////////////////////////// - -nsMenuListener::nsMenuListener(nsIMenuParent* aMenuParent) -{ - mMenuParent = aMenuParent; -} - -//////////////////////////////////////////////////////////////////////// -nsMenuListener::~nsMenuListener() -{ -} - - - -//////////////////////////////////////////////////////////////////////// -nsresult -nsMenuListener::KeyUp(nsIDOMEvent* aKeyEvent) -{ - aKeyEvent->StopPropagation(); - aKeyEvent->PreventDefault(); - - return NS_ERROR_BASE; // I am consuming event -} - -//////////////////////////////////////////////////////////////////////// -nsresult -nsMenuListener::KeyDown(nsIDOMEvent* aKeyEvent) -{ - 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 keyEvent = do_QueryInterface(aKeyEvent); - keyEvent->GetKeyCode(&theChar); - - if (theChar == (PRUint32)menuAccessKey) { - // No other modifiers can be down. - // Especially CTRL. CTRL+ALT == AltGR, and - // we'll fuck up on non-US enhanced 102-key - // keyboards if we don't check this. - 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. - mMenuParent->DismissChain(); - } - } - } - - // Since a menu was open, eat the event to keep other event - // listeners from becoming confused. - aKeyEvent->StopPropagation(); - aKeyEvent->PreventDefault(); - return NS_ERROR_BASE; // I am consuming event -} - -//////////////////////////////////////////////////////////////////////// -nsresult -nsMenuListener::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. - - //handlers shouldn't be triggered by non-trusted events. - nsCOMPtr domNSEvent = do_QueryInterface(aKeyEvent); - PRBool trustedEvent = PR_FALSE; - - if (domNSEvent) { - domNSEvent->GetIsTrusted(&trustedEvent); - } - - if (!trustedEvent) - return NS_OK; - - nsCOMPtr keyEvent = do_QueryInterface(aKeyEvent); - PRUint32 theChar; - keyEvent->GetKeyCode(&theChar); - PRBool handled = PR_FALSE; - - 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) { - // The navigation keys were pressed. User is moving around within - // the menus. - mMenuParent->KeyboardNavigation(theChar, handled); - } - else if (theChar == NS_VK_ESCAPE) { - // Close one level. - // Prevents us from getting destroyed by Escape(), we need to return to ourselves - NS_ADDREF_THIS(); - mMenuParent->Escape(handled); - NS_RELEASE_THIS(); - if (!handled) - mMenuParent->DismissChain(); - } - else if (theChar == NS_VK_TAB) - mMenuParent->DismissChain(); - else if (theChar == NS_VK_ENTER || - theChar == NS_VK_RETURN) { - // Open one level. - mMenuParent->Enter(); - } -#if !defined(XP_MAC) && !defined(XP_MACOSX) - else if (theChar == NS_VK_F10) { - // doesn't matter what modifier keys are down in Non-Mac platform - // if the menu bar is active and F10 is pressed - deactivate it - mMenuParent->DismissChain(); - } -#endif // !XP_MAC && !XP_MACOSX - else { - PRInt32 menuAccessKey = -1; - nsMenuBarListener::GetMenuAccessKey(&menuAccessKey); - if (menuAccessKey) { - // Do shortcut navigation. - // A letter was pressed. We want to see if a shortcut gets matched. If - // so, we'll know the menu got activated. - mMenuParent->ShortcutNavigation(keyEvent, handled); - } - } - - aKeyEvent->StopPropagation(); - aKeyEvent->PreventDefault(); - return NS_ERROR_BASE; // I am consuming event -} - -//////////////////////////////////////////////////////////////////////// - -nsresult -nsMenuListener::Focus(nsIDOMEvent* aEvent) -{ - return NS_OK; // means I am NOT consuming event -} - -//////////////////////////////////////////////////////////////////////// -nsresult -nsMenuListener::Blur(nsIDOMEvent* aEvent) -{ - - return NS_OK; // means I am NOT consuming event -} - -//////////////////////////////////////////////////////////////////////// -nsresult -nsMenuListener::MouseDown(nsIDOMEvent* aMouseEvent) -{ - return NS_OK; // means I am NOT consuming event -} - -//////////////////////////////////////////////////////////////////////// -nsresult -nsMenuListener::MouseUp(nsIDOMEvent* aMouseEvent) -{ - return NS_OK; // means I am NOT consuming event -} - -nsresult -nsMenuListener::MouseClick(nsIDOMEvent* aMouseEvent) -{ - return NS_OK; // means I am NOT consuming event -} - -//////////////////////////////////////////////////////////////////////// -nsresult -nsMenuListener::MouseDblClick(nsIDOMEvent* aMouseEvent) -{ - return NS_OK; // means I am NOT consuming event -} - -//////////////////////////////////////////////////////////////////////// -nsresult -nsMenuListener::MouseOver(nsIDOMEvent* aMouseEvent) -{ - return NS_OK; // means I am NOT consuming event -} - -//////////////////////////////////////////////////////////////////////// -nsresult -nsMenuListener::MouseOut(nsIDOMEvent* aMouseEvent) -{ - return NS_OK; // means I am NOT consuming event -} - -//////////////////////////////////////////////////////////////////////// -nsresult -nsMenuListener::HandleEvent(nsIDOMEvent* aEvent) -{ - return NS_OK; -} - - diff --git a/mozilla/layout/xul/base/src/nsMenuListener.h b/mozilla/layout/xul/base/src/nsMenuListener.h deleted file mode 100644 index 282d68b77e4..00000000000 --- a/mozilla/layout/xul/base/src/nsMenuListener.h +++ /dev/null @@ -1,81 +0,0 @@ -/* -*- 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 Communicator client code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * 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 ***** */ -#ifndef nsMenuListener_h__ -#define nsMenuListener_h__ - -#include "nsIDOMMouseMotionListener.h" -#include "nsIDOMMouseListener.h" -#include "nsIDOMKeyListener.h" -#include "nsIDOMFocusListener.h" -#include "nsIDOMEventTarget.h" - -class nsIMenuParent; -class nsPresContext; - -/** editor Implementation of the DragListener interface - */ -class nsMenuListener : public nsIDOMKeyListener, public nsIDOMFocusListener, public nsIDOMMouseListener -{ -public: - nsMenuListener(nsIMenuParent* aMenuParent); - - virtual ~nsMenuListener(); - - NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent); - - NS_IMETHOD KeyUp(nsIDOMEvent* aKeyEvent); - NS_IMETHOD KeyDown(nsIDOMEvent* aKeyEvent); - NS_IMETHOD KeyPress(nsIDOMEvent* aKeyEvent); - - NS_IMETHOD Focus(nsIDOMEvent* aEvent); - NS_IMETHOD Blur(nsIDOMEvent* aEvent); - - NS_IMETHOD MouseDown(nsIDOMEvent* aMouseEvent); - NS_IMETHOD MouseUp(nsIDOMEvent* aMouseEvent); - NS_IMETHOD MouseClick(nsIDOMEvent* aMouseEvent); - NS_IMETHOD MouseDblClick(nsIDOMEvent* aMouseEvent); - NS_IMETHOD MouseOver(nsIDOMEvent* aMouseEvent); - NS_IMETHOD MouseOut(nsIDOMEvent* aMouseEvent); - - NS_DECL_ISUPPORTS - -protected: - nsIMenuParent* mMenuParent; // The outermost object capturing events (either a menu bar or menupopup). -}; - - -#endif diff --git a/mozilla/layout/xul/base/src/nsMenuPopupFrame.cpp b/mozilla/layout/xul/base/src/nsMenuPopupFrame.cpp index e7d3ed16dfa..1c20269018f 100644 --- a/mozilla/layout/xul/base/src/nsMenuPopupFrame.cpp +++ b/mozilla/layout/xul/base/src/nsMenuPopupFrame.cpp @@ -44,7 +44,6 @@ #include "nsMenuPopupFrame.h" #include "nsGkAtoms.h" #include "nsIContent.h" -#include "nsContentUtils.h" #include "prtypes.h" #include "nsIAtom.h" #include "nsPresContext.h" @@ -54,7 +53,9 @@ #include "nsIViewManager.h" #include "nsWidgetsCID.h" #include "nsMenuFrame.h" -#include "nsIPopupSetFrame.h" +#include "nsMenuBarFrame.h" +#include "nsPopupSetFrame.h" +#include "nsEventDispatcher.h" #include "nsPIDOMWindow.h" #include "nsIDOMScreen.h" #include "nsIPresShell.h" @@ -62,7 +63,6 @@ #include "nsIDocument.h" #include "nsIDeviceContext.h" #include "nsRect.h" -#include "nsIDOMXULDocument.h" #include "nsILookAndFeel.h" #include "nsIComponentManager.h" #include "nsBoxLayoutState.h" @@ -73,7 +73,9 @@ #include "nsIDocShellTreeItem.h" #include "nsReadableUtils.h" #include "nsUnicharUtils.h" +#include "nsLayoutUtils.h" #include "nsCSSFrameConstructor.h" +#include "nsIEventStateManager.h" #include "nsIBoxLayout.h" #include "nsIPopupBoxObject.h" #include "nsIReflowCallback.h" @@ -83,24 +85,16 @@ const PRInt32 kMaxZ = 0x7fffffff; //XXX: Shouldn't there be a define somewhere for MaxInt for PRInt32 - -static nsIPopupSetFrame* +static nsPopupSetFrame* GetPopupSetFrame(nsPresContext* aPresContext) { nsIRootBox* rootBox = nsIRootBox::GetRootBox(aPresContext->PresShell()); if (!rootBox) return nsnull; - nsIFrame* popupSetFrame = rootBox->GetPopupSetFrame(); - if (!popupSetFrame) - return nsnull; - - nsIPopupSetFrame* popupSet = nsnull; - CallQueryInterface(popupSetFrame, &popupSet); - return popupSet; + return rootBox->GetPopupSetFrame(); } - // NS_NewMenuPopupFrame // // Wrapper for creating a new menu popup container @@ -111,42 +105,25 @@ NS_NewMenuPopupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) return new (aPresShell) nsMenuPopupFrame (aPresShell, aContext); } -NS_IMETHODIMP_(nsrefcnt) -nsMenuPopupFrame::AddRef(void) -{ - return NS_OK; -} - -NS_IMETHODIMP_(nsrefcnt) -nsMenuPopupFrame::Release(void) -{ - return NS_OK; -} - - -// -// QueryInterface -// -NS_INTERFACE_MAP_BEGIN(nsMenuPopupFrame) - NS_INTERFACE_MAP_ENTRY(nsIMenuParent) -NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame) - - // // nsMenuPopupFrame ctor // nsMenuPopupFrame::nsMenuPopupFrame(nsIPresShell* aShell, nsStyleContext* aContext) :nsBoxFrame(aShell, aContext), mCurrentMenu(nsnull), - mTimerMenu(nsnull), - mCloseTimer(nsnull), + mPopupAlignment(POPUPALIGNMENT_NONE), + mPopupAnchor(POPUPALIGNMENT_NONE), + mPopupType(ePopupTypePanel), + mIsOpen(PR_FALSE), + mIsOpenChanged(PR_FALSE), + mIsOpenPending(PR_FALSE), + mIsContextMenu(PR_FALSE), + mGeneratedChildren(PR_FALSE), mMenuCanOverlapOSBar(PR_FALSE), mShouldAutoPosition(PR_TRUE), - mShouldRollup(PR_TRUE), mConsumeRollupEvent(nsIPopupBoxObject::ROLLUP_DEFAULT), mInContentShell(PR_TRUE) { - SetIsContextMenu(PR_FALSE); // we're not a context menu by default } // ctor @@ -158,11 +135,6 @@ nsMenuPopupFrame::Init(nsIContent* aContent, nsresult rv = nsBoxFrame::Init(aContent, aParent, aPrevInFlow); NS_ENSURE_SUCCESS(rv, rv); - // Set up a mediator which can be used for callbacks on this frame. - mTimerMediator = new nsMenuPopupTimerMediator(this); - if (NS_UNLIKELY(!mTimerMediator)) - return NS_ERROR_OUT_OF_MEMORY; - nsPresContext* presContext = PresContext(); // lookup if we're allowed to overlap the OS bar (menubar/taskbar) from the @@ -172,29 +144,29 @@ nsMenuPopupFrame::Init(nsIContent* aContent, GetMetric(nsILookAndFeel::eMetric_MenusCanOverlapOSBar, tempBool); mMenuCanOverlapOSBar = tempBool; - rv = CreateViewForFrame(presContext, this, GetStyleContext(), PR_TRUE); + rv = CreateViewForFrame(presContext, this, GetStyleContext(), PR_TRUE, PR_TRUE); NS_ENSURE_SUCCESS(rv, rv); - // Now that we've made a view, remove it and insert it at the correct - // position in the view hierarchy (as the root view). We do this so that we - // can draw the menus outside the confines of the window. - nsIView* ourView = GetView(); - nsIViewManager* viewManager = ourView->GetViewManager(); - - // Remove the view from its old position. - viewManager->RemoveChild(ourView); - - // Reinsert ourselves as the root view with a maximum z-index. - nsIView* rootView; - viewManager->GetRootView(rootView); - viewManager->SetViewZIndex(ourView, PR_FALSE, kMaxZ); - viewManager->InsertChild(rootView, ourView, nsnull, PR_TRUE); - - // XXX Hack. The menu's view should float above all other views, + // XXX Hack. The popup's view should float above all other views, // so we use the nsIView::SetFloating() to tell the view manager // about that constraint. + nsIView* ourView = GetView(); + nsIViewManager* viewManager = ourView->GetViewManager(); viewManager->SetViewFloating(ourView, PR_TRUE); + mPopupType = ePopupTypePanel; + nsIDocument* doc = aContent->GetOwnerDoc(); + if (doc) { + PRInt32 namespaceID; + nsCOMPtr tag = doc->BindingManager()->ResolveTag(aContent, &namespaceID); + if (namespaceID == kNameSpaceID_XUL) { + if (tag == nsGkAtoms::menupopup || tag == nsGkAtoms::popup) + mPopupType = ePopupTypeMenu; + else if (tag == nsGkAtoms::tooltip) + mPopupType = ePopupTypeTooltip; + } + } + nsCOMPtr cont = PresContext()->GetContainer(); nsCOMPtr dsti = do_QueryInterface(cont); PRInt32 type = -1; @@ -202,17 +174,27 @@ nsMenuPopupFrame::Init(nsIContent* aContent, type == nsIDocShellTreeItem::typeChrome) mInContentShell = PR_FALSE; - // XXX make sure we are hidden (shouldn't this be done automatically?) - viewManager->SetViewVisibility(ourView, nsViewVisibility_kHide); - if (!ourView->HasWidget()) { + // To improve performance, create the widget for the popup only if it is not + // a leaf. Leaf popups such as menus will create their widgets later when + // the popup opens. + if (!IsLeaf() && !ourView->HasWidget()) { CreateWidgetForView(ourView); } - MoveToAttributePosition(); - return rv; } +void +nsMenuPopupFrame::EnsureWidget() +{ + nsIView* ourView = GetView(); + if (!ourView->HasWidget()) { + NS_ASSERTION(!mGeneratedChildren && !GetFirstChild(nsnull), + "Creating widget for MenuPopupFrame with children"); + CreateWidgetForView(ourView); + } +} + nsresult nsMenuPopupFrame::CreateWidgetForView(nsIView* aView) { @@ -242,6 +224,351 @@ nsMenuPopupFrame::CreateWidgetForView(nsIView* aView) return NS_OK; } +// this class is used for dispatching popupshowing events asynchronously. +class nsXULPopupShownEvent : public nsRunnable +{ +public: + nsXULPopupShownEvent(nsIContent *aPopup, nsPresContext* aPresContext) + : mPopup(aPopup), mPresContext(aPresContext) + { + } + + NS_IMETHOD Run() + { + nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_SHOWN, nsnull, nsMouseEvent::eReal); + return nsEventDispatcher::Dispatch(mPopup, mPresContext, &event); + } + +private: + nsCOMPtr mPopup; + nsRefPtr mPresContext; +}; + +NS_IMETHODIMP +nsMenuPopupFrame::SetInitialChildList(nsIAtom* aListName, + nsIFrame* aChildList) +{ + // unless the list is empty, indicate that children have been generated. + if (aChildList) + mGeneratedChildren = PR_TRUE; + return nsBoxFrame::SetInitialChildList(aListName, aChildList); +} + +void +nsMenuPopupFrame::AdjustView() +{ + if (mIsOpen) { + // if the popup has just opened, make sure the scrolled window is at 0,0 + if (mIsOpenChanged) { + nsIBox* child = GetChildBox(); + nsCOMPtr scrollframe(do_QueryInterface(child)); + if (scrollframe) + scrollframe->ScrollTo(nsPoint(0,0)); + } + + nsIView* view = GetView(); + nsIViewManager* viewManager = view->GetViewManager(); + nsRect rect = GetRect(); + rect.x = rect.y = 0; + viewManager->ResizeView(view, rect); + viewManager->SetViewVisibility(view, nsViewVisibility_kShow); + + nsPresContext* pc = PresContext(); + nsContainerFrame::SyncFrameViewProperties(pc, this, nsnull, view, 0); + + // fire popupshown event when the state has changed + if (mIsOpenChanged) { + mIsOpenChanged = PR_FALSE; + nsCOMPtr event = new nsXULPopupShownEvent(GetContent(), pc); + NS_DispatchToCurrentThread(event); + } + } +} + +void +nsMenuPopupFrame::InitPositionFromAnchorAlign(const nsAString& aAnchor, + const nsAString& aAlign) +{ + if (aAnchor.EqualsLiteral("topleft")) + mPopupAnchor = POPUPALIGNMENT_TOPLEFT; + else if (aAnchor.EqualsLiteral("topright")) + mPopupAnchor = POPUPALIGNMENT_TOPRIGHT; + else if (aAnchor.EqualsLiteral("bottomleft")) + mPopupAnchor = POPUPALIGNMENT_BOTTOMLEFT; + else if (aAnchor.EqualsLiteral("bottomright")) + mPopupAnchor = POPUPALIGNMENT_BOTTOMRIGHT; + else + mPopupAnchor = POPUPALIGNMENT_NONE; + + if (aAlign.EqualsLiteral("topleft")) + mPopupAlignment = POPUPALIGNMENT_TOPLEFT; + else if (aAlign.EqualsLiteral("topright")) + mPopupAlignment = POPUPALIGNMENT_TOPRIGHT; + else if (aAlign.EqualsLiteral("bottomleft")) + mPopupAlignment = POPUPALIGNMENT_BOTTOMLEFT; + else if (aAlign.EqualsLiteral("bottomright")) + mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT; + else + mPopupAlignment = POPUPALIGNMENT_NONE; +} + +void +nsMenuPopupFrame::InitializePopup(nsIContent* aAnchorContent, + const nsAString& aPosition, + PRInt32 aXPos, PRInt32 aYPos, + PRBool aAttributesOverride) +{ + EnsureWidget(); + + mIsOpenPending = PR_TRUE; + mAnchorContent = aAnchorContent; + mXPos = aXPos; + mYPos = aYPos; + + // if aAttributesOverride is true, then the popupanchor, popupalign and + // position attributes on the override those values passed in. + // If false, those attributes are only used if the values passed in are empty + if (aAnchorContent) { + nsAutoString anchor, align, position; + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::popupanchor, anchor); + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::popupalign, align); + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::position, position); + + if (aAttributesOverride) { + // if the attributes are set, clear the offset position. Otherwise, + // the offset is used to adjust the position from the anchor point + if (anchor.IsEmpty() && align.IsEmpty() && position.IsEmpty()) + position.Assign(aPosition); + else + mXPos = mYPos = 0; + } + else if (!aPosition.IsEmpty()) { + position.Assign(aPosition); + } + + if (position.EqualsLiteral("before_start")) { + mPopupAnchor = POPUPALIGNMENT_TOPLEFT; + mPopupAlignment = POPUPALIGNMENT_BOTTOMLEFT; + } + else if (position.EqualsLiteral("before_end")) { + mPopupAnchor = POPUPALIGNMENT_TOPRIGHT; + mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT; + } + else if (position.EqualsLiteral("after_start")) { + mPopupAnchor = POPUPALIGNMENT_BOTTOMLEFT; + mPopupAlignment = POPUPALIGNMENT_TOPLEFT; + } + else if (position.EqualsLiteral("after_end")) { + mPopupAnchor = POPUPALIGNMENT_BOTTOMRIGHT; + mPopupAlignment = POPUPALIGNMENT_TOPRIGHT; + } + else if (position.EqualsLiteral("start_before")) { + mPopupAnchor = POPUPALIGNMENT_TOPLEFT; + mPopupAlignment = POPUPALIGNMENT_TOPRIGHT; + } + else if (position.EqualsLiteral("start_after")) { + mPopupAnchor = POPUPALIGNMENT_BOTTOMLEFT; + mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT; + } + else if (position.EqualsLiteral("end_before")) { + mPopupAnchor = POPUPALIGNMENT_TOPRIGHT; + mPopupAlignment = POPUPALIGNMENT_TOPLEFT; + } + else if (position.EqualsLiteral("end_after")) { + mPopupAnchor = POPUPALIGNMENT_BOTTOMRIGHT; + mPopupAlignment = POPUPALIGNMENT_BOTTOMLEFT; + } + else if (position.EqualsLiteral("overlap")) { + mPopupAnchor = POPUPALIGNMENT_TOPLEFT; + mPopupAlignment = POPUPALIGNMENT_TOPLEFT; + } + else if (position.EqualsLiteral("after_pointer")) { + mPopupAnchor = POPUPALIGNMENT_NONE; + mPopupAlignment = POPUPALIGNMENT_NONE; + // XXXndeakin this is supposed to anchor vertically after, but with the + // horizontal position as the mouse pointer. + mYPos += 21; + } + else { + InitPositionFromAnchorAlign(anchor, align); + } + } + + mScreenXPos = -1; + mScreenYPos = -1; + + if (aAttributesOverride) { + // Use |left| and |top| dimension attributes to position the popup if + // present, as they may have been persisted. + nsAutoString left, top; + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::left, left); + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::top, top); + + PRInt32 err; + if (!left.IsEmpty()) { + PRInt32 x = left.ToInteger(&err); + if (NS_SUCCEEDED(err)) + mScreenXPos = x; + } + if (!top.IsEmpty()) { + PRInt32 y = top.ToInteger(&err); + if (NS_SUCCEEDED(err)) + mScreenYPos = y; + } + } +} + +void +nsMenuPopupFrame::InitializePopupAtScreen(PRInt32 aXPos, PRInt32 aYPos) +{ + EnsureWidget(); + + mIsOpenPending = PR_TRUE; + mAnchorContent = nsnull; + mScreenXPos = aXPos; + mScreenYPos = aYPos; + mPopupAnchor = POPUPALIGNMENT_NONE; + mPopupAlignment = POPUPALIGNMENT_NONE; +} + +void +nsMenuPopupFrame::InitializePopupWithAnchorAlign(nsIContent* aAnchorContent, + nsAString& aAnchor, + nsAString& aAlign, + PRInt32 aXPos, PRInt32 aYPos) +{ + EnsureWidget(); + + mIsOpenPending = PR_TRUE; + mXPos = aXPos; + mYPos = aYPos; + + // this popup opening function is provided for backwards compatibility + // only. It accepts either coordinates or an anchor and alignment value + // but doesn't use both together. + if (aXPos == -1 && aYPos == -1) { + mAnchorContent = aAnchorContent; + mScreenXPos = -1; + mScreenYPos = -1; + InitPositionFromAnchorAlign(aAnchor, aAlign); + } + else { + mAnchorContent = nsnull; + mPopupAnchor = POPUPALIGNMENT_NONE; + mPopupAlignment = POPUPALIGNMENT_NONE; + mScreenXPos = aXPos; + mScreenYPos = aYPos; + } +} + +void PR_CALLBACK +LazyGeneratePopupDone(nsIContent* aPopup, nsIFrame* aFrame, void* aArg) +{ + // be safe and check the frame type + if (aFrame->GetType() == nsGkAtoms::menuPopupFrame) { + nsWeakFrame weakFrame(aFrame); + nsMenuPopupFrame* popupFrame = NS_STATIC_CAST(nsMenuPopupFrame*, aFrame); + + popupFrame->SetGeneratedChildren(); + + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm && popupFrame->IsMenu()) { + nsCOMPtr popup = aPopup; + PRBool selectFirstItem = (PRBool)aArg; + if (selectFirstItem) { + nsMenuFrame* next = pm->GetNextMenuItem(popupFrame, nsnull, PR_TRUE); + popupFrame->SetCurrentMenuItem(next); + } + + pm->UpdateMenuItems(popup); + } + + if (weakFrame.IsAlive()) { + popupFrame->PresContext()->PresShell()-> + FrameNeedsReflow(popupFrame, nsIPresShell::eTreeChange, + NS_FRAME_HAS_DIRTY_CHILDREN); + } + } +} + + +PRBool +nsMenuPopupFrame::ShowPopup(PRBool aIsContextMenu, PRBool aSelectFirstItem) +{ + mIsContextMenu = aIsContextMenu; + + PRBool hasChildren = PR_FALSE; + + if (!mIsOpen) { + mIsOpen = PR_TRUE; + mIsOpenChanged = PR_TRUE; + + nsIFrame* parent = GetParent(); + if (parent && parent->GetType() == nsGkAtoms::menuFrame) { + nsWeakFrame weakFrame(this); + (NS_STATIC_CAST(nsMenuFrame*, parent))->PopupOpened(); + if (!weakFrame.IsAlive()) + return PR_FALSE; + PresContext()->RootPresContext()->NotifyAddedActivePopupToTop(this); + } + + // the frames for the child menus have not been created yet, so tell the + // frame constructor to build them + if (mFrames.IsEmpty() && !mGeneratedChildren) { + PresContext()->PresShell()->FrameConstructor()-> + AddLazyChildren(mContent, LazyGeneratePopupDone, (void *)aSelectFirstItem); + } + else { + hasChildren = PR_TRUE; + PresContext()->PresShell()-> + FrameNeedsReflow(this, nsIPresShell::eTreeChange, + NS_FRAME_HAS_DIRTY_CHILDREN); + } + } + + mShouldAutoPosition = PR_TRUE; + return hasChildren; +} + +void +nsMenuPopupFrame::HidePopup(PRBool aDeselectMenu) +{ + if (mIsOpen) { + if (IsMenu()) + SetCurrentMenuItem(nsnull); + + mIncrementalString.Truncate(); + + mIsOpen = PR_FALSE; + mIsOpenChanged = PR_FALSE; + mCurrentMenu = nsnull; // make sure no current menu is set + + nsIView* view = GetView(); + nsIViewManager* viewManager = view->GetViewManager(); + viewManager->SetViewVisibility(view, nsViewVisibility_kHide); + viewManager->ResizeView(view, nsRect(0, 0, 0, 0)); + + FireDOMEvent(NS_LITERAL_STRING("DOMMenuInactive"), mContent); + } + + // XXX, bug 137033, In Windows, if mouse is outside the window when the menupopup closes, no + // mouse_enter/mouse_exit event will be fired to clear current hover state, we should clear it manually. + // This code may not the best solution, but we can leave it here until we find the better approach. + nsIEventStateManager *esm = PresContext()->EventStateManager(); + + PRInt32 state; + esm->GetContentState(mContent, state); + + if (state & NS_EVENT_STATE_HOVER) + esm->SetContentState(nsnull, NS_EVENT_STATE_HOVER); + + nsIFrame* parent = GetParent(); + if (parent && parent->GetType() == nsGkAtoms::menuFrame) { + (NS_STATIC_CAST(nsMenuFrame*, parent))->PopupClosed(aDeselectMenu); + PresContext()->RootPresContext()->NotifyRemovedActivePopup(this); + } +} + void nsMenuPopupFrame::InvalidateInternal(const nsRect& aDamageRect, nscoord aX, nscoord aY, nsIFrame* aForChild, @@ -284,13 +611,10 @@ nsMenuPopupFrame::GetViewOffset(nsIView* aView, nsPoint& aPoint) // root view. This is the root view of the pres context's // viewmanager if aStopAtViewManagerRoot is true; otherwise it's the // root view of the root viewmanager. -void +nsIView* nsMenuPopupFrame::GetRootViewForPopup(nsIFrame* aStartFrame, - PRBool aStopAtViewManagerRoot, - nsIView** aResult) + PRBool aStopAtViewManagerRoot) { - *aResult = nsnull; - nsIView* view = aStartFrame->GetClosestView(); NS_ASSERTION(view, "frame must have a closest view!"); if (view) { @@ -308,210 +632,96 @@ nsMenuPopupFrame::GetRootViewForPopup(nsIFrame* aStartFrame, nsWindowType wtype; widget->GetWindowType(wtype); if (wtype == eWindowType_popup) { - *aResult = view; - return; + return view; } } if (aStopAtViewManagerRoot && view == rootView) { - *aResult = view; - return; + return view; } nsIView* temp = view->GetParent(); if (!temp) { // Otherwise, we've walked all the way up to the root view and not // found a view for a popup window widget. Just return the root view. - *aResult = view; + return view; } view = temp; } } + + return nsnull; } - -// -// AdjustClientXYForNestedDocuments -// -// almost certainly, the document where the mouse was clicked is not -// the document that contains the popup, especially if we're viewing a page -// with frames. Thus we need to make adjustments to the client coordinates to -// take this into account and get them back into the relative coordinates of -// this document. -// -void -nsMenuPopupFrame::AdjustClientXYForNestedDocuments ( nsIDOMXULDocument* inPopupDoc, nsIPresShell* inPopupShell, - PRInt32 inClientX, PRInt32 inClientY, - PRInt32* outAdjX, PRInt32* outAdjY ) -{ - if ( !inPopupDoc || !outAdjX || !outAdjY ) - return; - - // Find the widget associated with the popup's document - nsIWidget* popupDocumentWidget = nsnull; - nsIViewManager* viewManager = inPopupShell->GetViewManager(); - if ( viewManager ) { - nsIView* rootView; - viewManager->GetRootView(rootView); - if ( rootView ) - popupDocumentWidget = rootView->GetNearestWidget(nsnull); - } - NS_ASSERTION(popupDocumentWidget, "ACK, BAD WIDGET"); - - // Find the widget associated with the target's document. - // For tooltips, we check the document's tooltipNode (which is set by - // nsXULTooltipListener). For regular popups, use popupNode (set by - // nsXULPopupListener). - - nsCOMPtr targetNode; - if (mContent->Tag() == nsGkAtoms::tooltip) - inPopupDoc->TrustedGetTooltipNode(getter_AddRefs(targetNode)); - else - inPopupDoc->TrustedGetPopupNode(getter_AddRefs(targetNode)); - - //NS_ASSERTION(targetNode, "no popup/tooltip node on document!"); - nsCOMPtr targetAsContent ( do_QueryInterface(targetNode) ); - nsIWidget* targetDocumentWidget = nsnull; - if ( targetAsContent ) { - nsCOMPtr targetDocument = targetAsContent->GetDocument(); - if (targetDocument) { - nsIPresShell *shell = targetDocument->GetPrimaryShell(); - if ( shell ) { - // We might be inside a popup widget. If so, we need to use that widget and - // not the root view's widget. - nsIFrame* targetFrame = shell->GetPrimaryFrameFor(targetAsContent); - nsIView* parentView = nsnull; - if (targetFrame) { - GetRootViewForPopup(targetFrame, PR_TRUE, &parentView); - if (parentView) { - targetDocumentWidget = parentView->GetNearestWidget(nsnull); - } - } - if (!targetDocumentWidget) { - // We aren't inside a popup. This means we should use the root view's - // widget. - nsIViewManager* viewManagerTarget = shell->GetViewManager(); - if ( viewManagerTarget ) { - nsIView* rootViewTarget; - viewManagerTarget->GetRootView(rootViewTarget); - if ( rootViewTarget ) { - targetDocumentWidget = rootViewTarget->GetNearestWidget(nsnull); - } - } - } - } - } - } - //NS_ASSERTION(targetDocumentWidget, "ACK, BAD TARGET"); - - // the offset we need is the difference between the upper left corner of the two widgets. Use - // screen coordinates to find the global offset between them. - nsRect popupDocTopLeft; - if ( popupDocumentWidget ) { - nsRect topLeftClient ( 0, 0, 10, 10 ); - popupDocumentWidget->WidgetToScreen ( topLeftClient, popupDocTopLeft ); - } - nsRect targetDocTopLeft; - if ( targetDocumentWidget ) { - nsRect topLeftClient ( 0, 0, 10, 10 ); - targetDocumentWidget->WidgetToScreen ( topLeftClient, targetDocTopLeft ); - } - nsPoint pixelOffset ( targetDocTopLeft.x - popupDocTopLeft.x, targetDocTopLeft.y - popupDocTopLeft.y ); - - nsPresContext* context = PresContext(); - *outAdjX = nsPresContext::CSSPixelsToAppUnits(inClientX) + - context->DevPixelsToAppUnits(pixelOffset.x); - *outAdjY = nsPresContext::CSSPixelsToAppUnits(inClientY) + - context->DevPixelsToAppUnits(pixelOffset.y); - -} // AdjustClientXYForNestedDocuments - - // // AdjustPositionForAnchorAlign // -// Uses the |popupanchor| and |popupalign| attributes on the popup to move the popup around and -// anchor it to its parent. |outFlushWithTopBottom| will be TRUE if the popup is flush with either -// the top or bottom edge of its parent, and FALSE if it is flush with the left or right edge of -// the parent. +// Uses the anchor and alignment to move the popup around and anchor it to its +// parent. |outFlushWithTopBottom| will be TRUE if the popup is flush with +// either the top or bottom edge of its parent, and FALSE if it is flush with +// the left or right edge of the parent. // void -nsMenuPopupFrame::AdjustPositionForAnchorAlign ( PRInt32* ioXPos, PRInt32* ioYPos, const nsRect & inParentRect, - const nsString& aPopupAnchor, const nsString& aPopupAlign, - PRBool* outFlushWithTopBottom ) +nsMenuPopupFrame::AdjustPositionForAnchorAlign(PRInt32* ioXPos, PRInt32* ioYPos, const nsRect & inParentRect, + PRBool* outFlushWithTopBottom) { - nsAutoString popupAnchor(aPopupAnchor); - nsAutoString popupAlign(aPopupAlign); + PRInt8 popupAnchor(mPopupAnchor); + PRInt8 popupAlign(mPopupAlignment); if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { - if (popupAnchor.EqualsLiteral("topright")) - popupAnchor.AssignLiteral("topleft"); - else if (popupAnchor.EqualsLiteral("topleft")) - popupAnchor.AssignLiteral("topright"); - else if (popupAnchor.EqualsLiteral("bottomleft")) - popupAnchor.AssignLiteral("bottomright"); - else if (popupAnchor.EqualsLiteral("bottomright")) - popupAnchor.AssignLiteral("bottomleft"); - - if (popupAlign.EqualsLiteral("topright")) - popupAlign.AssignLiteral("topleft"); - else if (popupAlign.EqualsLiteral("topleft")) - popupAlign.AssignLiteral("topright"); - else if (popupAlign.EqualsLiteral("bottomleft")) - popupAlign.AssignLiteral("bottomright"); - else if (popupAnchor.EqualsLiteral("bottomright")) - popupAlign.AssignLiteral("bottomleft"); + popupAnchor = -popupAnchor; + popupAlign = -popupAlign; } // Adjust position for margins at the aligned corner nsMargin margin; GetStyleMargin()->GetMargin(margin); - if (popupAlign.EqualsLiteral("topleft")) { + if (popupAlign == POPUPALIGNMENT_TOPLEFT) { *ioXPos += margin.left; *ioYPos += margin.top; - } else if (popupAlign.EqualsLiteral("topright")) { + } else if (popupAlign == POPUPALIGNMENT_TOPRIGHT) { *ioXPos += margin.right; *ioYPos += margin.top; - } else if (popupAlign.EqualsLiteral("bottomleft")) { + } else if (popupAlign == POPUPALIGNMENT_BOTTOMLEFT) { *ioXPos += margin.left; *ioYPos += margin.bottom; - } else if (popupAlign.EqualsLiteral("bottomright")) { + } else if (popupAlign == POPUPALIGNMENT_BOTTOMRIGHT) { *ioXPos += margin.right; *ioYPos += margin.bottom; } - if (popupAnchor.EqualsLiteral("topright") && popupAlign.EqualsLiteral("topleft")) { + if (popupAnchor == POPUPALIGNMENT_TOPRIGHT && popupAlign == POPUPALIGNMENT_TOPLEFT) { *ioXPos += inParentRect.width; } - else if (popupAnchor.EqualsLiteral("topleft") && popupAlign.EqualsLiteral("topleft")) { + else if (popupAnchor == POPUPALIGNMENT_TOPLEFT && popupAlign == POPUPALIGNMENT_TOPLEFT) { *outFlushWithTopBottom = PR_TRUE; } - else if (popupAnchor.EqualsLiteral("topright") && popupAlign.EqualsLiteral("bottomright")) { + else if (popupAnchor == POPUPALIGNMENT_TOPRIGHT && popupAlign == POPUPALIGNMENT_BOTTOMRIGHT) { *ioXPos -= (mRect.width - inParentRect.width); *ioYPos -= mRect.height; *outFlushWithTopBottom = PR_TRUE; } - else if (popupAnchor.EqualsLiteral("bottomright") && popupAlign.EqualsLiteral("bottomleft")) { + else if (popupAnchor == POPUPALIGNMENT_BOTTOMRIGHT && popupAlign == POPUPALIGNMENT_BOTTOMLEFT) { *ioXPos += inParentRect.width; *ioYPos -= (mRect.height - inParentRect.height); } - else if (popupAnchor.EqualsLiteral("bottomright") && popupAlign.EqualsLiteral("topright")) { + else if (popupAnchor == POPUPALIGNMENT_BOTTOMRIGHT && popupAlign == POPUPALIGNMENT_TOPRIGHT) { *ioXPos -= (mRect.width - inParentRect.width); *ioYPos += inParentRect.height; *outFlushWithTopBottom = PR_TRUE; } - else if (popupAnchor.EqualsLiteral("topleft") && popupAlign.EqualsLiteral("topright")) { + else if (popupAnchor == POPUPALIGNMENT_TOPLEFT && popupAlign == POPUPALIGNMENT_TOPRIGHT) { *ioXPos -= mRect.width; } - else if (popupAnchor.EqualsLiteral("topleft") && popupAlign.EqualsLiteral("bottomleft")) { + else if (popupAnchor == POPUPALIGNMENT_TOPLEFT && popupAlign == POPUPALIGNMENT_BOTTOMLEFT) { *ioYPos -= mRect.height; *outFlushWithTopBottom = PR_TRUE; } - else if (popupAnchor.EqualsLiteral("bottomleft") && popupAlign.EqualsLiteral("bottomright")) { + else if (popupAnchor == POPUPALIGNMENT_BOTTOMLEFT && popupAlign == POPUPALIGNMENT_BOTTOMRIGHT) { *ioXPos -= mRect.width; *ioYPos -= (mRect.height - inParentRect.height); } - else if (popupAnchor.EqualsLiteral("bottomleft") && popupAlign.EqualsLiteral("topleft")) { + else if (popupAnchor == POPUPALIGNMENT_BOTTOMLEFT && popupAlign == POPUPALIGNMENT_TOPLEFT) { *ioYPos += inParentRect.height; *outFlushWithTopBottom = PR_TRUE; } @@ -617,46 +827,43 @@ nsMenuPopupFrame::MovePopupToOtherSideOfParent ( PRBool inFlushAboveBelow, PRInt } // MovePopupToOtherSideOfParent -class nsASyncMenuActivation : public nsIReflowCallback +// XXXndeakin this function will be reworked in bug 384062 such that positioning +// of the popup is done only when the popup is first opened, so that the popup doesn't +// move around when it is changed in some way. +nsresult +nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame) { -public: - nsASyncMenuActivation(nsIContent* aContent) - : mContent(aContent) - { - } - - virtual PRBool ReflowFinished() { - PRBool shouldFlush = PR_FALSE; - if (mContent && - !mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menuactive, - nsGkAtoms::_true, eCaseMatters) && - mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menutobedisplayed, - nsGkAtoms::_true, eCaseMatters)) { - mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::menuactive, - NS_LITERAL_STRING("true"), PR_TRUE); - shouldFlush = PR_TRUE; - } - - delete this; - return shouldFlush; - } - - nsCOMPtr mContent; -}; - -nsresult -nsMenuPopupFrame::SyncViewWithFrame(nsPresContext* aPresContext, - const nsString& aPopupAnchor, - const nsString& aPopupAlign, - nsIFrame* aFrame, - PRInt32 aXPos, PRInt32 aYPos) -{ - NS_ENSURE_ARG(aPresContext); - NS_ENSURE_ARG(aFrame); - if (!mShouldAutoPosition && !mInContentShell) return NS_OK; + PRBool sizedToPopup = PR_FALSE; + + nsPresContext* presContext = PresContext(); + + // if the frame is not specified, use the anchor node passed to ShowPopup. If + // that wasn't specified either, use the root frame. Note that mAnchorContent + // might be a different document so its presshell must be used. + if (!aAnchorFrame) { + if (mAnchorContent) { + nsCOMPtr document = mAnchorContent->GetDocument(); + nsIPresShell *shell = document->GetPrimaryShell(); + if (!shell) + return NS_ERROR_FAILURE; + + aAnchorFrame = shell->GetPrimaryFrameFor(mAnchorContent); + } + else { + aAnchorFrame = presContext->PresShell()->FrameManager()->GetRootFrame(); + } + + if (!aAnchorFrame) + return NS_OK; + } + else { + // the popup should be the same size as the anchor menu, for example, a menulist. + sizedToPopup = nsMenuFrame::IsSizedToPopup(aAnchorFrame->GetContent(), PR_FALSE); + } + // |containingView| // The view that contains the frame that is invoking this popup. This is // the canvas view inside the scrollport view. It can have negative bounds @@ -664,15 +871,10 @@ nsMenuPopupFrame::SyncViewWithFrame(nsPresContext* aPresContext, nsIView* containingView = nsnull; nsPoint offset; nsMargin margin; - containingView = aFrame->GetClosestView(&offset); + containingView = aAnchorFrame->GetClosestView(&offset); if (!containingView) return NS_OK; - // |view| - // The root view for the popup window widget associated with this frame, - // or, the view associated with this frame. - nsIView* view = GetView(); - // |parentPos| // The distance between the containingView and the root view. This provides // a hint as to where to position the menu relative to the window. @@ -681,58 +883,93 @@ nsMenuPopupFrame::SyncViewWithFrame(nsPresContext* aPresContext, // |parentRect| // The dimensions of the frame invoking the popup. - nsRect parentRect = aFrame->GetRect(); + nsRect parentRect = aAnchorFrame->GetRect(); // get the document and the global script object - nsIPresShell *presShell = aPresContext->PresShell(); + nsIPresShell *presShell = presContext->PresShell(); nsIDocument *document = presShell->GetDocument(); - PRBool sizedToPopup = (mContent->Tag() != nsGkAtoms::tooltip) && - (nsMenuFrame::IsSizedToPopup(aFrame->GetContent(), PR_FALSE)); - // If we stick to our parent's width, set it here before we move the // window around, because moving is done with respect to the width... if (sizedToPopup) { mRect.width = parentRect.width; } + // Use containingView instead of parentView, to account for the scrollarrows + // that a parent menu might have. + nsPoint parentViewWidgetOffset; + nsIWidget* parentViewWidget = containingView->GetNearestWidget(&parentViewWidgetOffset); + nsRect localParentWidgetRect(0,0,0,0), screenParentWidgetRect; + parentViewWidget->WidgetToScreen ( localParentWidgetRect, screenParentWidgetRect ); + // |xpos| and |ypos| hold the x and y positions of where the popup will be moved to, // in _twips_, in the coordinate system of the _parent view_. - PRInt32 xpos = 0, ypos = 0; - - // if we are anchored to our parent, there are certain things we don't want to do - // when repositioning the view to fit on the screen, such as end up positioned over - // the parent. When doing this reposition, we want to move the popup to the side with - // the most room. The combination of anchor and alignment dictate if we readjst - // above/below or to the left/right. - PRBool anchoredToParent = PR_FALSE; PRBool readjustAboveBelow = PR_FALSE; + PRInt32 xpos = 0, ypos = 0; + PRInt32 screenViewLocX, screenViewLocY; - if ( aXPos != -1 || aYPos != -1 ) { - - // for this case, we've been handed a specific x/y location (in client coordinates) for - // the popup. However, we may be deeply nested in a frameset, etc and so the client coordinates - // need some adjusting. - nsCOMPtr xulDoc ( do_QueryInterface(document) ); - AdjustClientXYForNestedDocuments ( xulDoc, presShell, aXPos, aYPos, &xpos, &ypos ); + if (mScreenXPos == -1 && mScreenYPos == -1) { + // if we are anchored to our parent, there are certain things we don't want to do + // when repositioning the view to fit on the screen, such as end up positioned over + // the parent. When doing this reposition, we want to move the popup to the side with + // the most room. The combination of anchor and alignment dictate if we readjust + // above/below or to the left/right. - // Add in the top and left margins - GetStyleMargin()->GetMargin(margin); + if (mAnchorContent) { + xpos = parentPos.x + offset.x; + ypos = parentPos.y + offset.y; - xpos += margin.left; - ypos += margin.top; - } - else { - anchoredToParent = PR_TRUE; + // move the popup according to the anchor and alignment. This will also tell us + // which axis the popup is flush against in case we have to move it around later. + AdjustPositionForAnchorAlign(&xpos, &ypos, parentRect, &readjustAboveBelow); - xpos = parentPos.x + offset.x; - ypos = parentPos.y + offset.y; - - // move the popup according to the anchor/alignment attributes. This will also tell us - // which axis the popup is flush against in case we have to move it around later. - AdjustPositionForAnchorAlign ( &xpos, &ypos, parentRect, aPopupAnchor, aPopupAlign, &readjustAboveBelow ); + // the x and y position may be used to offset the popup after it has been anchored + xpos += presContext->DevPixelsToAppUnits(mXPos); + ypos += presContext->DevPixelsToAppUnits(mYPos); + } + else { + GetStyleMargin()->GetMargin(margin); + xpos = presContext->DevPixelsToAppUnits(mXPos) + margin.left; + ypos = presContext->DevPixelsToAppUnits(mYPos) + margin.top; + } + + // Recall that |xpos| and |ypos| are in the coordinate system of the parent view. In + // order to determine the screen coordinates of where our view will end up, we + // need to find the x/y position of the parent view in screen coords. That is done + // by getting the widget associated with the parent view and determining the offset + // based on converting (0,0) in its coordinate space to screen coords. We then + // offset that point by (|xpos|,|ypos|) to get the true screen coordinates of + // the view. *whew* + + // |parentView| + // The root view for the window that contains the frame, for frames inside + // menupopups this is the first view inside the popup window widget, for + // frames inside a toplevel window, this is the root view of the toplevel + // window. + nsIView* parentView = GetRootViewForPopup(aAnchorFrame, PR_FALSE); + if (!parentView) + return NS_OK; + + screenViewLocX = presContext->DevPixelsToAppUnits(screenParentWidgetRect.x) + + (xpos - parentPos.x) + parentViewWidgetOffset.x; + screenViewLocY = presContext->DevPixelsToAppUnits(screenParentWidgetRect.y) + + (ypos - parentPos.y) + parentViewWidgetOffset.y; } - + else { + // positioned on screen + GetStyleMargin()->GetMargin(margin); + screenViewLocX = nsPresContext::CSSPixelsToAppUnits(mScreenXPos) + margin.left; + screenViewLocY = nsPresContext::CSSPixelsToAppUnits(mScreenYPos) + margin.top; + + xpos = screenViewLocX - presContext->DevPixelsToAppUnits(screenParentWidgetRect.x) - + parentViewWidgetOffset.x - parentPos.x; + ypos = screenViewLocY - presContext->DevPixelsToAppUnits(screenParentWidgetRect.y) - + parentViewWidgetOffset.y - parentPos.y; + + // once the popup is positioned on screen, it doesn't need to be positioned again + mShouldAutoPosition = PR_FALSE; + } + // Compute info about the screen dimensions. Because of multiple monitor systems, // the left or top sides of the screen may be in negative space (main monitor is on the // right, etc). We need to be sure to do the right thing. @@ -756,7 +993,7 @@ nsMenuPopupFrame::SyncViewWithFrame(nsPresContext* aPresContext, // for content shells, clip to the client area rather than the screen area if (mInContentShell) { nsRect rootScreenRect = presShell->GetRootFrame()->GetScreenRect(); - rootScreenRect.ScaleRoundIn(aPresContext->AppUnitsPerDevPixel()); + rootScreenRect.ScaleRoundIn(presContext->AppUnitsPerDevPixel()); rect.IntersectRect(rect, rootScreenRect); } @@ -766,39 +1003,8 @@ nsMenuPopupFrame::SyncViewWithFrame(nsPresContext* aPresContext, PRInt32 screenHeightTwips = rect.height; PRInt32 screenRightTwips = rect.XMost(); PRInt32 screenBottomTwips = rect.YMost(); - - // Recall that |xpos| and |ypos| are in the coordinate system of the parent view. In - // order to determine the screen coordinates of where our view will end up, we - // need to find the x/y position of the parent view in screen coords. That is done - // by getting the widget associated with the parent view and determining the offset - // based on converting (0,0) in its coordinate space to screen coords. We then - // offset that point by (|xpos|,|ypos|) to get the true screen coordinates of - // the view. *whew* - // |parentView| - // The root view for the window that contains the frame, for frames inside - // menupopups this is the first view inside the popup window widget, for - // frames inside a toplevel window, this is the root view of the toplevel - // window. - nsIView* parentView = nsnull; - GetRootViewForPopup(aFrame, PR_FALSE, &parentView); - if (!parentView) - return NS_OK; - - // Use containingView instead of parentView, to account for the scrollarrows - // that a parent menu might have. - - nsPoint parentViewWidgetOffset; - nsIWidget* parentViewWidget = containingView->GetNearestWidget(&parentViewWidgetOffset); - nsRect localParentWidgetRect(0,0,0,0), screenParentWidgetRect; - parentViewWidget->WidgetToScreen ( localParentWidgetRect, screenParentWidgetRect ); - PRInt32 screenViewLocX = aPresContext->DevPixelsToAppUnits(screenParentWidgetRect.x) + - (xpos - parentPos.x) + parentViewWidgetOffset.x; - PRInt32 screenViewLocY = aPresContext->DevPixelsToAppUnits(screenParentWidgetRect.y) + - (ypos - parentPos.y) + parentViewWidgetOffset.y; - - if ( anchoredToParent ) { - + if (mPopupAnchor != POPUPALIGNMENT_NONE) { // // Popup is anchored to the parent, guarantee that it does not cover the parent. We // shouldn't do anything funky if it will already fit on the screen as is. @@ -825,11 +1031,11 @@ nsMenuPopupFrame::SyncViewWithFrame(nsPresContext* aPresContext, // compute screen coordinates of parent frame so we can play with it. Make sure we put it // into twips as everything else is as well. - nsRect screenParentFrameRect (aPresContext->AppUnitsToDevPixels(offset.x), aPresContext->AppUnitsToDevPixels(offset.y), + nsRect screenParentFrameRect (presContext->AppUnitsToDevPixels(offset.x), presContext->AppUnitsToDevPixels(offset.y), parentRect.width, parentRect.height ); parentViewWidget->WidgetToScreen ( screenParentFrameRect, screenParentFrameRect ); - screenParentFrameRect.x = aPresContext->DevPixelsToAppUnits(screenParentFrameRect.x); - screenParentFrameRect.y = aPresContext->DevPixelsToAppUnits(screenParentFrameRect.y); + screenParentFrameRect.x = presContext->DevPixelsToAppUnits(screenParentFrameRect.x); + screenParentFrameRect.y = presContext->DevPixelsToAppUnits(screenParentFrameRect.y); // Don't let it spill off the screen to the top if (screenViewLocY < screenTopTwips) { @@ -943,10 +1149,10 @@ nsMenuPopupFrame::SyncViewWithFrame(nsPresContext* aPresContext, // y-direction too). // shrink to fit onto the screen, vertically and horizontally - if(mRect.width > screenWidthTwips) - mRect.width = screenWidthTwips; + if(mRect.width > screenWidthTwips) + mRect.width = screenWidthTwips; if(mRect.height > screenHeightTwips) - mRect.height = screenHeightTwips; + mRect.height = screenHeightTwips; // First, adjust the X position. For the X position, we slide the popup // left or right as needed to get it on screen. @@ -991,7 +1197,7 @@ nsMenuPopupFrame::SyncViewWithFrame(nsPresContext* aPresContext, } } - aPresContext->GetViewManager()->MoveViewTo(view, xpos, ypos); + presContext->GetViewManager()->MoveViewTo(GetView(), xpos, ypos); // Now that we've positioned the view, sync up the frame's origin. nsPoint frameOrigin = GetPosition(); @@ -1001,189 +1207,45 @@ nsMenuPopupFrame::SyncViewWithFrame(nsPresContext* aPresContext, nsBoxFrame::SetPosition(frameOrigin); if (sizedToPopup) { - nsBoxLayoutState state(PresContext()); - SetBounds(state, nsRect(mRect.x, mRect.y, parentRect.width, mRect.height)); - } - - if (!mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menuactive, - nsGkAtoms::_true, eCaseMatters) && - mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menutobedisplayed, - nsGkAtoms::_true, eCaseMatters)) { - nsIReflowCallback* cb = new nsASyncMenuActivation(mContent); - NS_ENSURE_TRUE(cb, NS_ERROR_OUT_OF_MEMORY); - PresContext()->PresShell()->PostReflowCallback(cb); + nsBoxLayoutState state(PresContext()); + SetBounds(state, nsRect(mRect.x, mRect.y, parentRect.width, mRect.height)); } return NS_OK; } -static void GetInsertionPoint(nsIPresShell* aShell, nsIFrame* aFrame, nsIFrame* aChild, - nsIFrame** aResult) -{ - nsIContent* child = nsnull; - if (aChild) - child = aChild->GetContent(); - aShell->FrameConstructor()->GetInsertionPoint(aFrame, child, aResult); -} - -/* virtual */ nsIMenuFrame* -nsMenuPopupFrame::GetNextMenuItem(nsIMenuFrame* aStart) -{ - nsIFrame* immediateParent = nsnull; - GetInsertionPoint(PresContext()->PresShell(), this, nsnull, - &immediateParent); - if (!immediateParent) - immediateParent = this; - - nsIFrame* currFrame = nsnull; - nsIFrame* startFrame = nsnull; - if (aStart) { - aStart->QueryInterface(NS_GET_IID(nsIFrame), (void**)&currFrame); - if (currFrame) { - startFrame = currFrame; - currFrame = currFrame->GetNextSibling(); - } - } - else - currFrame = immediateParent->GetFirstChild(nsnull); - - while (currFrame) { - // See if it's a menu item. - if (IsValidItem(currFrame->GetContent())) { - nsIMenuFrame *menuFrame; - if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame))) - menuFrame = nsnull; - return menuFrame; - } - currFrame = currFrame->GetNextSibling(); - } - - currFrame = immediateParent->GetFirstChild(nsnull); - - // Still don't have anything. Try cycling from the beginning. - while (currFrame && currFrame != startFrame) { - // See if it's a menu item. - if (IsValidItem(currFrame->GetContent())) { - nsIMenuFrame *menuFrame; - if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame))) - menuFrame = nsnull; - return menuFrame; - } - - currFrame = currFrame->GetNextSibling(); - } - - // No luck. Just return our start value. - return aStart; -} - -/* virtual */ nsIMenuFrame* -nsMenuPopupFrame::GetPreviousMenuItem(nsIMenuFrame* aStart) -{ - nsIFrame* immediateParent = nsnull; - GetInsertionPoint(PresContext()->PresShell(), this, nsnull, - &immediateParent); - if (!immediateParent) - immediateParent = this; - - nsFrameList frames(immediateParent->GetFirstChild(nsnull)); - - nsIFrame* currFrame = nsnull; - nsIFrame* startFrame = nsnull; - if (aStart) { - aStart->QueryInterface(NS_GET_IID(nsIFrame), (void**)&currFrame); - if (currFrame) { - startFrame = currFrame; - currFrame = frames.GetPrevSiblingFor(currFrame); - } - } - else currFrame = frames.LastChild(); - - while (currFrame) { - // See if it's a menu item. - if (IsValidItem(currFrame->GetContent())) { - nsIMenuFrame *menuFrame; - if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame))) - menuFrame = nsnull; - return menuFrame; - } - currFrame = frames.GetPrevSiblingFor(currFrame); - } - - currFrame = frames.LastChild(); - - // Still don't have anything. Try cycling from the end. - while (currFrame && currFrame != startFrame) { - // See if it's a menu item. - if (IsValidItem(currFrame->GetContent())) { - nsIMenuFrame *menuFrame; - if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame))) - menuFrame = nsnull; - return menuFrame; - } - - currFrame = frames.GetPrevSiblingFor(currFrame); - } - - // No luck. Just return our start value. - return aStart; -} - -/* virtual */ nsIMenuFrame* +/* virtual */ nsMenuFrame* nsMenuPopupFrame::GetCurrentMenuItem() { return mCurrentMenu; } -NS_IMETHODIMP nsMenuPopupFrame::ConsumeOutsideClicks(PRBool& aConsumeOutsideClicks) +PRBool nsMenuPopupFrame::ConsumeOutsideClicks() { - /* - * When this popup is open, should clicks outside of it be consumed? - * Return PR_TRUE if the popup hould rollup on an outside click, - * but consume that click so it can't be used for anything else. - * Return PR_FALSE to allow clicks outside the popup to activate content - * even when the popup is open. - * --------------------------------------------------------------------- - * - * Should clicks outside of a popup be eaten? - * - * Menus Autocomplete Comboboxes - * Mac Eat No Eat - * Win No No Eat - * Unix Eat No Eat - * - */ - // If the popup has explicitly set a consume mode, honor that. - if (mConsumeRollupEvent != nsIPopupBoxObject::ROLLUP_DEFAULT) { - aConsumeOutsideClicks = mConsumeRollupEvent == nsIPopupBoxObject::ROLLUP_CONSUME; - return NS_OK; - } - - aConsumeOutsideClicks = PR_TRUE; + if (mConsumeRollupEvent != nsIPopupBoxObject::ROLLUP_DEFAULT) + return (mConsumeRollupEvent == nsIPopupBoxObject::ROLLUP_CONSUME); nsCOMPtr parentContent = mContent->GetParent(); - if (parentContent) { - nsIAtom *parentTag = parentContent->Tag(); - if (parentTag == nsGkAtoms::menulist) - return NS_OK; // Consume outside clicks for combo boxes on all platforms - if (parentTag == nsGkAtoms::menu || parentTag == nsGkAtoms::popupset) { + nsINodeInfo *ni = parentContent->NodeInfo(); + if (ni->Equals(nsGkAtoms::menulist, kNameSpaceID_XUL)) + return PR_TRUE; // Consume outside clicks for combo boxes on all platforms #if defined(XP_WIN) || defined(XP_OS2) - // Don't consume outside clicks for menus in Windows - aConsumeOutsideClicks = PR_FALSE; + // Don't consume outside clicks for menus in Windows + if (ni->Equals(nsGkAtoms::menu, kNameSpaceID_XUL) || + (ni->Equals(nsGkAtoms::popupset, kNameSpaceID_XUL))) + return PR_FALSE; #endif - return NS_OK; - } - if (parentTag == nsGkAtoms::textbox) { + if (ni->Equals(nsGkAtoms::textbox, kNameSpaceID_XUL)) { // Don't consume outside clicks for autocomplete widget if (parentContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, nsGkAtoms::autocomplete, eCaseMatters)) - aConsumeOutsideClicks = PR_FALSE; + return PR_FALSE; } } - return NS_OK; + return PR_TRUE; } static nsIScrollableView* GetScrollableViewForFrame(nsIFrame* aFrame) @@ -1228,20 +1290,17 @@ nsIScrollableView* nsMenuPopupFrame::GetScrollableView(nsIFrame* aStart) return nsnull; } -void nsMenuPopupFrame::EnsureMenuItemIsVisible(nsIMenuFrame* aMenuItem) +void nsMenuPopupFrame::EnsureMenuItemIsVisible(nsMenuFrame* aMenuItem) { - nsIFrame* frame=nsnull; - aMenuItem->QueryInterface(NS_GET_IID(nsIFrame), (void**)&frame); - if ( frame ) { - nsIFrame* childFrame=nsnull; - childFrame = GetFirstChild(nsnull); + if (aMenuItem) { + nsIFrame* childFrame = GetFirstChild(nsnull); nsIScrollableView *scrollableView; - scrollableView=GetScrollableView(childFrame); - if ( scrollableView ) { + scrollableView = GetScrollableView(childFrame); + if (scrollableView) { nscoord scrollX, scrollY; nsRect viewRect = scrollableView->View()->GetBounds(); - nsRect itemRect = frame->GetRect(); + nsRect itemRect = aMenuItem->GetRect(); scrollableView->GetScrollPosition(scrollX, scrollY); // scroll down @@ -1255,36 +1314,53 @@ void nsMenuPopupFrame::EnsureMenuItemIsVisible(nsIMenuFrame* aMenuItem) } } -NS_IMETHODIMP nsMenuPopupFrame::SetCurrentMenuItem(nsIMenuFrame* aMenuItem) +NS_IMETHODIMP nsMenuPopupFrame::SetCurrentMenuItem(nsMenuFrame* aMenuItem) { - // When a context menu is open, the current menu is locked, and no change - // to the menu is allowed. - nsIMenuParent *contextMenu = GetContextMenu(); - if (contextMenu) - return NS_OK; - if (mCurrentMenu == aMenuItem) return NS_OK; - + + if (mCurrentMenu) { + mCurrentMenu->SelectMenu(PR_FALSE); + } + + if (aMenuItem) { + EnsureMenuItemIsVisible(aMenuItem); + aMenuItem->SelectMenu(PR_TRUE); + } + + mCurrentMenu = aMenuItem; + + return NS_OK; +} + +void +nsMenuPopupFrame::CurrentMenuIsBeingDestroyed() +{ + mCurrentMenu = nsnull; +} + +NS_IMETHODIMP +nsMenuPopupFrame::ChangeMenuItem(nsMenuFrame* aMenuItem, + PRBool aSelectFirstItem) +{ + if (mCurrentMenu == aMenuItem) + return NS_OK; + + // When a context menu is open, the current menu is locked, and no change + // to the menu is allowed. + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (!mIsContextMenu && pm && pm->HasContextMenu(this)) + return NS_OK; + // Unset the current child. if (mCurrentMenu) { - PRBool isOpen = PR_FALSE; - mCurrentMenu->MenuIsOpen(isOpen); mCurrentMenu->SelectMenu(PR_FALSE); - // XXX bug 294183 sometimes mCurrentMenu gets cleared - if (mCurrentMenu && isOpen) { - // Don't close up immediately. - // Kick off a close timer. - KillCloseTimer(); // Ensure we don't have another stray waiting closure. - PRInt32 menuDelay = 300; // ms - - PresContext()->LookAndFeel()-> - GetMetric(nsILookAndFeel::eMetric_SubmenuDelay, menuDelay); - - // Kick off the timer. - mCloseTimer = do_CreateInstance("@mozilla.org/timer;1"); - mCloseTimer->InitWithCallback(mTimerMediator, menuDelay, nsITimer::TYPE_ONE_SHOT); - mTimerMenu = mCurrentMenu; + nsMenuPopupFrame* popup = mCurrentMenu->GetPopup(); + if (popup) { + if (mCurrentMenu->IsOpen()) { + if (pm) + pm->HidePopupAfterDelay(popup); + } } } @@ -1299,74 +1375,19 @@ NS_IMETHODIMP nsMenuPopupFrame::SetCurrentMenuItem(nsIMenuFrame* aMenuItem) return NS_OK; } - -NS_IMETHODIMP -nsMenuPopupFrame::Escape(PRBool& aHandledFlag) -{ - mIncrementalString.Truncate(); - - // See if we have a context menu open. - nsIMenuParent* contextMenu = GetContextMenu(); - if (contextMenu) { - // Get the context menu parent. - nsIFrame* childFrame; - CallQueryInterface(contextMenu, &childFrame); - nsIPopupSetFrame* popupSetFrame = GetPopupSetFrame(PresContext()); - if (popupSetFrame) - // Destroy the popup. - popupSetFrame->DestroyPopup(childFrame, PR_FALSE); - aHandledFlag = PR_TRUE; - return NS_OK; - } - - if (!mCurrentMenu) - return NS_OK; - - // See if our menu is open. - PRBool isOpen = PR_FALSE; - mCurrentMenu->MenuIsOpen(isOpen); - if (isOpen) { - // Let the child menu handle this. - mCurrentMenu->Escape(aHandledFlag); - if (!aHandledFlag) { - // We should close up. - mCurrentMenu->OpenMenu(PR_FALSE); - // SelectMenu() so DOMMenuItemActive is fired for accessibility - mCurrentMenu->SelectMenu(PR_TRUE); - aHandledFlag = PR_TRUE; - } - } - - return NS_OK; -} - -NS_IMETHODIMP +nsMenuFrame* nsMenuPopupFrame::Enter() { mIncrementalString.Truncate(); - // See if we have a context menu open. - nsIMenuParent *contextMenu = GetContextMenu(); - if (contextMenu) - return contextMenu->Enter(); - // Give it to the child. if (mCurrentMenu) - mCurrentMenu->Enter(); + return mCurrentMenu->Enter(); - return NS_OK; + return nsnull; } -nsIMenuParent* -nsMenuPopupFrame::GetContextMenu() -{ - if (mIsContextMenu) - return nsnull; - - return nsMenuFrame::GetContextMenu(); -} - -nsIMenuFrame* +nsMenuFrame* nsMenuPopupFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, PRBool& doAction) { PRUint32 charCode, keyCode; @@ -1377,22 +1398,22 @@ nsMenuPopupFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, PRBool& doActi // Enumerate over our list of frames. nsIFrame* immediateParent = nsnull; - GetInsertionPoint(PresContext()->PresShell(), this, nsnull, - &immediateParent); + PresContext()->PresShell()-> + FrameConstructor()->GetInsertionPoint(this, nsnull, &immediateParent); if (!immediateParent) immediateParent = this; PRUint32 matchCount = 0, matchShortcutCount = 0; PRBool foundActive = PR_FALSE; PRBool isShortcut; - nsIMenuFrame* frameBefore = nsnull; - nsIMenuFrame* frameAfter = nsnull; - nsIMenuFrame* frameShortcut = nsnull; + nsMenuFrame* frameBefore = nsnull; + nsMenuFrame* frameAfter = nsnull; + nsMenuFrame* frameShortcut = nsnull; nsIContent* parentContent = mContent->GetParent(); - PRBool isMenu = - parentContent && parentContent->Tag() != nsGkAtoms::menulist; + PRBool isMenu = parentContent && + !parentContent->NodeInfo()->Equals(nsGkAtoms::menulist, kNameSpaceID_XUL); static DOMTimeStamp lastKeyTime = 0; DOMTimeStamp keyTime; @@ -1445,16 +1466,21 @@ nsMenuPopupFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, PRBool& doActi // currFrame = immediateParent->GetFirstChild(nsnull); + PRInt32 menuAccessKey = -1; + nsMenuBarListener::GetMenuAccessKey(&menuAccessKey); + // We start searching from first child. This process is divided into two parts // -- before current and after current -- by the current item while (currFrame) { nsIContent* current = currFrame->GetContent(); // See if it's a menu item. - if (IsValidItem(current)) { + if (nsXULPopupManager::IsValidMenuItem(PresContext(), current, PR_TRUE)) { nsAutoString textKey; - // Get the shortcut attribute. - current->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, textKey); + if (menuAccessKey >= 0) { + // Get the shortcut attribute. + current->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, textKey); + } if (textKey.IsEmpty()) { // No shortcut, try first letter isShortcut = PR_FALSE; current->GetAttr(kNameSpaceID_None, nsGkAtoms::label, textKey); @@ -1467,25 +1493,24 @@ nsMenuPopupFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, PRBool& doActi if (StringBeginsWith(textKey, incrementalString, nsCaseInsensitiveStringComparator())) { // mIncrementalString is a prefix of textKey - nsIMenuFrame* menuFrame; - if (NS_SUCCEEDED(CallQueryInterface(currFrame, &menuFrame))) { + if (currFrame->GetType() == nsGkAtoms::menuFrame) { // There is one match matchCount++; if (isShortcut) { // There is one shortcut-key match matchShortcutCount++; // Record the matched item. If there is only one matched shortcut item, do it - frameShortcut = menuFrame; + frameShortcut = NS_STATIC_CAST(nsMenuFrame *, currFrame); } if (!foundActive) { // It's a first candidate item located before/on the current item if (!frameBefore) - frameBefore = menuFrame; + frameBefore = NS_STATIC_CAST(nsMenuFrame *, currFrame); } else { // It's a first candidate item located after the current item if (!frameAfter) - frameAfter = menuFrame; + frameAfter = NS_STATIC_CAST(nsMenuFrame *, currFrame); } } else @@ -1499,11 +1524,8 @@ nsMenuPopupFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, PRBool& doActi if (stringLength > 1) { // If there is more than one char typed, the current item has highest priority, // otherwise the item next to current has highest priority - nsIMenuFrame* menuFrame; - if (NS_SUCCEEDED(CallQueryInterface(currFrame, &menuFrame)) && - menuFrame == frameBefore) { + if (currFrame == frameBefore) return frameBefore; - } } } } @@ -1536,237 +1558,12 @@ nsMenuPopupFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, PRBool& doActi return nsnull; } -NS_IMETHODIMP -nsMenuPopupFrame::ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag) -{ - // See if we have a context menu open. - nsIMenuParent *contextMenu = GetContextMenu(); - if (contextMenu) - return contextMenu->ShortcutNavigation(aKeyEvent, aHandledFlag); - - if (mCurrentMenu) { - PRBool isOpen = PR_FALSE; - mCurrentMenu->MenuIsOpen(isOpen); - if (isOpen) { - // No way this applies to us. Give it to our child. - mCurrentMenu->ShortcutNavigation(aKeyEvent, aHandledFlag); - return NS_OK; - } - } - - // This applies to us. Let's see if one of the shortcuts applies - PRBool action; - nsIMenuFrame* result = FindMenuWithShortcut(aKeyEvent, action); - if (result) { - // We got one! - nsIFrame* frame = nsnull; - CallQueryInterface(result, &frame); - nsWeakFrame weakResult(frame); - aHandledFlag = PR_TRUE; - SetCurrentMenuItem(result); - if (action && weakResult.IsAlive()) { - result->Enter(); - } - } - - return NS_OK; -} - -NS_IMETHODIMP -nsMenuPopupFrame::KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag) -{ - // See if we have a context menu open. - nsIMenuParent *contextMenu = GetContextMenu(); - if (contextMenu) - return contextMenu->KeyboardNavigation(aKeyCode, aHandledFlag); - - nsNavigationDirection theDirection; - NS_DIRECTION_FROM_KEY_CODE(theDirection, aKeyCode); - - mIncrementalString.Truncate(); - - // This method only gets called if we're open. - if (!mCurrentMenu && NS_DIRECTION_IS_INLINE(theDirection)) { - // We've been opened, but we haven't had anything selected. - // We can handle End, but our parent handles Start. - if (theDirection == eNavigationDirection_End) { - nsIMenuFrame* nextItem = GetNextMenuItem(nsnull); - if (nextItem) { - aHandledFlag = PR_TRUE; - SetCurrentMenuItem(nextItem); - } - } - return NS_OK; - } - - PRBool isContainer = PR_FALSE; - PRBool isOpen = PR_FALSE; - PRBool isDisabled = PR_FALSE; - nsWeakFrame weakFrame(this); - if (mCurrentMenu) { - mCurrentMenu->MenuIsContainer(isContainer); - mCurrentMenu->MenuIsOpen(isOpen); - mCurrentMenu->MenuIsDisabled(isDisabled); - - if (isOpen) { - // Give our child a shot. - mCurrentMenu->KeyboardNavigation(aKeyCode, aHandledFlag); - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); - } - else if (theDirection == eNavigationDirection_End && - isContainer && !isDisabled) { - // The menu is not yet open. Open it and select the first item. - aHandledFlag = PR_TRUE; - nsIFrame* frame = nsnull; - CallQueryInterface(mCurrentMenu, &frame); - nsWeakFrame weakCurrentFrame(frame); - mCurrentMenu->OpenMenu(PR_TRUE); - NS_ENSURE_TRUE(weakCurrentFrame.IsAlive(), NS_OK); - mCurrentMenu->SelectFirstItem(); - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); - } - } - - if (aHandledFlag) - return NS_OK; // The child menu took it for us. - - // For block progression, we can move in either direction - if (NS_DIRECTION_IS_BLOCK(theDirection) || - NS_DIRECTION_IS_BLOCK_TO_EDGE(theDirection)) { - - nsIMenuFrame* nextItem; - - if (theDirection == eNavigationDirection_Before) - nextItem = GetPreviousMenuItem(mCurrentMenu); - else if (theDirection == eNavigationDirection_After) - nextItem = GetNextMenuItem(mCurrentMenu); - else if (theDirection == eNavigationDirection_First) - nextItem = GetNextMenuItem(nsnull); - else - nextItem = GetPreviousMenuItem(nsnull); - - if (nextItem) { - aHandledFlag = PR_TRUE; - SetCurrentMenuItem(nextItem); - } - } - else if (mCurrentMenu && isContainer && isOpen) { - if (theDirection == eNavigationDirection_Start) { - // Close it up. - mCurrentMenu->OpenMenu(PR_FALSE); - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK); - // SelectMenu() so DOMMenuItemActive is fired for accessibility - mCurrentMenu->SelectMenu(PR_TRUE); - aHandledFlag = PR_TRUE; - } - } - - return NS_OK; -} - -NS_IMETHODIMP -nsMenuPopupFrame::GetParentPopup(nsIMenuParent** aMenuParent) -{ - *aMenuParent = nsnull; - nsIFrame* parent = GetParent(); - while (parent) { - nsCOMPtr menuParent = do_QueryInterface(parent); - if (menuParent) { - *aMenuParent = menuParent.get(); - NS_ADDREF(*aMenuParent); - return NS_OK; - } - parent = parent->GetParent(); - } - return NS_OK; -} - -NS_IMETHODIMP -nsMenuPopupFrame::HideChain() -{ - if (!mShouldRollup) - return NS_OK; - - // Stop capturing rollups - // (must do this during Hide, which happens before the menu item is executed, - // since this reinstates normal event handling.) - nsMenuDismissalListener::Shutdown(); - - nsIFrame* frame = GetParent(); - if (frame) { - nsWeakFrame weakMenu(frame); - nsIMenuFrame* menuFrame; - if (NS_FAILED(CallQueryInterface(frame, &menuFrame))) { - nsIPopupSetFrame* popupSetFrame = GetPopupSetFrame(PresContext()); - if (popupSetFrame) - // Hide the popup. - popupSetFrame->HidePopup(this); - return NS_OK; - } - - menuFrame->ActivateMenu(PR_FALSE); - NS_ENSURE_TRUE(weakMenu.IsAlive(), NS_OK); - menuFrame->SelectMenu(PR_FALSE); - NS_ENSURE_TRUE(weakMenu.IsAlive(), NS_OK); - - // Get the parent. - nsIMenuParent *menuParent = menuFrame->GetMenuParent(); - if (menuParent) - menuParent->HideChain(); - } - - return NS_OK; -} - -NS_IMETHODIMP -nsMenuPopupFrame::DismissChain() -{ - if (!mShouldRollup) - return NS_OK; - - // Stop capturing rollups - nsMenuDismissalListener::Shutdown(); - - // Get our menu parent. - nsIFrame* frame = GetParent(); - if (frame) { - nsIMenuFrame *menuFrame = nsnull; - CallQueryInterface(frame, &menuFrame); - if (!menuFrame) { - nsIPopupSetFrame* popupSetFrame = GetPopupSetFrame(PresContext()); - if (popupSetFrame) { - // make sure the menu is not highlighted - if (mCurrentMenu) { - PRBool wasOpen; - mCurrentMenu->MenuIsOpen(wasOpen); - if (wasOpen) - mCurrentMenu->OpenMenu(PR_FALSE); - mCurrentMenu->SelectMenu(PR_FALSE); - } - // Destroy the popup. - popupSetFrame->DestroyPopup(this, PR_TRUE); - } - return NS_OK; - } - - menuFrame->OpenMenu(PR_FALSE); - - // Get the parent. - nsIMenuParent* menuParent = menuFrame->GetMenuParent(); - if (menuParent) - menuParent->DismissChain(); - } - - return NS_OK; -} - NS_IMETHODIMP nsMenuPopupFrame::GetWidget(nsIWidget **aWidget) { // Get parent view - nsIView * view = nsnull; // XXX should this be passing PR_FALSE or PR_TRUE for aStopAtViewManagerRoot? - nsMenuPopupFrame::GetRootViewForPopup(this, PR_FALSE, &view); + nsIView * view = GetRootViewForPopup(this, PR_FALSE); if (!view) return NS_OK; @@ -1775,79 +1572,14 @@ nsMenuPopupFrame::GetWidget(nsIWidget **aWidget) return NS_OK; } -NS_IMETHODIMP +void nsMenuPopupFrame::AttachedDismissalListener() { mConsumeRollupEvent = nsIPopupBoxObject::ROLLUP_DEFAULT; - return NS_OK; -} - -NS_IMETHODIMP -nsMenuPopupFrame::InstallKeyboardNavigator() -{ - if (mKeyboardNavigator) - return NS_OK; - - nsCOMPtr target = do_QueryInterface(mContent->GetDocument()); - - mTarget = target; - mKeyboardNavigator = new nsMenuListener(this); - NS_IF_ADDREF(mKeyboardNavigator); - - target->AddEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); - target->AddEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); - target->AddEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); - - nsContentUtils::NotifyInstalledMenuKeyboardListener(PR_TRUE); - - return NS_OK; -} - -NS_IMETHODIMP -nsMenuPopupFrame::RemoveKeyboardNavigator() -{ - if (!mKeyboardNavigator) - return NS_OK; - - mTarget->RemoveEventListener(NS_LITERAL_STRING("keypress"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); - mTarget->RemoveEventListener(NS_LITERAL_STRING("keydown"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); - mTarget->RemoveEventListener(NS_LITERAL_STRING("keyup"), (nsIDOMKeyListener*)mKeyboardNavigator, PR_TRUE); - - NS_IF_RELEASE(mKeyboardNavigator); - - nsContentUtils::NotifyInstalledMenuKeyboardListener(PR_FALSE); - - return NS_OK; } // helpers ///////////////////////////////////////////////////////////// -PRBool -nsMenuPopupFrame::IsValidItem(nsIContent* aContent) -{ - nsIAtom *tag = aContent->Tag(); - - PRBool skipNavigatingDisabledMenuItem; - PresContext()->LookAndFeel()-> - GetMetric(nsILookAndFeel::eMetric_SkipNavigatingDisabledMenuItem, - skipNavigatingDisabledMenuItem); - - PRBool result = (tag == nsGkAtoms::menu || - tag == nsGkAtoms::menuitem || - tag == nsGkAtoms::option); - if (skipNavigatingDisabledMenuItem) - result = result && !IsDisabled(aContent); - - return result; -} - -PRBool -nsMenuPopupFrame::IsDisabled(nsIContent* aContent) -{ - return aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, - nsGkAtoms::_true, eCaseMatters); -} - NS_IMETHODIMP nsMenuPopupFrame::AttributeChanged(PRInt32 aNameSpaceID, nsIAtom* aAttribute, @@ -1859,11 +1591,19 @@ nsMenuPopupFrame::AttributeChanged(PRInt32 aNameSpaceID, if (aAttribute == nsGkAtoms::left || aAttribute == nsGkAtoms::top) MoveToAttributePosition(); + + // accessibility needs this to ensure the frames get constructed when the + // menugenerated attribute is set, see bug 279703 comment 42 for discussion + if (aAttribute == nsGkAtoms::menugenerated && + mFrames.IsEmpty() && !mGeneratedChildren) { + PresContext()->PresShell()->FrameConstructor()-> + AddLazyChildren(mContent, LazyGeneratePopupDone, nsnull); + } return rv; } -void +void nsMenuPopupFrame::MoveToAttributePosition() { // Move the widget around when the user sets the |left| and |top| attributes. @@ -1873,145 +1613,29 @@ nsMenuPopupFrame::MoveToAttributePosition() nsAutoString left, top; mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::left, left); mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::top, top); - PRInt32 err1, err2, xPos, yPos; - xPos = left.ToInteger(&err1); - yPos = top.ToInteger(&err2); + PRInt32 err1, err2; + mScreenXPos = left.ToInteger(&err1); + mScreenYPos = top.ToInteger(&err2); - if (NS_SUCCEEDED(err1) && NS_SUCCEEDED(err2)) { - MoveToInternal(xPos, yPos); - } -} - - -NS_IMETHODIMP -nsMenuPopupFrame::HandleEvent(nsPresContext* aPresContext, - nsGUIEvent* aEvent, - nsEventStatus* aEventStatus) -{ - return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus); + if (NS_SUCCEEDED(err1) && NS_SUCCEEDED(err2)) + MoveToInternal(mScreenXPos, mScreenYPos); } void nsMenuPopupFrame::Destroy() { - // Null out the pointer to this frame in the mediator wrapper so that it - // doesn't try to interact with a deallocated frame. - mTimerMediator->ClearFrame(); - - if (mCloseTimer) - mCloseTimer->Cancel(); + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) + pm->PopupDestroyed(this); nsPresContext* rootPresContext = PresContext()->RootPresContext(); if (rootPresContext->ContainsActivePopup(this)) { rootPresContext->NotifyRemovedActivePopup(this); } - RemoveKeyboardNavigator(); nsBoxFrame::Destroy(); } -// REVIEW: The override here was doing nothing at all since nsBoxFrame is our -// parent class -// -// 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 -// |mCurrentMenu| is the item the mouse is currently over, which could be none of them. -// -// 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 -// enters the submenus. Now when the timer fires, |mCurrentMenu| is null (!= |mTimerMenu|) -// 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, -// the timer is set and |mTimerMenu| is A and |mCurrentMenu| is C. As the timer fires, -// 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 -nsMenuPopupFrame::Notify(nsITimer* aTimer) -{ - // Our timer has fired. - if (aTimer == mCloseTimer.get()) { - PRBool menuOpen = PR_FALSE; - mTimerMenu->MenuIsOpen(menuOpen); - if (menuOpen) - mTimerMenu->OpenMenu(PR_FALSE); - - if (mCloseTimer) - mCloseTimer->Cancel(); - } - - mCloseTimer = nsnull; - mTimerMenu = nsnull; - return NS_OK; -} - -NS_IMETHODIMP -nsMenuPopupFrame::KillCloseTimer() -{ - if (mCloseTimer && mTimerMenu) { - PRBool menuOpen = PR_FALSE; - mTimerMenu->MenuIsOpen(menuOpen); - if (menuOpen) { - mTimerMenu->OpenMenu(PR_FALSE); - } - mCloseTimer->Cancel(); - mCloseTimer = nsnull; - mTimerMenu = nsnull; - } - return NS_OK; -} - - - -NS_IMETHODIMP -nsMenuPopupFrame::KillPendingTimers ( ) -{ - return KillCloseTimer(); - -} // KillPendingTimers - -NS_IMETHODIMP -nsMenuPopupFrame::CancelPendingTimers() -{ - if (mCloseTimer && mTimerMenu) { - if (mTimerMenu != mCurrentMenu) { - SetCurrentMenuItem(mTimerMenu); - } - mCloseTimer->Cancel(); - mCloseTimer = nsnull; - mTimerMenu = nsnull; - } - return NS_OK; -} - void nsMenuPopupFrame::MoveTo(PRInt32 aLeft, PRInt32 aTop) { @@ -2042,10 +1666,14 @@ nsMenuPopupFrame::MoveToInternal(PRInt32 aLeft, PRInt32 aTop) nsIView* view = GetView(); NS_ASSERTION(view->GetParent(), "Must have parent!"); - + // Retrieve screen position of parent view nsIntPoint screenPos = view->GetParent()->GetScreenPosition(); + nsPresContext* context = PresContext(); + aLeft = context->AppUnitsToDevPixels(nsPresContext::CSSPixelsToAppUnits(aLeft)); + aTop = context->AppUnitsToDevPixels(nsPresContext::CSSPixelsToAppUnits(aTop)); + // Move the widget // XXXbz don't we want screenPos to be the parent _widget_'s position, then? view->GetWidget()->Move(aLeft - screenPos.x, aTop - screenPos.y); @@ -2063,60 +1691,8 @@ nsMenuPopupFrame::SetAutoPosition(PRBool aShouldAutoPosition) mShouldAutoPosition = aShouldAutoPosition; } -void -nsMenuPopupFrame::EnableRollup(PRBool aShouldRollup) -{ - if (!nsMenuDismissalListener::sInstance || - nsMenuDismissalListener::sInstance->GetCurrentMenuParent() != this) - return; - - if (aShouldRollup) - nsMenuDismissalListener::sInstance->Register(); - else - nsMenuDismissalListener::sInstance->Unregister(); -} - void nsMenuPopupFrame::SetConsumeRollupEvent(PRUint32 aConsumeMode) { mConsumeRollupEvent = aConsumeMode; } - -// nsMenuPopupTimerMediator implementation. -NS_IMPL_ISUPPORTS1(nsMenuPopupTimerMediator, nsITimerCallback) - -/** - * Constructs a wrapper around an nsMenuFrame. - * @param aFrame nsMenuFrame to create a wrapper around. - */ -nsMenuPopupTimerMediator::nsMenuPopupTimerMediator(nsMenuPopupFrame *aFrame) : - mFrame(aFrame) -{ - NS_ASSERTION(mFrame, "Must have frame"); -} - -nsMenuPopupTimerMediator::~nsMenuPopupTimerMediator() -{ -} - -/** - * Delegates the notification to the contained frame if it has not been destroyed. - * @param aTimer Timer which initiated the callback. - * @return NS_ERROR_FAILURE if the frame has been destroyed. - */ -NS_IMETHODIMP nsMenuPopupTimerMediator::Notify(nsITimer* aTimer) -{ - if (!mFrame) - return NS_ERROR_FAILURE; - - return mFrame->Notify(aTimer); -} - -/** - * Clear the pointer to the contained nsMenuFrame. This should be called - * when the contained nsMenuFrame is destroyed. - */ -void nsMenuPopupTimerMediator::ClearFrame() -{ - mFrame = nsnull; -} diff --git a/mozilla/layout/xul/base/src/nsMenuPopupFrame.h b/mozilla/layout/xul/base/src/nsMenuPopupFrame.h index 717d593040d..217b07a81da 100644 --- a/mozilla/layout/xul/base/src/nsMenuPopupFrame.h +++ b/mozilla/layout/xul/base/src/nsMenuPopupFrame.h @@ -47,9 +47,10 @@ #include "prtypes.h" #include "nsIAtom.h" +#include "nsGkAtoms.h" #include "nsCOMPtr.h" +#include "nsMenuFrame.h" #include "nsIDOMEventTarget.h" -#include "nsMenuListener.h" #include "nsBoxFrame.h" #include "nsIMenuParent.h" @@ -57,6 +58,20 @@ #include "nsITimer.h" +enum nsPopupType { + ePopupTypePanel, + ePopupTypeMenu, + ePopupTypeTooltip +}; + +// values are selected so that the direction can be flipped just by +// changing the sign +#define POPUPALIGNMENT_NONE 0 +#define POPUPALIGNMENT_TOPLEFT 1 +#define POPUPALIGNMENT_TOPRIGHT -1 +#define POPUPALIGNMENT_BOTTOMLEFT 2 +#define POPUPALIGNMENT_BOTTOMRIGHT -2 + #define INC_TYP_INTERVAL 1000 // 1s. If the interval between two keypresses is shorter than this, // treat as a continue typing // XXX, kyle.yuan@sun.com, there are 4 definitions for the same purpose: @@ -69,75 +84,54 @@ nsIFrame* NS_NewMenuPopupFrame(nsIPresShell* aPresShell, nsStyleContext* aContex class nsIViewManager; class nsIView; class nsIMenuParent; -class nsIMenuFrame; -class nsIDOMXULDocument; - class nsMenuPopupFrame; -/** - * nsMenuPopupTimerMediator is a wrapper around an nsMenuPopupFrame which can be safely - * passed to timers. The class is reference counted unlike the underlying - * nsMenuPopupFrame, so that it will exist as long as the timer holds a reference - * to it. The callback is delegated to the contained nsMenuPopupFrame as long as - * the contained nsMenuPopupFrame has not been destroyed. - */ -class nsMenuPopupTimerMediator : public nsITimerCallback -{ -public: - nsMenuPopupTimerMediator(nsMenuPopupFrame* aFrame); - ~nsMenuPopupTimerMediator(); - - NS_DECL_ISUPPORTS - NS_DECL_NSITIMERCALLBACK - - void ClearFrame(); - -private: - - // Pointer to the wrapped frame. - nsMenuPopupFrame* mFrame; -}; - class nsMenuPopupFrame : public nsBoxFrame, public nsIMenuParent { public: nsMenuPopupFrame(nsIPresShell* aShell, nsStyleContext* aContext); - NS_DECL_ISUPPORTS - - // nsIMenuParentInterface - virtual nsIMenuFrame* GetCurrentMenuItem(); - NS_IMETHOD SetCurrentMenuItem(nsIMenuFrame* aMenuItem); - virtual nsIMenuFrame* GetNextMenuItem(nsIMenuFrame* aStart); - virtual nsIMenuFrame* GetPreviousMenuItem(nsIMenuFrame* aStart); + virtual nsMenuFrame* GetCurrentMenuItem(); + NS_IMETHOD SetCurrentMenuItem(nsMenuFrame* aMenuItem); + virtual void CurrentMenuIsBeingDestroyed(); + NS_IMETHOD ChangeMenuItem(nsMenuFrame* aMenuItem, PRBool aSelectFirstItem); + + // as popups are opened asynchronously, the popup pending state is used to + // prevent multiple requests from attempting to open the same popup twice + PRBool IsOpenPending() { return mIsOpenPending; } + void ClearOpenPending() { mIsOpenPending = PR_FALSE; } + NS_IMETHOD SetActive(PRBool aActiveFlag) { return NS_OK; } // We don't care. - NS_IMETHOD GetIsActive(PRBool& isActive) { isActive = PR_FALSE; return NS_OK; } - NS_IMETHOD IsMenuBar(PRBool& isMenuBar) { isMenuBar = PR_FALSE; return NS_OK; } - NS_IMETHOD ConsumeOutsideClicks(PRBool& aConsumeOutsideClicks); - NS_IMETHOD ClearRecentlyRolledUp() {return NS_OK;} - NS_IMETHOD RecentlyRolledUp(nsIMenuFrame *aMenuFrame, PRBool *aJustRolledUp) {*aJustRolledUp = PR_FALSE; return NS_OK;} - NS_IMETHOD SetIsContextMenu(PRBool aIsContextMenu) { mIsContextMenu = aIsContextMenu; return NS_OK; } - NS_IMETHOD GetIsContextMenu(PRBool& aIsContextMenu) { aIsContextMenu = mIsContextMenu; return NS_OK; } - - NS_IMETHOD GetParentPopup(nsIMenuParent** aResult); + virtual PRBool IsActive() { return PR_FALSE; } + virtual PRBool IsMenuBar() { return PR_FALSE; } - // Closes up the chain of open cascaded menus. - NS_IMETHOD DismissChain(); + /* + * When this popup is open, should clicks outside of it be consumed? + * Return PR_TRUE if the popup should rollup on an outside click, + * but consume that click so it can't be used for anything else. + * Return PR_FALSE to allow clicks outside the popup to activate content + * even when the popup is open. + * --------------------------------------------------------------------- + * + * Should clicks outside of a popup be eaten? + * + * Menus Autocomplete Comboboxes + * Mac Eat No Eat + * Win No No Eat + * Unix Eat No Eat + * + */ + PRBool ConsumeOutsideClicks(); - // Hides the chain of cascaded menus without closing them up. - NS_IMETHOD HideChain(); + virtual PRBool IsContextMenu() { return mIsContextMenu; } - NS_IMETHOD KillPendingTimers(); - NS_IMETHOD CancelPendingTimers(); - - NS_IMETHOD InstallKeyboardNavigator(); - NS_IMETHOD RemoveKeyboardNavigator(); + virtual PRBool MenuClosed() { return PR_TRUE; } NS_IMETHOD GetWidget(nsIWidget **aWidget); // The dismissal listener gets created and attached to the window. - NS_IMETHOD AttachedDismissalListener(); + void AttachedDismissalListener(); // Overridden methods NS_IMETHOD Init(nsIContent* aContent, @@ -148,41 +142,92 @@ public: nsIAtom* aAttribute, PRInt32 aModType); - NS_IMETHOD HandleEvent(nsPresContext* aPresContext, - nsGUIEvent* aEvent, - nsEventStatus* aEventStatus); - virtual void Destroy(); virtual void InvalidateInternal(const nsRect& aDamageRect, nscoord aX, nscoord aY, nsIFrame* aForChild, PRBool aImmediate); + void EnsureWidget(); + virtual nsresult CreateWidgetForView(nsIView* aView); + NS_IMETHOD SetInitialChildList(nsIAtom* aListName, + nsIFrame* aChildList); + + virtual PRBool IsLeaf() const + { + if (!mGeneratedChildren && mPopupType == ePopupTypeMenu) { + // menu popups generate their child frames lazily only when opened, so + // behave like a leaf frame. However, generate child frames normally if + // the parent menu has a sizetopopup attribute. In this case the size of + // the parent menu is dependant on the size of the popup, so the frames + // need to exist in order to calculate this size. + nsIContent* parentContent = mContent->GetParent(); + if (parentContent && + !parentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::sizetopopup)) + return PR_TRUE; + } + + return PR_FALSE; + } + + // AdjustView should be called by the parent frame after the popup has been + // laid out, so that the view can be shown. + void AdjustView(); + void GetViewOffset(nsIView* aView, nsPoint& aPoint); - static void GetRootViewForPopup(nsIFrame* aStartFrame, - PRBool aStopAtViewManagerRoot, - nsIView** aResult); + nsIView* GetRootViewForPopup(nsIFrame* aStartFrame, + PRBool aStopAtViewManagerRoot); - nsresult SyncViewWithFrame(nsPresContext* aPresContext, const nsString& aPopupAnchor, - const nsString& aPopupAlign, - nsIFrame* aFrame, PRInt32 aXPos, PRInt32 aYPos); + // set the position of the popup either relative to the anchor aAnchorFrame + // (or the frame for mAnchorContent if aAnchorFrame is null) or at a specific + // point if a screen position (mScreenXPos and mScreenYPos) are set. The popup + // will be adjusted so that it is on screen. + nsresult SetPopupPosition(nsIFrame* aAnchorFrame); - NS_IMETHOD KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag); - NS_IMETHOD ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag); - - NS_IMETHOD Escape(PRBool& aHandledFlag); - NS_IMETHOD Enter(); + PRBool HasGeneratedChildren() { return mGeneratedChildren; } + void SetGeneratedChildren() { mGeneratedChildren = PR_TRUE; } - nsIMenuFrame* FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, PRBool& doAction); + // called when the Enter key is pressed while the popup is open. This will + // just pass the call down to the current menu, if any. Also, calling Enter + // will reset the current incremental search string, calculated in + // FindMenuWithShortcut + nsMenuFrame* Enter(); - PRBool IsValidItem(nsIContent* aContent); - PRBool IsDisabled(nsIContent* aContent); + PRInt32 PopupType() const { return mPopupType; } + PRBool IsMenu() { return mPopupType == ePopupTypeMenu; } + PRBool IsOpen() { return mIsOpen; } + PRBool HasOpenChanged() { return mIsOpenChanged; } - nsIMenuParent* GetContextMenu(); + // the Initialize methods are used to set the anchor position for + // each way of opening a popup. + void InitializePopup(nsIContent* aAnchorContent, + const nsAString& aPosition, + PRInt32 aXPos, PRInt32 aYPos, + PRBool aAttributesOverride); - NS_IMETHOD KillCloseTimer(); + void InitializePopupAtScreen(PRInt32 aXPos, PRInt32 aYPos); + + void InitializePopupWithAnchorAlign(nsIContent* aAnchorContent, + nsAString& aAnchor, + nsAString& aAlign, + PRInt32 aXPos, PRInt32 aYPos); + + // indicate that the popup should be opened + PRBool ShowPopup(PRBool aIsContextMenu, PRBool aSelectFirstItem); + // indicate that the popup should be hidden + void HidePopup(PRBool aDeselectMenu); + + // locate and return the menu frame that should be activated for the + // supplied key event. If doAction is set to true by this method, + // then the menu's action should be carried out, as if the user had pressed + // the Enter key. If doAction is false, the menu should just be highlighted. + // This method also handles incremental searching in menus so the user can + // type the first few letters of an item/s name to select it. + nsMenuFrame* FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, PRBool& doAction); + + void ClearIncrementalString() { mIncrementalString.Truncate(); } virtual nsIAtom* GetType() const { return nsGkAtoms::menuPopupFrame; } @@ -193,7 +238,7 @@ public: } #endif - void EnsureMenuItemIsVisible(nsIMenuFrame* aMenuFrame); + void EnsureMenuItemIsVisible(nsMenuFrame* aMenuFrame); // This sets 'left' and 'top' attributes. // May kill the frame. @@ -201,30 +246,22 @@ public: void GetAutoPosition(PRBool* aShouldAutoPosition); void SetAutoPosition(PRBool aShouldAutoPosition); - void EnableRollup(PRBool aShouldRollup); void SetConsumeRollupEvent(PRUint32 aConsumeMode); nsIScrollableView* GetScrollableView(nsIFrame* aStart); protected: - friend class nsMenuPopupTimerMediator; - NS_HIDDEN_(nsresult) Notify(nsITimer* aTimer); + // Move without updating attributes. + void MoveToInternal(PRInt32 aLeft, PRInt32 aTop); - // Move without updating attributes. - void MoveToInternal(PRInt32 aLeft, PRInt32 aTop); - - // redefine to tell the box system not to move the - // views. + // redefine to tell the box system not to move the views. virtual void GetLayoutFlags(PRUint32& aFlags); - // given x,y in client coordinates, compensate for nested documents like framesets. - void AdjustClientXYForNestedDocuments ( nsIDOMXULDocument* inPopupDoc, nsIPresShell* inPopupShell, - PRInt32 inClientX, PRInt32 inClientY, - PRInt32* outAdjX, PRInt32* outAdjY ) ; + void InitPositionFromAnchorAlign(const nsAString& aAnchor, + const nsAString& aAlign); void AdjustPositionForAnchorAlign ( PRInt32* ioXPos, PRInt32* ioYPos, const nsRect & inParentRect, - const nsString& aPopupAnchor, const nsString& aPopupAlign, - PRBool* outFlushWithTopBottom ) ; + PRBool* outFlushWithTopBottom ) ; PRBool IsMoreRoomOnOtherSideOfParent ( PRBool inFlushAboveBelow, PRInt32 inScreenViewLocX, PRInt32 inScreenViewLocY, const nsRect & inScreenParentFrameRect, PRInt32 inScreenTopTwips, PRInt32 inScreenLeftTwips, @@ -238,24 +275,33 @@ protected: // Move the popup to the position specified in its |left| and |top| attributes. void MoveToAttributePosition(); + // the content that the popup is anchored to, if any, which may be in a + // different document than the popup. + nsCOMPtr mAnchorContent; - nsIMenuFrame* mCurrentMenu; // The current menu that is active. + nsMenuFrame* mCurrentMenu; // The current menu that is active. - nsMenuListener* mKeyboardNavigator; // The listener that tells us about key events. - nsIDOMEventTarget* mTarget; + // popup alignment relative to the anchor node + PRInt8 mPopupAlignment; + PRInt8 mPopupAnchor; - nsIMenuFrame* mTimerMenu; // A menu awaiting closure. - nsCOMPtr mCloseTimer; // Close timer. + // the position of the popup. The screen coordinates, if set to values other + // than -1, override mXPos and mYPos. + PRInt32 mXPos; + PRInt32 mYPos; + PRInt32 mScreenXPos; + PRInt32 mScreenYPos; - // Reference to the mediator which wraps this frame. - nsRefPtr mTimerMediator; + nsPopupType mPopupType; // type of popup + + PRPackedBool mIsOpen; // true if the popup is open + PRPackedBool mIsOpenChanged; // true if the open state changed since the last layout + PRPackedBool mIsOpenPending; // true if an open is pending + PRPackedBool mIsContextMenu; // true for context menus + PRPackedBool mGeneratedChildren; // true if the contents have been created - PRPackedBool mIsContextMenu; // is this a context menu? - PRPackedBool mMenuCanOverlapOSBar; // can we appear over the taskbar/menubar? - - PRPackedBool mShouldAutoPosition; // Should SyncViewWithFrame be allowed to auto position popup? - PRPackedBool mShouldRollup; // Should this menupopup be allowed to dismiss automatically? + PRPackedBool mShouldAutoPosition; // Should SetPopupPosition be allowed to auto position popup? PRPackedBool mConsumeRollupEvent; // Should the rollup event be consumed? PRPackedBool mInContentShell; // True if the popup is in a content shell diff --git a/mozilla/layout/xul/base/src/nsPopupBoxObject.cpp b/mozilla/layout/xul/base/src/nsPopupBoxObject.cpp index bd496793bd0..07f1e2fdbf9 100644 --- a/mozilla/layout/xul/base/src/nsPopupBoxObject.cpp +++ b/mozilla/layout/xul/base/src/nsPopupBoxObject.cpp @@ -38,7 +38,6 @@ * ***** END LICENSE BLOCK ***** */ #include "nsCOMPtr.h" #include "nsIPopupBoxObject.h" -#include "nsIPopupSetFrame.h" #include "nsIRootBox.h" #include "nsBoxObject.h" #include "nsIPresShell.h" @@ -64,87 +63,86 @@ public: protected: virtual ~nsPopupBoxObject() {} - nsIPopupSetFrame* GetPopupSetFrame(); + nsPopupSetFrame* GetPopupSetFrame(); nsMenuPopupFrame* GetMenuPopupFrame() - { return NS_STATIC_CAST(nsMenuPopupFrame*, GetFrame(PR_FALSE)); } + { + nsIFrame* frame = GetFrame(PR_FALSE); + if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) + return NS_STATIC_CAST(nsMenuPopupFrame*, frame); + return nsnull; + } }; NS_IMPL_ISUPPORTS_INHERITED1(nsPopupBoxObject, nsBoxObject, nsIPopupBoxObject) -nsIPopupSetFrame* +nsPopupSetFrame* nsPopupBoxObject::GetPopupSetFrame() { nsIRootBox* rootBox = nsIRootBox::GetRootBox(GetPresShell(PR_FALSE)); if (!rootBox) return nsnull; - nsIFrame* popupSetFrame = rootBox->GetPopupSetFrame(); - if (!popupSetFrame) - return nsnull; - - nsIPopupSetFrame *popupSet = nsnull; - CallQueryInterface(popupSetFrame, &popupSet); - return popupSet; + return rootBox->GetPopupSetFrame(); } NS_IMETHODIMP nsPopupBoxObject::HidePopup() { - nsIPopupSetFrame *popupSet = GetPopupSetFrame(); - nsIFrame *ourFrame = GetFrame(PR_FALSE); - if (ourFrame && popupSet) { - nsWeakFrame weakFrame(ourFrame); - popupSet->HidePopup(ourFrame); - if (weakFrame.IsAlive()) { - popupSet->DestroyPopup(ourFrame, PR_TRUE); - } + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) + pm->HidePopup(mContent, PR_FALSE, PR_TRUE, PR_FALSE); + + return NS_OK; +} + +NS_IMETHODIMP +nsPopupBoxObject::ShowPopup(nsIDOMElement* aAnchorElement, + nsIDOMElement* aPopupElement, + PRInt32 aXPos, PRInt32 aYPos, + const PRUnichar *aPopupType, + const PRUnichar *aAnchorAlignment, + const PRUnichar *aPopupAlignment) +{ + NS_ENSURE_TRUE(aPopupElement, NS_ERROR_INVALID_ARG); + // srcContent can be null. + + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) { + nsCOMPtr anchorContent(do_QueryInterface(aAnchorElement)); + nsAutoString popupType(aPopupType); + nsAutoString anchor(aAnchorAlignment); + nsAutoString align(aPopupAlignment); + pm->ShowPopupWithAnchorAlign(mContent, anchorContent, anchor, align, + aXPos, aYPos, popupType.EqualsLiteral("context")); } return NS_OK; } NS_IMETHODIMP -nsPopupBoxObject::ShowPopup(nsIDOMElement* aSrcContent, - nsIDOMElement* aPopupContent, - PRInt32 aXPos, PRInt32 aYPos, - const PRUnichar *aPopupType, - const PRUnichar *anAnchorAlignment, - const PRUnichar *aPopupAlignment) +nsPopupBoxObject::OpenPopup(nsIDOMElement* aAnchorElement, + const nsAString& aPosition, + PRInt32 aXPos, PRInt32 aYPos, + PRBool aIsContextMenu, + PRBool aAttributesOverride) { - nsIPopupSetFrame *popupSet = GetPopupSetFrame(); - if (!popupSet) { - return NS_OK; + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) { + nsCOMPtr anchorContent(do_QueryInterface(aAnchorElement)); + pm->ShowPopup(mContent, anchorContent, aPosition, aXPos, aYPos, + aIsContextMenu, aAttributesOverride, PR_FALSE); } - nsCOMPtr srcContent(do_QueryInterface(aSrcContent)); - nsCOMPtr popupContent(do_QueryInterface(aPopupContent)); - NS_ENSURE_TRUE(popupContent, NS_ERROR_INVALID_ARG); - // srcContent can be null. + return NS_OK; +} - nsAutoString popupType(aPopupType); - nsAutoString anchorAlign(anAnchorAlignment); - nsAutoString popupAlign(aPopupAlignment); - - // Use |left| and |top| dimension attributes to position the popup if - // present, as they may have been persisted. - nsAutoString left, top; - popupContent->GetAttr(kNameSpaceID_None, nsGkAtoms::left, left); - popupContent->GetAttr(kNameSpaceID_None, nsGkAtoms::top, top); - - PRInt32 err; - if (!left.IsEmpty()) { - aXPos = left.ToInteger(&err); - if (NS_FAILED(err)) - return err; - } - if (!top.IsEmpty()) { - aYPos = top.ToInteger(&err); - if (NS_FAILED(err)) - return err; - } - - return popupSet->ShowPopup(srcContent, popupContent, aXPos, aYPos, - popupType, anchorAlign, popupAlign); +NS_IMETHODIMP +nsPopupBoxObject::OpenPopupAtScreen(PRInt32 aXPos, PRInt32 aYPos, PRBool aIsContextMenu) +{ + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) + pm->ShowPopupAtScreen(mContent, aXPos, aYPos, aIsContextMenu); + return NS_OK; } NS_IMETHODIMP @@ -197,11 +195,7 @@ nsPopupBoxObject::SetAutoPosition(PRBool aShouldAutoPosition) NS_IMETHODIMP nsPopupBoxObject::EnableRollup(PRBool aShouldRollup) { - nsMenuPopupFrame *menuPopupFrame = GetMenuPopupFrame(); - if (menuPopupFrame) { - menuPopupFrame->EnableRollup(aShouldRollup); - } - + // this does nothing nows return NS_OK; } @@ -219,14 +213,12 @@ nsPopupBoxObject::SetConsumeRollupEvent(PRUint32 aConsume) NS_IMETHODIMP nsPopupBoxObject::EnableKeyboardNavigator(PRBool aEnableKeyboardNavigator) { - nsMenuPopupFrame *menuPopupFrame = GetMenuPopupFrame(); - if (menuPopupFrame) { - if (aEnableKeyboardNavigator) { - menuPopupFrame->InstallKeyboardNavigator(); - } else { - menuPopupFrame->RemoveKeyboardNavigator(); - } - } + // Use ignorekeys="true" on the popup instead of using this function. + if (aEnableKeyboardNavigator) + mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::ignorekeys, + NS_LITERAL_STRING("true"), PR_TRUE); + else + mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::ignorekeys, PR_TRUE); return NS_OK; } @@ -242,4 +234,3 @@ NS_NewPopupBoxObject(nsIBoxObject** aResult) NS_ADDREF(*aResult); return NS_OK; } - diff --git a/mozilla/layout/xul/base/src/nsPopupSetFrame.cpp b/mozilla/layout/xul/base/src/nsPopupSetFrame.cpp index 96ebd78f46c..8d90a719cab 100644 --- a/mozilla/layout/xul/base/src/nsPopupSetFrame.cpp +++ b/mozilla/layout/xul/base/src/nsPopupSetFrame.cpp @@ -66,47 +66,16 @@ #include "nsCSSFrameConstructor.h" #include "nsGUIEvent.h" #include "nsIRootBox.h" -#include "nsIFocusController.h" -#include "nsIDocShellTreeItem.h" -#include "nsIDocShell.h" -#include "nsPIDOMWindow.h" -#include "nsIInterfaceRequestorUtils.h" -#include "nsIBaseWindow.h" -#include "nsIViewManager.h" #define NS_MENU_POPUP_LIST_INDEX 0 nsPopupFrameList::nsPopupFrameList(nsIContent* aPopupContent, nsPopupFrameList* aNext) :mNextPopup(aNext), mPopupFrame(nsnull), - mPopupContent(aPopupContent), - mElementContent(nsnull), - mCreateHandlerSucceeded(PR_FALSE), - mIsOpen(PR_FALSE), - mLastPref(-1,-1) + mPopupContent(aPopupContent) { } -nsPopupFrameList* nsPopupFrameList::GetEntry(nsIContent* aPopupContent) { - if (aPopupContent == mPopupContent) - return this; - - if (mNextPopup) - return mNextPopup->GetEntry(aPopupContent); - - return nsnull; -} - -nsPopupFrameList* nsPopupFrameList::GetEntryByFrame(nsIFrame* aPopupFrame) { - if (aPopupFrame == mPopupFrame) - return this; - - if (mNextPopup) - return mNextPopup->GetEntryByFrame(aPopupFrame); - - return nsnull; -} - // // NS_NewPopupSetFrame // @@ -118,25 +87,6 @@ NS_NewPopupSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) return new (aPresShell) nsPopupSetFrame (aPresShell, aContext); } -NS_IMETHODIMP_(nsrefcnt) -nsPopupSetFrame::AddRef(void) -{ - return NS_OK; -} - -NS_IMETHODIMP_(nsrefcnt) -nsPopupSetFrame::Release(void) -{ - return NS_OK; -} - -// -// QueryInterface -// -NS_INTERFACE_MAP_BEGIN(nsPopupSetFrame) - NS_INTERFACE_MAP_ENTRY(nsIPopupSetFrame) -NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame) - NS_IMETHODIMP nsPopupSetFrame::Init(nsIContent* aContent, nsIFrame* aParent, @@ -198,31 +148,14 @@ nsPopupSetFrame::SetInitialChildList(nsIAtom* aListName, void nsPopupSetFrame::Destroy() { - // Remove our frame list. - if (mPopupList) { - // Try to hide any active popups - if (nsMenuDismissalListener::sInstance) { - nsIMenuParent *menuParent = - nsMenuDismissalListener::sInstance->GetCurrentMenuParent(); - nsIFrame* frame; - CallQueryInterface(menuParent, &frame); - // Rollup popups, but only if they're ours - if (frame && mPopupList->GetEntryByFrame(frame)) { - nsMenuDismissalListener::sInstance->Rollup(); - } - } + // remove each popup from the list as we go. + while (mPopupList) { + if (mPopupList->mPopupFrame) + mPopupList->mPopupFrame->Destroy(); - // Actually remove each popup from the list as we go. This - // keeps things consistent so reentering won't crash us - while (mPopupList) { - if (mPopupList->mPopupFrame) { - mPopupList->mPopupFrame->Destroy(); - } - - nsPopupFrameList* temp = mPopupList; - mPopupList = mPopupList->mNextPopup; - delete temp; - } + nsPopupFrameList* temp = mPopupList; + mPopupList = mPopupList->mNextPopup; + delete temp; } nsIRootBox *rootBox; @@ -244,10 +177,8 @@ nsPopupSetFrame::DoLayout(nsBoxLayoutState& aState) // lay out all of our currently open popups. nsPopupFrameList* currEntry = mPopupList; while (currEntry) { - nsIFrame* popupChild = currEntry->mPopupFrame; - if (popupChild) { - NS_ASSERTION(popupChild->IsBoxFrame(), "popupChild is not box!!"); - + nsMenuPopupFrame* popupChild = currEntry->mPopupFrame; + if (popupChild && popupChild->IsOpen()) { // then get its preferred size nsSize prefSize = popupChild->GetPrefSize(aState); nsSize minSize = popupChild->GetMinSize(aState); @@ -255,13 +186,8 @@ nsPopupSetFrame::DoLayout(nsBoxLayoutState& aState) BoundsCheck(minSize, prefSize, maxSize); - // if the pref size changed then set bounds to be the pref size - // and sync the view. Also set new pref size. - // if (currEntry->mLastPref != prefSize) { - popupChild->SetBounds(aState, nsRect(0,0,prefSize.width, prefSize.height)); - RepositionPopup(currEntry, aState); - currEntry->mLastPref = prefSize; - // } + popupChild->SetBounds(aState, nsRect(0,0,prefSize.width, prefSize.height)); + popupChild->SetPopupPosition(nsnull); // is the new size too small? Make sure we handle scrollbars correctly nsIBox* child = popupChild->GetChildBox(); @@ -280,493 +206,22 @@ nsPopupSetFrame::DoLayout(nsBoxLayoutState& aState) if (bounds.width < prefSize.width + scrollbars.left + scrollbars.right) { bounds.width += scrollbars.left + scrollbars.right; - //printf("Width=%d\n",width); popupChild->SetBounds(aState, bounds); } } } - + // layout the child popupChild->Layout(aState); - - // only size popup if open - if (currEntry->mCreateHandlerSucceeded) { - nsIView* view = popupChild->GetView(); - nsIViewManager* viewManager = view->GetViewManager(); - nsRect r(0, 0, bounds.width, bounds.height); - viewManager->ResizeView(view, r); - viewManager->SetViewVisibility(view, nsViewVisibility_kShow); - } + popupChild->AdjustView(); } currEntry = currEntry->mNextPopup; } - SyncLayout(aState); - return rv; } - -#ifdef DEBUG_LAYOUT -NS_IMETHODIMP -nsPopupSetFrame::SetDebug(nsBoxLayoutState& aState, PRBool aDebug) -{ - // see if our state matches the given debug state - PRBool debugSet = mState & NS_STATE_CURRENTLY_IN_DEBUG; - PRBool debugChanged = (!aDebug && debugSet) || (aDebug && !debugSet); - - // if it doesn't then tell each child below us the new debug state - if (debugChanged) - { - // XXXdwh fix later. nobody uses this anymore anyway. - } - - return NS_OK; -} - -nsresult -nsPopupSetFrame::SetDebug(nsBoxLayoutState& aState, nsIFrame* aList, PRBool aDebug) -{ - if (!aList) - return NS_OK; - - while (aList) { - if (aList->IsBoxFrame()) - aList->SetDebug(aState, aDebug); - - aList = aList->GetNextSibling(); - } - - return NS_OK; -} -#endif - - -void -nsPopupSetFrame::RepositionPopup(nsPopupFrameList* aEntry, nsBoxLayoutState& aState) -{ - // Sync up the view. - if (aEntry && aEntry->mElementContent) { - nsPresContext* presContext = aState.PresContext(); - nsIFrame* frameToSyncTo = presContext->PresShell()-> - GetPrimaryFrameFor(aEntry->mElementContent); - ((nsMenuPopupFrame*)(aEntry->mPopupFrame))->SyncViewWithFrame(presContext, - aEntry->mPopupAnchor, aEntry->mPopupAlign, frameToSyncTo, aEntry->mXPos, aEntry->mYPos); - } -} - -NS_IMETHODIMP -nsPopupSetFrame::ShowPopup(nsIContent* aElementContent, nsIContent* aPopupContent, - PRInt32 aXPos, PRInt32 aYPos, - const nsString& aPopupType, const nsString& anAnchorAlignment, - const nsString& aPopupAlignment) -{ - NS_ASSERTION(aElementContent != aPopupContent, "self referential popup"); - - if (!MayOpenPopup(this)) - return NS_OK; - - nsWeakFrame weakFrame(this); - // First fire the popupshowing event. - if (!OnCreate(aXPos, aYPos, aPopupContent) || !weakFrame.IsAlive()) - return NS_OK; - - // See if we already have an entry in our list. We must create a new one on a miss. - nsPopupFrameList* entry = nsnull; - if (mPopupList) - entry = mPopupList->GetEntry(aPopupContent); - if (!entry) { - entry = new nsPopupFrameList(aPopupContent, mPopupList); - if (!entry) - return NS_ERROR_OUT_OF_MEMORY; - mPopupList = entry; - } - - // Cache the element content we're supposed to sync to - entry->mPopupType = aPopupType; - entry->mElementContent = aElementContent; - entry->mPopupAlign = aPopupAlignment; - entry->mPopupAnchor = anAnchorAlignment; - entry->mXPos = aXPos; - entry->mYPos = aYPos; - - // If a frame exists already, go ahead and use it. - entry->mPopupFrame = PresContext()->PresShell() - ->GetPrimaryFrameFor(aPopupContent); - -#ifdef DEBUG_PINK - printf("X Pos: %d\n", mXPos); - printf("Y Pos: %d\n", mYPos); -#endif - - // Generate the popup. - entry->mCreateHandlerSucceeded = PR_TRUE; - entry->mIsOpen = PR_TRUE; - // This may destroy or change entry->mPopupFrame or remove the entry from - // mPopupList. |this| may also get deleted. - MarkAsGenerated(aPopupContent); - - if (!weakFrame.IsAlive()) { - return NS_OK; - } - - nsPopupFrameList* newEntry = - mPopupList ? mPopupList->GetEntry(aPopupContent) : nsnull; - if (!newEntry || newEntry != entry) { - NS_WARNING("The popup entry for aPopupContent has changed!"); - return NS_OK; - } - - // determine if this menu is a context menu and flag it - nsIMenuParent* childPopup = nsnull; - if (entry->mPopupFrame) - CallQueryInterface(entry->mPopupFrame, &childPopup); - if ( childPopup && aPopupType.EqualsLiteral("context") ) - childPopup->SetIsContextMenu(PR_TRUE); - - // Now open the popup. - OpenPopup(entry, PR_TRUE); - - if (!weakFrame.IsAlive()) { - return NS_OK; - } - - // Now fire the popupshown event. - OnCreated(aXPos, aYPos, aPopupContent); - - return NS_OK; -} - -NS_IMETHODIMP -nsPopupSetFrame::HidePopup(nsIFrame* aPopup) -{ - if (!mPopupList) - return NS_OK; // No active popups - - nsPopupFrameList* entry = mPopupList->GetEntryByFrame(aPopup); - if (!entry) - return NS_OK; - - if (entry->mCreateHandlerSucceeded) - ActivatePopup(entry, PR_FALSE); - - if (entry->mElementContent && entry->mPopupType.EqualsLiteral("context")) { - // If we are a context menu, and if we are attached to a - // menupopup, then hiding us should also hide the parent menu - // popup. - if (entry->mElementContent->Tag() == nsGkAtoms::menupopup) { - nsIFrame* popupFrame = PresContext()->PresShell() - ->GetPrimaryFrameFor(entry->mElementContent); - if (popupFrame) { - nsIMenuParent *menuParent; - if (NS_SUCCEEDED(CallQueryInterface(popupFrame, &menuParent))) { - menuParent->HideChain(); - } - } - } - } - - return NS_OK; -} - -NS_IMETHODIMP -nsPopupSetFrame::DestroyPopup(nsIFrame* aPopup, PRBool aDestroyEntireChain) -{ - if (!mPopupList) - return NS_OK; // No active popups - - nsPopupFrameList* entry = mPopupList->GetEntryByFrame(aPopup); - - if (entry && entry->mCreateHandlerSucceeded) { // ensure the popup was created before we try to destroy it - nsWeakFrame weakFrame(this); - OpenPopup(entry, PR_FALSE); - nsCOMPtr popupContent = entry->mPopupContent; - if (weakFrame.IsAlive()) { - if (aDestroyEntireChain && entry->mElementContent && entry->mPopupType.EqualsLiteral("context")) { - // If we are a context menu, and if we are attached to a - // menupopup, then destroying us should also dismiss the parent - // menu popup. - if (entry->mElementContent->Tag() == nsGkAtoms::menupopup) { - nsIFrame* popupFrame = PresContext()->PresShell() - ->GetPrimaryFrameFor(entry->mElementContent); - if (popupFrame) { - nsIMenuParent *menuParent; - if (NS_SUCCEEDED(CallQueryInterface(popupFrame, &menuParent))) { - menuParent->DismissChain(); - } - } - } - } - - // clear things out for next time - entry->mPopupType.Truncate(); - entry->mCreateHandlerSucceeded = PR_FALSE; - entry->mElementContent = nsnull; - entry->mXPos = entry->mYPos = 0; - entry->mLastPref.width = -1; - entry->mLastPref.height = -1; - } - // ungenerate the popup. - popupContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::menugenerated, PR_TRUE); - } - - return NS_OK; -} - -void -nsPopupSetFrame::MarkAsGenerated(nsIContent* aPopupContent) -{ - // Set our attribute, but only if we aren't already generated. - // Retrieve the menugenerated attribute. - if (!aPopupContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menugenerated, - nsGkAtoms::_true, eCaseMatters)) { - // Generate this element. - aPopupContent->SetAttr(kNameSpaceID_None, nsGkAtoms::menugenerated, NS_LITERAL_STRING("true"), - PR_TRUE); - } -} - -void -nsPopupSetFrame::OpenPopup(nsPopupFrameList* aEntry, PRBool aActivateFlag) -{ - nsWeakFrame weakFrame(this); - nsIFrame* activeChild = aEntry->mPopupFrame; - nsWeakFrame weakPopupFrame(activeChild); - nsRefPtr presContext = PresContext(); - nsCOMPtr popupContent = aEntry->mPopupContent; - PRBool createHandlerSucceeded = aEntry->mCreateHandlerSucceeded; - nsAutoString popupType = aEntry->mPopupType; - if (aActivateFlag) { - ActivatePopup(aEntry, PR_TRUE); - - // register the rollup listeners, etc, but not if we're a tooltip - if (!popupType.EqualsLiteral("tooltip")) { - nsIMenuParent* childPopup = nsnull; - if (weakPopupFrame.IsAlive()) - CallQueryInterface(activeChild, &childPopup); - - // Tooltips don't get keyboard navigation - if (childPopup && !nsMenuDismissalListener::sInstance) { - // First check and make sure this popup wants keyboard navigation - if (!popupContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ignorekeys, - nsGkAtoms::_true, eCaseMatters)) - childPopup->InstallKeyboardNavigator(); - } - - nsMenuDismissalListener* listener = nsMenuDismissalListener::GetInstance(); - if (listener) - listener->SetCurrentMenuParent(childPopup); - } - } - else { - if (createHandlerSucceeded && !OnDestroy(popupContent)) - return; - - // Unregister, but not if we're a tooltip - if (!popupType.EqualsLiteral("tooltip") ) { - nsMenuDismissalListener::Shutdown(); - } - - // Remove any keyboard navigators - nsIMenuParent* childPopup = nsnull; - if (weakPopupFrame.IsAlive()) - CallQueryInterface(activeChild, &childPopup); - if (childPopup) - childPopup->RemoveKeyboardNavigator(); - - if (weakPopupFrame.IsAlive()) - ActivatePopup(aEntry, PR_FALSE); - - OnDestroyed(presContext, popupContent); - } - - if (weakFrame.IsAlive()) { - PresContext()->PresShell()-> - FrameNeedsReflow(this, nsIPresShell::eTreeChange, - NS_FRAME_HAS_DIRTY_CHILDREN); - } -} - -void -nsPopupSetFrame::ActivatePopup(nsPopupFrameList* aEntry, PRBool aActivateFlag) -{ - if (aEntry->mPopupContent) { - // When we sync the popup view with the frame, we'll show the popup if |menutobedisplayed| - // is set by setting the |menuactive| attribute. This used to trip css into showing the menu - // but now we do it ourselves. - if (aActivateFlag) - // XXXben hook in |width| and |height| usage here? - aEntry->mPopupContent->SetAttr(kNameSpaceID_None, nsGkAtoms::menutobedisplayed, NS_LITERAL_STRING("true"), PR_TRUE); - else { - nsWeakFrame weakFrame(this); - nsWeakFrame weakActiveChild(aEntry->mPopupFrame); - nsCOMPtr content = aEntry->mPopupContent; - content->UnsetAttr(kNameSpaceID_None, nsGkAtoms::menuactive, PR_TRUE); - content->UnsetAttr(kNameSpaceID_None, nsGkAtoms::menutobedisplayed, PR_TRUE); - - // get rid of the reflows we just created. If we leave them hanging around, we - // can get into trouble if a dialog with a modal event loop comes along and - // processes the reflows before we get to call DestroyChain(). Processing the - // reflow will cause the popup to show itself again. (bug 71219) - nsIDocument* doc = content->GetDocument(); - if (doc) - doc->FlushPendingNotifications(Flush_OnlyReflow); - - // make sure we hide the popup. We can't assume that we'll have a view - // since we could be cleaning up after someone that didn't correctly - // destroy the popup. - if (weakFrame.IsAlive() && weakActiveChild.IsAlive()) { - nsIView* view = weakActiveChild.GetFrame()->GetView(); - NS_ASSERTION(view, "View is gone, looks like someone forgot to roll up the popup!"); - if (view) { - nsIViewManager* viewManager = view->GetViewManager(); - viewManager->SetViewVisibility(view, nsViewVisibility_kHide); - nsRect r(0, 0, 0, 0); - viewManager->ResizeView(view, r); - if (aEntry->mIsOpen) { - aEntry->mIsOpen = PR_FALSE; - FireDOMEventSynch(NS_LITERAL_STRING("DOMMenuInactive"), content); - } - } - } - } - } -} - -PRBool -nsPopupSetFrame::OnCreate(PRInt32 aX, PRInt32 aY, nsIContent* aPopupContent) -{ - nsEventStatus status = nsEventStatus_eIgnore; - nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_SHOWING, nsnull, - nsMouseEvent::eReal); - // XXX This is messed up: it needs to account for widgets. - nsPoint dummy; - event.widget = GetClosestView()->GetNearestWidget(&dummy); - event.refPoint.x = aX; - event.refPoint.y = aY; - - if (aPopupContent) { - nsCOMPtr kungFuDeathGrip(aPopupContent); - nsCOMPtr shell = PresContext()->GetPresShell(); - if (shell) { - nsresult rv = shell->HandleDOMEventWithTarget(aPopupContent, &event, - &status); - if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault ) - return PR_FALSE; - } - - nsCOMPtr domDoc(do_QueryInterface(aPopupContent->GetDocument())); - if (!domDoc) return PR_FALSE; - - // The menu is going to show, and the create handler has executed. - // We should now walk all of our menu item children, checking to see if any - // of them has a command attribute. If so, then several attributes must - // potentially be updated. - - PRUint32 count = aPopupContent->GetChildCount(); - for (PRUint32 i = 0; i < count; i++) { - nsCOMPtr grandChild = aPopupContent->GetChildAt(i); - - if (grandChild->Tag() == nsGkAtoms::menuitem) { - // 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 commandElt; - domDoc->GetElementById(command, getter_AddRefs(commandElt)); - nsCOMPtr 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); - } - } - } - } - } - - return PR_TRUE; -} - -PRBool -nsPopupSetFrame::OnCreated(PRInt32 aX, PRInt32 aY, nsIContent* aPopupContent) -{ - nsEventStatus status = nsEventStatus_eIgnore; - nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_SHOWN, nsnull, - nsMouseEvent::eReal); - // XXX See OnCreate above - //event.point.x = aX; - //event.point.y = aY; - - if (aPopupContent) { - nsCOMPtr shell = PresContext()->GetPresShell(); - if (shell) { - nsresult rv = shell->HandleDOMEventWithTarget(aPopupContent, &event, - &status); - if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault ) - return PR_FALSE; - } - } - - return PR_TRUE; -} - -PRBool -nsPopupSetFrame::OnDestroy(nsIContent* aPopupContent) -{ - nsEventStatus status = nsEventStatus_eIgnore; - nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_HIDING, nsnull, - nsMouseEvent::eReal); - - if (aPopupContent) { - nsCOMPtr shell = PresContext()->GetPresShell(); - if (shell) { - nsresult rv = shell->HandleDOMEventWithTarget(aPopupContent, &event, - &status); - if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault ) - return PR_FALSE; - } - } - return PR_TRUE; -} - -PRBool -nsPopupSetFrame::OnDestroyed(nsPresContext* aPresContext, - nsIContent* aPopupContent) -{ - nsEventStatus status = nsEventStatus_eIgnore; - nsMouseEvent event(PR_TRUE, NS_XUL_POPUP_HIDDEN, nsnull, - nsMouseEvent::eReal); - - if (aPopupContent && aPresContext) { - nsCOMPtr shell = aPresContext->GetPresShell(); - if (shell) { - nsresult rv = shell->HandleDOMEventWithTarget(aPopupContent, &event, - &status); - if ( NS_FAILED(rv) || status == nsEventStatus_eConsumeNoDefault ) - return PR_FALSE; - } - } - return PR_TRUE; -} - nsresult nsPopupSetFrame::RemovePopupFrame(nsIFrame* aPopup) { @@ -813,14 +268,18 @@ nsPopupSetFrame::AddPopupFrameList(nsIFrame* aPopupFrameList) nsresult nsPopupSetFrame::AddPopupFrame(nsIFrame* aPopup) { + NS_ASSERTION(aPopup->GetType() == nsGkAtoms::menuPopupFrame, + "expected a menupopup frame to be added to a popupset"); + if (aPopup->GetType() != nsGkAtoms::menuPopupFrame) + return NS_ERROR_UNEXPECTED; + // The entry should already exist, but might not (if someone decided to make their // popup visible straightaway, e.g., the autocomplete widget). - // First look for an entry by content. nsIContent* content = aPopup->GetContent(); - nsPopupFrameList* entry = nsnull; - if (mPopupList) - entry = mPopupList->GetEntry(content); + nsPopupFrameList* entry = mPopupList; + while (entry && entry->mPopupContent != content) + entry = entry->mNextPopup; if (!entry) { entry = new nsPopupFrameList(content, mPopupList); if (!entry) @@ -832,49 +291,7 @@ nsPopupSetFrame::AddPopupFrame(nsIFrame* aPopup) } // Set the frame connection. - entry->mPopupFrame = aPopup; + entry->mPopupFrame = NS_STATIC_CAST(nsMenuPopupFrame *, aPopup); - // Now return. The remaining entry values will be filled in if/when showPopup is - // called for this popup. return NS_OK; } - -//static -PRBool -nsPopupSetFrame::MayOpenPopup(nsIFrame* aFrame) -{ - nsCOMPtr cont = aFrame->PresContext()->GetContainer(); - nsCOMPtr dsti = do_QueryInterface(cont); - if (!dsti) - return PR_FALSE; - - // chrome shells can always open popups - PRInt32 type = -1; - if (NS_SUCCEEDED(dsti->GetItemType(&type)) && type == nsIDocShellTreeItem::typeChrome) - return PR_TRUE; - - nsCOMPtr shell = do_QueryInterface(dsti); - if (!shell) - return PR_FALSE; - - nsCOMPtr win = do_GetInterface(shell); - if (!win) - return PR_FALSE; - - // only allow popups in active windows - PRBool active; - nsIFocusController* focusController = win->GetRootFocusController(); - focusController->GetActive(&active); - if (!active) - return PR_FALSE; - - nsCOMPtr baseWin = do_QueryInterface(shell); - if (!baseWin) - return PR_FALSE; - - // only allow popups in visible frames - PRBool visible; - baseWin->GetVisibility(&visible); - return visible; -} - diff --git a/mozilla/layout/xul/base/src/nsPopupSetFrame.h b/mozilla/layout/xul/base/src/nsPopupSetFrame.h index ce76e6b07e2..858833f0879 100644 --- a/mozilla/layout/xul/base/src/nsPopupSetFrame.h +++ b/mozilla/layout/xul/base/src/nsPopupSetFrame.h @@ -46,10 +46,11 @@ #include "prtypes.h" #include "nsIAtom.h" #include "nsCOMPtr.h" +#include "nsGkAtoms.h" -#include "nsIPopupSetFrame.h" #include "nsBoxFrame.h" #include "nsFrameList.h" +#include "nsMenuPopupFrame.h" #include "nsIMenuParent.h" #include "nsITimer.h" #include "nsISupportsArray.h" @@ -58,35 +59,20 @@ nsIFrame* NS_NewPopupSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext struct nsPopupFrameList { nsPopupFrameList* mNextPopup; // The next popup in the list. - nsIFrame* mPopupFrame; // Our popup. + nsMenuPopupFrame* mPopupFrame; // Our popup. nsIContent* mPopupContent; // The content element for the itself. - - nsIContent* mElementContent; // The content that is having something popped up over it - - PRInt32 mXPos; // This child's x position - PRInt32 mYPos; // This child's y position - - nsAutoString mPopupAnchor; // This child's anchor. - nsAutoString mPopupAlign; // This child's align. - - nsAutoString mPopupType; - PRPackedBool mCreateHandlerSucceeded; // Did the create handler succeed? - PRPackedBool mIsOpen; - nsSize mLastPref; public: nsPopupFrameList(nsIContent* aPopupContent, nsPopupFrameList* aNext); - nsPopupFrameList* GetEntry(nsIContent* aPopupContent); - nsPopupFrameList* GetEntryByFrame(nsIFrame* aPopupFrame); }; -class nsPopupSetFrame : public nsBoxFrame, public nsIPopupSetFrame +class nsPopupSetFrame : public nsBoxFrame { public: nsPopupSetFrame(nsIPresShell* aShell, nsStyleContext* aContext): nsBoxFrame(aShell, aContext) {} - NS_DECL_ISUPPORTS + ~nsPopupSetFrame() {} NS_IMETHOD Init(nsIContent* aContent, nsIFrame* aParent, @@ -100,41 +86,14 @@ public: nsIFrame* aFrameList); NS_IMETHOD SetInitialChildList(nsIAtom* aListName, nsIFrame* aChildList); - + // nsIBox NS_IMETHOD DoLayout(nsBoxLayoutState& aBoxLayoutState); -#ifdef DEBUG_LAYOUT - NS_IMETHOD SetDebug(nsBoxLayoutState& aState, PRBool aDebug); -#endif // Used to destroy our popup frames. virtual void Destroy(); - // Reflow methods - virtual void RepositionPopup(nsPopupFrameList* aEntry, nsBoxLayoutState& aState); - - NS_IMETHOD ShowPopup(nsIContent* aElementContent, nsIContent* aPopupContent, - PRInt32 aXPos, PRInt32 aYPos, - const nsString& aPopupType, const nsString& anAnchorAlignment, - const nsString& aPopupAlignment); - NS_IMETHOD HidePopup(nsIFrame* aPopup); - NS_IMETHOD DestroyPopup(nsIFrame* aPopup, PRBool aDestroyEntireChain); - - PRBool OnCreate(PRInt32 aX, PRInt32 aY, nsIContent* aPopupContent); - PRBool OnDestroy(nsIContent* aPopupContent); - PRBool OnCreated(PRInt32 aX, PRInt32 aY, nsIContent* aPopupContent); - static PRBool OnDestroyed(nsPresContext* aPresContext, - nsIContent* aPopupContent); - - void ActivatePopup(nsPopupFrameList* aEntry, PRBool aActivateFlag); - void OpenPopup(nsPopupFrameList* aEntry, PRBool aOpenFlag); - - /** - * Return true if the docshell containing aFrame may open a popup. aFrame - * doesn't need to be any particular type of frame, just a frame in the - * same document. - */ - static PRBool MayOpenPopup(nsIFrame* aFrame); + virtual nsIAtom* GetType() const { return nsGkAtoms::popupSetFrame; } #ifdef DEBUG NS_IMETHOD GetFrameName(nsAString& aResult) const @@ -149,13 +108,6 @@ protected: nsresult AddPopupFrame(nsIFrame* aPopup); nsresult RemovePopupFrame(nsIFrame* aPopup); - void MarkAsGenerated(nsIContent* aPopupContent); - -protected: -#ifdef DEBUG_LAYOUT - nsresult SetDebug(nsBoxLayoutState& aState, nsIFrame* aList, PRBool aDebug); -#endif - nsPopupFrameList* mPopupList; }; // class nsPopupSetFrame diff --git a/mozilla/layout/xul/base/src/nsRootBoxFrame.cpp b/mozilla/layout/xul/base/src/nsRootBoxFrame.cpp index 302c8158e86..47c013331b0 100644 --- a/mozilla/layout/xul/base/src/nsRootBoxFrame.cpp +++ b/mozilla/layout/xul/base/src/nsRootBoxFrame.cpp @@ -85,8 +85,8 @@ public: NS_DECL_ISUPPORTS_INHERITED - virtual nsIFrame* GetPopupSetFrame(); - virtual void SetPopupSetFrame(nsIFrame* aPopupSet); + virtual nsPopupSetFrame* GetPopupSetFrame(); + virtual void SetPopupSetFrame(nsPopupSetFrame* aPopupSet); virtual nsIContent* GetDefaultTooltip(); virtual void SetDefaultTooltip(nsIContent* aTooltip); virtual nsresult AddTooltipSupport(nsIContent* aNode); @@ -131,7 +131,7 @@ public: NS_IMETHOD GetFrameName(nsAString& aResult) const; #endif - nsIFrame* mPopupSetFrame; + nsPopupSetFrame* mPopupSetFrame; protected: nsIContent* mDefaultTooltip; @@ -275,14 +275,14 @@ nsRootBoxFrame::GetType() const return nsGkAtoms::rootFrame; } -nsIFrame* +nsPopupSetFrame* nsRootBoxFrame::GetPopupSetFrame() { return mPopupSetFrame; } void -nsRootBoxFrame::SetPopupSetFrame(nsIFrame* aPopupSet) +nsRootBoxFrame::SetPopupSetFrame(nsPopupSetFrame* aPopupSet) { // Under normal conditions this should only be called once. However, // if something triggers ReconstructDocElementHierarchy, we will diff --git a/mozilla/layout/xul/base/src/nsXULPopupManager.cpp b/mozilla/layout/xul/base/src/nsXULPopupManager.cpp new file mode 100644 index 00000000000..ab34b9043da --- /dev/null +++ b/mozilla/layout/xul/base/src/nsXULPopupManager.cpp @@ -0,0 +1,1530 @@ +/* -*- 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" +#include "nsEventDispatcher.h" +#include "nsCSSFrameConstructor.h" +#include "nsLayoutUtils.h" +#include "nsIViewManager.h" +#include "nsILookAndFeel.h" +#include "nsIComponentManager.h" +#include "nsITimer.h" +#include "nsIFocusController.h" +#include "nsIDocShellTreeItem.h" +#include "nsIDocShell.h" +#include "nsPIDOMWindow.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIBaseWindow.h" + +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); + } +} + +NS_IMPL_ISUPPORTS4(nsXULPopupManager, nsIDOMKeyListener, + nsIMenuRollup, nsIRollupListener, nsITimerCallback) + +nsXULPopupManager::nsXULPopupManager() : + mRangeOffset(0), + mActiveMenuBar(nsnull), + mCurrentMenu(nsnull), + mPanels(nsnull), + mTimerMenu(nsnull) +{ +} + +nsXULPopupManager::~nsXULPopupManager() +{ + NS_ASSERTION(!mCurrentMenu && !mPanels, "XUL popups still open"); +} + +nsresult +nsXULPopupManager::Init() +{ + sInstance = new nsXULPopupManager(); + NS_ENSURE_TRUE(sInstance, NS_ERROR_OUT_OF_MEMORY); + NS_ADDREF(sInstance); + return NS_OK; +} + +void +nsXULPopupManager::Shutdown() +{ + NS_RELEASE(sInstance); + sInstance = nsnull; +} + +nsXULPopupManager* +nsXULPopupManager::GetInstance() +{ + return sInstance; +} + +NS_IMETHODIMP +nsXULPopupManager::Rollup() +{ + if (mCurrentMenu) + HidePopup(mCurrentMenu->Content(), PR_TRUE, PR_TRUE, PR_TRUE); + 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 + *aShouldRollup = (mCurrentMenu && !mCurrentMenu->Frame()->IsMenu()); + 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; +} + +NS_IMETHODIMP +nsXULPopupManager::GetSubmenuWidgetChain(nsISupportsArray **_retval) +{ + nsresult rv = NS_NewISupportsArray(_retval); + NS_ENSURE_SUCCESS(rv, rv); + nsMenuChainItem* item = mCurrentMenu; + while (item) { + nsCOMPtr widget; + item->Frame()->GetWidget(getter_AddRefs(widget)); + nsCOMPtr genericWidget(do_QueryInterface(widget)); + (*_retval)->AppendElement(genericWidget); + item = item->GetParent(); + } + + return NS_OK; +} + +nsIFrame* +nsXULPopupManager::GetFrameOfTypeForContent(nsIContent* aContent, + nsIAtom* aFrameType) +{ + nsIDocument *document = aContent->GetCurrentDoc(); + if (document) { + nsIPresShell* presShell = document->GetPrimaryShell(); + if (presShell) { + nsIFrame* frame = presShell->GetPrimaryFrameFor(aContent); + if (frame && frame->GetType() == aFrameType) + return frame; + } + } + + return nsnull; +} + +nsMenuFrame* +nsXULPopupManager::GetMenuFrameForContent(nsIContent* aContent) +{ + return NS_STATIC_CAST(nsMenuFrame *, + GetFrameOfTypeForContent(aContent, nsGkAtoms::menuFrame)); +} + +nsMenuPopupFrame* +nsXULPopupManager::GetPopupFrameForContent(nsIContent* aContent) +{ + return NS_STATIC_CAST(nsMenuPopupFrame *, + GetFrameOfTypeForContent(aContent, nsGkAtoms::menuPopupFrame)); +} + +void +nsXULPopupManager::GetMouseLocation(nsIDOMNode** aNode, PRInt32* aOffset) +{ + *aNode = mRangeParent; + NS_IF_ADDREF(*aNode); + *aOffset = mRangeOffset; +} + +void +nsXULPopupManager::SetMouseLocation(nsIDOMEvent* aEvent) +{ + nsCOMPtr uiEvent = do_QueryInterface(aEvent); + NS_ASSERTION(uiEvent, "Expected an nsIDOMNSUIEvent"); + if (uiEvent) { + uiEvent->GetRangeParent(getter_AddRefs(mRangeParent)); + uiEvent->GetRangeOffset(&mRangeOffset); + } + 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) +{ + 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(); + + nsIMenuParent* parent = menuFrame->GetMenuParent(); + if (parent && onmenu) { + parentIsContextMenu = parent->IsContextMenu(); + onMenuBar = parent->IsMenuBar(); + } + + nsAutoString position; + if (onMenuBar || !onmenu) + position.AssignLiteral("after_start"); + else + position.AssignLiteral("end_before"); + popupFrame->InitializePopup(aMenu, position, 0, 0, PR_TRUE); + + if (aAsynchronous) { + nsCOMPtr event = + new nsXULPopupShowingEvent(popupFrame->GetContent(), aMenu, + parentIsContextMenu, aSelectFirstItem); + NS_DispatchToCurrentThread(event); + } + else { + FirePopupShowingEvent(popupFrame->GetContent(), aMenu, + popupFrame->PresContext(), + parentIsContextMenu, aSelectFirstItem); + } +} + +void +nsXULPopupManager::ShowPopup(nsIContent* aPopup, + nsIContent* aAnchorContent, + const nsAString& aPosition, + PRInt32 aXPos, PRInt32 aYPos, + PRBool aIsContextMenu, + PRBool aAttributesOverride, + PRBool aSelectFirstItem) +{ + nsMenuPopupFrame* popupFrame = GetPopupFrameForContent(aPopup); + if (!popupFrame || !MayShowPopup(popupFrame)) + return; + + popupFrame->InitializePopup(aAnchorContent, aPosition, aXPos, aYPos, + aAttributesOverride); + + FirePopupShowingEvent(aPopup, nsnull, popupFrame->PresContext(), + aIsContextMenu, aSelectFirstItem); +} + +void +nsXULPopupManager::ShowPopupAtScreen(nsIContent* aPopup, + PRInt32 aXPos, PRInt32 aYPos, + PRBool aIsContextMenu) +{ + nsMenuPopupFrame* popupFrame = GetPopupFrameForContent(aPopup); + if (!popupFrame || !MayShowPopup(popupFrame)) + return; + + popupFrame->InitializePopupAtScreen(aXPos, aYPos); + + FirePopupShowingEvent(aPopup, nsnull, popupFrame->PresContext(), + aIsContextMenu, PR_FALSE); +} + +void +nsXULPopupManager::ShowPopupWithAnchorAlign(nsIContent* aPopup, + nsIContent* aAnchorContent, + nsAString& aAnchor, + nsAString& aAlign, + PRInt32 aXPos, PRInt32 aYPos, + PRBool aIsContextMenu) +{ + nsMenuPopupFrame* popupFrame = GetPopupFrameForContent(aPopup); + if (!popupFrame || !MayShowPopup(popupFrame)) + return; + + popupFrame->InitializePopupWithAnchorAlign(aAnchorContent, aAnchor, + aAlign, aXPos, aYPos); + + FirePopupShowingEvent(aPopup, nsnull, popupFrame->PresContext(), + aIsContextMenu, PR_FALSE); +} + +void +nsXULPopupManager::ShowPopupCallback(nsIContent* aPopup, + nsMenuPopupFrame* aPopupFrame, + PRBool aIsContextMenu, + PRBool aSelectFirstItem) +{ + // clear these as they are no longer valid + mRangeParent = nsnull; + mRangeOffset = 0; + + PRInt32 popupType = aPopupFrame->PopupType(); + PRBool ismenu = (popupType == ePopupTypeMenu); + + nsMenuChainItem* item = + new nsMenuChainItem(aPopupFrame, aIsContextMenu, ismenu); + if (!item) + return; + + // install keyboard event listeners for navigating menus, but only if + // another menu isn't already open. + // The ignorekeys attribute may be used to disable adding these event listeners + // for menus that want to handle their own keyboard events. + if (ismenu) { + if (aPopup->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ignorekeys, + nsGkAtoms::_true, eCaseMatters)) + item->SetIgnoreKeys(PR_TRUE); + + // if the menu is on a menubar, use the menubar's listener instead + nsIFrame* parent = aPopupFrame->GetParent(); + if (parent && parent->GetType() == nsGkAtoms::menuFrame) { + nsMenuFrame* menuFrame = NS_STATIC_CAST(nsMenuFrame *, parent); + item->SetOnMenuBar(menuFrame->IsOnMenuBar()); + } + } + + // use a weak frame as the popup will set an open attribute if it is a menu + nsWeakFrame weakFrame(aPopupFrame); + PRBool hasChildren = aPopupFrame->ShowPopup(aIsContextMenu, aSelectFirstItem); + 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. + if (popupType == ePopupTypeTooltip || + (popupType == ePopupTypePanel && + aPopup->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautohide, + nsGkAtoms::_true, eIgnoreCase))) { + item->SetParent(mPanels); + mPanels = item; + } + else { + nsIContent* oldmenu = nsnull; + if (mCurrentMenu) + oldmenu = mCurrentMenu->Content(); + item->SetParent(mCurrentMenu); + mCurrentMenu = item; + SetCaptureState(oldmenu); + } + + if (hasChildren) { + if (aSelectFirstItem) { + nsMenuFrame* next = GetNextMenuItem(aPopupFrame, nsnull, PR_TRUE); + aPopupFrame->SetCurrentMenuItem(next); + } + + if (ismenu) + UpdateMenuItems(aPopup); + } +} + +void +nsXULPopupManager::HidePopup(nsIContent* aPopup, + PRBool aHideChain, + PRBool aDeselectMenu, + PRBool aAsynchronous) +{ + // remove the popup from the open lists. Just to be safe, check both the + // menu and panels lists. + + // if the popup is on the panels list, remove it but don't close any other panels + nsMenuPopupFrame* popupFrame = nsnull; + PRBool foundPanel = PR_FALSE; + nsMenuChainItem* item = mPanels; + while (item) { + if (item->Content() == aPopup) { + foundPanel = PR_TRUE; + popupFrame = item->Frame(); + item->Detach(&mPanels); + delete item; + break; + } + item = item->GetParent(); + } + + // when removing a menu, all of the child popups must be closed + nsMenuChainItem* foundMenu = nsnull; + item = mCurrentMenu; + while (item) { + if (item->Content() == aPopup) { + foundMenu = item; + break; + } + item = item->GetParent(); + } + + PRBool ismenu = PR_FALSE; + PRBool deselectMenu = PR_FALSE; + nsCOMPtr popupToHide, nextPopup, lastPopup; + if (foundMenu) { + // at this point, item will be set to the found item in the list. If item + // is the topmost menu, the one being deleted, then there are no other + // popups to hide. If item is not the topmost menu, then there are + // open submenus below it. In this case, we need to make sure that those + // submenus are closed up first. To do this, we start at mCurrentMenu and + // close that popup. 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 FirePopupHidingEvent method is + // called asynchrounsly. In either case, 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 + deselectMenu = aDeselectMenu; + popupToHide = mCurrentMenu->Content(); + popupFrame = mCurrentMenu->Frame(); + ismenu = mCurrentMenu->IsMenu(); + + // unhook the top item from the list + nsMenuChainItem* todelete = mCurrentMenu; + mCurrentMenu = mCurrentMenu->GetParent(); + if (todelete) + todelete->SetParent(nsnull); + + if (mCurrentMenu && (aHideChain || todelete != item)) + nextPopup = mCurrentMenu->Content(); + + SetCaptureState(popupToHide); + delete todelete; + + lastPopup = aHideChain ? nsnull : aPopup; + } + else if (foundPanel) { + popupToHide = aPopup; + } + + if (popupFrame) { + if (aAsynchronous) { + nsCOMPtr event = + new nsXULPopupHidingEvent(popupToHide, nextPopup, lastPopup, + ismenu, deselectMenu); + NS_DispatchToCurrentThread(event); + } + else { + FirePopupHidingEvent(popupToHide, nextPopup, lastPopup, + popupFrame->PresContext(), ismenu, deselectMenu); + } + } +} + +void +nsXULPopupManager::HidePopupCallback(nsIContent* aPopup, + nsMenuPopupFrame* aPopupFrame, + nsIContent* aNextPopup, + nsIContent* aLastPopup, + PRBool aIsMenu, + PRBool aDeselectMenu) +{ + if (mCloseTimer) { + mCloseTimer->Cancel(); + mCloseTimer = nsnull; + mTimerMenu = nsnull; + } + + nsWeakFrame weakFrame(aPopupFrame); + aPopupFrame->HidePopup(aDeselectMenu); + ENSURE_TRUE(weakFrame.IsAlive()); + + // send the popuphidden event synchronously. This event has no default behaviour. + nsEventStatus status = nsEventStatus_eIgnore; + nsEvent event(PR_TRUE, NS_XUL_POPUP_HIDDEN); + 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; + nsMenuChainItem* item = mCurrentMenu; + 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. + if (foundMenu && (aLastPopup || aIsMenu == foundMenu->IsMenu())) { + PRBool ismenu = foundMenu->IsMenu(); + nsCOMPtr popupToHide = item->Content(); + item->Detach(&mCurrentMenu); + + nsCOMPtr nextPopup; + if (item->GetParent() && popupToHide != aLastPopup) + nextPopup = item->GetParent()->Content(); + + nsPresContext* presContext = item->Frame()->PresContext(); + + SetCaptureState(popupToHide); + delete item; + + FirePopupHidingEvent(popupToHide, nextPopup, aLastPopup, + presContext, ismenu, aDeselectMenu); + } + } +} + +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; +} + +void +nsXULPopupManager::HidePopupsInDocument(nsIDocument* aDocument) +{ + nsMenuChainItem* item = mCurrentMenu; + while (item) { + if (item->Content()->GetOwnerDoc() == aDocument) + item->Frame()->HidePopup(PR_TRUE); + item = item->GetParent(); + } + + item = mPanels; + while (item) { + if (item->Content()->GetOwnerDoc() == aDocument) + item->Frame()->HidePopup(PR_TRUE); + item = item->GetParent(); + } +} + +void +nsXULPopupManager::ExecuteMenu(nsIContent* aMenu, nsEvent* aEvent) +{ + // 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. + nsMenuChainItem* item = mCurrentMenu; + while (item) { + // if it isn't a , don't close it automatically + if (!item->IsMenu()) + break; + nsMenuChainItem* next = item->GetParent(); + item->Frame()->HidePopup(PR_TRUE); + item = next; + } + + // Create a trusted event if the triggering event was trusted, or if + // we're called from chrome code (since at least one of our caller + // passes in a null event). + PRBool isTrusted = aEvent ? NS_IS_TRUSTED_EVENT(aEvent) : + nsContentUtils::IsCallerChrome(); + + PRBool shift = PR_FALSE, control = PR_FALSE, alt = PR_FALSE, meta = PR_FALSE; + if (aEvent && (aEvent->eventStructType == NS_MOUSE_EVENT || + aEvent->eventStructType == NS_KEY_EVENT || + aEvent->eventStructType == NS_ACCESSIBLE_EVENT)) { + shift = NS_STATIC_CAST(nsInputEvent *, aEvent)->isShift; + control = NS_STATIC_CAST(nsInputEvent *, aEvent)->isControl; + alt = NS_STATIC_CAST(nsInputEvent *, aEvent)->isAlt; + meta = NS_STATIC_CAST(nsInputEvent *, aEvent)->isMeta; + } + + nsCOMPtr event = + new nsXULMenuCommandEvent(aMenu, isTrusted, shift, control, alt, meta); + NS_DispatchToCurrentThread(event); +} + +void +nsXULPopupManager::FirePopupShowingEvent(nsIContent* aPopup, + nsIContent* aMenu, + nsPresContext* aPresContext, + PRBool aIsContextMenu, + PRBool aSelectFirstItem) +{ + nsCOMPtr presShell = aPresContext->PresShell(); + + // set the open attribute on the menu first so that templates will generate + // their content before the popupshowing event fires. + if (aMenu) + aMenu->SetAttr(kNameSpaceID_None, nsGkAtoms::open, + NS_LITERAL_STRING("true"), PR_TRUE); + + // XXXndeakin (bug 383930) + // eventually, the popup events will be a different event type with + // additional fields for the anchor node and position and so forth. This + // is where those details would be retrieved. This removes the need for + // all the globals people keep adding to nsIDOMXULDocument. + nsEventStatus status = nsEventStatus_eIgnore; + nsEvent event(PR_TRUE, NS_XUL_POPUP_SHOWING); + nsEventDispatcher::Dispatch(aPopup, aPresContext, &event, nsnull, &status); + + // it is common to append content to the menu during the popupshowing event. + // Flush the notifications so that the frames are up to date before showing + // the popup, otherwise the new frames will reflow after the popup appears, + // causing the popup to flicker. Frame code always calls this asynchronously, + // so this should be safe. + nsIDocument *document = aPopup->GetCurrentDoc(); + if (document) + document->FlushPendingNotifications(Flush_Layout); + + // get the frame again in case it went away + nsIFrame* frame = presShell->GetPrimaryFrameFor(aPopup); + if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) { + nsMenuPopupFrame* popupFrame = NS_STATIC_CAST(nsMenuPopupFrame *, frame); + popupFrame->ClearOpenPending(); + + if (status != nsEventStatus_eConsumeNoDefault) + ShowPopupCallback(aPopup, popupFrame, aIsContextMenu, aSelectFirstItem); + } +} + +void +nsXULPopupManager::FirePopupHidingEvent(nsIContent* aPopup, + nsIContent* aNextPopup, + nsIContent* aLastPopup, + nsPresContext *aPresContext, + PRBool aIsMenu, + PRBool aDeselectMenu) +{ + nsCOMPtr presShell = aPresContext->PresShell(); + + nsEventStatus status = nsEventStatus_eIgnore; + nsEvent event(PR_TRUE, NS_XUL_POPUP_HIDING); + nsEventDispatcher::Dispatch(aPopup, aPresContext, &event, nsnull, &status); + + // get frame again in case it went away + nsIFrame* frame = presShell->GetPrimaryFrameFor(aPopup); + if (frame && frame->GetType() == nsGkAtoms::menuPopupFrame) { + nsMenuPopupFrame* popupFrame = NS_STATIC_CAST(nsMenuPopupFrame *, frame); + popupFrame->ClearOpenPending(); + + if (status != nsEventStatus_eConsumeNoDefault) { + HidePopupCallback(aPopup, popupFrame, aNextPopup, aLastPopup, + aIsMenu, aDeselectMenu); + } + } +} + +PRBool +nsXULPopupManager::IsPopupOpenForMenuParent(nsIMenuParent* aMenuParent) +{ + nsMenuChainItem* item = mCurrentMenu; + while (item) { + nsIFrame* parent = item->Frame()->GetParent(); + if (parent && parent->GetType() == nsGkAtoms::menuFrame) { + nsMenuFrame* menuFrame = NS_STATIC_CAST(nsMenuFrame *, parent); + if (menuFrame->GetMenuParent() == aMenuParent) + return PR_TRUE; + } + item = item->GetParent(); + } + + return PR_FALSE; +} + +PRBool +nsXULPopupManager::MayShowPopup(nsMenuPopupFrame* aPopup) +{ + // this will return true if the popup is already in the process of being opened + if (aPopup->IsOpenPending()) + return PR_FALSE; + + nsCOMPtr cont = aPopup->PresContext()->GetContainer(); + nsCOMPtr dsti = do_QueryInterface(cont); + if (!dsti) + return PR_FALSE; + + // chrome shells can always open popups, but other types of shells can only + // open popups when they are focused + PRInt32 type = -1; + if (NS_FAILED(dsti->GetItemType(&type))) + return PR_FALSE; + + if (type != nsIDocShellTreeItem::typeChrome) { + nsCOMPtr win = do_GetInterface(dsti); + if (!win) + return PR_FALSE; + + // only allow popups in active windows + PRBool active; + nsIFocusController* focusController = win->GetRootFocusController(); + focusController->GetActive(&active); + if (!active) + return PR_FALSE; + + nsCOMPtr baseWin = do_QueryInterface(dsti); + if (!baseWin) + return PR_FALSE; + + // only allow popups in visible frames + PRBool visible; + baseWin->GetVisibility(&visible); + if (!visible) + return PR_FALSE; + } + + // next, check if the popup is already open + nsMenuChainItem* item = mCurrentMenu; + while (item) { + if (item->Frame() == aPopup) + return PR_FALSE; + item = item->GetParent(); + } + + item = mPanels; + while (item) { + if (item->Frame() == aPopup) + return PR_FALSE; + item = item->GetParent(); + } + + // cannot open a popup that is a submenu of a menupopup that isn't open. + nsIFrame* parent = aPopup->GetParent(); + if (parent && parent->GetType() == nsGkAtoms::menuFrame) { + nsMenuFrame* menuFrame = NS_STATIC_CAST(nsMenuFrame *, parent); + nsIMenuParent* parentPopup = menuFrame->GetMenuParent(); + 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; + } + + nsMenuChainItem* item = mPanels; + while (item) { + if (item->Frame() == aPopup) { + item->Detach(&mPanels); + delete item; + break; + } + item = item->GetParent(); + } + + nsCOMPtr oldMenu; + if (mCurrentMenu) + oldMenu = mCurrentMenu->Content(); + + nsMenuChainItem* menuToDestroy = nsnull; + item = mCurrentMenu; + while (item) { + if (item->Frame() == aPopup) { + item->Detach(&mCurrentMenu); + menuToDestroy = item; + break; + } + item = item->GetParent(); + } + + if (menuToDestroy) { + // menuToDestroy will be set to the item to delete. Iterate through any + // child menus and destroy them as well, since the parent is going away + nsIFrame* menuToDestroyFrame = menuToDestroy->Frame(); + item = menuToDestroy->GetChild(); + while (item) { + nsMenuChainItem* next = item->GetChild(); + + // if the popup is a child frame of the menu that was destroyed, unhook + // it from the list of open menus and inform the popup frame that it + // should be hidden. 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 + if (nsLayoutUtils::IsProperAncestorFrame(menuToDestroyFrame, item->Frame())) { + item->Detach(&mCurrentMenu); + item->Frame()->HidePopup(PR_FALSE); + } + else { + HidePopup(item->Content(), PR_FALSE, PR_FALSE, PR_TRUE); + break; + } + + delete item; + item = next; + } + + delete menuToDestroy; + } + + if (oldMenu) + SetCaptureState(oldMenu); +} + +PRBool +nsXULPopupManager::HasContextMenu(nsMenuPopupFrame* aPopup) +{ + nsMenuChainItem* item = mCurrentMenu; + while (item && item->Frame() != aPopup) { + if (item->IsContextMenu()) + return PR_TRUE; + item = item->GetParent(); + } + + return PR_FALSE; +} + +void +nsXULPopupManager::SetCaptureState(nsIContent* aOldPopup) +{ + if (mCurrentMenu && aOldPopup == mCurrentMenu->Content()) + return; + + if (mWidget) { + mWidget->CaptureRollupEvents(this, PR_FALSE, PR_FALSE); + mWidget = nsnull; + } + + if (mCurrentMenu) { + nsMenuPopupFrame* popup = mCurrentMenu->Frame(); + nsCOMPtr widget; + popup->GetWidget(getter_AddRefs(widget)); + if (widget) { + widget->CaptureRollupEvents(this, PR_TRUE, popup->ConsumeOutsideClicks()); + mWidget = widget; + popup->AttachedDismissalListener(); + } + } + + UpdateKeyboardListeners(); +} + +void +nsXULPopupManager::UpdateKeyboardListeners() +{ + nsCOMPtr newTarget; + if (mCurrentMenu) { + if (!mCurrentMenu->IgnoreKeys()) + newTarget = do_QueryInterface(mCurrentMenu->Content()->GetDocument()); + } + else if (mActiveMenuBar) { + newTarget = do_QueryInterface(mActiveMenuBar->GetContent()->GetDocument()); + } + + 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); + nsContentUtils::NotifyInstalledMenuKeyboardListener(PR_TRUE); + 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 domDoc(do_QueryInterface(aPopup->GetDocument())); + PRUint32 count = aPopup->GetChildCount(); + for (PRUint32 i = 0; i < count; i++) { + nsCOMPtr 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 commandElt; + domDoc->GetElementById(command, getter_AddRefs(commandElt)); + nsCOMPtr 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 +// |mCurrentMenu| is the item the mouse is currently over, which could be none of them. +// +// 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 +// enters the submenus. Now when the timer fires, |mCurrentMenu| is null (!= |mTimerMenu|) +// 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, +// the timer is set and |mTimerMenu| is A and |mCurrentMenu| is C. As the timer fires, +// 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; +} + +PRBool +nsXULPopupManager::HandleShortcutNavigation(nsIDOMKeyEvent* aKeyEvent) +{ + if (mCurrentMenu) { + nsMenuPopupFrame* currentPopup = mCurrentMenu->Frame(); + + PRBool action; + nsMenuFrame* result = currentPopup->FindMenuWithShortcut(aKeyEvent, action); + if (result) { + currentPopup->ChangeMenuItem(result, PR_FALSE); + if (action) + result->Enter(); + 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; + nsMenuChainItem* nextitem = mCurrentMenu; + + 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. + nsIMenuParent* expectedParent = NS_STATIC_CAST(nsIMenuParent *, nextitem->Frame()); + nsIFrame* parent = item->Frame()->GetParent(); + if (parent && parent->GetType() == nsGkAtoms::menuFrame) { + nsMenuFrame* menuFrame = NS_STATIC_CAST(nsMenuFrame *, parent); + if (menuFrame->GetMenuParent() != expectedParent) + break; + } + else { + break; + } + } + } + + nsIFrame* itemFrame; + if (item) + itemFrame = item->Frame(); + else if (mActiveMenuBar) + itemFrame = mActiveMenuBar; + else + return PR_FALSE; + + nsNavigationDirection theDirection; + NS_DIRECTION_FROM_KEY_CODE(itemFrame, theDirection, aKeyCode); + + // 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; + } + else if NS_DIRECTION_IS_BLOCK(theDirection) { + // Open the menu and select its first item. + nsCOMPtr content = currentMenu->GetContent(); + ShowMenu(content, PR_TRUE, PR_FALSE); + return PR_TRUE; + } + } + + return PR_FALSE; +} + +PRBool +nsXULPopupManager::HandleKeyboardNavigationInPopup(nsMenuChainItem* item, + nsNavigationDirection aDir) +{ + nsMenuPopupFrame* popupFrame = item->Frame(); + nsMenuFrame* currentMenu = popupFrame->GetCurrentMenuItem(); + + popupFrame->ClearIncrementalString(); + + // 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) { + nsMenuFrame* nextItem = GetNextMenuItem(popupFrame, nsnull, PR_TRUE); + if (nextItem) { + popupFrame->ChangeMenuItem(nextItem, PR_FALSE); + 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 + nsMenuChainItem* child = item->GetChild(); + 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 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) + nextItem = GetPreviousMenuItem(popupFrame, currentMenu, PR_TRUE); + else if (aDir == eNavigationDirection_After) + nextItem = GetNextMenuItem(popupFrame, currentMenu, PR_TRUE); + else if (aDir == eNavigationDirection_First) + nextItem = GetNextMenuItem(popupFrame, nsnull, PR_TRUE); + else + nextItem = GetPreviousMenuItem(popupFrame, nsnull, PR_TRUE); + + if (nextItem) { + popupFrame->ChangeMenuItem(nextItem, PR_FALSE); + 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) ? + NS_STATIC_CAST(nsMenuFrame *, currFrame) : nsnull; + } + 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) ? + NS_STATIC_CAST(nsMenuFrame *, currFrame) : nsnull; + } + + 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; + + nsFrameList frames(immediateParent->GetFirstChild(nsnull)); + + nsIFrame* currFrame = nsnull; + if (aStart) + currFrame = frames.GetPrevSiblingFor(aStart); + else + currFrame = frames.LastChild(); + + while (currFrame) { + // See if it's a menu item. + if (IsValidMenuItem(presContext, currFrame->GetContent(), aIsPopup)) { + return (currFrame->GetType() == nsGkAtoms::menuFrame) ? + NS_STATIC_CAST(nsMenuFrame *, currFrame) : nsnull; + } + currFrame = frames.GetPrevSiblingFor(currFrame); + } + + 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) ? + NS_STATIC_CAST(nsMenuFrame *, currFrame) : nsnull; + } + + currFrame = frames.GetPrevSiblingFor(currFrame); + } + + // 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(); + if (ns == kNameSpaceID_XUL && + tag != nsGkAtoms::menu && + tag != nsGkAtoms::menuitem) + return PR_FALSE; + + if (ns == kNameSpaceID_XHTML && (!aOnPopup || tag != nsGkAtoms::option)) + return PR_FALSE; + + 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) +{ + aKeyEvent->StopPropagation(); + aKeyEvent->PreventDefault(); + + return NS_OK; // I am consuming event +} + +nsresult +nsXULPopupManager::KeyDown(nsIDOMEvent* aKeyEvent) +{ + 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 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. + Rollup(); + } + } + } + + // 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. + + //handlers shouldn't be triggered by non-trusted events. + nsCOMPtr domNSEvent = do_QueryInterface(aKeyEvent); + PRBool trustedEvent = PR_FALSE; + + if (domNSEvent) { + domNSEvent->GetIsTrusted(&trustedEvent); + } + + if (!trustedEvent) + return NS_OK; + + nsCOMPtr keyEvent = do_QueryInterface(aKeyEvent); + PRUint32 theChar; + keyEvent->GetKeyCode(&theChar); + + 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) { + // Pressing Escape hides one level of menus only + if (mCurrentMenu) + HidePopup(mCurrentMenu->Content(), PR_FALSE, PR_FALSE, PR_FALSE); + } + else if (theChar == NS_VK_TAB) { + Rollup(); + } + 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; + if (mCurrentMenu) + menuToOpen = mCurrentMenu->Frame()->Enter(); + else if (mActiveMenuBar) + menuToOpen = mActiveMenuBar->Enter(); + if (menuToOpen) { + nsCOMPtr content = menuToOpen->GetContent(); + ShowMenu(content, PR_TRUE, PR_FALSE); + } + } +#if !defined(XP_MAC) && !defined(XP_MACOSX) + else if (theChar == NS_VK_F10) { + // doesn't matter what modifier keys are down in Non-Mac platform + // if the menu bar is active and F10 is pressed - deactivate it + Rollup(); + } +#endif // !XP_MAC && !XP_MACOSX + else { + HandleShortcutNavigation(keyEvent); + } + + aKeyEvent->StopPropagation(); + aKeyEvent->PreventDefault(); + return NS_OK; // I am consuming event +} + +static nsPresContext* +GetPresContextFor(nsIContent* aContent) +{ + nsIDocument *document = aContent->GetCurrentDoc(); + if (document) { + nsIPresShell* presShell = document->GetPrimaryShell(); + if (presShell) + return presShell->GetPresContext(); + } + + return nsnull; +} + +NS_IMETHODIMP +nsXULPopupShowingEvent::Run() +{ + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + nsPresContext* context = GetPresContextFor(mPopup); + if (pm && context) { + pm->FirePopupShowingEvent(mPopup, mMenu, context, + mIsContextMenu, mSelectFirstItem); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULPopupHidingEvent::Run() +{ + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + nsPresContext* context = GetPresContextFor(mPopup); + if (pm && context) { + pm->FirePopupHidingEvent(mPopup, mNextPopup, mLastPopup, + context, mIsMenu, mDeselectMenu); + } + + 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? + nsMenuFrame* menuFrame = pm->GetMenuFrameForContent(mMenu); + if (menuFrame) { + nsPresContext* presContext = menuFrame->PresContext(); + nsCOMPtr kungFuDeathGrip = presContext->GetViewManager(); + nsCOMPtr shell = presContext->PresShell(); + + // Deselect ourselves. + menuFrame->SelectMenu(PR_FALSE); + + nsEventStatus status = nsEventStatus_eIgnore; + nsXULCommandEvent commandEvent(mIsTrusted, NS_XUL_COMMAND, nsnull); + commandEvent.isShift = mShift; + commandEvent.isControl = mControl; + commandEvent.isAlt = mAlt; + commandEvent.isMeta = mMeta; + shell->HandleDOMEventWithTarget(mMenu, &commandEvent, &status); + } + + pm->Rollup(); + + return NS_OK; +} diff --git a/mozilla/layout/xul/base/src/nsXULTooltipListener.cpp b/mozilla/layout/xul/base/src/nsXULTooltipListener.cpp index 1293b1e503b..9c16d362f30 100644 --- a/mozilla/layout/xul/base/src/nsXULTooltipListener.cpp +++ b/mozilla/layout/xul/base/src/nsXULTooltipListener.cpp @@ -46,7 +46,6 @@ #include "nsGkAtoms.h" #include "nsIPresShell.h" #include "nsIFrame.h" -#include "nsIMenuFrame.h" #include "nsIPopupBoxObject.h" #include "nsIServiceManager.h" #ifdef MOZ_XUL @@ -59,6 +58,7 @@ #include "nsIScriptContext.h" #include "nsPIDOMWindow.h" #include "nsContentUtils.h" +#include "nsXULPopupManager.h" #include "nsIRootBox.h" nsXULTooltipListener* nsXULTooltipListener::mInstance = nsnull; @@ -209,8 +209,8 @@ nsXULTooltipListener::MouseMove(nsIDOMEvent* aMouseEvent) // mouse has really moved before proceeding. nsCOMPtr mouseEvent(do_QueryInterface(aMouseEvent)); PRInt32 newMouseX, newMouseY; - mouseEvent->GetClientX(&newMouseX); - mouseEvent->GetClientY(&newMouseY); + mouseEvent->GetScreenX(&newMouseX); + mouseEvent->GetScreenY(&newMouseY); if (mMouseClientX == newMouseX && mMouseClientY == newMouseY) return NS_OK; mMouseClientX = newMouseX; @@ -413,12 +413,8 @@ nsXULTooltipListener::ShowTooltip() // at this point, |mCurrentTooltip| holds the content node of // the tooltip. If there is an attribute on the popup telling us // not to create the auto-hide timer, don't. - nsCOMPtr tooltipEl(do_QueryInterface(mCurrentTooltip)); - if (!tooltipEl) - return NS_ERROR_FAILURE; - nsAutoString noAutoHide; - tooltipEl->GetAttribute(NS_LITERAL_STRING("noautohide"), noAutoHide); - if (!noAutoHide.EqualsLiteral("true")) + if (mCurrentTooltip->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautohide, + nsGkAtoms::_true, eCaseMatters)) CreateAutoHideTimer(); // listen for popuphidden on the tooltip node, so that we can @@ -489,66 +485,38 @@ nsXULTooltipListener::LaunchTooltip() if (!mCurrentTooltip) return; - nsCOMPtr popupBox; - nsCOMPtr xulTooltipEl(do_QueryInterface(mCurrentTooltip)); - if (!xulTooltipEl) { - NS_ERROR("tooltip isn't a XUL element!"); - return; - } - - xulTooltipEl->GetBoxObject(getter_AddRefs(popupBox)); - nsCOMPtr popupBoxObject(do_QueryInterface(popupBox)); - if (popupBoxObject) { - PRInt32 x = mMouseClientX; - PRInt32 y = mMouseClientY; #ifdef MOZ_XUL - if (mIsSourceTree && mNeedTitletip) { - nsCOMPtr obx; - GetSourceTreeBoxObject(getter_AddRefs(obx)); -#ifdef DEBUG_crap - GetTreeCellCoords(obx, mSourceNode, - mLastTreeRow, mLastTreeCol, &x, &y); -#endif + if (mIsSourceTree && mNeedTitletip) { + nsCOMPtr obx; + GetSourceTreeBoxObject(getter_AddRefs(obx)); - SetTitletipLabel(obx, mCurrentTooltip, mLastTreeRow, mLastTreeCol); - if (!mCurrentTooltip) { - // Because of mutation events, mCurrentTooltip can be null. - return; - } - mCurrentTooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::titletip, - NS_LITERAL_STRING("true"), PR_TRUE); - } else { - mCurrentTooltip->UnsetAttr(kNameSpaceID_None, nsGkAtoms::titletip, - PR_TRUE); - } + SetTitletipLabel(obx, mCurrentTooltip, mLastTreeRow, mLastTreeCol); if (!mCurrentTooltip) { // Because of mutation events, mCurrentTooltip can be null. return; } + mCurrentTooltip->SetAttr(nsnull, nsGkAtoms::titletip, NS_LITERAL_STRING("true"), PR_TRUE); + } else { + mCurrentTooltip->UnsetAttr(nsnull, nsGkAtoms::titletip, PR_TRUE); + } + if (!mCurrentTooltip) { + // Because of mutation events, mCurrentTooltip can be null. + return; + } #endif - nsCOMPtr targetEl(do_QueryInterface(mSourceNode)); - popupBoxObject->ShowPopup(targetEl, xulTooltipEl, x, y, - NS_LITERAL_STRING("tooltip").get(), - NS_LITERAL_STRING("none").get(), - NS_LITERAL_STRING("topleft").get()); - } - - return; + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) + pm->ShowPopupAtScreen(mCurrentTooltip, mMouseClientX, mMouseClientY, PR_FALSE); } nsresult nsXULTooltipListener::HideTooltip() { if (mCurrentTooltip) { - // hide the popup through its box object - nsCOMPtr tooltipEl(do_QueryInterface(mCurrentTooltip)); - nsCOMPtr boxObject; - if (tooltipEl) - tooltipEl->GetBoxObject(getter_AddRefs(boxObject)); - nsCOMPtr popupObject(do_QueryInterface(boxObject)); - if (popupObject) - popupObject->HidePopup(); + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) + pm->HidePopup(mCurrentTooltip, PR_FALSE, PR_FALSE, PR_FALSE); } DestroyTooltip(); @@ -669,10 +637,9 @@ nsXULTooltipListener::GetTooltipFor(nsIContent* aTarget, nsIContent** aTooltip) nsIDocument* doc = parent->GetCurrentDoc(); nsIPresShell* presShell = doc ? doc->GetPrimaryShell() : nsnull; nsIFrame* frame = presShell ? presShell->GetPrimaryFrameFor(parent) : nsnull; - if (frame) { - nsIMenuFrame* menu = nsnull; - CallQueryInterface(frame, &menu); - NS_ENSURE_FALSE(menu, NS_ERROR_FAILURE); + if (frame && frame->GetType() == nsGkAtoms::menuFrame) { + NS_WARNING("Menu cannot be used as a tooltip"); + return NS_ERROR_FAILURE; } } @@ -741,7 +708,7 @@ nsXULTooltipListener::CreateAutoHideTimer() mAutoHideTimer->Cancel(); mAutoHideTimer = nsnull; } - + mAutoHideTimer = do_CreateInstance("@mozilla.org/timer;1"); if ( mAutoHideTimer ) mAutoHideTimer->InitWithFuncCallback(sAutoHideCallback, this, kTooltipAutoHideTime, diff --git a/mozilla/themes/classic/global/mac/popup.css b/mozilla/themes/classic/global/mac/popup.css index 4b1cbb3e048..7898e47986f 100644 --- a/mozilla/themes/classic/global/mac/popup.css +++ b/mozilla/themes/classic/global/mac/popup.css @@ -45,7 +45,8 @@ /* ::::: menupopup ::::: */ menupopup, -popup { +popup, +panel { -moz-appearance: menupopup; } diff --git a/mozilla/themes/classic/global/win/popup.css b/mozilla/themes/classic/global/win/popup.css index 640bc73a0cf..4c7502f33f8 100644 --- a/mozilla/themes/classic/global/win/popup.css +++ b/mozilla/themes/classic/global/win/popup.css @@ -45,7 +45,8 @@ /* ::::: menupopup ::::: */ menupopup, -popup { +popup, +panel { border: 2px solid; -moz-border-top-colors: ThreeDLightShadow ThreeDHighlight; -moz-border-right-colors: ThreeDDarkShadow ThreeDShadow; diff --git a/mozilla/toolkit/components/autocomplete/public/nsIAutoCompleteController.idl b/mozilla/toolkit/components/autocomplete/public/nsIAutoCompleteController.idl index 58e2e4cdea9..c3656b089e2 100644 --- a/mozilla/toolkit/components/autocomplete/public/nsIAutoCompleteController.idl +++ b/mozilla/toolkit/components/autocomplete/public/nsIAutoCompleteController.idl @@ -41,7 +41,7 @@ interface nsIAutoCompleteInput; -[scriptable, uuid(bafcfe4f-0850-4106-a176-be5aef2e1e52)] +[scriptable, uuid(476E1472-4357-4CD0-AFE3-FEA3112617B2)] interface nsIAutoCompleteController : nsISupports { /* @@ -141,10 +141,4 @@ interface nsIAutoCompleteController : nsISupports * Set the current search string, but don't start searching */ void setSearchString(in AString aSearchString); - - /* - * Attach or Detach rollup-listener - */ - void attachRollupListener(); - void detachRollupListener(); }; diff --git a/mozilla/toolkit/components/autocomplete/public/nsIAutoCompletePopup.idl b/mozilla/toolkit/components/autocomplete/public/nsIAutoCompletePopup.idl index d8e1754922a..38c7350809d 100644 --- a/mozilla/toolkit/components/autocomplete/public/nsIAutoCompletePopup.idl +++ b/mozilla/toolkit/components/autocomplete/public/nsIAutoCompletePopup.idl @@ -39,7 +39,7 @@ interface nsIAutoCompleteInput; -[scriptable, uuid(65F6CD46-22EC-4329-BB3B-BCD1103F2204)] +[scriptable, uuid(816668CC-6AC0-47C3-944E-9C2CF37F224A)] interface nsIAutoCompletePopup : nsISupports { /* @@ -71,7 +71,7 @@ interface nsIAutoCompletePopup : nsISupports * @param y - The y coordinate to display the popup at * @param width - The width that the popup should size itself to */ - void openPopup(in nsIAutoCompleteInput input, in long x, in long y, in long width); + void openAutocompletePopup(in nsIAutoCompleteInput input, in long x, in long y, in long width); /* * Close the popup and detach from the bound input diff --git a/mozilla/toolkit/components/autocomplete/src/nsAutoCompleteController.cpp b/mozilla/toolkit/components/autocomplete/src/nsAutoCompleteController.cpp index 1b214f0063c..01f26d12444 100644 --- a/mozilla/toolkit/components/autocomplete/src/nsAutoCompleteController.cpp +++ b/mozilla/toolkit/components/autocomplete/src/nsAutoCompleteController.cpp @@ -611,27 +611,6 @@ nsAutoCompleteController::SetSearchString(const nsAString &aSearchString) return NS_OK; } -NS_IMETHODIMP -nsAutoCompleteController::AttachRollupListener() -{ - nsIWidget* widget = GetPopupWidget(); - NS_ENSURE_TRUE(widget, NS_ERROR_FAILURE); - NS_ASSERTION(mInput, "mInput must not be null."); - PRBool consumeRollupEvent = PR_FALSE; - mInput->GetConsumeRollupEvent(&consumeRollupEvent); - return widget->CaptureRollupEvents((nsIRollupListener*)this, - PR_TRUE, consumeRollupEvent); -} - -NS_IMETHODIMP -nsAutoCompleteController::DetachRollupListener() -{ - nsIWidget* widget = GetPopupWidget(); - NS_ENSURE_TRUE(widget, NS_ERROR_FAILURE); - return widget->CaptureRollupEvents((nsIRollupListener*)this, - PR_FALSE, PR_FALSE); -} - //////////////////////////////////////////////////////////////////////// //// nsIAutoCompleteObserver diff --git a/mozilla/toolkit/components/satchel/src/nsFormFillController.cpp b/mozilla/toolkit/components/satchel/src/nsFormFillController.cpp index c16d885ddc2..02dee2b6ae3 100644 --- a/mozilla/toolkit/components/satchel/src/nsFormFillController.cpp +++ b/mozilla/toolkit/components/satchel/src/nsFormFillController.cpp @@ -246,7 +246,9 @@ nsFormFillController::SetPopupOpen(PRBool aPopupOpen) NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE); nsRect popupRect = GetScreenOrigin(mFocusedInput); - mFocusedPopup->OpenPopup(this, popupRect.x, popupRect.y+popupRect.height, popupRect.width); + mFocusedPopup->OpenAutocompletePopup(this, popupRect.x, + popupRect.y+popupRect.height, + popupRect.width); } else mFocusedPopup->ClosePopup(); } diff --git a/mozilla/toolkit/content/widgets/autocomplete.xml b/mozilla/toolkit/content/widgets/autocomplete.xml index f77b5d2f3e8..d2cff5ff1e4 100644 --- a/mozilla/toolkit/content/widgets/autocomplete.xml +++ b/mozilla/toolkit/content/widgets/autocomplete.xml @@ -110,9 +110,8 @@ if (popupId) popup = document.getElementById(popupId); if (!popup) { - popup = document.createElement("popup"); + popup = document.createElement("panel"); popup.setAttribute("type", "autocomplete"); - popup.setAttribute("hidden", "true"); var popupset = document.getAnonymousElementByAttribute(this, "anonid", "popupset"); popupset.appendChild(popup); @@ -320,7 +319,7 @@ @@ -565,7 +564,7 @@ - + @@ -578,7 +577,6 @@ this.showCommentColumn = this.mInput.showCommentColumn; - this.removeAttribute("hidden"); this.setAttribute("width", aWidth < 100 ? 100 : aWidth); document.popupNode = null; @@ -588,8 +586,7 @@ else { this.showPopup(document.documentElement, aX, aY, "popup", null, null); } - this.enableRollup(false); - this.mInput.controller.attachRollupListener(); + this.popupBoxObject.setConsumeRollupEvent(this.mInput.consumeRollupEvent); } ]]> @@ -599,10 +596,8 @@ if (this.mPopupOpen) { this.hidePopup(); document.popupNode = null; - - this.setAttribute("hidden", "true"); +// this.setAttribute("hidden", "true"); this.removeAttribute("width"); - this.mInput.controller.detachRollupListener(); } ]]> @@ -716,6 +711,8 @@ // detect the desired height of the tree var bx = this.tree.treeBoxObject; var view = this.tree.view; + if (!view) + return; var rows = this.maxRows; if (!view.rowCount || (rows && view.rowCount < rows)) rows = view.rowCount; diff --git a/mozilla/toolkit/content/widgets/popup.xml b/mozilla/toolkit/content/widgets/popup.xml index 743211d3606..e9c3279c7e6 100644 --- a/mozilla/toolkit/content/widgets/popup.xml +++ b/mozilla/toolkit/content/widgets/popup.xml @@ -11,12 +11,8 @@ - - - - - - + @@ -34,6 +30,40 @@ return this.boxObject.QueryInterface(Components.interfaces.nsIPopupBoxObject); + + + + + + + + + + + + + + + + + + + + + @@ -131,6 +161,16 @@ + + + + + + + + + diff --git a/mozilla/toolkit/content/xul.css b/mozilla/toolkit/content/xul.css index 8b206a1eaab..ceede9a00df 100644 --- a/mozilla/toolkit/content/xul.css +++ b/mozilla/toolkit/content/xul.css @@ -320,24 +320,23 @@ popup, menupopup { -moz-binding: url("chrome://global/content/bindings/popup.xml#popup"); -moz-box-orient: vertical; - display: none; +} + +panel { + -moz-binding: url("chrome://global/content/bindings/popup.xml#panel"); + -moz-box-orient: vertical; } popup, menupopup, +panel, tooltip { - z-index: 2147483647; -} - -menupopup[menugenerated="true"], -popup[menugenerated="true"], -tooltip[menugenerated="true"] { display: -moz-popup; + z-index: 2147483647; } tooltip { -moz-binding: url("chrome://global/content/bindings/popup.xml#tooltip"); - display: -moz-popup; margin-top: 21px; } @@ -782,9 +781,8 @@ textbox[type="autocomplete"] { -moz-binding: url("chrome://global/content/bindings/autocomplete.xml#autocomplete"); } -popup[type="autocomplete"] { +panel[type="autocomplete"] { -moz-binding: url("chrome://global/content/bindings/autocomplete.xml#autocomplete-result-popup"); - display: -moz-popup !important; } .autocomplete-tree { diff --git a/mozilla/toolkit/themes/gnomestripe/global/popup.css b/mozilla/toolkit/themes/gnomestripe/global/popup.css index a6f5187c301..46d78eeb546 100644 --- a/mozilla/toolkit/themes/gnomestripe/global/popup.css +++ b/mozilla/toolkit/themes/gnomestripe/global/popup.css @@ -44,7 +44,9 @@ /* ::::: menupopup ::::: */ -menupopup, popup { +menupopup, +popup, +panel { -moz-appearance: menupopup; min-width: 1px; } diff --git a/mozilla/toolkit/themes/pinstripe/global/autocomplete.css b/mozilla/toolkit/themes/pinstripe/global/autocomplete.css index 6dd9776cf3b..3265ef7841e 100644 --- a/mozilla/toolkit/themes/pinstripe/global/autocomplete.css +++ b/mozilla/toolkit/themes/pinstripe/global/autocomplete.css @@ -74,7 +74,7 @@ textbox.padded { /* ::::: autocomplete popups ::::: */ -popup[type="autocomplete"], +panel[type="autocomplete"], .autocomplete-history-popup { padding: 0px !important; background-color: -moz-Field !important; diff --git a/mozilla/toolkit/themes/pinstripe/global/popup.css b/mozilla/toolkit/themes/pinstripe/global/popup.css index 06a43f073fc..af8f7d9413c 100644 --- a/mozilla/toolkit/themes/pinstripe/global/popup.css +++ b/mozilla/toolkit/themes/pinstripe/global/popup.css @@ -46,7 +46,8 @@ /* ::::: menupopup ::::: */ menupopup, -popup { +popup, +panel { -moz-appearance: menupopup; } diff --git a/mozilla/toolkit/themes/winstripe/global/autocomplete.css b/mozilla/toolkit/themes/winstripe/global/autocomplete.css index 4ed4a32f019..851bc0ab89b 100644 --- a/mozilla/toolkit/themes/winstripe/global/autocomplete.css +++ b/mozilla/toolkit/themes/winstripe/global/autocomplete.css @@ -167,7 +167,7 @@ textbox[chromedir="rtl"] .autocomplete-history-dropmarker { /* ::::: autocomplete popups ::::: */ -popup[type="autocomplete"], +panel[type="autocomplete"], .autocomplete-history-popup { border-width: 1px; -moz-border-top-colors: ThreeDDarkShadow; diff --git a/mozilla/toolkit/themes/winstripe/global/popup.css b/mozilla/toolkit/themes/winstripe/global/popup.css index 2a3dea7e8cb..4fa663a9a81 100644 --- a/mozilla/toolkit/themes/winstripe/global/popup.css +++ b/mozilla/toolkit/themes/winstripe/global/popup.css @@ -45,7 +45,8 @@ /* ::::: menupopup ::::: */ menupopup, -popup { +popup, +panel { border: 2px solid transparent; -moz-border-top-colors : ThreeDLightShadow ThreeDHighlight; -moz-border-left-colors : ThreeDLightShadow ThreeDHighlight; diff --git a/mozilla/widget/src/gtk2/nsNativeThemeGTK.cpp b/mozilla/widget/src/gtk2/nsNativeThemeGTK.cpp index 864c4e12531..5893aadc050 100644 --- a/mozilla/widget/src/gtk2/nsNativeThemeGTK.cpp +++ b/mozilla/widget/src/gtk2/nsNativeThemeGTK.cpp @@ -55,7 +55,6 @@ #include "nsGfxCIID.h" #include "nsTransform2D.h" #include "nsIMenuFrame.h" -#include "nsIMenuParent.h" #include "prlink.h" #include "nsIDOMHTMLInputElement.h" #include "nsWidgetAtoms.h" @@ -278,15 +277,11 @@ nsNativeThemeGTK::GetGtkWidgetAndState(PRUint8 aWidgetType, nsIFrame* aFrame, CallQueryInterface(aFrame, &menuFrame); if (menuFrame) { - nsIMenuParent *menuParent = menuFrame->GetMenuParent(); - if (menuParent) - menuParent->IsMenuBar(isTopLevel); + isTopLevel = menuFrame->IsOnMenuBar(); } if (isTopLevel) { - PRBool isOpen; - menuFrame->MenuIsOpen(isOpen); - aState->inHover = isOpen; + aState->inHover = menuFrame->IsOpen(); } else { aState->inHover = CheckBooleanAttr(aFrame, nsWidgetAtoms::mozmenuactive); } diff --git a/mozilla/widget/src/windows/nsNativeThemeWin.cpp b/mozilla/widget/src/windows/nsNativeThemeWin.cpp index 7b0a7f64ca0..b0011188a23 100644 --- a/mozilla/widget/src/windows/nsNativeThemeWin.cpp +++ b/mozilla/widget/src/windows/nsNativeThemeWin.cpp @@ -55,7 +55,6 @@ #include "nsILookAndFeel.h" #include "nsIDOMHTMLInputElement.h" #include "nsIMenuFrame.h" -#include "nsIMenuParent.h" #include "nsWidgetAtoms.h" #include @@ -789,8 +788,8 @@ nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, PRUint8 aWidgetType, nsIContent* content = aFrame->GetContent(); nsIFrame* parentFrame = aFrame->GetParent(); - nsCOMPtr menuFrame(do_QueryInterface(parentFrame)); - if (menuFrame || (content && content->IsNodeOfType(nsINode::eHTML)) ) + if (parentFrame->GetType() == nsWidgetAtoms::menuFrame || + (content && content->IsNodeOfType(nsINode::eHTML))) // XUL menu lists and HTML selects get state from parent aFrame = parentFrame; @@ -1421,9 +1420,7 @@ nsNativeThemeWin::ClassicGetWidgetBorder(nsIDeviceContext* aContext, if (menuFrame) { // If this is a real menu item, we should check if it is part of the // main menu bar or not, as this affects rendering. - nsIMenuParent *menuParent = menuFrame->GetMenuParent(); - if (menuParent) - menuParent->IsMenuBar(isTopLevel); + isTopLevel = menuFrame->IsOnMenuBar(); } // These values are obtained from visual inspection of equivelant @@ -1643,11 +1640,9 @@ nsresult nsNativeThemeWin::ClassicGetThemePartAndState(nsIFrame* aFrame, PRUint8 // If this is a real menu item, we should check if it is part of the // main menu bar or not, and if it is a container, as these affect // rendering. - nsIMenuParent *menuParent = menuFrame->GetMenuParent(); - if (menuParent) - menuParent->IsMenuBar(isTopLevel); - menuFrame->MenuIsOpen(isOpen); - menuFrame->MenuIsContainer(isContainer); + isTopLevel = menuFrame->IsOnMenuBar(); + isOpen = menuFrame->IsOpen(); + isContainer = menuFrame->IsMenu(); } if (IsDisabled(aFrame)) @@ -1714,8 +1709,8 @@ nsresult nsNativeThemeWin::ClassicGetThemePartAndState(nsIFrame* aFrame, PRUint8 nsIContent* content = aFrame->GetContent(); nsIFrame* parentFrame = aFrame->GetParent(); - nsCOMPtr menuFrame(do_QueryInterface(parentFrame)); - if (menuFrame || (content && content->IsNodeOfType(nsINode::eHTML)) ) + if (parentFrame->GetType() == nsWidgetAtoms::menuFrame || + (content && content->IsNodeOfType(nsINode::eHTML))) // XUL menu lists and HTML selects get state from parent aFrame = parentFrame; // XXX the button really shouldn't depress when clicking the diff --git a/mozilla/widget/src/xpwidgets/nsWidgetAtomList.h b/mozilla/widget/src/xpwidgets/nsWidgetAtomList.h index 16784e2d4d9..db88bfb65d2 100644 --- a/mozilla/widget/src/xpwidgets/nsWidgetAtomList.h +++ b/mozilla/widget/src/xpwidgets/nsWidgetAtomList.h @@ -88,6 +88,7 @@ WIDGET_ATOM(menu, "menu") // Represents an XP menu WIDGET_ATOM(menuitem, "menuitem") // Represents an XP menu item WIDGET_ATOM(menupopup, "menupopup") // The XP menu's children. WIDGET_ATOM(menuseparator, "menuseparator") // Divider between menu items +WIDGET_ATOM(menuFrame, "menuFrame") WIDGET_ATOM(minpos, "minpos") WIDGET_ATOM(mode, "mode") WIDGET_ATOM(modifiers, "modifiers") // The modifiers attribute diff --git a/mozilla/xpfe/appshell/src/nsWebShellWindow.cpp b/mozilla/xpfe/appshell/src/nsWebShellWindow.cpp index d102b43ebc5..7e25b7169cf 100644 --- a/mozilla/xpfe/appshell/src/nsWebShellWindow.cpp +++ b/mozilla/xpfe/appshell/src/nsWebShellWindow.cpp @@ -114,8 +114,6 @@ #define USE_NATIVE_MENUS #endif -#include "nsIPopupSetFrame.h" - /* Define Class IDs */ static NS_DEFINE_CID(kWindowCID, NS_WINDOW_CID); diff --git a/mozilla/xpfe/components/autocomplete/resources/content/autocomplete.xml b/mozilla/xpfe/components/autocomplete/resources/content/autocomplete.xml index 7490fbdff04..c5cfd0ffcd1 100644 --- a/mozilla/xpfe/components/autocomplete/resources/content/autocomplete.xml +++ b/mozilla/xpfe/components/autocomplete/resources/content/autocomplete.xml @@ -30,7 +30,7 @@ xbl:inherits="open,hidden=disablehistory" anonid="historydropmarker"/> - diff --git a/mozilla/xpfe/global/resources/content/bindings/popup.xml b/mozilla/xpfe/global/resources/content/bindings/popup.xml index 743211d3606..3257f6dac46 100644 --- a/mozilla/xpfe/global/resources/content/bindings/popup.xml +++ b/mozilla/xpfe/global/resources/content/bindings/popup.xml @@ -34,7 +34,41 @@ return this.boxObject.QueryInterface(Components.interfaces.nsIPopupBoxObject); - + + + + + + + + + + + + + + + + + + + + + + diff --git a/mozilla/xpfe/global/resources/content/xul.css b/mozilla/xpfe/global/resources/content/xul.css index 572c3f0c155..d804943e5fc 100644 --- a/mozilla/xpfe/global/resources/content/xul.css +++ b/mozilla/xpfe/global/resources/content/xul.css @@ -298,27 +298,22 @@ menuseparator { /* is deprecated. Only and are still valid. */ popup, -menupopup { +menupopup, +panel { -moz-binding: url("chrome://global/content/bindings/popup.xml#popup"); -moz-box-orient: vertical; - display: none; } popup, menupopup, +panel, tooltip { - z-index: 2147483647; -} - -menupopup[menugenerated="true"], -popup[menugenerated="true"], -tooltip[menugenerated="true"] { display: -moz-popup; + z-index: 2147483647; } tooltip { -moz-binding: url("chrome://global/content/bindings/popup.xml#tooltip"); - display: -moz-popup; margin-top: 21px; }