--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -57,16 +57,17 @@
#include "nsIDOMXULMenuListElement.h"
#include "nsIDOMXULMultSelectCntrlEl.h"
#include "nsIDOMXULSelectCntrlItemEl.h"
#include "nsIDOMXULPopupElement.h"
#include "nsIDocument.h"
#include "nsIEventListenerManager.h"
#include "nsIFocusController.h"
#include "nsIFrame.h"
+#include "nsIMenuFrame.h"
#include "nsIHTMLDocument.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIMenuParent.h"
#include "nsIScrollableView.h"
#include "nsISelectionPrivate.h"
#include "nsIServiceManager.h"
#include "nsIViewManager.h"
#include "nsPIDOMWindow.h"
@@ -796,32 +797,33 @@ nsresult nsRootAccessible::HandleEventWi
else if (eventType.EqualsLiteral("DOMMenuInactive")) {
if (Role(accessible) == nsIAccessibleRole::ROLE_MENUPOPUP) {
privAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
accessible, nsnull);
}
}
else if (eventType.EqualsLiteral("DOMMenuItemActive")) {
if (!treeItemAccessible) {
- nsCOMPtr<nsIAccessible> containerAccessible;
- accessible->GetParent(getter_AddRefs(containerAccessible));
- NS_ENSURE_TRUE(containerAccessible, NS_OK);
- if (Role(containerAccessible) == nsIAccessibleRole::ROLE_MENUBAR) {
- nsCOMPtr<nsPIAccessNode> menuBarAccessNode(do_QueryInterface(containerAccessible));
- NS_ENSURE_TRUE(menuBarAccessNode, NS_ERROR_FAILURE);
- nsCOMPtr<nsIMenuParent> menuParent = do_QueryInterface(menuBarAccessNode->GetFrame());
- NS_ENSURE_TRUE(menuParent, NS_ERROR_FAILURE);
- PRBool isActive;
- menuParent->GetIsActive(isActive);
- if (!isActive) {
+ nsCOMPtr<nsPIAccessNode> 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<nsIAccessible> 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)
return NS_OK;
}
}
FireAccessibleFocusEvent(accessible, aTargetNode, aEvent, PR_TRUE);
}
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -102,17 +102,17 @@
oncommand="gotoHistoryIndex(event);"
onclick="checkForMiddleClick(this, event);"/>
<tooltip id="aHTMLTooltip" onpopupshowing="return FillInHTMLTooltip(document.tooltipNode);">
<hbox>
<label flex="1" class="htmltooltip-label"/>
</hbox>
</tooltip>
- <popup type="autocomplete" chromedir="&locale.dir;" id="PopupAutoComplete"/>
+ <panel type="autocomplete" chromedir="&locale.dir;" id="PopupAutoComplete"/>
<popup id="toolbar-context-menu"
onpopupshowing="onViewToolbarsPopupShowing(event);">
<menuseparator/>
<menuitem command="cmd_CustomizeToolbars"
label="&viewCustomizeToolbar.label;"
accesskey="&viewCustomizeToolbar.accesskey;"/>
</popup>
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -3677,16 +3677,17 @@ nsDocument::GetBoxObjectFor(nsIDOMElemen
if (tag == nsGkAtoms::browser ||
tag == nsGkAtoms::editor ||
tag == nsGkAtoms::iframe)
contractID += "-container";
else if (tag == nsGkAtoms::menu)
contractID += "-menu";
else if (tag == nsGkAtoms::popup ||
tag == nsGkAtoms::menupopup ||
+ tag == nsGkAtoms::panel ||
tag == nsGkAtoms::tooltip)
contractID += "-popup";
else if (tag == nsGkAtoms::tree)
contractID += "-tree";
else if (tag == nsGkAtoms::listbox)
contractID += "-listbox";
else if (tag == nsGkAtoms::scrollbox)
contractID += "-scrollbox";
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -483,17 +483,16 @@ GK_ATOM(mediaType, "media-type")
GK_ATOM(member, "member")
GK_ATOM(menu, "menu")
GK_ATOM(menubar, "menubar")
GK_ATOM(menubutton, "menubutton")
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")
GK_ATOM(middle, "middle")
GK_ATOM(min, "min")
GK_ATOM(minheight, "minheight")
GK_ATOM(minpos, "minpos")
GK_ATOM(minusSign, "minus-sign")
@@ -515,16 +514,17 @@ GK_ATOM(_namespace, "namespace")
GK_ATOM(namespaceAlias, "namespace-alias")
GK_ATOM(namespaceUri, "namespace-uri")
GK_ATOM(NaN, "NaN")
GK_ATOM(negate, "negate")
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")
GK_ATOM(noembed, "noembed")
GK_ATOM(noframes, "noframes")
GK_ATOM(nohref, "nohref")
GK_ATOM(none, "none")
GK_ATOM(noresize, "noresize")
@@ -629,16 +629,17 @@ GK_ATOM(overlay, "overlay")
GK_ATOM(overlap, "overlap")
GK_ATOM(p, "p")
GK_ATOM(pack, "pack")
GK_ATOM(page, "page")
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")
GK_ATOM(parsetype, "parsetype")
GK_ATOM(patternSeparator, "pattern-separator")
GK_ATOM(perMille, "per-mille")
GK_ATOM(percent, "percent")
GK_ATOM(persist, "persist")
@@ -1386,22 +1387,25 @@ GK_ATOM(imageBoxFrame, "ImageBoxFrame")
GK_ATOM(imageFrame, "ImageFrame")
GK_ATOM(imageControlFrame, "ImageControlFrame")
GK_ATOM(inlineFrame, "InlineFrame")
GK_ATOM(leafBoxFrame, "LeafBoxFrame")
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")
GK_ATOM(scrollFrame, "ScrollFrame")
GK_ATOM(scrollbarFrame, "ScrollbarFrame")
GK_ATOM(sequenceFrame, "SequenceFrame")
GK_ATOM(sliderFrame, "sliderFrame")
GK_ATOM(tableCaptionFrame, "TableCaptionFrame")
--- a/content/xul/content/Makefile.in
+++ b/content/xul/content/Makefile.in
@@ -38,12 +38,12 @@
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = xul
-DIRS = public src test
+DIRS = src test
include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/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
-
deleted file mode 100644
--- a/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__
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -102,28 +102,28 @@
#include "nsIScriptGlobalObjectOwner.h"
#include "nsIServiceManager.h"
#include "nsICSSStyleRule.h"
#include "nsIStyleSheet.h"
#include "nsIURL.h"
#include "nsIViewManager.h"
#include "nsIWidget.h"
#include "nsIXULDocument.h"
-#include "nsIXULPopupListener.h"
#include "nsIXULTemplateBuilder.h"
#include "nsIXBLService.h"
#include "nsLayoutCID.h"
#include "nsContentCID.h"
#include "nsRDFCID.h"
#include "nsStyleConsts.h"
#include "nsXPIDLString.h"
#include "nsXULControllers.h"
#include "nsIBoxObject.h"
#include "nsPIBoxObject.h"
#include "nsXULDocument.h"
+#include "nsXULPopupListener.h"
#include "nsRuleWalker.h"
#include "nsIDOMViewCSS.h"
#include "nsIDOMCSSStyleDeclaration.h"
#include "nsCSSDeclaration.h"
#include "nsIListBoxObject.h"
#include "nsContentUtils.h"
#include "nsContentList.h"
#include "nsMutationEvent.h"
@@ -2041,73 +2041,67 @@ nsXULElement::IsNodeOfType(PRUint32 aFla
{
return !(aFlags & ~(eCONTENT | eELEMENT | eXUL));
}
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<nsIDOMEventListener> eventListener = do_QueryInterface(listener);
nsCOMPtr<nsIDOMEventTarget> 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);
}
nsresult
nsXULElement::AddPopupListener(nsIAtom* aName)
{
- XULPopupType popupType;
- nsCOMPtr<nsIAtom> listenerAtom;
- if (aName == nsGkAtoms::context || aName == nsGkAtoms::contextmenu) {
- popupType = eXULPopupType_context;
- listenerAtom = nsGkAtoms::contextmenulistener;
- } else {
- popupType = eXULPopupType_popup;
- listenerAtom = nsGkAtoms::popuplistener;
- }
-
- nsCOMPtr<nsIXULPopupListener> popupListener =
- NS_STATIC_CAST(nsIXULPopupListener*, GetProperty(listenerAtom));
+ // Add a popup listener to the element
+ PRBool isContext = (aName == nsGkAtoms::context ||
+ aName == nsGkAtoms::contextmenu);
+ nsIAtom* listenerAtom = isContext ?
+ nsGkAtoms::contextmenulistener :
+ nsGkAtoms::popuplistener;
+
+ nsCOMPtr<nsIDOMEventListener> 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<nsIDOMEventListener> eventListener = do_QueryInterface(popupListener);
nsCOMPtr<nsIDOMEventTarget> 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;
}
PRInt32
nsXULElement::IntrinsicState() const
{
PRInt32 state = nsGenericElement::IntrinsicState();
--- a/content/xul/content/src/nsXULPopupListener.cpp
+++ b/content/xul/content/src/nsXULPopupListener.cpp
@@ -39,180 +39,101 @@
*
* ***** END LICENSE BLOCK ***** */
/*
This file provides the implementation for xul popup listener which
tracks xul popups and context menus
*/
+#include "nsXULPopupListener.h"
#include "nsCOMPtr.h"
#include "nsGkAtoms.h"
#include "nsIDOMElement.h"
#include "nsIDOMXULElement.h"
#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"
#include "nsIPresShell.h"
#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,
// they appear on mouse down. Certain bits of code care about this difference.
#if !defined(XP_WIN) && !defined(XP_OS2)
#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<nsIPopupBoxObject> 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;
nsCOMPtr<nsIDOMMouseEvent> mouseEvent;
mouseEvent = do_QueryInterface(aMouseEvent);
if (!mouseEvent) {
//non-ui event passed in. bad things.
return NS_OK;
@@ -225,17 +146,17 @@ XULPopupListenerImpl::PreLaunchPopup(nsI
return NS_OK;
}
// Get the node that was clicked on.
nsCOMPtr<nsIDOMEventTarget> target;
mouseEvent->GetTarget(getter_AddRefs(target));
nsCOMPtr<nsIDOMNode> 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<nsIDOMWindow> domWin = do_QueryInterface(target);
if (!domWin) {
return NS_ERROR_DOM_WRONG_TYPE_ERR;
}
// Try to use the root node as target node.
nsCOMPtr<nsIDOMDocument> domDoc;
domWin->GetDocument(getter_AddRefs(domDoc));
@@ -245,17 +166,17 @@ XULPopupListenerImpl::PreLaunchPopup(nsI
targetNode = do_QueryInterface(doc->GetRootContent());
if (!targetNode) {
return NS_ERROR_FAILURE;
}
}
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 =
nsContentUtils::GetBoolPref("dom.event.contextmenu.enabled", PR_TRUE);
if (!eventEnabled) {
// The user wants his contextmenus. Let's make sure that this is a website
// and not chrome since there could be places in chrome which don't want
// contextmenus.
@@ -273,75 +194,65 @@ XULPopupListenerImpl::PreLaunchPopup(nsI
}
}
if (preventDefault) {
// someone called preventDefault. bail.
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<nsIContent> targetContent = do_QueryInterface(target);
+ nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
+ if (!mIsContext) {
nsIAtom *tag = targetContent ? targetContent->Tag() : nsnull;
if (tag == nsGkAtoms::menu || tag == nsGkAtoms::menuitem)
return NS_OK;
}
// Get the document with the popup.
nsCOMPtr<nsIContent> content = do_QueryInterface(mElement);
// Turn the document into a XUL document so we can use SetPopupNode.
nsCOMPtr<nsIDOMXULDocument> 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<nsIDOMNSEvent> 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:
+ 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);
+#endif
+ }
+ else {
+ // Only open popups when the left mouse button is down.
+ mouseEvent->GetButton(&button);
+ if (button != 0)
+ return NS_OK;
+ }
- // Time to launch a context menu
-#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);
-#endif
- LaunchPopup(aMouseEvent);
- aMouseEvent->StopPropagation();
- aMouseEvent->PreventDefault();
- break;
- }
- xulDocument->SetTrustedPopupEvent(nsnull);
+ // 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<nsIDOMDocument> domDoc;
rv = aTargetNode->GetOwnerDocument(getter_AddRefs(domDoc));
if(NS_SUCCEEDED(rv) && domDoc)
{
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
@@ -403,36 +314,35 @@ XULPopupListenerImpl::FireFocusOnTargetC
} else if (!suppressBlur)
esm->SetContentState(nsnull, NS_EVENT_STATE_FOCUS);
esm->SetContentState(focusableContent, NS_EVENT_STATE_ACTIVE);
}
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<nsIDOMMouseEvent> 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)
{
*aResult = nsnull;
PRInt32 childCount = aContent->GetChildCount();
for (PRInt32 i = 0; i < childCount; i++) {
nsIContent *child = aContent->GetChildAt(i);
@@ -441,89 +351,39 @@ GetImmediateChild(nsIContent* aContent,
NS_ADDREF(*aResult);
return;
}
}
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
//
// Given the element on which the event was triggered and the mouse locations in
// 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);
if (identifier.IsEmpty()) {
if (type.EqualsLiteral("popup"))
mElement->GetAttribute(NS_LITERAL_STRING("menu"), identifier);
else if (type.EqualsLiteral("context"))
@@ -539,107 +399,110 @@ XULPopupListenerImpl::LaunchPopup(PRInt3
// Turn the document into a DOM document so we can use getElementById
nsCOMPtr<nsIDOMDocument> domDocument = do_QueryInterface(document);
if (!domDocument) {
NS_ERROR("Popup attached to an element that isn't in XUL!");
return NS_ERROR_FAILURE;
}
// Handle the _child case for popups and context menus
- nsCOMPtr<nsIDOMElement> popupContent;
+ nsCOMPtr<nsIDOMElement> popupElement;
if (identifier.EqualsLiteral("_child")) {
nsCOMPtr<nsIContent> popup;
GetImmediateChild(content, nsGkAtoms::menupopup, getter_AddRefs(popup));
if (popup)
- popupContent = do_QueryInterface(popup);
+ popupElement = do_QueryInterface(popup);
else {
nsCOMPtr<nsIDOMDocumentXBL> nsDoc(do_QueryInterface(domDocument));
nsCOMPtr<nsIDOMNodeList> list;
nsDoc->GetAnonymousNodes(mElement, getter_AddRefs(list));
if (list) {
PRUint32 ctr,listLength;
nsCOMPtr<nsIDOMNode> node;
list->GetLength(&listLength);
for (ctr = 0; ctr < listLength; ctr++) {
list->Item(ctr, getter_AddRefs(node));
nsCOMPtr<nsIContent> childContent(do_QueryInterface(node));
if (childContent->NodeInfo()->Equals(nsGkAtoms::menupopup,
kNameSpaceID_XUL)) {
- popupContent = do_QueryInterface(childContent);
+ popupElement = do_QueryInterface(childContent);
break;
}
}
}
}
}
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<nsIContent> popup = do_QueryInterface(popupContent);
+ nsCOMPtr<nsIContent> 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);
-
- nsAutoString popupAlignment;
- popupContent->GetAttribute(NS_LITERAL_STRING("popupalign"), popupAlignment);
-
- PRInt32 xPos = aClientX, yPos = aClientY;
+ // XXXndeakin this is temporary. It is needed to grab the mouse location details
+ // used by the spellchecking popup. See bug 383930.
+ pm->SetMouseLocation(aEvent);
- ConvertPosition(popupContent, anchorAlignment, popupAlignment, yPos);
- if (!anchorAlignment.IsEmpty() && !popupAlignment.IsEmpty())
- xPos = yPos = -1;
+ // 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<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent);
+ mouseEvent->GetScreenX(&xPos);
+ mouseEvent->GetScreenY(&yPos);
- nsCOMPtr<nsIBoxObject> popupBox;
- nsCOMPtr<nsIDOMXULElement> xulPopupElt(do_QueryInterface(popupContent));
- xulPopupElt->GetBoxObject(getter_AddRefs(popupBox));
- nsCOMPtr<nsIPopupBoxObject> 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;
}
////////////////////////////////////////////////////////////////
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;
}
new file mode 100644
--- /dev/null
+++ b/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 <dean_tessman@hotmail.com>
+ * Pierre Phaneuf <pp@ludusdesign.com>
+ * Robert O'Callahan <roc+moz@cs.cmu.edu>
+ *
+ * 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<nsIContent> 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___
--- a/content/xul/document/src/nsXULDocument.cpp
+++ b/content/xul/document/src/nsXULDocument.cpp
@@ -123,16 +123,17 @@
#include "nsIStyleSheetLinkingElement.h"
#include "nsEventDispatcher.h"
#include "nsContentErrors.h"
#include "nsIObserverService.h"
#include "nsNodeUtils.h"
#include "nsIDocShellTreeItem.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIXULWindow.h"
+#include "nsXULPopupManager.h"
//----------------------------------------------------------------------
//
// CIDs
//
static NS_DEFINE_CID(kLocalStoreCID, NS_LOCALSTORE_CID);
static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
@@ -1451,94 +1452,59 @@ nsXULDocument::SetPopupNode(nsIDOMNode*
GetFocusController(getter_AddRefs(focusController));
NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE);
// set popup node
rv = focusController->SetPopupNode(aNode);
return rv;
}
-NS_IMETHODIMP
-nsXULDocument::GetTrustedPopupEvent(nsIDOMEvent** aEvent)
-{
- nsresult rv;
-
- nsCOMPtr<nsIFocusController> 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<nsIFocusController> 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<nsIDOMEvent> event;
- nsresult rv = GetTrustedPopupEvent(getter_AddRefs(event));
- NS_ENSURE_SUCCESS(rv, rv);
- if (! event)
- return NS_ERROR_UNEXPECTED; // no event active
-
- nsCOMPtr<nsIDOMNSUIEvent> uiEvent = do_QueryInterface(event, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = uiEvent->GetRangeParent(aRangeParent); // addrefs
-
- if (NS_SUCCEEDED(rv) && *aRangeParent &&
- !nsContentUtils::CanCallerAccess(*aRangeParent)) {
+ nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+ if (!pm)
+ return NS_ERROR_FAILURE;
+
+ PRInt32 offset;
+ pm->GetMouseLocation(aRangeParent, &offset);
+
+ 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<nsIDOMEvent> event;
- nsresult rv = GetTrustedPopupEvent(getter_AddRefs(event));
- NS_ENSURE_SUCCESS(rv, rv);
- if (! event)
- return NS_ERROR_UNEXPECTED; // no event active
-
- nsCOMPtr<nsIDOMNSUIEvent> uiEvent = do_QueryInterface(event, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
+ nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+ if (!pm)
+ return NS_ERROR_FAILURE;
+
+ PRInt32 offset;
nsCOMPtr<nsIDOMNode> 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
nsXULDocument::GetTooltipNode(nsIDOMNode** aNode)
{
if (mTooltipNode && !nsContentUtils::CanCallerAccess(mTooltipNode)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
--- a/dom/public/idl/xul/nsIDOMXULDocument.idl
+++ b/dom/public/idl/xul/nsIDOMXULDocument.idl
@@ -105,14 +105,9 @@ interface nsIDOMXULDocument : nsISupport
* callers that can indirectly be called from content.
*/
[noscript] nsIDOMNode trustedGetPopupNode();
/**
* Like trustedGetPopupNode, but gets the tooltip node instead.
*/
[noscript] nsIDOMNode trustedGetTooltipNode();
-
- /**
- * Like trustedGetPopupNode, but gets the
- */
- [noscript] attribute nsIDOMEvent trustedPopupEvent;
};
--- a/dom/src/base/nsGlobalWindow.cpp
+++ b/dom/src/base/nsGlobalWindow.cpp
@@ -154,16 +154,17 @@
#include "nsContentUtils.h"
#include "nsCSSProps.h"
#include "nsIURIFixup.h"
#include "nsCDefaultURIFixup.h"
#include "nsEventDispatcher.h"
#include "nsIObserverService.h"
#include "nsIXULAppInfo.h"
#include "nsNetUtil.h"
+#include "nsXULPopupManager.h"
#include "plbase64.h"
#ifdef NS_PRINTING
#include "nsIPrintSettings.h"
#include "nsIPrintSettingsService.h"
#include "nsIWebBrowserPrint.h"
#endif
@@ -3032,20 +3033,20 @@ nsGlobalWindow::SetScreenY(PRInt32 aScre
return NS_OK;
}
nsresult
nsGlobalWindow::CheckSecurityWidthAndHeight(PRInt32* aWidth, PRInt32* aHeight)
{
if (!nsContentUtils::IsCallerTrustedForWrite()) {
// if attempting to resize the window, hide any open popups
- nsCOMPtr<nsIPresShell> presShell;
- mDocShell->GetPresShell(getter_AddRefs(presShell));
- if (presShell)
- presShell->HidePopups();
+ nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+ nsCOMPtr<nsIDocument> doc(do_QueryInterface(mDocument));
+ if (pm && doc)
+ pm->HidePopupsInDocument(doc);
}
// This one is easy. Just ensure the variable is greater than 100;
if ((aWidth && *aWidth < 100) || (aHeight && *aHeight < 100)) {
// Check security state for use in determing window dimensions
if (!nsContentUtils::IsCallerTrustedForWrite()) {
//sec check failed
@@ -3065,20 +3066,20 @@ nsresult
nsGlobalWindow::CheckSecurityLeftAndTop(PRInt32* aLeft, PRInt32* aTop)
{
// This one is harder. We have to get the screen size and window dimensions.
// Check security state for use in determing window dimensions
if (!nsContentUtils::IsCallerTrustedForWrite()) {
// if attempting to move the window, hide any open popups
- nsCOMPtr<nsIPresShell> presShell;
- mDocShell->GetPresShell(getter_AddRefs(presShell));
- if (presShell)
- presShell->HidePopups();
+ nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+ nsCOMPtr<nsIDocument> doc(do_QueryInterface(mDocument));
+ if (pm && doc)
+ pm->HidePopupsInDocument(doc);
PRInt32 screenLeft, screenTop, screenWidth, screenHeight;
PRInt32 winLeft, winTop, winWidth, winHeight;
nsGlobalWindow* rootWindow =
NS_STATIC_CAST(nsGlobalWindow*, GetPrivateRoot());
if (rootWindow) {
rootWindow->FlushPendingNotifications(Flush_Layout);
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -114,17 +114,16 @@
#include "nsAutoPtr.h"
#include "nsBoxFrame.h"
#include "nsIBoxLayout.h"
#include "nsImageFrame.h"
#include "nsIObjectLoadingContent.h"
#include "nsContentErrors.h"
#include "nsIDOMWindowInternal.h"
-#include "nsIMenuFrame.h"
#include "nsBox.h"
#ifdef MOZ_XUL
#include "nsIRootBox.h"
#include "nsIDOMXULCommandDispatcher.h"
#include "nsIDOMXULDocument.h"
#include "nsIXULDocument.h"
@@ -1701,21 +1700,18 @@ GetChildListNameFor(nsIFrame* aChi
listName = nsGkAtoms::absoluteList;
} else if (NS_STYLE_POSITION_FIXED == disp->mPosition) {
listName = nsGkAtoms::fixedList;
#ifdef MOZ_XUL
} else if (NS_STYLE_DISPLAY_POPUP == disp->mDisplay) {
// 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
// Return here, because the postcondition for this function actually
// fails for this case, since the popups are not in a "real" frame list
// in the popup set.
return nsGkAtoms::popupList;
#endif // MOZ_XUL
@@ -5951,31 +5947,29 @@ nsCSSFrameConstructor::ConstructXULFrame
}
else if (display->mDisplay == NS_STYLE_DISPLAY_POPUP) {
// If a popup is inside a menu, then the menu understands the complex
// rules/behavior governing the cascade of multiple menu popups and can handle
// having the real popup frame placed under it as a child.
// 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.
*aHaltProcessing = PR_TRUE;
NS_ASSERTION(!aState.mRootBox, "Popup containing block is missing");
return NS_OK;
}
#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;
}
// This is its own frame that derives from box.
newFrame = NS_NewMenuPopupFrame(mPresShell, aStyleContext);
if (aTag == nsGkAtoms::tooltip) {
@@ -6140,16 +6134,26 @@ nsCSSFrameConstructor::ConstructXULFrame
// the placeholder frame
if (!primaryFrameSet)
aState.mFrameManager->SetPrimaryFrameFor(aContent, topFrame);
}
return rv;
}
+nsresult
+nsCSSFrameConstructor::AddLazyChildren(nsIContent* aContent,
+ nsLazyFrameConstructionCallback* aCallback,
+ void* aArg)
+{
+ nsCOMPtr<nsIRunnable> event =
+ new LazyGenerateChildrenEvent(aContent, mPresShell, aCallback, aArg);
+ return NS_DispatchToCurrentThread(event);
+}
+
already_AddRefed<nsStyleContext>
nsCSSFrameConstructor::BeginBuildingScrollFrame(nsFrameConstructorState& aState,
nsIContent* aContent,
nsStyleContext* aContentStyle,
nsIFrame* aParentFrame,
nsIFrame* aContentParentFrame,
nsIAtom* aScrolledPseudo,
PRBool aIsRoot,
@@ -10128,39 +10132,16 @@ nsCSSFrameConstructor::AttributeChanged(
// See if we can optimize away the style re-resolution -- must be called after
// the frame's AttributeChanged() in case it does something that affects the style
nsFrameManager *frameManager = shell->FrameManager();
nsReStyleHint rshint = frameManager->HasAttributeDependentStyle(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;
}
void
nsCSSFrameConstructor::EndUpdate()
{
@@ -13020,8 +13001,47 @@ NS_IMETHODIMP nsCSSFrameConstructor::Res
mConstructor->mRestyleEvent.Forget();
mConstructor->ProcessPendingRestyles();
viewManager->EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
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;
+}
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -67,16 +67,19 @@ class nsStyleChangeList;
class nsIFrame;
struct nsFindFrameHint
{
nsIFrame *mPrimaryFrameForPrevSibling; // weak ref to the primary frame for the content for which we need a frame
nsFindFrameHint() : mPrimaryFrameForPrevSibling(nsnull) { }
};
+typedef void (PR_CALLBACK nsLazyFrameConstructionCallback)
+ (nsIContent* aContent, nsIFrame* aFrame, void* aArg);
+
class nsFrameConstructorState;
class nsFrameConstructorSaveState;
class nsCSSFrameConstructor
{
public:
nsCSSFrameConstructor(nsIDocument *aDocument, nsIPresShell* aPresShell);
~nsCSSFrameConstructor(void) { }
@@ -117,16 +120,27 @@ public:
nsresult CharacterDataChanged(nsIContent* aContent,
PRBool aAppend);
nsresult ContentStatesChanged(nsIContent* aContent1,
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);
nsresult AttributeChanged(nsIContent* aContent,
PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
PRInt32 aModType);
@@ -998,16 +1012,37 @@ public:
void Revoke() { mConstructor = nsnull; }
private:
nsCSSFrameConstructor *mConstructor;
};
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<nsIContent> mContent;
+ nsCOMPtr<nsIPresShell> mPresShell;
+ nsLazyFrameConstructionCallback* mCallback;
+ void* mArg;
+ };
+
nsIDocument* mDocument; // Weak ref
nsIPresShell* mPresShell; // Weak ref
nsIFrame* mInitialContainingBlock;
nsIFrame* mFixedContainingBlock;
nsIFrame* mDocElementContainingBlock;
nsIFrame* mGfxScrollFrame;
nsIFrame* mPageSequenceFrame;
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -106,16 +106,17 @@
#include "nsIDOMHTMLAreaElement.h"
#include "nsIDOMHTMLLinkElement.h"
#include "nsIImageLoadingContent.h"
#include "nsCopySupport.h"
#include "nsIDOMHTMLFrameSetElement.h"
#ifdef MOZ_XUL
#include "nsIXULDocument.h"
#endif
+#include "nsXULPopupManager.h"
#include "nsPrintfCString.h"
#include "nsIClipboardHelper.h"
#include "nsPIDOMWindow.h"
#include "nsJSEnvironment.h"
#include "nsIFocusController.h"
#include "nsIMenuParent.h"
@@ -1148,20 +1149,19 @@ DocumentViewerImpl::PageHide(PRBool aIsU
// here.
nsAutoPopupStatePusher popupStatePusher(openAbused, PR_TRUE);
nsEventDispatcher::Dispatch(window, mPresContext, &event, nsnull, &status);
}
// look for open menupopups and close them after the unload event, in case
// the unload event listeners open any new popups
- if (mPresShell) {
- nsCOMPtr<nsIPresShell> kungFuDeathGrip = mPresShell;
- mPresShell->HidePopups();
- }
+ nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+ if (pm && mDocument)
+ pm->HidePopupsInDocument(mDocument);
return NS_OK;
}
static void
AttachContainerRecurse(nsIDocShell* aShell)
{
nsCOMPtr<nsIContentViewer> viewer;
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -97,20 +97,20 @@ template<class E> class nsCOMArray;
class nsWeakFrame;
class nsIScrollableFrame;
class gfxASurface;
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
#define NS_PRESSHELL_SCROLL_BOTTOM 100
#define NS_PRESSHELL_SCROLL_LEFT 0
#define NS_PRESSHELL_SCROLL_RIGHT 100
#define NS_PRESSHELL_SCROLL_CENTER 50
#define NS_PRESSHELL_SCROLL_ANYWHERE -1
@@ -720,18 +720,16 @@ public:
* such that the mouse is over the same point in the scaled image as in
* the original. When scaling does not occur, the mouse point isn't used
* as the position can be determined from the displayed frames.
*/
virtual already_AddRefed<gfxASurface> RenderSelection(nsISelection* aSelection,
nsPoint& aPoint,
nsRect* aScreenRect) = 0;
- virtual void HidePopups() = 0;
-
void AddWeakFrame(nsWeakFrame* aWeakFrame);
void RemoveWeakFrame(nsWeakFrame* aWeakFrame);
#ifdef NS_DEBUG
nsIFrame* GetDrawEventTargetFrame() { return mDrawEventTargetFrame; }
#endif
protected:
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -184,17 +184,17 @@
#include "nsIAccessible.h"
#include "nsIAccessibleEvent.h"
#endif
// For style data reconstruction
#include "nsStyleChangeList.h"
#include "nsCSSFrameConstructor.h"
#ifdef MOZ_XUL
-#include "nsIMenuFrame.h"
+#include "nsMenuFrame.h"
#include "nsITreeBoxObject.h"
#endif
#include "nsIMenuParent.h"
#include "nsPlaceholderFrame.h"
// Content viewer interfaces
#include "nsIContentViewer.h"
#include "imgIEncoder.h"
@@ -883,18 +883,16 @@ public:
nsIRegion* aRegion,
nsPoint& aPoint,
nsRect* aScreenRect);
virtual already_AddRefed<gfxASurface> RenderSelection(nsISelection* aSelection,
nsPoint& aPoint,
nsRect* aScreenRect);
- virtual void HidePopups();
-
//nsIViewObserver interface
NS_IMETHOD Paint(nsIView *aView,
nsIRenderingContext* aRenderingContext,
const nsRegion& aDirtyRegion);
NS_IMETHOD ComputeRepaintRegionForCopy(nsIView* aRootView,
nsIView* aMovingView,
nsPoint aDelta,
@@ -5835,28 +5833,16 @@ PresShell::Thaw()
}
if (mDocument)
mDocument->EnumerateSubDocuments(ThawSubDocument, nsnull);
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
//--------------------------------------------------------
//-------------- Begin Reflow Event Definition ------------------------
NS_IMETHODIMP
PresShell::ReflowEvent::Run() {
@@ -6181,21 +6167,18 @@ ReResolveMenusAndTrees(nsIFrame *aFrame,
nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(aFrame));
if (treeBox)
treeBox->ClearStyleAndImageCaches();
// We deliberately don't re-resolve style on a menu's popup
// 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<nsIMenuFrame> 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;
}
PR_STATIC_CALLBACK(PRBool)
ReframeImageBoxes(nsIFrame *aFrame, void *aClosure)
{
nsStyleChangeList *list = NS_STATIC_CAST(nsStyleChangeList*, aClosure);
if (aFrame->GetType() == nsGkAtoms::imageBoxFrame) {
@@ -6323,37 +6306,16 @@ PresShell::EnumeratePlugins(nsIDOMDocume
nodes->Item(i, getter_AddRefs(node));
nsCOMPtr<nsIContent> content = do_QueryInterface(node);
if (content)
aCallback(this, content);
}
}
-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
//------------------------------------------------------
// Start of DEBUG only code
#ifdef NS_DEBUG
#include "nsViewsCID.h"
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -230,17 +230,16 @@ class nsIDocumentLoaderFactory;
{ /* 0DE2FBFA-6B7F-11D7-BBBA-0003938A9D96 */ \
0x0DE2FBFA, 0x6B7F, 0x11D7, {0xBB, 0xBA, 0x00, 0x03, 0x93, 0x8A, 0x9D, 0x96} }
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"
#ifndef MOZ_NO_INSPECTOR_APIS
#include "inDOMView.h"
#include "inDeepTreeWalker.h"
#include "inFlasher.h"
#include "inCSSValueSearch.h"
@@ -503,17 +502,16 @@ MAKE_CTOR(CreateSanitizingHTMLSerializer
MAKE_CTOR(CreateXBLService, nsIXBLService, NS_NewXBLService)
MAKE_CTOR(CreateContentPolicy, nsIContentPolicy, NS_NewContentPolicy)
MAKE_CTOR(CreateComputedDOMStyle, nsIComputedDOMStyle, NS_NewComputedDOMStyle)
#ifdef MOZ_XUL
MAKE_CTOR(CreateXULSortService, nsIXULSortService, NS_NewXULSortService)
// NS_NewXULContentBuilder
// NS_NewXULTreeBuilder
MAKE_CTOR(CreateXULDocument, nsIXULDocument, NS_NewXULDocument)
-MAKE_CTOR(CreateXULPopupListener, nsIXULPopupListener, NS_NewXULPopupListener)
// NS_NewXULControllers
// NS_NewXULPrototypeCache
#endif
#ifdef MOZ_XTF
MAKE_CTOR(CreateXTFService, nsIXTFService, NS_NewXTFService)
MAKE_CTOR(CreateXMLContentBuilder, nsIXMLContentBuilder, NS_NewXMLContentBuilder)
#endif
MAKE_CTOR(CreateContentDLF, nsIDocumentLoaderFactory, NS_NewContentDocumentLoaderFactory)
@@ -1187,21 +1185,16 @@ static const nsModuleComponentInfo gComp
"@mozilla.org/xul/xul-tree-builder;1",
NS_NewXULTreeBuilder },
{ "XUL Document",
NS_XULDOCUMENT_CID,
"@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",
NS_NewXULPrototypeCache },
#endif
#ifdef MOZ_XTF
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -74,16 +74,17 @@
#include "nsStyleSet.h"
#include "nsTextControlFrame.h"
#include "nsXBLWindowKeyHandler.h"
#include "txMozillaXSLTProcessor.h"
#include "nsDOMStorage.h"
#include "nsCellMap.h"
#include "nsTextFrameTextRunCache.h"
#include "nsCCUncollectableMarker.h"
+#include "nsXULPopupManager.h"
#include "nsTextFragment.h"
#ifdef MOZ_XUL
#include "nsXULContentUtils.h"
#include "nsXULElement.h"
#include "nsXULPrototypeCache.h"
#include "nsXULTooltipListener.h"
#endif
@@ -213,22 +214,29 @@ nsLayoutStatics::Initialize()
}
rv = nsCCUncollectableMarker::Init();
if (NS_FAILED(rv)) {
NS_ERROR("Could not initialize nsCCUncollectableMarker");
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();
nsDOMEventRTTearoff::Shutdown();
nsEventListenerManager::Shutdown();
nsContentList::Shutdown();
nsComputedDOMStyle::Shutdown();
CSSLoaderImpl::Shutdown();
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -492,26 +492,31 @@ nsContainerFrame::SyncFrameViewPropertie
if (!aStyleContext->GetStyleVisibility()->IsVisible() &&
!aFrame->SupportsVisibilityHidden()) {
// If it's a scrollable frame that can't hide its scrollbars,
// hide the view. This means that child elements can't override
// their parent's visibility, but it's not practical to leave it
// 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) {
nsWindowType windowType;
widget->GetWindowType(windowType);
if (windowType == eWindowType_popup) {
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 :
nsViewVisibility_kHide);
}
// See if the frame is being relatively positioned or absolutely
// positioned
@@ -711,17 +716,22 @@ nsContainerFrame::PositionChildViews(nsI
} else {
PositionChildViews(childFrame);
}
// Get the next sibling child frame
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);
}
/**
* The second half of frame reflow. Does the following:
* - sets the frame's bounds
* - sizes and positions (if requested) the frame's view. If the frame's final
* position differs from the current position and the frame itself does not
--- a/layout/xul/base/public/Makefile.in
+++ b/layout/xul/base/public/Makefile.in
@@ -44,18 +44,18 @@ include $(DEPTH)/config/autoconf.mk
MODULE = layout
XPIDL_MODULE = layout_xul
GRE_MODULE = 1
EXPORTS = \
nsPIBoxObject.h \
nsIMenuFrame.h \
- nsIPopupSetFrame.h \
nsIScrollbarMediator.h \
+ nsXULPopupManager.h \
$(NULL)
XPIDLSRCS= nsIBoxObject.idl \
nsIScrollBoxObject.idl \
nsIScrollBoxObject.idl \
nsIPopupBoxObject.idl \
nsIMenuBoxObject.idl \
nsIBrowserBoxObject.idl \
--- a/layout/xul/base/public/nsIMenuFrame.h
+++ b/layout/xul/base/public/nsIMenuFrame.h
@@ -33,61 +33,33 @@
* 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 nsIMenuFrame_h___
#define nsIMenuFrame_h___
-// {2281EFC8-A8BA-4a73-8CF7-DB4EECA5EAEC}
-#define NS_IMENUFRAME_IID \
-{ 0x2281efc8, 0xa8ba, 0x4a73, { 0x8c, 0xf7, 0xdb, 0x4e, 0xec, 0xa5, 0xea, 0xec } }
+#include "nsISupports.h"
-class nsIMenuParent;
-class nsIDOMElement;
-class nsIDOMKeyEvent;
+// this interface exists solely because native themes need to call into it.
+// Only menu frames should implement it
-enum nsMenuType {
- eMenuType_Normal = 0,
- eMenuType_Checkbox = 1,
- eMenuType_Radio = 2
-};
+// {212521C8-1509-4F41-ADDB-6A0B9356770F}
+#define NS_IMENUFRAME_IID \
+{ 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)
#endif
--- a/layout/xul/base/public/nsIPopupBoxObject.idl
+++ b/layout/xul/base/public/nsIPopupBoxObject.idl
@@ -36,34 +36,47 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsIBoxObject.idl"
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);
+
+ /**
+ * Hide the popup if it is open.
+ */
void hidePopup();
-
/**
* Allow the popup to automatically position itself.
*/
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);
/**
* Enable automatic popup dismissal. This only has effect when called
* on an open popup.
*/
void enableRollup(in boolean enableRollup);
@@ -80,19 +93,71 @@ interface nsIPopupBoxObject : nsISupport
void setConsumeRollupEvent(in PRUint32 consume);
/**
* Size the popup to the given dimensions
*/
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++
nsresult
NS_NewPopupBoxObject(nsIBoxObject** aResult);
%}
deleted file mode 100644
--- a/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
-
new file mode 100644
--- /dev/null
+++ b/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<nsIContent> mPopup;
+ nsCOMPtr<nsIContent> 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<nsIContent> mPopup;
+ nsCOMPtr<nsIContent> mNextPopup;
+ nsCOMPtr<nsIContent> 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<nsIContent> 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 <menu> 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 <menu>, 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 <menu> 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<nsIDOMEventTarget> mKeyListener;
+
+ // widget that is currently listening to rollup events
+ nsCOMPtr<nsIWidget> mWidget;
+
+ // range parent and offset set in SetMouseLocation
+ nsCOMPtr<nsIDOMNode> 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<nsITimer> mCloseTimer;
+
+ // a popup that is waiting on the timer
+ nsMenuPopupFrame* mTimerMenu;
+};
+
+#endif
--- a/layout/xul/base/src/Makefile.in
+++ b/layout/xul/base/src/Makefile.in
@@ -114,24 +114,23 @@ CPPSRCS += \
nsFrameNavigator.cpp \
nsSplitterFrame.cpp \
nsDeckFrame.cpp \
nsProgressMeterFrame.cpp \
nsMenuPopupFrame.cpp \
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
include $(topsrcdir)/config/config.mk
# we don't want the shared lib, but we want to force the creation of a static lib.
FORCE_STATIC_LIB = 1
--- a/layout/xul/base/src/nsBoxFrame.cpp
+++ b/layout/xul/base/src/nsBoxFrame.cpp
@@ -1788,35 +1788,47 @@ nsBoxFrame::GetFrameSizeWithMargin(nsIBo
/**
* Boxed don't support fixed positionioning of their children.
*/
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
nsIScrollableView* scrollingView = parentView->ToScrollableView();
if (scrollingView) {
scrollingView->SetScrolledView(view);
} else {
viewManager->SetViewZIndex(view, autoZIndex, zIndex);
--- a/layout/xul/base/src/nsBoxFrame.h
+++ b/layout/xul/base/src/nsBoxFrame.h
@@ -157,21 +157,24 @@ public:
NS_IMETHOD DidReflow(nsPresContext* aPresContext,
const nsHTMLReflowState* aReflowState,
nsDidReflowStatus aStatus);
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
NS_IMETHOD BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists);
NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder,
--- a/layout/xul/base/src/nsIMenuParent.h
+++ b/layout/xul/base/src/nsIMenuParent.h
@@ -34,150 +34,58 @@
* 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 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)
-
- virtual nsIMenuFrame *GetCurrentMenuItem() = 0;
- NS_IMETHOD SetCurrentMenuItem(nsIMenuFrame* aMenuItem) = 0;
- virtual nsIMenuFrame *GetNextMenuItem(nsIMenuFrame* aStart) = 0;
- virtual nsIMenuFrame *GetPreviousMenuItem(nsIMenuFrame* aStart) = 0;
-
- 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;
+ // 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;
- NS_IMETHOD AttachedDismissalListener() = 0;
-
- NS_IMETHOD InstallKeyboardNavigator() = 0;
- NS_IMETHOD RemoveKeyboardNavigator() = 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;
- // 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;
+ // indicate that the menubar should become active or inactive
+ NS_IMETHOD SetActive(PRBool aActiveFlag) = 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
--- a/layout/xul/base/src/nsIRootBox.h
+++ b/layout/xul/base/src/nsIRootBox.h
@@ -36,33 +36,33 @@
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsIRootBox_h___
#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 {
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;
virtual nsresult AddTooltipSupport(nsIContent* aNode) = 0;
virtual nsresult RemoveTooltipSupport(nsIContent* aNode) = 0;
static nsIRootBox* GetRootBox(nsIPresShell* aShell);
--- a/layout/xul/base/src/nsMenuBarFrame.cpp
+++ b/layout/xul/base/src/nsMenuBarFrame.cpp
@@ -32,21 +32,19 @@
* 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 "nsIServiceManager.h"
#include "nsIContent.h"
-#include "nsContentUtils.h"
#include "prtypes.h"
#include "nsIAtom.h"
#include "nsPresContext.h"
#include "nsStyleContext.h"
#include "nsCSSRendering.h"
#include "nsINameSpaceManager.h"
#include "nsIDocument.h"
#include "nsIDOMEventTarget.h"
@@ -74,61 +72,28 @@
// Wrapper for creating a new menu Bar container
//
nsIFrame*
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,
nsIFrame* aPrevInFlow)
{
nsresult rv = nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
// Create the menu bar listener.
@@ -152,36 +117,31 @@ nsMenuBarFrame::Init(nsIContent* aC
target->AddEventListener(NS_LITERAL_STRING("mousedown"), (nsIDOMMouseListener*)mMenuBarListener, PR_FALSE);
target->AddEventListener(NS_LITERAL_STRING("blur"), (nsIDOMFocusListener*)mMenuBarListener, PR_TRUE);
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)
{
// If the activity is not changed, there is nothing to do.
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();
}
else {
RemoveKeyboardNavigator();
}
@@ -234,65 +194,68 @@ nsMenuBarFrame::SetActive(PRBool aActive
if (!mIsActive) {
mCaretWasVisible = PR_FALSE;
}
} while (0);
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 {
// if the menu bar is already selected (eg. mouseover), deselect it
if (mCurrentMenu)
mCurrentMenu->SelectMenu(PR_FALSE);
// Activate the menu bar
SetActive(PR_TRUE);
// 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);
// Track this item for keyboard navigation.
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)
child = aChild->GetContent();
aShell->FrameConstructor()->GetInsertionPoint(aFrame, child, aResult);
}
-nsIMenuFrame*
+nsMenuFrame*
nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent)
{
PRUint32 charCode;
aKeyEvent->GetCharCode(&charCode);
// Enumerate over our list of frames.
nsIFrame* immediateParent = nsnull;
GetInsertionPoint(PresContext()->PresShell(), this, nsnull, &immediateParent);
@@ -300,519 +263,180 @@ nsMenuBarFrame::FindMenuWithShortcut(nsI
immediateParent = this;
nsIFrame* currFrame = immediateParent->GetFirstChild(nsnull);
while (currFrame) {
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);
if (!shortcutKey.IsEmpty()) {
// We've got something.
PRUnichar letter = PRUnichar(charCode); // throw away the high-zero-fill
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;
}
}
}
currFrame = currFrame->GetNextSibling();
}
// didn't find a matching menu item
#ifdef XP_WIN
// behavior on Windows - this item is on the menu bar, beep and deactivate the menu bar
if (mIsActive) {
nsCOMPtr<nsISound> soundInterface = do_CreateInstance("@mozilla.org/sound;1");
if (soundInterface)
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;
return NS_OK;
}
-
-NS_IMETHODIMP
-nsMenuBarFrame::Escape(PRBool& aHandledFlag)
+void
+nsMenuBarFrame::CurrentMenuIsBeingDestroyed()
{
- if (!mCurrentMenu)
- 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);
- }
- return NS_OK;
- }
-
- // Clear our current menu item if we've got one.
- SetCurrentMenuItem(nsnull);
- NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
-
- SetActive(PR_FALSE);
-
- // Clear out our dismissal listener
- nsMenuDismissalListener::Shutdown();
- return NS_OK;
-}
-
-NS_IMETHODIMP
-nsMenuBarFrame::Enter()
-{
- if (!mCurrentMenu)
- return NS_OK;
-
- ClearRecentlyRolledUp();
-
- // 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;
+ mCurrentMenu->SelectMenu(PR_FALSE);
+ mCurrentMenu = nsnull;
}
NS_IMETHODIMP
-nsMenuBarFrame::ClearRecentlyRolledUp()
-{
- // 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)
+nsMenuBarFrame::ChangeMenuItem(nsMenuFrame* aMenuItem,
+ PRBool aSelectFirstItem)
{
- // 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);
+ if (mCurrentMenu == aMenuItem)
+ return NS_OK;
- return 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;
-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);
+ // 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);
+ }
}
- // Stop capturing rollups
- // (must do this during Hide, which happens before the menu item is executed,
- // since this reinstates normal event handling.)
- nsMenuDismissalListener::Shutdown();
+ // set to null first in case the IsAlive check below returns false
+ mCurrentMenu = nsnull;
- ClearRecentlyRolledUp();
- if (mCurrentMenu) {
- mCurrentMenu->ActivateMenu(PR_FALSE);
- mCurrentMenu->SelectMenu(PR_FALSE);
- mRecentRollupMenu = mCurrentMenu;
- }
-
- if (mIsActive) {
- ToggleMenuActiveState();
+ // Set the new child.
+ if (aMenuItem) {
+ nsCOMPtr<nsIContent> 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);
}
return NS_OK;
}
-NS_IMETHODIMP
-nsMenuBarFrame::DismissChain()
+nsMenuFrame*
+nsMenuBarFrame::Enter()
{
- // Stop capturing rollups
- nsMenuDismissalListener::Shutdown();
- nsWeakFrame weakFrame(this);
- SetCurrentMenuItem(nsnull);
- if (weakFrame.IsAlive()) {
- SetActive(PR_FALSE);
- }
- return NS_OK;
+ if (!mCurrentMenu)
+ return nsnull;
+
+ if (mCurrentMenu->IsOpen())
+ return mCurrentMenu->Enter();
+
+ return mCurrentMenu;
}
-
-NS_IMETHODIMP
-nsMenuBarFrame::KillPendingTimers ( )
+PRBool
+nsMenuBarFrame::MenuClosed()
{
- 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;
+ SetActive(PR_FALSE);
+ if (!mIsActive && mCurrentMenu) {
+ mCurrentMenu->SelectMenu(PR_FALSE);
+ mCurrentMenu = nsnull;
+ return PR_TRUE;
+ }
+ return PR_FALSE;
}
-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);
mTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), (nsIDOMMouseListener*)mMenuBarListener, PR_FALSE);
mTarget->RemoveEventListener(NS_LITERAL_STRING("blur"), (nsIDOMFocusListener*)mMenuBarListener, PR_TRUE);
NS_IF_RELEASE(mMenuBarListener);
nsBoxFrame::Destroy();
}
-
--- a/layout/xul/base/src/nsMenuBarFrame.h
+++ b/layout/xul/base/src/nsMenuBarFrame.h
@@ -44,96 +44,73 @@
#ifndef nsMenuBarFrame_h__
#define nsMenuBarFrame_h__
#include "prtypes.h"
#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);
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; }
-
- NS_IMETHOD GetParentPopup(nsIMenuParent** aResult) { *aResult = nsnull;
- return NS_OK;}
- 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();
+ 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
- // Hides the chain of cascaded menus without closing them up.
- NS_IMETHOD HideChain();
+ PRBool IsMenuOpen() { return mCurrentMenu && mCurrentMenu->IsOpen(); }
- 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,
nsIFrame* aPrevInFlow);
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
{
// Override bogus IsFrameOfType in nsBoxFrame.
if (aFlags & (nsIFrame::eReplacedContainsBlock | nsIFrame::eReplaced))
return PR_FALSE;
return nsBoxFrame::IsFrameOfType(aFlags);
}
@@ -142,24 +119,21 @@ public:
NS_IMETHOD GetFrameName(nsAString& aResult) const
{
return MakeFrameName(NS_LITERAL_STRING("MenuBar"), aResult);
}
#endif
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;
private:
PRBool mCaretWasVisible;
}; // class nsMenuBarFrame
--- a/layout/xul/base/src/nsMenuBarListener.cpp
+++ b/layout/xul/base/src/nsMenuBarListener.cpp
@@ -35,16 +35,17 @@
* 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 "nsMenuBarListener.h"
#include "nsMenuBarFrame.h"
+#include "nsMenuPopupFrame.h"
#include "nsIDOMKeyListener.h"
#include "nsIDOMEventTarget.h"
#include "nsIDOMEventListener.h"
#include "nsIDOMNSUIEvent.h"
#include "nsIDOMNSEvent.h"
#include "nsGUIEvent.h"
// Drag & Drop, Clipboard
@@ -126,16 +127,28 @@ void nsMenuBarListener::InitAccessKey()
mAccessKeyMask = MODIFIER_ALT;
else if (mAccessKey == nsIDOMKeyEvent::DOM_VK_META)
mAccessKeyMask = MODIFIER_META;
mAccessKeyFocuses =
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)
{
InitAccessKey();
//handlers shouldn't be triggered by non-trusted events.
nsCOMPtr<nsIDOMNSEvent> domNSEvent = do_QueryInterface(aKeyEvent);
@@ -156,37 +169,35 @@ nsMenuBarListener::KeyUp(nsIDOMEvent* aK
nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
PRUint32 theChar;
keyEvent->GetKeyCode(&theChar);
if (mAccessKeyDown && (PRInt32)theChar == mAccessKey)
{
// The access key was down and is now up, and no other
// keys were pressed in between.
- mMenuBarFrame->ToggleMenuActiveState();
+ ToggleMenuActiveState();
}
mAccessKeyDown = PR_FALSE;
PRBool active = mMenuBarFrame->IsActive();
if (active) {
aKeyEvent->StopPropagation();
aKeyEvent->PreventDefault();
- return NS_ERROR_BASE; // I am consuming event
+ return NS_OK; // I am consuming event
}
}
return NS_OK; // means I am NOT consuming event
}
////////////////////////////////////////////////////////////////////////
nsresult
nsMenuBarListener::KeyPress(nsIDOMEvent* aKeyEvent)
{
- mMenuBarFrame->ClearRecentlyRolledUp();
-
// if event has already been handled, bail
nsCOMPtr<nsIDOMNSUIEvent> uiEvent ( do_QueryInterface(aKeyEvent) );
if ( uiEvent ) {
PRBool eventHandled = PR_FALSE;
uiEvent->GetPreventDefault ( &eventHandled );
if ( eventHandled )
return NS_OK; // don't consume event
}
@@ -224,42 +235,42 @@ nsMenuBarListener::KeyPress(nsIDOMEvent*
// If charCode == 0, then it is not a printable character.
// Don't attempt to handle accesskey for non-printable characters.
if (IsAccessKeyPressed(keyEvent) && charCode)
{
// 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)
// Also need to handle F10 specially on Non-Mac platform.
else if (keyCode == NS_VK_F10) {
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;
}
PRBool
nsMenuBarListener::IsAccessKeyPressed(nsIDOMKeyEvent* aKeyEvent)
{
InitAccessKey();
// No other modifiers are allowed to be down except for Shift.
@@ -344,46 +355,38 @@ nsMenuBarListener::Focus(nsIDOMEvent* aE
{
return NS_OK; // means I am NOT consuming event
}
////////////////////////////////////////////////////////////////////////
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
}
////////////////////////////////////////////////////////////////////////
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
}
////////////////////////////////////////////////////////////////////////
nsresult
nsMenuBarListener::MouseUp(nsIDOMEvent* aMouseEvent)
{
- mMenuBarFrame->ClearRecentlyRolledUp();
-
return NS_OK; // means I am NOT consuming event
}
nsresult
nsMenuBarListener::MouseClick(nsIDOMEvent* aMouseEvent)
{
return NS_OK; // means I am NOT consuming event
}
--- a/layout/xul/base/src/nsMenuBarListener.h
+++ b/layout/xul/base/src/nsMenuBarListener.h
@@ -82,16 +82,20 @@ public:
static PRBool IsAccessKeyPressed(nsIDOMKeyEvent* event);
protected:
static void InitAccessKey();
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?
static PRInt32 mAccessKey; // See nsIDOMKeyEvent.h for sample values
static PRUint32 mAccessKeyMask;// Modifier mask for the access key
};
--- a/layout/xul/base/src/nsMenuBoxObject.cpp
+++ b/layout/xul/base/src/nsMenuBoxObject.cpp
@@ -34,21 +34,22 @@
* 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 "nsISupportsUtils.h"
#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
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMENUBOXOBJECT
@@ -83,98 +84,97 @@ nsMenuBoxObject::nsMenuBoxObject()
nsMenuBoxObject::~nsMenuBoxObject()
{
/* destructor code */
}
/* void openMenu (in boolean openFlag); */
NS_IMETHODIMP nsMenuBoxObject::OpenMenu(PRBool aOpenFlag)
{
- nsIFrame* frame = GetFrame(PR_FALSE);
- if (!frame)
- return NS_OK;
-
- if (!nsPopupSetFrame::MayOpenPopup(frame))
- return NS_OK;
+ nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+ if (pm) {
+ nsIFrame* frame = GetFrame(PR_FALSE);
+ if (frame) {
+ if (aOpenFlag) {
+ nsCOMPtr<nsIContent> 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);
+ }
+ }
+ }
+ }
- 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;
}
/* boolean handleKeyPress (in nsIDOMKeyEvent keyEvent); */
NS_IMETHODIMP nsMenuBoxObject::HandleKeyPress(nsIDOMKeyEvent* aKeyEvent, PRBool* aHandledFlag)
{
*aHandledFlag = PR_FALSE;
NS_ENSURE_ARG(aKeyEvent);
+ nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+ if (!pm)
+ return NS_OK;
+
// if event has already been handled, bail
nsCOMPtr<nsIDOMNSUIEvent> uiEvent(do_QueryInterface(aKeyEvent));
if (!uiEvent)
return NS_OK;
PRBool eventHandled = PR_FALSE;
uiEvent->GetPreventDefault(&eventHandled);
if (eventHandled)
return NS_OK;
if (nsMenuBarListener::IsAccessKeyPressed(aKeyEvent))
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;
aKeyEvent->GetKeyCode(&keyCode);
switch (keyCode) {
case NS_VK_UP:
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;
}
}
// Creation Routine ///////////////////////////////////////////////////////////////////////
nsresult
NS_NewMenuBoxObject(nsIBoxObject** aResult)
{
deleted file mode 100644
--- a/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 <dean_tessman@hotmail.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 ***** */
-
-#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<nsIWidget> widget;
- curr->GetWidget ( getter_AddRefs(widget) );
- nsCOMPtr<nsISupports> 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<nsIWidget> 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;
-}
-
deleted file mode 100644
--- a/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<nsIWidget> mWidget;
- PRBool mEnabled;
-};
-
-
-#endif
--- a/layout/xul/base/src/nsMenuFrame.cpp
+++ b/layout/xul/base/src/nsMenuFrame.cpp
@@ -66,28 +66,29 @@
#include "nsWidgetsCID.h"
#include "nsBoxLayoutState.h"
#include "nsIScrollableFrame.h"
#include "nsIViewManager.h"
#include "nsBindingManager.h"
#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
#endif
static PRInt32 gEatMouseMove = PR_FALSE;
static NS_DEFINE_IID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
@@ -136,56 +137,63 @@ NS_INTERFACE_MAP_BEGIN(nsMenuFrame)
NS_INTERFACE_MAP_END_INHERITING(nsBoxFrame)
//
// nsMenuFrame cntr
//
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)
{
} // cntr
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);
+ InitMenuParent(NS_CONST_CAST(nsIFrame *, aParent));
+ return NS_OK;
+}
- currFrame = currFrame->GetParent();
+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();
}
-
- return NS_OK;
}
class nsASyncMenuInitialization : public nsIReflowCallback
{
public:
nsASyncMenuInitialization(nsIFrame* aFrame)
: mWeakFrame(aFrame)
{
}
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;
}
}
delete this;
return shouldFlush;
}
@@ -199,23 +207,17 @@ nsMenuFrame::Init(nsIContent* aCont
{
nsresult rv = nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
// Set up a mediator which can be used for callbacks on this frame.
mTimerMediator = new nsMenuTimerMediator(this);
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) {
nsCOMPtr<nsIStringBundleService> bundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
nsCOMPtr<nsIStringBundle> bundle;
if (NS_SUCCEEDED(rv) && bundleService) {
rv = bundleService->CreateBundle( "chrome://global-platform/locale/platformKeys.properties",
@@ -271,114 +273,75 @@ nsMenuFrame::~nsMenuFrame()
}
// The following methods are all overridden to ensure that the menupopup frame
// is placed in the appropriate list.
nsIFrame*
nsMenuFrame::GetFirstChild(nsIAtom* aListName) const
{
if (nsGkAtoms::popupList == aListName) {
- return mPopupFrames.FirstChild();
+ return mPopupFrame;
}
return nsBoxFrame::GetFirstChild(aListName);
}
NS_IMETHODIMP
nsMenuFrame::SetInitialChildList(nsIAtom* aListName,
nsIFrame* aChildList)
{
- nsresult rv = NS_OK;
- if (nsGkAtoms::popupList == aListName) {
- mPopupFrames.SetFrames(aChildList);
- } else {
-
- nsFrameList frames(aChildList);
+ // 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;
+ }
+ frame = frame->GetNextSibling();
+ }
- // 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();
- }
-
- // Didn't find it.
- rv = nsBoxFrame::SetInitialChildList(aListName, aChildList);
- }
- return rv;
+ // Didn't find it.
+ return nsBoxFrame::SetInitialChildList(aListName, aChildList);
}
nsIAtom*
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()
{
// Kill our timer if one is active. This is not strictly necessary as
// the pointer to this frame will be cleared from the mediator, but
// this is done for added safety.
if (mOpenTimer) {
mOpenTimer->Cancel();
}
// 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();
- 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();
}
NS_IMETHODIMP
nsMenuFrame::BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
{
@@ -387,625 +350,299 @@ nsMenuFrame::BuildDisplayListForChildren
nsDisplayListCollection set;
nsresult rv = nsBoxFrame::BuildDisplayListForChildren(aBuilder, aDirtyRect, set);
NS_ENSURE_SUCCESS(rv, rv);
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;
#ifdef XP_MACOSX
// 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
(aEvent->eventStructType == NS_MOUSE_EVENT &&
aEvent->message == NS_MOUSE_BUTTON_UP &&
NS_STATIC_CAST(nsMouseEvent*, aEvent)->button ==
nsMouseEvent::eRightButton) &&
#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
// another context menu.
//
// Furthermore (there's always more, isn't there?), on some platforms (win32
// being one of them) we get the context menu event on a mouse up while
// 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);
}
}
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);
}
else if (aEvent->message == NS_MOUSE_EXIT_SYNTH) {
// Kill our timer if one is active.
if (mOpenTimer) {
mOpenTimer->Cancel();
mOpenTimer = nsnull;
}
// 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<nsILookAndFeel> lookAndFeel(do_GetService(kLookAndFeelCID));
if (lookAndFeel)
lookAndFeel->GetMetric(nsILookAndFeel::eMetric_SubmenuDelay, menuDelay);
// 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()
{
+ if (IsOpen())
+ CloseMenu(PR_FALSE);
+ else
+ OpenMenu(PR_FALSE);
+}
+
+void
+nsMenuFrame::PopupOpened()
+{
nsWeakFrame weakFrame(this);
- if (mMenuOpen) {
- 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);
+ 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<nsIDOMEvent> event;
+ if (NS_SUCCEEDED(nsEventDispatcher::CreateEvent(mPresContext, nsnull,
+ NS_LITERAL_STRING("Events"),
+ getter_AddRefs(event)))) {
+ event->InitEvent(domEventToFire, PR_TRUE, PR_TRUE);
+
+ nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event));
+ privateEvent->SetTrusted(PR_TRUE);
+
+ nsEventDispatcher::DispatchDOMEvent(mMenu, nsnull, event,
+ mPresContext, nsnull);
+ }
+
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIContent> mMenu;
+ nsCOMPtr<nsPresContext> 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<nsIContent> 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<nsIContent> 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<nsIRunnable> event =
+ new nsMenuActivateEvent(mContent, PresContext(), aActivateFlag);
+ NS_DispatchToCurrentThread(event);
}
return NS_OK;
}
NS_IMETHODIMP
-nsMenuFrame::UngenerateMenu()
-{
- nsCOMPtr<nsIContent> 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<nsIScrollableFrame> 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,
PRInt32 aModType)
{
nsAutoString value;
if (aAttribute == nsGkAtoms::checked) {
if (mType != eMenuType_Normal)
UpdateMenuSpecialState(PresContext());
} else if (aAttribute == nsGkAtoms::acceltext) {
// someone reset the accelText attribute, so clear the bit that says *we* set it
AddStateBits(NS_STATE_ACCELTEXT_IS_DERIVED);
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;
+ return;
- 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);
+ gEatMouseMove = PR_TRUE;
- mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open, NS_LITERAL_STRING("true"), PR_TRUE);
- NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
- FireDOMEventSynch(NS_LITERAL_STRING("DOMMenuItemActive"));
+ nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+ if (pm) {
+ pm->KillMenuTimer();
+ // This opens the menu asynchronously
+ pm->ShowMenu(mContent, aSelectFirstItem, PR_TRUE);
}
- 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)
+void
+nsMenuFrame::CloseMenu(PRBool aDeselectMenu)
{
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);
-
- }
- 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)
-{
- *aResult = nsContentUtils::FindFirstChildWithResolvedTag(mContent,
- kNameSpaceID_XUL,
- nsGkAtoms::menupopup);
- NS_IF_ADDREF(*aResult);
+ // Close the menu asynchronously
+ nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+ if (pm && mPopupFrame)
+ pm->HidePopup(mPopupFrame->GetContent(), PR_FALSE, aDeselectMenu, PR_TRUE);
}
PRBool
nsMenuFrame::IsSizedToPopup(nsIContent* aContent, PRBool aRequireAlways)
{
PRBool sizeToPopup;
if (aContent->Tag() == nsGkAtoms::select)
sizeToPopup = PR_TRUE;
@@ -1033,93 +670,84 @@ nsMenuFrame::GetMinSize(nsBoxLayoutState
NS_IMETHODIMP
nsMenuFrame::DoLayout(nsBoxLayoutState& aState)
{
// lay us out
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);
if (sizeToPopup)
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;
}
+ // 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);
+
// is the new size too small? Make sure we handle scrollbars correctly
- nsIBox* child = popupChild->GetChildBox();
+ nsIBox* child = mPopupFrame->GetChildBox();
- nsRect bounds(popupChild->GetRect());
+ nsRect bounds(mPopupFrame->GetRect());
nsCOMPtr<nsIScrollableFrame> 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;
}
#ifdef DEBUG_LAYOUT
NS_IMETHODIMP
nsMenuFrame::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)
{
nsBoxFrame::SetDebug(aState, aDebug);
- SetDebug(aState, mPopupFrames.FirstChild(), aDebug);
+ if (mPopupFrame)
+ SetDebug(aState, mPopupFrame, aDebug);
}
return NS_OK;
}
nsresult
nsMenuFrame::SetDebug(nsBoxLayoutState& aState, nsIFrame* aList, PRBool aDebug)
{
@@ -1132,238 +760,83 @@ nsMenuFrame::SetDebug(nsBoxLayoutState&
aList = aList->GetNextSibling();
}
return NS_OK;
}
#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 <Enter>/<Return> 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;
- }
-
- nsIFrame* frame = mPopupFrames.FirstChild();
- if (frame) {
- nsMenuPopupFrame* popup = (nsMenuPopupFrame*)frame;
- popup->Enter();
+ else
+ return this;
}
- 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
nsMenuFrame::IsMenu()
{
return mIsMenu;
}
nsresult
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;
}
PRBool
nsMenuFrame::IsDisabled()
{
return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
nsGkAtoms::_true, eCaseMatters);
@@ -1392,21 +865,21 @@ nsMenuFrame::UpdateMenuType(nsPresContex
mType = eMenuType_Normal;
break;
}
UpdateMenuSpecialState(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 */
if (mType != eMenuType_Radio)
return; // only Radio possibly cares about other kinds of change
if (!mChecked || mGroupName.IsEmpty())
return; // no interesting change
@@ -1433,53 +906,37 @@ nsMenuFrame::UpdateMenuSpecialState(nsPr
*
* The only other reasonable behaviour would be to check for another selected
* item in that group. If found, unselect ourselves, otherwise we're the
* selected item. That, however, would be a lot more work, and I don't think
* it's better at all.
*/
/* 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
nsMenuFrame::BuildAcceleratorText()
{
nsAutoString accelText;
if ((GetStateBits() & NS_STATE_ACCELTEXT_IS_DERIVED) == 0) {
@@ -1639,247 +1096,35 @@ nsMenuFrame::Execute(nsGUIEvent *aEvent)
mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::checked,
PR_TRUE);
ENSURE_TRUE(weakFrame.IsAlive());
}
else {
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<nsIContent> 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<nsIViewManager> kungFuDeathGrip = presContext->GetViewManager();
- nsCOMPtr<nsIPresShell> 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<nsIContent> child;
- GetMenuChildrenElement(getter_AddRefs(child));
-
- nsresult rv = NS_OK;
-
- nsCOMPtr<nsIPresShell> 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<nsIDOMDocument> domDoc(do_QueryInterface(child->GetDocument()));
-
- PRUint32 count = child->GetChildCount();
- for (PRUint32 i = 0; i < count; i++) {
- nsCOMPtr<nsIContent> 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<nsIDOMElement> commandElt;
- domDoc->GetElementById(command, getter_AddRefs(commandElt));
- nsCOMPtr<nsIContent> 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<nsIContent> child;
- GetMenuChildrenElement(getter_AddRefs(child));
-
- nsresult rv = NS_OK;
- nsCOMPtr<nsIPresShell> 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<nsIContent> child;
- GetMenuChildrenElement(getter_AddRefs(child));
-
- nsresult rv = NS_OK;
- nsCOMPtr<nsIPresShell> 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<nsIContent> child;
- GetMenuChildrenElement(getter_AddRefs(child));
-
- nsresult rv = NS_OK;
- nsCOMPtr<nsIPresShell> 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);
rv = NS_OK;
} else {
rv = nsBoxFrame::RemoveFrame(aListName, aOldFrame);
}
@@ -1888,23 +1133,21 @@ nsMenuFrame::RemoveFrame(nsIAtom*
NS_IMETHODIMP
nsMenuFrame::InsertFrames(nsIAtom* aListName,
nsIFrame* aPrevFrame,
nsIFrame* aFrameList)
{
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()->
FrameNeedsReflow(this, nsIPresShell::eTreeChange,
NS_FRAME_HAS_DIRTY_CHILDREN);
rv = NS_OK;
} else {
rv = nsBoxFrame::InsertFrames(aListName, aPrevFrame, aFrameList);
@@ -1917,99 +1160,44 @@ NS_IMETHODIMP
nsMenuFrame::AppendFrames(nsIAtom* aListName,
nsIFrame* aFrameList)
{
if (!aFrameList)
return NS_OK;
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()->
FrameNeedsReflow(this, nsIPresShell::eTreeChange,
NS_FRAME_HAS_DIRTY_CHILDREN);
rv = NS_OK;
} else {
rv = nsBoxFrame::AppendFrames(aListName, aFrameList);
}
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<nsIDocument> mDocument;
-};
-
PRBool
nsMenuFrame::SizeToPopup(nsBoxLayoutState& aState, nsSize& aSize)
{
if (!IsCollapsed(aState)) {
nsSize tmpSize(-1, 0);
nsIBox::AddCSSPrefSize(aState, this, tmpSize);
if (tmpSize.width == -1 && GetFlex(aState) == 0) {
- nsIFrame* frame = mPopupFrames.FirstChild();
- if (!frame) {
- nsCOMPtr<nsIContent> 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;
}
}
return PR_FALSE;
}
@@ -2031,129 +1219,60 @@ nsMenuFrame::GetPrefSize(nsBoxLayoutStat
}
return size;
}
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<nsIDOMElement> elt(do_QueryInterface(f->GetContent()));
+ nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(menuFrame->GetContent()));
*aResult = elt;
NS_IF_ADDREF(*aResult);
}
return NS_OK;
}
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<nsIContent> 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<nsIDOMXULMenuListElement> 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<nsIDOMElement> 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;
}
// nsMenuTimerMediator implementation.
NS_IMPL_ISUPPORTS1(nsMenuTimerMediator, nsITimerCallback)
/**
* Constructs a wrapper around an nsMenuFrame.
--- a/layout/xul/base/src/nsMenuFrame.h
+++ b/layout/xul/base/src/nsMenuFrame.h
@@ -44,33 +44,44 @@
#define nsMenuFrame_h__
#include "prtypes.h"
#include "nsIAtom.h"
#include "nsCOMPtr.h"
#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"
#include "nsIContent.h"
#include "nsIScrollableViewProvider.h"
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;
/**
* nsMenuTimerMediator is a wrapper around an nsMenuFrame which can be safely
* passed to timers. The class is reference counted unlike the underlying
* nsMenuFrame, so that it will exist as long as the timer holds a reference
* to it. The callback is delegated to the contained nsMenuFrame as long as
* the contained nsMenuFrame has not been destroyed.
@@ -88,17 +99,17 @@ public:
private:
// Pointer to the wrapped frame.
nsMenuFrame* mFrame;
};
/**
- * @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.
*/
class nsMenuFrame : public nsBoxFrame,
public nsIMenuFrame,
public nsIScrollableViewProvider
{
@@ -117,164 +128,161 @@ public:
nsIFrame* aPrevInFlow);
#ifdef DEBUG_LAYOUT
NS_IMETHOD SetDebug(nsBoxLayoutState& aState, PRBool aDebug);
#endif
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,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists);
NS_IMETHOD HandleEvent(nsPresContext* aPresContext,
nsGUIEvent* aEvent,
- nsEventStatus* aEventStatus); // @see comment ***
+ nsEventStatus* aEventStatus); // @see comment above ***
NS_IMETHOD AppendFrames(nsIAtom* aListName,
nsIFrame* aFrameList);
NS_IMETHOD InsertFrames(nsIAtom* aListName,
nsIFrame* aPrevFrame,
nsIFrame* aFrameList);
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 ***
- 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 ***
+ /**
+ * 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);
- NS_IMETHOD UngenerateMenu(); // @see comment ***
+ PRBool IsChecked() { return mChecked; }
- NS_IMETHOD SelectFirstItem(); // @see comment ***
+ NS_IMETHOD GetActiveChild(nsIDOMElement** aResult);
+ NS_IMETHOD SetActiveChild(nsIDOMElement* aChild); // @see comment above ***
- 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
virtual nsIScrollableView* GetScrollableView();
// nsMenuFrame methods
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
NS_IMETHOD GetFrameName(nsAString& aResult) const
{
return MakeFrameName(NS_LITERAL_STRING("Menu"), aResult);
}
#endif
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;
- friend class nsASyncMenuInitialization;
- void UpdateMenuType(nsPresContext* aPresContext); // @see comment ***
- void UpdateMenuSpecialState(nsPresContext* aPresContext); // @see comment ***
+ // 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 OpenMenuInternal(PRBool aActivateFlag); // @see comment ***
- void GetMenuChildrenElement(nsIContent** aResult);
+ 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);
protected:
#ifdef DEBUG_LAYOUT
nsresult SetDebug(nsBoxLayoutState& aState, nsIFrame* aList, PRBool aDebug);
#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<nsMenuTimerMediator> mTimerMediator;
nsCOMPtr<nsITimer> mOpenTimer;
nsString mGroupName;
- nsSize mLastPref;
//we load some display strings from platformKeys.properties only once
static nsrefcnt gRefCnt;
static nsString *gShiftText;
static nsString *gControlText;
static nsString *gMetaText;
static nsString *gAltText;
static nsString *gModifierSeparator;
deleted file mode 100644
--- a/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 <dean_tessman@hotmail.com>
- * Mark Hammond <markh@ActiveState.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 ***** */
-
-#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<nsIDOMKeyEvent> 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<nsIDOMNSEvent> domNSEvent = do_QueryInterface(aKeyEvent);
- PRBool trustedEvent = PR_FALSE;
-
- if (domNSEvent) {
- domNSEvent->GetIsTrusted(&trustedEvent);
- }
-
- if (!trustedEvent)
- return NS_OK;
-
- nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
- PRUint32 theChar;
- keyEvent->GetKeyCode(&theChar);
- 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;
-}
-
-
deleted file mode 100644
--- a/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
--- a/layout/xul/base/src/nsMenuPopupFrame.cpp
+++ b/layout/xul/base/src/nsMenuPopupFrame.cpp
@@ -39,183 +39,165 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsMenuPopupFrame.h"
#include "nsGkAtoms.h"
#include "nsIContent.h"
-#include "nsContentUtils.h"
#include "prtypes.h"
#include "nsIAtom.h"
#include "nsPresContext.h"
#include "nsStyleContext.h"
#include "nsCSSRendering.h"
#include "nsINameSpaceManager.h"
#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"
#include "nsFrameManager.h"
#include "nsIDocument.h"
#include "nsIDeviceContext.h"
#include "nsRect.h"
-#include "nsIDOMXULDocument.h"
#include "nsILookAndFeel.h"
#include "nsIComponentManager.h"
#include "nsBoxLayoutState.h"
#include "nsIScrollableView.h"
#include "nsIScrollableFrame.h"
#include "nsGUIEvent.h"
#include "nsIRootBox.h"
#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"
#ifdef XP_WIN
#include "nsISound.h"
#endif
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
//
nsIFrame*
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
NS_IMETHODIMP
nsMenuPopupFrame::Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* aPrevInFlow)
{
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
// look&feel object
PRBool tempBool;
presContext->LookAndFeel()->
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.
+ // 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();
-
- // Remove the view from its old position.
- viewManager->RemoveChild(ourView);
+ viewManager->SetViewFloating(ourView, PR_TRUE);
- // 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,
- // so we use the nsIView::SetFloating() to tell the view manager
- // about that constraint.
- viewManager->SetViewFloating(ourView, PR_TRUE);
+ mPopupType = ePopupTypePanel;
+ nsIDocument* doc = aContent->GetOwnerDoc();
+ if (doc) {
+ PRInt32 namespaceID;
+ nsCOMPtr<nsIAtom> 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<nsISupports> cont = PresContext()->GetContainer();
nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(cont);
PRInt32 type = -1;
if (dsti && NS_SUCCEEDED(dsti->GetItemType(&type)) &&
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;
+}
- 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)
{
// Create a widget for ourselves.
nsWidgetInitData widgetData;
widgetData.mWindowType = eWindowType_popup;
@@ -237,16 +219,361 @@ nsMenuPopupFrame::CreateWidgetForView(ns
#else
static NS_DEFINE_IID(kCChildCID, NS_CHILD_CID);
aView->CreateWidget(kCChildCID, &widgetData, nsnull, PR_TRUE, PR_TRUE);
#endif
aView->GetWidget()->SetWindowTranslucency(viewHasTransparentContent);
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<nsIContent> mPopup;
+ nsRefPtr<nsPresContext> 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<nsIScrollableFrame> 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<nsIRunnable> 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 <popup> 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<nsIContent> 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,
PRBool aImmediate)
{
InvalidateRoot(aDamageRect, aX, aY, aImmediate);
}
@@ -279,23 +606,20 @@ nsMenuPopupFrame::GetViewOffset(nsIView*
///////////////////////////////////////////////////////////////////////////////
// GetRootViewForPopup
// Retrieves the view for the popup widget that contains the given frame.
// If the given frame is not contained by a popup widget, return the
// 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) {
nsIView* rootView = nsnull;
if (aStopAtViewManagerRoot) {
view->GetViewManager()->GetRootView(rootView);
}
@@ -303,220 +627,106 @@ nsMenuPopupFrame::GetRootViewForPopup(ns
// Walk up the view hierarchy looking for a view whose widget has a
// window type of eWindowType_popup - in other words a popup window
// widget. If we find one, this is the view we want.
nsIWidget* widget = view->GetWidget();
if (widget) {
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<nsIDOMNode> 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<nsIContent> targetAsContent ( do_QueryInterface(targetNode) );
- nsIWidget* targetDocumentWidget = nsnull;
- if ( targetAsContent ) {
- nsCOMPtr<nsIDocument> 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;
}
else
NS_WARNING ( "Hmmm, looks like you've hit a anchor/align case we weren't setup for." );
} // AdjustPositionForAnchorAlign
@@ -612,132 +822,159 @@ nsMenuPopupFrame::MovePopupToOtherSideOf
PRInt32 shiftDistX = inScreenParentFrameRect.width + mRect.width;
*ioXPos += shiftDistX;
*ioScreenViewLocX += shiftDistX;
}
}
} // 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)
- {
- }
+ if (!mShouldAutoPosition && !mInContentShell)
+ return NS_OK;
+
+ PRBool sizedToPopup = PR_FALSE;
+
+ nsPresContext* presContext = PresContext();
- 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;
+ // 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<nsIDocument> document = mAnchorContent->GetDocument();
+ nsIPresShell *shell = document->GetPrimaryShell();
+ if (!shell)
+ return NS_ERROR_FAILURE;
+
+ aAnchorFrame = shell->GetPrimaryFrameFor(mAnchorContent);
+ }
+ else {
+ aAnchorFrame = presContext->PresShell()->FrameManager()->GetRootFrame();
}
- delete this;
- return shouldFlush;
+ if (!aAnchorFrame)
+ return NS_OK;
}
-
- nsCOMPtr<nsIContent> 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;
+ 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
// if the canvas is scrolled so that part is off screen.
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.
nsPoint parentPos;
GetViewOffset(containingView, parentPos);
// |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_.
+ PRBool readjustAboveBelow = PR_FALSE;
PRInt32 xpos = 0, ypos = 0;
+ PRInt32 screenViewLocX, screenViewLocY;
+
+ 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.
- // 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;
+ if (mAnchorContent) {
+ xpos = parentPos.x + offset.x;
+ ypos = parentPos.y + offset.y;
+
+ // 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);
- 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<nsIDOMXULDocument> xulDoc ( do_QueryInterface(document) );
- AdjustClientXYForNestedDocuments ( xulDoc, presShell, aXPos, aYPos, &xpos, &ypos );
+ // 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;
+ }
- // Add in the top and left margins
- GetStyleMargin()->GetMargin(margin);
+ // 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*
- xpos += margin.left;
- ypos += margin.top;
- }
- else {
- anchoredToParent = PR_TRUE;
+ // |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;
- 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 );
+ 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.
nsPIDOMWindow *window = document->GetWindow();
if (!window)
return NS_OK;
nsIDeviceContext* devContext = PresContext()->DeviceContext();
@@ -751,59 +988,28 @@ nsMenuPopupFrame::SyncViewWithFrame(nsPr
// keep 3px margin to the right and bottom of the screen for WinXP dropshadow
rect.width -= nsPresContext::CSSPixelsToAppUnits(3);
rect.height -= nsPresContext::CSSPixelsToAppUnits(3);
// 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);
}
PRInt32 screenLeftTwips = rect.x;
PRInt32 screenTopTwips = rect.y;
PRInt32 screenWidthTwips = rect.width;
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.
//
///////////////////////////////////////////////////////////////////////////////
//
// +------------------------+
@@ -820,21 +1026,21 @@ nsMenuPopupFrame::SyncViewWithFrame(nsPr
// +------------------------+| ( = mRect )
// | \/ ||
// +------------------------+
// 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) {
PRInt32 moveDist = screenTopTwips - screenViewLocY;
screenViewLocY = screenTopTwips;
ypos += moveDist;
}
@@ -938,20 +1144,20 @@ nsMenuPopupFrame::SyncViewWithFrame(nsPr
// XXXbz this is really silly. We should be able to anchor popups to a
// point or rect, not a frame, and we should be doing so with context
// menus. Furthermore, we should not be adding in pixels manually to
// adjust position (in XULPopupListenerImpl::LaunchPopup comes to mind,
// though ConvertPosition in the same file has some 21-px bogosity in the
// 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.
if ( screenViewLocX < screenLeftTwips ) {
PRInt32 moveDistX = screenLeftTwips - screenViewLocX;
xpos += moveDistX;
screenViewLocX += moveDistX;
}
@@ -986,209 +1192,65 @@ nsMenuPopupFrame::SyncViewWithFrame(nsPr
// We wouldn't fit. Shorten before flipping.
mRect.height = screenViewLocY - screenTopTwips;
}
ypos -= (mRect.height + margin.top + margin.bottom);
}
}
}
- 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();
nsPoint offsetToView;
GetOriginToViewOffset(offsetToView, nsnull);
frameOrigin -= offsetToView;
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<nsIContent> 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)
{
nsIScrollableFrame* sf;
nsresult rv = CallQueryInterface(aFrame, &sf);
if (NS_FAILED(rv))
return nsnull;
@@ -1223,181 +1285,140 @@ nsIScrollableView* nsMenuPopupFrame::Get
if ( scrollableView )
return scrollableView;
currFrame = currFrame->GetNextSibling();
} while ( currFrame );
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
if ( itemRect.y + itemRect.height > scrollY + viewRect.height )
scrollableView->ScrollTo(scrollX, itemRect.y + itemRect.height - viewRect.height, NS_SCROLL_PROPERTY_ALWAYS_BLIT);
// scroll up
else if ( itemRect.y < scrollY )
scrollableView->ScrollTo(scrollX, itemRect.y, NS_SCROLL_PROPERTY_ALWAYS_BLIT);
}
}
}
-NS_IMETHODIMP nsMenuPopupFrame::SetCurrentMenuItem(nsIMenuFrame* aMenuItem)
+NS_IMETHODIMP nsMenuPopupFrame::SetCurrentMenuItem(nsMenuFrame* aMenuItem)
{
+ 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.
- nsIMenuParent *contextMenu = GetContextMenu();
- if (contextMenu)
+ nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+ if (!mIsContextMenu && pm && pm->HasContextMenu(this))
return NS_OK;
- if (mCurrentMenu == aMenuItem)
- 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);
+ }
}
}
// Set the new child.
if (aMenuItem) {
EnsureMenuItemIsVisible(aMenuItem);
aMenuItem->SelectMenu(PR_TRUE);
}
mCurrentMenu = 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;
aKeyEvent->GetCharCode(&charCode);
aKeyEvent->GetKeyCode(&keyCode);
doAction = PR_FALSE;
// 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;
aKeyEvent->GetTimeStamp(&keyTime);
if (charCode == 0) {
if (keyCode == NS_VK_BACK) {
if (!isMenu && !mIncrementalString.IsEmpty()) {
@@ -1440,75 +1461,76 @@ nsMenuPopupFrame::FindMenuWithShortcut(n
nsIFrame* currFrame;
// NOTE: If you crashed here due to a bogus |immediateParent| it is
// possible that the menu whose shortcut is being looked up has
// been destroyed already. One strategy would be to
// setTimeout(<func>,0) as detailed in:
// <http://bugzilla.mozilla.org/show_bug.cgi?id=126675#c32>
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);
if (textKey.IsEmpty()) // No label, try another attribute (value)
current->GetAttr(kNameSpaceID_None, nsGkAtoms::value, textKey);
}
else
isShortcut = PR_TRUE;
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
return nsnull;
}
// Get the active status
if (current->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menuactive,
nsGkAtoms::_true, eCaseMatters)) {
foundActive = PR_TRUE;
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;
- }
}
}
}
currFrame = currFrame->GetNextSibling();
}
doAction = (isMenu && (matchCount == 1 || matchShortcutCount == 1));
@@ -1531,492 +1553,94 @@ nsMenuPopupFrame::FindMenuWithShortcut(n
if (soundInterface)
soundInterface->Beep();
}
#endif // #ifdef XP_WIN
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<nsIMenuParent> 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;
*aWidget = view->GetWidget();
NS_IF_ADDREF(*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<nsIDOMEventTarget> 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;
<