Bug 279703, backing out the popup changes due to performance regressions. sigh.

This commit is contained in:
enndeakin@sympatico.ca 2007-06-29 15:39:50 -07:00
parent 95deb19d78
commit 271e5c520d
6 changed files with 678 additions and 181 deletions

View File

@ -488,6 +488,7 @@ 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")
@ -519,7 +520,6 @@ GK_ATOM(never, "never")
GK_ATOM(_new, "new")
GK_ATOM(nextBidi, "NextBidi")
GK_ATOM(no, "no")
GK_ATOM(noautohide, "noautohide")
GK_ATOM(nobr, "nobr")
GK_ATOM(node, "node")
GK_ATOM(nodeSet, "node-set")
@ -634,7 +634,6 @@ GK_ATOM(pageincrement, "pageincrement")
GK_ATOM(pagex, "pagex")
GK_ATOM(pagey, "pagey")
GK_ATOM(palettename, "palettename")
GK_ATOM(panel, "panel")
GK_ATOM(param, "param")
GK_ATOM(parameter, "parameter")
GK_ATOM(parent, "parent")
@ -1391,15 +1390,12 @@ 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")

View File

@ -43,7 +43,7 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = xul
DIRS = src test
DIRS = public src test
include $(topsrcdir)/config/rules.mk

View File

@ -39,53 +39,145 @@
#ifndef nsIMenuParent_h___
#define nsIMenuParent_h___
class nsMenuFrame;
// {33f700c8-976a-4cdb-8f6c-d9f4cfee8366}
#define NS_IMENUPARENT_IID \
{ 0x33f700c8, 0x976a, 0x4cdb, { 0x8f, 0x6c, 0xd9, 0xf4, 0xcf, 0xee, 0x83, 0x66 } }
class nsIMenuFrame;
class nsIDOMKeyEvent;
/*
* nsIMenuParent is an interface implemented by nsMenuBarFrame and nsMenuPopupFrame
* as both serve as parent frames to nsMenuFrame.
*
* 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.
* nsIMenuParent is implemented on frames and thus should not be
* refcounted. Eventually it should not inherit from nsISupports.
*/
class nsIMenuParent {
/**
* 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
};
#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 {
public:
// 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_DECLARE_STATIC_IID_ACCESSOR(NS_IMENUPARENT_IID)
// 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;
virtual nsIMenuFrame *GetCurrentMenuItem() = 0;
NS_IMETHOD SetCurrentMenuItem(nsIMenuFrame* aMenuItem) = 0;
virtual nsIMenuFrame *GetNextMenuItem(nsIMenuFrame* aStart) = 0;
virtual nsIMenuFrame *GetPreviousMenuItem(nsIMenuFrame* aStart) = 0;
// indicate that the menubar should become active or inactive
NS_IMETHOD SetActive(PRBool aActiveFlag) = 0;
NS_IMETHOD GetIsActive(PRBool& isActive) = 0;
NS_IMETHOD GetWidget(nsIWidget **aWidget) = 0;
// 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_IMETHOD IsMenuBar(PRBool& isMenuBar) = 0;
NS_IMETHOD ConsumeOutsideClicks(PRBool& aConsumeOutsideClicks) = 0;
NS_IMETHOD ClearRecentlyRolledUp() = 0;
NS_IMETHOD RecentlyRolledUp(nsIMenuFrame *aMenuFrame, PRBool *aJustRolledUp) = 0;
NS_IMETHOD DismissChain() = 0;
NS_IMETHOD HideChain() = 0;
NS_IMETHOD KillPendingTimers() = 0;
NS_IMETHOD CancelPendingTimers() = 0;
NS_IMETHOD AttachedDismissalListener() = 0;
NS_IMETHOD InstallKeyboardNavigator() = 0;
NS_IMETHOD RemoveKeyboardNavigator() = 0;
// Used to move up, down, left, and right in menus.
NS_IMETHOD KeyboardNavigation(PRUint32 aKeyCode, PRBool& aHandledFlag) = 0;
NS_IMETHOD ShortcutNavigation(nsIDOMKeyEvent* aKeyEvent, PRBool& aHandledFlag) = 0;
// Called when the ESC key is held down to close levels of menus.
NS_IMETHOD Escape(PRBool& aHandledFlag) = 0;
// Called to execute a menu item.
NS_IMETHOD Enter() = 0;
NS_IMETHOD SetIsContextMenu(PRBool aIsContextMenu) = 0;
NS_IMETHOD GetIsContextMenu(PRBool& aIsContextMenu) = 0;
NS_IMETHOD GetParentPopup(nsIMenuParent** aResult) = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIMenuParent, NS_IMENUPARENT_IID)
#endif

View File

@ -41,14 +41,14 @@
#define nsIRootBox_h___
#include "nsISupports.h"
class nsPopupSetFrame;
class nsIFrame;
class nsIContent;
class nsIPresShell;
// {9777EC2A-9A46-4D01-8CEB-B9CEB2C262A5}
// {2256d568-3f5a-42ec-b932-3d0f78551a1a}
#define NS_IROOTBOX_IID \
{ 0x9777EC2A, 0x9A46, 0x4D01, \
{ 0x8C, 0xEB, 0xB9, 0xCE, 0xB2, 0xC2, 0x62, 0xA5 } }
{ 0x2256d568, 0x3f5a, 0x42ec, \
{ 0xb9, 0x32, 0x3d, 0x0f, 0x78, 0x55, 0x1a, 0x1a } }
class nsIRootBox : public nsISupports {
@ -56,8 +56,8 @@ class nsIRootBox : public nsISupports {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IROOTBOX_IID)
virtual nsPopupSetFrame* GetPopupSetFrame() = 0;
virtual void SetPopupSetFrame(nsPopupSetFrame* aPopupSet) = 0;
virtual nsIFrame* GetPopupSetFrame() = 0;
virtual void SetPopupSetFrame(nsIFrame* aPopupSet)=0;
virtual nsIContent* GetDefaultTooltip() = 0;
virtual void SetDefaultTooltip(nsIContent* aTooltip) = 0;

View File

@ -37,9 +37,11 @@
*
* ***** 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"
@ -77,18 +79,51 @@ 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,
@ -121,6 +156,20 @@ nsMenuBarFrame::Init(nsIContent* aContent,
return rv;
}
NS_IMETHODIMP
nsMenuBarFrame::IsOpen()
{
PRBool isOpen = PR_FALSE;
if(mCurrentMenu) {
mCurrentMenu->MenuIsOpen(isOpen);
if (isOpen) {
return PR_TRUE;
}
}
return PR_FALSE;
}
NS_IMETHODIMP
nsMenuBarFrame::SetActive(PRBool aActiveFlag)
{
@ -128,15 +177,6 @@ nsMenuBarFrame::SetActive(PRBool aActiveFlag)
if (mIsActive == aActiveFlag)
return NS_OK;
if (!aActiveFlag) {
// if there is a request to deactivate the menu bar, check to see whether
// there is a menu popup open for the menu bar. In this case, don't
// deactivate the menu bar.
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm && pm->IsPopupOpenForMenuParent(this))
return NS_OK;
}
mIsActive = aActiveFlag;
if (mIsActive) {
InstallKeyboardNavigator();
@ -199,22 +239,22 @@ nsMenuBarFrame::SetActive(PRBool aActiveFlag)
NS_NAMED_LITERAL_STRING(active, "DOMMenuBarActive");
NS_NAMED_LITERAL_STRING(inactive, "DOMMenuBarInactive");
FireDOMEvent(mIsActive ? active : inactive, mContent);
FireDOMEventSynch(mIsActive ? active : inactive);
return NS_OK;
}
nsMenuFrame*
void
nsMenuBarFrame::ToggleMenuActiveState()
{
if (mIsActive) {
// Deactivate the menu bar
SetActive(PR_FALSE);
if (mCurrentMenu) {
nsMenuFrame* closeframe = mCurrentMenu;
closeframe->SelectMenu(PR_FALSE);
// Deactivate the menu.
mCurrentMenu->OpenMenu(PR_FALSE);
mCurrentMenu->SelectMenu(PR_FALSE);
mCurrentMenu = nsnull;
return closeframe;
}
}
else {
@ -228,7 +268,7 @@ nsMenuBarFrame::ToggleMenuActiveState()
// Set the active menu to be the top left item (e.g., the File menu).
// We use an attribute called "menuactive" to track the current
// active menu.
nsMenuFrame* firstFrame = nsXULPopupManager::GetNextMenuItem(this, nsnull, PR_FALSE);
nsIMenuFrame* firstFrame = GetNextMenuItem(nsnull);
if (firstFrame) {
firstFrame->SelectMenu(PR_TRUE);
@ -236,12 +276,9 @@ nsMenuBarFrame::ToggleMenuActiveState()
mCurrentMenu = firstFrame;
}
}
return nsnull;
}
static void
GetInsertionPoint(nsIPresShell* aShell, nsIFrame* aFrame, nsIFrame* aChild,
static void GetInsertionPoint(nsIPresShell* aShell, nsIFrame* aFrame, nsIFrame* aChild,
nsIFrame** aResult)
{
nsIContent* child = nsnull;
@ -250,7 +287,7 @@ GetInsertionPoint(nsIPresShell* aShell, nsIFrame* aFrame, nsIFrame* aChild,
aShell->FrameConstructor()->GetInsertionPoint(aFrame, child, aResult);
}
nsMenuFrame*
nsIMenuFrame*
nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent)
{
PRUint32 charCode;
@ -268,7 +305,7 @@ nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent)
nsIContent* current = currFrame->GetContent();
// See if it's a menu item.
if (nsXULPopupManager::IsValidMenuItem(PresContext(), current, PR_FALSE)) {
if (IsValidItem(current)) {
// Get the shortcut attribute.
nsAutoString shortcutKey;
current->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, shortcutKey);
@ -278,8 +315,11 @@ nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent)
if ( shortcutKey.Equals(Substring(&letter, &letter+1),
nsCaseInsensitiveStringComparator()) ) {
// We match!
return (currFrame->GetType() == nsGkAtoms::menuFrame) ?
NS_STATIC_CAST(nsMenuFrame *, currFrame) : nsnull;
nsIMenuFrame *menuFrame;
if (NS_FAILED(CallQueryInterface(currFrame, &menuFrame))) {
menuFrame = nsnull;
}
return menuFrame;
}
}
}
@ -295,36 +335,263 @@ nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent)
soundInterface->Beep();
}
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm)
pm->Rollup();
SetCurrentMenuItem(nsnull);
SetActive(PR_FALSE);
DismissChain();
#endif // #ifdef XP_WIN
return nsnull;
}
/* virtual */ nsMenuFrame*
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*
nsMenuBarFrame::GetCurrentMenuItem()
{
return mCurrentMenu;
}
NS_IMETHODIMP
nsMenuBarFrame::SetCurrentMenuItem(nsMenuFrame* aMenuItem)
NS_IMETHODIMP nsMenuBarFrame::SetCurrentMenuItem(nsIMenuFrame* aMenuItem)
{
if (mCurrentMenu == aMenuItem)
return NS_OK;
nsWeakFrame weakFrame(this);
if (mCurrentMenu)
mCurrentMenu->SelectMenu(PR_FALSE);
PRBool wasOpen = PR_FALSE;
if (aMenuItem)
// check if there's an open context menu, we ignore this
if (nsMenuFrame::GetContextMenu())
return NS_OK;
nsWeakFrame weakFrame(this);
// 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);
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;
@ -332,103 +599,211 @@ nsMenuBarFrame::SetCurrentMenuItem(nsMenuFrame* aMenuItem)
return NS_OK;
}
void
nsMenuBarFrame::CurrentMenuIsBeingDestroyed()
NS_IMETHODIMP
nsMenuBarFrame::Escape(PRBool& aHandledFlag)
{
mCurrentMenu->SelectMenu(PR_FALSE);
mCurrentMenu = nsnull;
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::ChangeMenuItem(nsMenuFrame* aMenuItem,
PRBool aSelectFirstItem)
{
if (mCurrentMenu == aMenuItem)
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;
// 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);
}
}
// set to null first in case the IsAlive check below returns false
mCurrentMenu = nsnull;
// 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;
}
nsMenuFrame*
nsMenuBarFrame::Enter()
{
if (!mCurrentMenu)
return nsnull;
return NS_OK;
if (mCurrentMenu->IsOpen())
return mCurrentMenu->Enter();
ClearRecentlyRolledUp();
return mCurrentMenu;
// 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;
}
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)
{
// Don't let a click reopen a menu that was just rolled up
// from the same click. Otherwise, the user can't click on
// a menubar item to toggle its submenu closed.
*aJustRolledUp = (mRecentRollupMenu == aMenuFrame);
return NS_OK;
}
NS_IMETHODIMP
nsMenuBarFrame::HideChain()
{
// XXX hack if a context menu is active, do an Escape, which is
// currently bugged and destroys everything. We need to close
// the context menu first, otherwise SetCurrentMenuItem above
// would get blocked.
if (nsMenuFrame::GetContextMenu()) {
PRBool dummy;
mCurrentMenu->Escape(dummy);
}
// Stop capturing rollups
// (must do this during Hide, which happens before the menu item is executed,
// since this reinstates normal event handling.)
nsMenuDismissalListener::Shutdown();
ClearRecentlyRolledUp();
if (mCurrentMenu) {
mCurrentMenu->ActivateMenu(PR_FALSE);
mCurrentMenu->SelectMenu(PR_FALSE);
mRecentRollupMenu = mCurrentMenu;
}
if (mIsActive) {
ToggleMenuActiveState();
}
return NS_OK;
}
NS_IMETHODIMP
nsMenuBarFrame::DismissChain()
{
// Stop capturing rollups
nsMenuDismissalListener::Shutdown();
nsWeakFrame weakFrame(this);
SetCurrentMenuItem(nsnull);
if (weakFrame.IsAlive()) {
SetActive(PR_FALSE);
}
return NS_OK;
}
NS_IMETHODIMP
nsMenuBarFrame::KillPendingTimers ( )
{
return NS_OK;
} // KillPendingTimers
NS_IMETHODIMP
nsMenuBarFrame::GetWidget(nsIWidget **aWidget)
{
// (pinkerton/hyatt)
// since the menubar is a menuparent but not a menuItem, the win32 rollup code
// would erroneously add the entire top-level window to the widget list built up for
// determining if a click is in a submenu's menu chain. To get around this, we just
// don't let the menubar have a widget. Things seem to work because the dismissal
// listener is registered when a new menu is popped up, which is the only real reason
// why we need a widget at all.
*aWidget = nsnull;
return NS_OK;
}
NS_IMETHODIMP
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;
}
NS_IMETHODIMP
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::MenuClosed()
nsMenuBarFrame::IsDisabled(nsIContent* aContent)
{
SetActive(PR_FALSE);
if (!mIsActive && mCurrentMenu) {
mCurrentMenu->SelectMenu(PR_FALSE);
mCurrentMenu = nsnull;
return PR_TRUE;
}
return PR_FALSE;
}
void
nsMenuBarFrame::InstallKeyboardNavigator()
{
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm)
pm->SetActiveMenuBar(this, PR_TRUE);
}
void
nsMenuBarFrame::RemoveKeyboardNavigator()
{
if (!mIsActive) {
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm)
pm->SetActiveMenuBar(this, PR_FALSE);
}
return aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
nsGkAtoms::_true, eCaseMatters);
}
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);
@ -440,3 +815,4 @@ nsMenuBarFrame::Destroy()
nsBoxFrame::Destroy();
}

View File

@ -46,6 +46,7 @@
#include "nsGkAtoms.h"
#include "nsIPresShell.h"
#include "nsIFrame.h"
#include "nsIMenuFrame.h"
#include "nsIPopupBoxObject.h"
#include "nsIServiceManager.h"
#ifdef MOZ_XUL
@ -58,7 +59,6 @@
#include "nsIScriptContext.h"
#include "nsPIDOMWindow.h"
#include "nsContentUtils.h"
#include "nsXULPopupManager.h"
#include "nsIRootBox.h"
nsXULTooltipListener* nsXULTooltipListener::mInstance = nsnull;
@ -209,8 +209,8 @@ nsXULTooltipListener::MouseMove(nsIDOMEvent* aMouseEvent)
// mouse has really moved before proceeding.
nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aMouseEvent));
PRInt32 newMouseX, newMouseY;
mouseEvent->GetScreenX(&newMouseX);
mouseEvent->GetScreenY(&newMouseY);
mouseEvent->GetClientX(&newMouseX);
mouseEvent->GetClientY(&newMouseY);
if (mMouseClientX == newMouseX && mMouseClientY == newMouseY)
return NS_OK;
mMouseClientX = newMouseX;
@ -413,8 +413,12 @@ nsXULTooltipListener::ShowTooltip()
// at this point, |mCurrentTooltip| holds the content node of
// the tooltip. If there is an attribute on the popup telling us
// not to create the auto-hide timer, don't.
if (mCurrentTooltip->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautohide,
nsGkAtoms::_true, eCaseMatters))
nsCOMPtr<nsIDOMElement> tooltipEl(do_QueryInterface(mCurrentTooltip));
if (!tooltipEl)
return NS_ERROR_FAILURE;
nsAutoString noAutoHide;
tooltipEl->GetAttribute(NS_LITERAL_STRING("noautohide"), noAutoHide);
if (!noAutoHide.EqualsLiteral("true"))
CreateAutoHideTimer();
// listen for popuphidden on the tooltip node, so that we can
@ -485,19 +489,37 @@ nsXULTooltipListener::LaunchTooltip()
if (!mCurrentTooltip)
return;
nsCOMPtr<nsIBoxObject> popupBox;
nsCOMPtr<nsIDOMXULElement> xulTooltipEl(do_QueryInterface(mCurrentTooltip));
if (!xulTooltipEl) {
NS_ERROR("tooltip isn't a XUL element!");
return;
}
xulTooltipEl->GetBoxObject(getter_AddRefs(popupBox));
nsCOMPtr<nsIPopupBoxObject> popupBoxObject(do_QueryInterface(popupBox));
if (popupBoxObject) {
PRInt32 x = mMouseClientX;
PRInt32 y = mMouseClientY;
#ifdef MOZ_XUL
if (mIsSourceTree && mNeedTitletip) {
nsCOMPtr<nsITreeBoxObject> obx;
GetSourceTreeBoxObject(getter_AddRefs(obx));
#ifdef DEBUG_crap
GetTreeCellCoords(obx, mSourceNode,
mLastTreeRow, mLastTreeCol, &x, &y);
#endif
SetTitletipLabel(obx, mCurrentTooltip, mLastTreeRow, mLastTreeCol);
if (!mCurrentTooltip) {
// Because of mutation events, mCurrentTooltip can be null.
return;
}
mCurrentTooltip->SetAttr(nsnull, nsGkAtoms::titletip, NS_LITERAL_STRING("true"), PR_TRUE);
mCurrentTooltip->SetAttr(kNameSpaceID_None, nsGkAtoms::titletip,
NS_LITERAL_STRING("true"), PR_TRUE);
} else {
mCurrentTooltip->UnsetAttr(nsnull, nsGkAtoms::titletip, PR_TRUE);
mCurrentTooltip->UnsetAttr(kNameSpaceID_None, nsGkAtoms::titletip,
PR_TRUE);
}
if (!mCurrentTooltip) {
// Because of mutation events, mCurrentTooltip can be null.
@ -505,18 +527,28 @@ nsXULTooltipListener::LaunchTooltip()
}
#endif
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm)
pm->ShowPopupAtScreen(mCurrentTooltip, mMouseClientX, mMouseClientY, PR_FALSE);
nsCOMPtr<nsIDOMElement> targetEl(do_QueryInterface(mSourceNode));
popupBoxObject->ShowPopup(targetEl, xulTooltipEl, x, y,
NS_LITERAL_STRING("tooltip").get(),
NS_LITERAL_STRING("none").get(),
NS_LITERAL_STRING("topleft").get());
}
return;
}
nsresult
nsXULTooltipListener::HideTooltip()
{
if (mCurrentTooltip) {
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm)
pm->HidePopup(mCurrentTooltip, PR_FALSE, PR_FALSE, PR_FALSE);
// hide the popup through its box object
nsCOMPtr<nsIDOMXULElement> tooltipEl(do_QueryInterface(mCurrentTooltip));
nsCOMPtr<nsIBoxObject> boxObject;
if (tooltipEl)
tooltipEl->GetBoxObject(getter_AddRefs(boxObject));
nsCOMPtr<nsIPopupBoxObject> popupObject(do_QueryInterface(boxObject));
if (popupObject)
popupObject->HidePopup();
}
DestroyTooltip();
@ -637,9 +669,10 @@ nsXULTooltipListener::GetTooltipFor(nsIContent* aTarget, nsIContent** aTooltip)
nsIDocument* doc = parent->GetCurrentDoc();
nsIPresShell* presShell = doc ? doc->GetPrimaryShell() : nsnull;
nsIFrame* frame = presShell ? presShell->GetPrimaryFrameFor(parent) : nsnull;
if (frame && frame->GetType() == nsGkAtoms::menuFrame) {
NS_WARNING("Menu cannot be used as a tooltip");
return NS_ERROR_FAILURE;
if (frame) {
nsIMenuFrame* menu = nsnull;
CallQueryInterface(frame, &menu);
NS_ENSURE_FALSE(menu, NS_ERROR_FAILURE);
}
}