зеркало из https://github.com/mozilla/gecko-dev.git
Improve support for DOM manipulation handling in native menu system, add tests. Re-landing with regression fixes. b=442972 r=kreeger sr=roc
This commit is contained in:
Родитель
d8344434be
Коммит
08caec849c
|
@ -169,6 +169,16 @@ interface nsIDOMWindowUtils : nsISupports {
|
|||
*/
|
||||
void activateNativeMenuItemAt(in AString indexString);
|
||||
|
||||
/**
|
||||
* See nsIWidget::ForceNativeMenuReload
|
||||
*
|
||||
* This is used for native menu system testing. Calling this forces a full
|
||||
* reload of the menu system, reloading all native menus and their items.
|
||||
* This is important for testing because changes to the DOM can affect the
|
||||
* native menu system lazily.
|
||||
*/
|
||||
void forceNativeMenuReload();
|
||||
|
||||
/**
|
||||
* Focus the element aElement. The element should be in the same document
|
||||
* that the window is displaying. Pass null to blur the element, if any,
|
||||
|
|
|
@ -324,6 +324,24 @@ nsDOMWindowUtils::ActivateNativeMenuItemAt(const nsAString& indexString)
|
|||
return widget->ActivateNativeMenuItemAt(indexString);
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::ForceNativeMenuReload()
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap))
|
||||
|| !hasCap)
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
|
||||
// get the widget to send the event to
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (!widget)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return widget->ForceNativeMenuReload();
|
||||
}
|
||||
|
||||
|
||||
nsIWidget*
|
||||
nsDOMWindowUtils::GetWidget()
|
||||
{
|
||||
|
|
|
@ -1196,6 +1196,14 @@ class nsIWidget : public nsISupports {
|
|||
*/
|
||||
NS_IMETHOD GetToggledKeyState(PRUint32 aKeyCode, PRBool* aLEDState) = 0;
|
||||
|
||||
/**
|
||||
* This is used for native menu system testing. Calling this forces a full
|
||||
* reload of the menu system, reloading all native menus and their items.
|
||||
* This is important for testing because changes to the DOM can affect the
|
||||
* native menu system lazily.
|
||||
*/
|
||||
virtual nsresult ForceNativeMenuReload() = 0;
|
||||
|
||||
protected:
|
||||
// keep the list of children. We also keep track of our siblings.
|
||||
// The ownership model is as follows: parent holds a strong ref to
|
||||
|
|
|
@ -332,6 +332,7 @@ public:
|
|||
NS_IMETHOD GetAttention(PRInt32 aCycleCount);
|
||||
|
||||
NS_IMETHOD ActivateNativeMenuItemAt(const nsAString& indexString);
|
||||
NS_IMETHOD ForceNativeMenuReload();
|
||||
|
||||
NS_IMETHOD ResetInputState();
|
||||
NS_IMETHOD SetIMEOpenState(PRBool aState);
|
||||
|
|
|
@ -1381,6 +1381,8 @@ nsresult nsChildView::SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
|
|||
// Used for testing native menu system structure and event handling.
|
||||
NS_IMETHODIMP nsChildView::ActivateNativeMenuItemAt(const nsAString& indexString)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
NSString* title = [NSString stringWithCharacters:indexString.BeginReading() length:indexString.Length()];
|
||||
NSArray* indexes = [title componentsSeparatedByString:@"|"];
|
||||
unsigned int indexCount = [indexes count];
|
||||
|
@ -1411,7 +1413,9 @@ NS_IMETHODIMP nsChildView::ActivateNativeMenuItemAt(const nsAString& indexString
|
|||
|
||||
int itemCount = [currentSubmenu numberOfItems];
|
||||
int targetIndex = [[indexes objectAtIndex:(indexCount - 1)] intValue];
|
||||
if (targetIndex < itemCount) {
|
||||
// We can't perform an action on an item with a submenu, that will raise
|
||||
// an obj-c exception.
|
||||
if (targetIndex < itemCount && ![[currentSubmenu itemAtIndex:targetIndex] hasSubmenu]) {
|
||||
// NSLog(@"Performing action for native menu item titled: %@\n",
|
||||
// [[currentSubmenu itemAtIndex:targetIndex] title]);
|
||||
[currentSubmenu performActionForItemAtIndex:targetIndex];
|
||||
|
@ -1421,8 +1425,28 @@ NS_IMETHODIMP nsChildView::ActivateNativeMenuItemAt(const nsAString& indexString
|
|||
}
|
||||
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP nsChildView::ForceNativeMenuReload()
|
||||
{
|
||||
id windowDelegate = [[mView nativeWindow] delegate];
|
||||
if (windowDelegate && [windowDelegate isKindOfClass:[WindowDelegate class]]) {
|
||||
nsCocoaWindow *widget = [(WindowDelegate *)windowDelegate geckoWidget];
|
||||
if (widget) {
|
||||
nsMenuBarX* mb = widget->GetMenuBar();
|
||||
if (mb) {
|
||||
mb->ForceNativeMenuReload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ public:
|
|||
NS_DECL_NSIMUTATIONOBSERVER
|
||||
|
||||
// nsMenuObjectX
|
||||
void* NativeData() {return (void*)mRootMenu;}
|
||||
void* NativeData() {return (void*)mNativeMenu;}
|
||||
nsMenuObjectTypeX MenuObjectType() {return eMenuBarObjectType;}
|
||||
|
||||
// nsMenuBarX
|
||||
|
@ -117,13 +117,16 @@ public:
|
|||
PRUint32 RegisterForCommand(nsMenuItemX* aItem);
|
||||
void UnregisterCommand(PRUint32 aCommandID);
|
||||
PRUint32 GetMenuCount();
|
||||
bool MenuContainsAppMenu();
|
||||
nsMenuX* GetMenuAt(PRUint32 aIndex);
|
||||
nsMenuItemX* GetMenuItemForCommandID(PRUint32 inCommandID);
|
||||
nsresult Paint();
|
||||
void ForceNativeMenuReload(); // used for testing
|
||||
|
||||
protected:
|
||||
nsresult AddMenu(nsMenuX* aMenu);
|
||||
void RemoveMenu(PRUint32 aIndex);
|
||||
void ConstructNativeMenus();
|
||||
nsresult InsertMenuAtIndex(nsMenuX* aMenu, PRUint32 aIndex);
|
||||
void RemoveMenuAtIndex(PRUint32 aIndex);
|
||||
nsChangeObserver* LookupContentChangeObserver(nsIContent* aContent);
|
||||
void HideItem(nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode);
|
||||
void AquifyMenuBar();
|
||||
|
@ -132,10 +135,10 @@ protected:
|
|||
nsresult CreateApplicationMenu(nsMenuX* inMenu);
|
||||
|
||||
nsTArray< nsAutoPtr<nsMenuX> > mMenuArray;
|
||||
nsIWidget* mParent; // [weak]
|
||||
nsIWidget* mParentWindow; // [weak]
|
||||
PRUint32 mCurrentCommandID; // unique command id (per menu-bar) to give to next item that asks
|
||||
nsIDocument* mDocument; // pointer to document
|
||||
GeckoNSMenu* mRootMenu; // root menu, representing entire menu bar
|
||||
GeckoNSMenu* mNativeMenu; // root menu, representing entire menu bar
|
||||
nsHashtable mObserverTable; // stores observers for content change notification
|
||||
};
|
||||
|
||||
|
|
|
@ -98,13 +98,13 @@ NS_IMETHODIMP nsNativeMenuServiceX::CreateNativeMenuBar(nsIWidget* aParent, nsIC
|
|||
|
||||
|
||||
nsMenuBarX::nsMenuBarX()
|
||||
: mParent(nsnull),
|
||||
: mParentWindow(nsnull),
|
||||
mCurrentCommandID(eCommand_ID_Last),
|
||||
mDocument(nsnull)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
mRootMenu = [[GeckoNSMenu alloc] initWithTitle:@"MainMenuBar"];
|
||||
mNativeMenu = [[GeckoNSMenu alloc] initWithTitle:@"MainMenuBar"];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ nsMenuBarX::~nsMenuBarX()
|
|||
// before the registration hash table is destroyed.
|
||||
mMenuArray.Clear();
|
||||
|
||||
[mRootMenu release];
|
||||
[mNativeMenu release];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ nsresult nsMenuBarX::Create(nsIWidget* aParent, nsIContent* aContent)
|
|||
if (!aParent || !aContent)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
mParent = aParent;
|
||||
mParentWindow = aParent;
|
||||
mContent = aContent;
|
||||
|
||||
AquifyMenuBar();
|
||||
|
@ -158,28 +158,31 @@ nsresult nsMenuBarX::Create(nsIWidget* aParent, nsIContent* aContent)
|
|||
doc->AddMutationObserver(this);
|
||||
mDocument = doc;
|
||||
|
||||
ConstructNativeMenus();
|
||||
|
||||
// Give this to the parent window. The parent takes ownership.
|
||||
return mParentWindow->SetMenuBar(this);
|
||||
}
|
||||
|
||||
|
||||
void nsMenuBarX::ConstructNativeMenus()
|
||||
{
|
||||
PRUint32 count = mContent->GetChildCount();
|
||||
for (PRUint32 i = 0; i < count; i++) {
|
||||
nsIContent *menuContent = mContent->GetChildAt(i);
|
||||
if (menuContent) {
|
||||
if (menuContent->Tag() == nsWidgetAtoms::menu &&
|
||||
if (menuContent &&
|
||||
menuContent->Tag() == nsWidgetAtoms::menu &&
|
||||
menuContent->IsNodeOfType(nsINode::eXUL)) {
|
||||
nsAutoString menuName;
|
||||
menuContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::label, menuName);
|
||||
nsMenuX* newMenu = new nsMenuX();
|
||||
if (newMenu) {
|
||||
nsresult rv = newMenu->Create(this, menuName, this, menuContent);
|
||||
nsresult rv = newMenu->Create(this, this, menuContent);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
AddMenu(newMenu);
|
||||
InsertMenuAtIndex(newMenu, GetMenuCount());
|
||||
else
|
||||
delete newMenu;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Give this to the parent window. The parent takes ownership.
|
||||
return mParent->SetMenuBar(this);
|
||||
}
|
||||
|
||||
|
||||
|
@ -189,7 +192,18 @@ PRUint32 nsMenuBarX::GetMenuCount()
|
|||
}
|
||||
|
||||
|
||||
nsresult nsMenuBarX::AddMenu(nsMenuX* aMenu)
|
||||
bool nsMenuBarX::MenuContainsAppMenu()
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
||||
|
||||
return ([mNativeMenu numberOfItems] > 0 &&
|
||||
[[mNativeMenu itemAtIndex:0] submenu] == sApplicationMenu);
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
|
||||
}
|
||||
|
||||
|
||||
nsresult nsMenuBarX::InsertMenuAtIndex(nsMenuX* aMenu, PRUint32 aIndex)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
|
@ -205,16 +219,21 @@ nsresult nsMenuBarX::AddMenu(nsMenuX* aMenu)
|
|||
[[mainMenu itemAtIndex:0] setSubmenu:sApplicationMenu];
|
||||
}
|
||||
|
||||
// keep track of all added menus
|
||||
mMenuArray.AppendElement(aMenu); // owner
|
||||
// add menu to array that owns our menus
|
||||
mMenuArray.InsertElementAt(aIndex, aMenu);
|
||||
|
||||
NSMenu* nativeMenu = (NSMenu*)aMenu->NativeData();
|
||||
// hook up submenus
|
||||
nsIContent* menuContent = aMenu->Content();
|
||||
if (menuContent->GetChildCount() > 0 &&
|
||||
!nsMenuUtilsX::NodeIsHiddenOrCollapsed(menuContent)) {
|
||||
NSMenuItem* newMenuItem = [[[NSMenuItem alloc] initWithTitle:[nativeMenu title] action:NULL keyEquivalent:@""] autorelease];
|
||||
[mRootMenu addItem:newMenuItem];
|
||||
[newMenuItem setSubmenu:nativeMenu];
|
||||
PRUint32 insertAfter = 0;
|
||||
nsresult rv = nsMenuUtilsX::CountVisibleBefore(this, aMenu, &insertAfter);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "nsMenuUtilsX::CountVisibleBefore failed!\n");
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
if (MenuContainsAppMenu())
|
||||
insertAfter++;
|
||||
[mNativeMenu insertItem:aMenu->NativeMenuItem() atIndex:insertAfter];
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -223,7 +242,7 @@ nsresult nsMenuBarX::AddMenu(nsMenuX* aMenu)
|
|||
}
|
||||
|
||||
|
||||
void nsMenuBarX::RemoveMenu(PRUint32 aIndex)
|
||||
void nsMenuBarX::RemoveMenuAtIndex(PRUint32 aIndex)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
|
@ -232,13 +251,32 @@ void nsMenuBarX::RemoveMenu(PRUint32 aIndex)
|
|||
// Our native menu and our internal menu object array might be out of sync.
|
||||
// This happens, for example, when a submenu is hidden. Because of this we
|
||||
// should not assume that a native submenu is hooked up.
|
||||
[mRootMenu removeItem:(mMenuArray[aIndex]->NativeMenuItem())];
|
||||
NSMenuItem* nativeMenuItem = mMenuArray[aIndex]->NativeMenuItem();
|
||||
int nativeMenuItemIndex = [mNativeMenu indexOfItem:nativeMenuItem];
|
||||
if (nativeMenuItemIndex != -1)
|
||||
[mNativeMenu removeItemAtIndex:nativeMenuItemIndex];
|
||||
|
||||
mMenuArray.RemoveElementAt(aIndex);
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
}
|
||||
|
||||
|
||||
// Calling this forces a full reload of the menu system, reloading all native
|
||||
// menus and their items.
|
||||
// Without this testing is hard because changes to the DOM affect the native
|
||||
// menu system lazily.
|
||||
void nsMenuBarX::ForceNativeMenuReload()
|
||||
{
|
||||
// tear down everything
|
||||
while (GetMenuCount() > 0)
|
||||
RemoveMenuAtIndex(0);
|
||||
|
||||
// construct everything
|
||||
ConstructNativeMenus();
|
||||
}
|
||||
|
||||
|
||||
nsMenuX* nsMenuBarX::GetMenuAt(PRUint32 aIndex)
|
||||
{
|
||||
if (mMenuArray.Length() <= aIndex) {
|
||||
|
@ -253,18 +291,22 @@ nsresult nsMenuBarX::Paint()
|
|||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
NSMenu* mainMenu = [NSApp mainMenu];
|
||||
NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
|
||||
// Don't try to optimize anything in this painting by checking
|
||||
// sLastGeckoMenuBarPainted because the menubar can be manipulated by
|
||||
// native dialogs and sheet code and other things besides this paint method.
|
||||
|
||||
// Swap out first item into incoming menu bar. We have to keep the same menu item for the
|
||||
// Application menu and its submenu is global so we keep passing it along.
|
||||
NSMenuItem* firstMenuItem = [[mainMenu itemAtIndex:0] retain];
|
||||
[mainMenu removeItemAtIndex:0];
|
||||
[mRootMenu insertItem:firstMenuItem atIndex:0];
|
||||
[firstMenuItem release];
|
||||
// We have to keep the same menu item for the Application menu so we keep
|
||||
// passing it along.
|
||||
NSMenu* outgoingMenu = [NSApp mainMenu];
|
||||
NS_ASSERTION([outgoingMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
|
||||
|
||||
NSMenuItem* appMenuItem = [[outgoingMenu itemAtIndex:0] retain];
|
||||
[outgoingMenu removeItemAtIndex:0];
|
||||
[mNativeMenu insertItem:appMenuItem atIndex:0];
|
||||
[appMenuItem release];
|
||||
|
||||
// Set menu bar and event target.
|
||||
[NSApp setMainMenu:mRootMenu];
|
||||
[NSApp setMainMenu:mNativeMenu];
|
||||
nsMenuBarX::sLastGeckoMenuBarPainted = this;
|
||||
|
||||
gSomeMenuBarPainted = YES;
|
||||
|
@ -554,13 +596,15 @@ nsresult nsMenuBarX::CreateApplicationMenu(nsMenuX* inMenu)
|
|||
|
||||
void nsMenuBarX::SetParent(nsIWidget* aParent)
|
||||
{
|
||||
mParent = aParent;
|
||||
mParentWindow = aParent;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// nsIMutationObserver
|
||||
//
|
||||
|
||||
|
||||
void nsMenuBarX::CharacterDataWillChange(nsIDocument* aDocument,
|
||||
nsIContent* aContent,
|
||||
CharacterDataChangeInfo* aInfo)
|
||||
|
@ -578,24 +622,18 @@ void nsMenuBarX::CharacterDataChanged(nsIDocument* aDocument,
|
|||
void nsMenuBarX::ContentAppended(nsIDocument* aDocument, nsIContent* aContainer,
|
||||
PRInt32 aNewIndexInContainer)
|
||||
{
|
||||
if (aContainer != mContent) {
|
||||
nsChangeObserver* obs = LookupContentChangeObserver(aContainer);
|
||||
if (obs)
|
||||
obs->ObserveContentInserted(aDocument, aContainer, aNewIndexInContainer);
|
||||
else {
|
||||
nsCOMPtr<nsIContent> parent = aContainer->GetParent();
|
||||
if (parent) {
|
||||
obs = LookupContentChangeObserver(parent);
|
||||
if (obs)
|
||||
obs->ObserveContentInserted(aDocument, aContainer, aNewIndexInContainer);
|
||||
}
|
||||
}
|
||||
PRUint32 childCount = aContainer->GetChildCount();
|
||||
while ((PRUint32)aNewIndexInContainer < childCount) {
|
||||
nsIContent *child = aContainer->GetChildAt(aNewIndexInContainer);
|
||||
ContentInserted(aDocument, aContainer, child, aNewIndexInContainer);
|
||||
aNewIndexInContainer++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void nsMenuBarX::NodeWillBeDestroyed(const nsINode * aNode)
|
||||
{
|
||||
// our menu bar node is being destroyed
|
||||
mDocument = nsnull;
|
||||
}
|
||||
|
||||
|
@ -604,7 +642,6 @@ void nsMenuBarX::AttributeChanged(nsIDocument * aDocument, nsIContent * aContent
|
|||
PRInt32 aNameSpaceID, nsIAtom * aAttribute,
|
||||
PRInt32 aModType, PRUint32 aStateMask)
|
||||
{
|
||||
// lookup and dispatch to registered thang
|
||||
nsChangeObserver* obs = LookupContentChangeObserver(aContent);
|
||||
if (obs)
|
||||
obs->ObserveAttributeChanged(aDocument, aContent, aAttribute);
|
||||
|
@ -615,8 +652,7 @@ void nsMenuBarX::ContentRemoved(nsIDocument * aDocument, nsIContent * aContainer
|
|||
nsIContent * aChild, PRInt32 aIndexInContainer)
|
||||
{
|
||||
if (aContainer == mContent) {
|
||||
UnregisterForContentChanges(aChild);
|
||||
RemoveMenu(aIndexInContainer);
|
||||
RemoveMenuAtIndex(aIndexInContainer);
|
||||
}
|
||||
else {
|
||||
nsChangeObserver* obs = LookupContentChangeObserver(aContainer);
|
||||
|
@ -624,6 +660,9 @@ void nsMenuBarX::ContentRemoved(nsIDocument * aDocument, nsIContent * aContainer
|
|||
obs->ObserveContentRemoved(aDocument, aChild, aIndexInContainer);
|
||||
}
|
||||
else {
|
||||
// We do a lookup on the parent container in case things were removed
|
||||
// under a "menupopup" item. That is basically a wrapper for the contents
|
||||
// of a "menu" node.
|
||||
nsCOMPtr<nsIContent> parent = aContainer->GetParent();
|
||||
if (parent) {
|
||||
obs = LookupContentChangeObserver(parent);
|
||||
|
@ -638,11 +677,24 @@ void nsMenuBarX::ContentRemoved(nsIDocument * aDocument, nsIContent * aContainer
|
|||
void nsMenuBarX::ContentInserted(nsIDocument * aDocument, nsIContent * aContainer,
|
||||
nsIContent * aChild, PRInt32 aIndexInContainer)
|
||||
{
|
||||
if (aContainer != mContent) {
|
||||
if (aContainer == mContent) {
|
||||
nsMenuX* newMenu = new nsMenuX();
|
||||
if (newMenu) {
|
||||
nsresult rv = newMenu->Create(this, this, aChild);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
InsertMenuAtIndex(newMenu, aIndexInContainer);
|
||||
else
|
||||
delete newMenu;
|
||||
}
|
||||
}
|
||||
else {
|
||||
nsChangeObserver* obs = LookupContentChangeObserver(aContainer);
|
||||
if (obs)
|
||||
obs->ObserveContentInserted(aDocument, aChild, aIndexInContainer);
|
||||
else {
|
||||
// We do a lookup on the parent container in case things were removed
|
||||
// under a "menupopup" item. That is basically a wrapper for the contents
|
||||
// of a "menu" node.
|
||||
nsCOMPtr<nsIContent> parent = aContainer->GetParent();
|
||||
if (parent) {
|
||||
obs = LookupContentChangeObserver(parent);
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
#include "nscore.h"
|
||||
#include "nsGUIEvent.h"
|
||||
#include "nsMenuBaseX.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
@ -58,6 +59,7 @@ namespace nsMenuUtilsX
|
|||
nsMenuBarX* GetHiddenWindowMenuBar(); // returned object is not retained
|
||||
NSMenuItem* GetStandardEditMenuItem(); // returned object is not retained
|
||||
PRBool NodeIsHiddenOrCollapsed(nsIContent* inContent);
|
||||
nsresult CountVisibleBefore(nsMenuObjectX* aMenuObject, nsMenuObjectX* aChild, PRUint32* outVisibleBefore);
|
||||
}
|
||||
|
||||
#endif // nsMenuUtilsX_h_
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
|
||||
#include "nsMenuUtilsX.h"
|
||||
#include "nsMenuBarX.h"
|
||||
#include "nsMenuX.h"
|
||||
#include "nsMenuItemX.h"
|
||||
#include "nsObjCExceptions.h"
|
||||
#include "nsCocoaUtils.h"
|
||||
|
@ -195,3 +196,44 @@ PRBool nsMenuUtilsX::NodeIsHiddenOrCollapsed(nsIContent* inContent)
|
|||
inContent->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::collapsed,
|
||||
nsWidgetAtoms::_true, eCaseMatters));
|
||||
}
|
||||
|
||||
|
||||
// Determines how many items are visible among the siblings in a menu that are
|
||||
// before the given child. Note that this will not count the application menu.
|
||||
nsresult nsMenuUtilsX::CountVisibleBefore(nsMenuObjectX* aParentMenu, nsMenuObjectX* aChild, PRUint32* outVisibleBefore)
|
||||
{
|
||||
NS_ASSERTION(outVisibleBefore, "bad index param in nsMenuX::CountVisibleBefore");
|
||||
|
||||
nsMenuObjectTypeX parentType = aParentMenu->MenuObjectType();
|
||||
if (parentType == eMenuBarObjectType) {
|
||||
*outVisibleBefore = 0;
|
||||
nsMenuBarX* menubarParent = static_cast<nsMenuBarX*>(aParentMenu);
|
||||
PRUint32 numMenus = menubarParent->GetMenuCount();
|
||||
for (PRUint32 i = 0; i < numMenus; i++) {
|
||||
nsMenuX* currMenu = menubarParent->GetMenuAt(i);
|
||||
if (currMenu == aChild)
|
||||
return NS_OK; // we found ourselves, break out
|
||||
if (currMenu) {
|
||||
nsIContent* menuContent = currMenu->Content();
|
||||
if (menuContent->GetChildCount() > 0 &&
|
||||
!nsMenuUtilsX::NodeIsHiddenOrCollapsed(menuContent)) {
|
||||
++(*outVisibleBefore);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (parentType == eSubmenuObjectType) {
|
||||
*outVisibleBefore = 0;
|
||||
nsMenuX* menuParent = static_cast<nsMenuX*>(aParentMenu);
|
||||
PRUint32 numItems = menuParent->GetItemCount();
|
||||
for (PRUint32 i = 0; i < numItems; i++) {
|
||||
// Using GetItemAt instead of GetVisibleItemAt to avoid O(N^2)
|
||||
nsMenuObjectX* currItem = menuParent->GetItemAt(i);
|
||||
if (currItem == aChild)
|
||||
return NS_OK; // we found ourselves, break out
|
||||
if (!nsMenuUtilsX::NodeIsHiddenOrCollapsed(currItem->Content()))
|
||||
++(*outVisibleBefore);
|
||||
}
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -83,12 +83,11 @@ public:
|
|||
NS_DECL_CHANGEOBSERVER
|
||||
|
||||
// nsMenuObjectX
|
||||
void* NativeData() {return (void*)mMacMenu;}
|
||||
void* NativeData() {return (void*)mNativeMenu;}
|
||||
nsMenuObjectTypeX MenuObjectType() {return eSubmenuObjectType;}
|
||||
|
||||
// nsMenuX
|
||||
nsresult Create(nsMenuObjectX* aParent, const nsAString &aLabel,
|
||||
nsMenuBarX* aMenuBar, nsIContent* aNode);
|
||||
nsresult Create(nsMenuObjectX* aParent, nsMenuBarX* aMenuBar, nsIContent* aNode);
|
||||
PRUint32 GetItemCount();
|
||||
nsMenuObjectX* GetItemAt(PRUint32 aPos);
|
||||
nsresult GetVisibleItemCount(PRUint32 &aCount);
|
||||
|
@ -99,12 +98,11 @@ public:
|
|||
NSMenuItem* NativeMenuItem();
|
||||
|
||||
protected:
|
||||
void MenuConstruct(nsIWidget* aParentWindow, void* aMenuNode);
|
||||
void MenuConstruct();
|
||||
nsresult RemoveAll();
|
||||
nsresult SetEnabled(PRBool aIsEnabled);
|
||||
nsresult GetEnabled(PRBool* aIsEnabled);
|
||||
nsresult SetupIcon();
|
||||
nsresult CountVisibleBefore(PRUint32* outVisibleBefore);
|
||||
void GetMenuPopupContent(nsIContent** aResult);
|
||||
PRBool OnOpen();
|
||||
PRBool OnOpened();
|
||||
|
@ -122,7 +120,7 @@ protected:
|
|||
nsMenuObjectX* mParent; // [weak]
|
||||
nsMenuBarX* mMenuBar; // [weak]
|
||||
nsRefPtr<nsMenuItemIconX> mIcon;
|
||||
GeckoNSMenu* mMacMenu; // [strong]
|
||||
GeckoNSMenu* mNativeMenu; // [strong]
|
||||
MenuDelegate* mMenuDelegate; // [strong]
|
||||
NSMenuItem* mNativeMenuItem; // [strong]
|
||||
PRPackedBool mIsEnabled;
|
||||
|
|
|
@ -84,7 +84,7 @@ PRInt32 nsMenuX::sIndexingMenuLevel = 0;
|
|||
|
||||
nsMenuX::nsMenuX()
|
||||
: mVisibleItemsCount(0), mParent(nsnull), mMenuBar(nsnull),
|
||||
mMacMenu(nil), mNativeMenuItem(nil), mIsEnabled(PR_TRUE),
|
||||
mNativeMenu(nil), mNativeMenuItem(nil), mIsEnabled(PR_TRUE),
|
||||
mDestroyHandlerCalled(PR_FALSE), mNeedsRebuild(PR_TRUE),
|
||||
mConstructed(PR_FALSE), mVisible(PR_TRUE), mXBLAttached(PR_FALSE)
|
||||
{
|
||||
|
@ -118,8 +118,8 @@ nsMenuX::~nsMenuX()
|
|||
|
||||
RemoveAll();
|
||||
|
||||
[mMacMenu setDelegate:nil];
|
||||
[mMacMenu release];
|
||||
[mNativeMenu setDelegate:nil];
|
||||
[mNativeMenu release];
|
||||
[mMenuDelegate release];
|
||||
[mNativeMenuItem release];
|
||||
|
||||
|
@ -133,14 +133,13 @@ nsMenuX::~nsMenuX()
|
|||
}
|
||||
|
||||
|
||||
nsresult nsMenuX::Create(nsMenuObjectX* aParent, const nsAString& aLabel,
|
||||
nsMenuBarX* aMenuBar, nsIContent* aNode)
|
||||
nsresult nsMenuX::Create(nsMenuObjectX* aParent, nsMenuBarX* aMenuBar, nsIContent* aNode)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
mContent = aNode;
|
||||
mLabel = aLabel;
|
||||
mMacMenu = CreateMenuWithGeckoString(mLabel);
|
||||
mContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::label, mLabel);
|
||||
mNativeMenu = CreateMenuWithGeckoString(mLabel);
|
||||
|
||||
// register this menu to be notified when changes are made to our content object
|
||||
mMenuBar = aMenuBar; // weak ref
|
||||
|
@ -164,6 +163,7 @@ nsresult nsMenuX::Create(nsMenuObjectX* aParent, const nsAString& aLabel,
|
|||
NSString *newCocoaLabelString = nsMenuUtilsX::CreateTruncatedCocoaLabel(mLabel);
|
||||
mNativeMenuItem = [[NSMenuItem alloc] initWithTitle:newCocoaLabelString action:nil keyEquivalent:@""];
|
||||
[newCocoaLabelString release];
|
||||
[mNativeMenuItem setSubmenu:mNativeMenu];
|
||||
|
||||
[mNativeMenuItem setEnabled:(BOOL)mIsEnabled];
|
||||
|
||||
|
@ -171,7 +171,7 @@ nsresult nsMenuX::Create(nsMenuObjectX* aParent, const nsAString& aLabel,
|
|||
// native menu items being created. If we only call MenuConstruct when a menu
|
||||
// is actually selected, then we can't access keyboard commands until the
|
||||
// menu gets selected, which is bad.
|
||||
MenuConstruct(nsnull, nsnull);
|
||||
MenuConstruct();
|
||||
|
||||
mIcon = new nsMenuItemIconX(this, mContent, mNativeMenuItem);
|
||||
|
||||
|
@ -196,7 +196,7 @@ nsresult nsMenuX::AddMenuItem(nsMenuItemX* aMenuItem)
|
|||
NSMenuItem* newNativeMenuItem = (NSMenuItem*)aMenuItem->NativeData();
|
||||
|
||||
// add the menu item to this menu
|
||||
[mMacMenu addItem:newNativeMenuItem];
|
||||
[mNativeMenu addItem:newNativeMenuItem];
|
||||
|
||||
// set up target/action
|
||||
[newNativeMenuItem setTarget:nsMenuBarX::sNativeEventTarget];
|
||||
|
@ -225,10 +225,10 @@ nsresult nsMenuX::AddMenu(nsMenuX* aMenu)
|
|||
++mVisibleItemsCount;
|
||||
|
||||
// We have to add a menu item and then associate the menu with it
|
||||
NSMenuItem* newNativeMenuItem = (static_cast<nsMenuX*>(aMenu))->NativeMenuItem();
|
||||
NSMenuItem* newNativeMenuItem = aMenu->NativeMenuItem();
|
||||
if (!newNativeMenuItem)
|
||||
return NS_ERROR_FAILURE;
|
||||
[mMacMenu addItem:newNativeMenuItem];
|
||||
[mNativeMenu addItem:newNativeMenuItem];
|
||||
|
||||
[newNativeMenuItem setSubmenu:(NSMenu*)aMenu->NativeData()];
|
||||
|
||||
|
@ -255,14 +255,6 @@ nsMenuObjectX* nsMenuX::GetItemAt(PRUint32 aPos)
|
|||
}
|
||||
|
||||
|
||||
// Checks submenus and menu items. Not suitable for submenus that are children
|
||||
// of a menu bar, which has slightly different rules for visiblity.
|
||||
static PRBool MenuNodeIsVisible(nsMenuObjectX* item)
|
||||
{
|
||||
return (!nsMenuUtilsX::NodeIsHiddenOrCollapsed(item->Content()));
|
||||
}
|
||||
|
||||
|
||||
// Only includes visible items
|
||||
nsresult nsMenuX::GetVisibleItemCount(PRUint32 &aCount)
|
||||
{
|
||||
|
@ -289,7 +281,7 @@ nsMenuObjectX* nsMenuX::GetVisibleItemAt(PRUint32 aPos)
|
|||
PRUint32 visibleNodeIndex = 0;
|
||||
for (PRUint32 i = 0; i < count; i++) {
|
||||
item = mMenuObjectsArray[i];
|
||||
if (MenuNodeIsVisible(item)) {
|
||||
if (!nsMenuUtilsX::NodeIsHiddenOrCollapsed(item->Content())) {
|
||||
if (aPos == visibleNodeIndex) {
|
||||
// we found the visible node we're looking for, return it
|
||||
return item;
|
||||
|
@ -306,14 +298,14 @@ nsresult nsMenuX::RemoveAll()
|
|||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
if (mMacMenu) {
|
||||
if (mNativeMenu) {
|
||||
// clear command id's
|
||||
int itemCount = [mMacMenu numberOfItems];
|
||||
int itemCount = [mNativeMenu numberOfItems];
|
||||
for (int i = 0; i < itemCount; i++)
|
||||
mMenuBar->UnregisterCommand((PRUint32)[[mMacMenu itemAtIndex:i] tag]);
|
||||
mMenuBar->UnregisterCommand((PRUint32)[[mNativeMenu itemAtIndex:i] tag]);
|
||||
// get rid of Cocoa menu items
|
||||
for (int i = [mMacMenu numberOfItems] - 1; i >= 0; i--)
|
||||
[mMacMenu removeItemAtIndex:i];
|
||||
for (int i = [mNativeMenu numberOfItems] - 1; i >= 0; i--)
|
||||
[mNativeMenu removeItemAtIndex:i];
|
||||
}
|
||||
|
||||
mMenuObjectsArray.Clear();
|
||||
|
@ -334,7 +326,7 @@ nsEventStatus nsMenuX::MenuOpened(const nsMenuEvent & aMenuEvent)
|
|||
|
||||
// at this point, the carbon event handler was installed so there
|
||||
// must be a carbon MenuRef to be had
|
||||
if (_NSGetCarbonMenu(mMacMenu) == selectedMenuHandle) {
|
||||
if (_NSGetCarbonMenu(mNativeMenu) == selectedMenuHandle) {
|
||||
// Open the node.
|
||||
mContent->SetAttr(kNameSpaceID_None, nsWidgetAtoms::open, NS_LITERAL_STRING("true"), PR_TRUE);
|
||||
|
||||
|
@ -348,7 +340,7 @@ nsEventStatus nsMenuX::MenuOpened(const nsMenuEvent & aMenuEvent)
|
|||
if (mNeedsRebuild)
|
||||
RemoveAll();
|
||||
|
||||
MenuConstruct(nsnull, nsnull);
|
||||
MenuConstruct();
|
||||
mConstructed = true;
|
||||
}
|
||||
|
||||
|
@ -394,7 +386,7 @@ void nsMenuX::MenuClosed(const nsMenuEvent & aMenuEvent)
|
|||
}
|
||||
|
||||
|
||||
void nsMenuX::MenuConstruct(nsIWidget* aParentWindow, void* aMenuNode)
|
||||
void nsMenuX::MenuConstruct()
|
||||
{
|
||||
mConstructed = false;
|
||||
gConstructingMenu = PR_TRUE;
|
||||
|
@ -402,7 +394,7 @@ void nsMenuX::MenuConstruct(nsIWidget* aParentWindow, void* aMenuNode)
|
|||
// reset destroy handler flag so that we'll know to fire it next time this menu goes away.
|
||||
mDestroyHandlerCalled = PR_FALSE;
|
||||
|
||||
//printf("nsMenuX::MenuConstruct called for %s = %d \n", NS_LossyConvertUTF16toASCII(mLabel).get(), mMacMenu);
|
||||
//printf("nsMenuX::MenuConstruct called for %s = %d \n", NS_LossyConvertUTF16toASCII(mLabel).get(), mNativeMenu);
|
||||
|
||||
// Retrieve our menupopup.
|
||||
nsCOMPtr<nsIContent> menuPopup;
|
||||
|
@ -551,15 +543,11 @@ void nsMenuX::LoadMenuItem(nsIContent* inMenuItemContent)
|
|||
|
||||
void nsMenuX::LoadSubMenu(nsIContent* inMenuContent)
|
||||
{
|
||||
nsAutoString menuName;
|
||||
inMenuContent->GetAttr(kNameSpaceID_None, nsWidgetAtoms::label, menuName);
|
||||
//printf("Creating Menu [%s] \n", NS_LossyConvertUTF16toASCII(menuName).get());
|
||||
|
||||
nsAutoPtr<nsMenuX> menu(new nsMenuX());
|
||||
if (!menu)
|
||||
return;
|
||||
|
||||
nsresult rv = menu->Create(this, menuName, mMenuBar, inMenuContent);
|
||||
nsresult rv = menu->Create(this, mMenuBar, inMenuContent);
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
|
||||
|
@ -754,54 +742,6 @@ void nsMenuX::GetMenuPopupContent(nsIContent** aResult)
|
|||
}
|
||||
|
||||
|
||||
// Determines how many menus are visible among the siblings that are before me.
|
||||
// It doesn't matter if I am visible. Note that this will always count the
|
||||
// Application menu, since we always put it in there.
|
||||
nsresult nsMenuX::CountVisibleBefore(PRUint32* outVisibleBefore)
|
||||
{
|
||||
NS_ASSERTION(outVisibleBefore, "bad index param in nsMenuX::CountVisibleBefore");
|
||||
|
||||
nsMenuObjectTypeX parentType = mParent->MenuObjectType();
|
||||
if (parentType == eMenuBarObjectType) {
|
||||
nsMenuBarX* menubarParent = static_cast<nsMenuBarX*>(mParent);
|
||||
PRUint32 numMenus = menubarParent->GetMenuCount();
|
||||
|
||||
// Find this menu among the children of my parent menubar
|
||||
*outVisibleBefore = 1; // start at 1, the Application menu will always be there
|
||||
for (PRUint32 i = 0; i < numMenus; i++) {
|
||||
nsMenuX* currMenu = menubarParent->GetMenuAt(i);
|
||||
if (currMenu == this) {
|
||||
// we found ourselves, break out
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (currMenu) {
|
||||
nsIContent* menuContent = currMenu->Content();
|
||||
if (menuContent->GetChildCount() > 0 &&
|
||||
!nsMenuUtilsX::NodeIsHiddenOrCollapsed(menuContent)) {
|
||||
++(*outVisibleBefore);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (parentType == eSubmenuObjectType) {
|
||||
// Find this menu among the children of my parent menu
|
||||
nsMenuX* menuParent = static_cast<nsMenuX*>(mParent);
|
||||
PRUint32 numItems = menuParent->GetItemCount();
|
||||
for (PRUint32 i = 0; i < numItems; i++) {
|
||||
// Using GetItemAt instead of GetVisibleItemAt to avoid O(N^2)
|
||||
nsMenuObjectX* currItem = menuParent->GetItemAt(i);
|
||||
if (currItem == this)
|
||||
return NS_OK; // we found ourselves, break out
|
||||
// If the node is visible increment the outparam.
|
||||
if (MenuNodeIsVisible(currItem))
|
||||
++(*outVisibleBefore);
|
||||
}
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
NSMenuItem* nsMenuX::NativeMenuItem()
|
||||
{
|
||||
return mNativeMenuItem;
|
||||
|
@ -839,9 +779,9 @@ void nsMenuX::ObserveAttributeChanged(nsIDocument *aDocument, nsIContent *aConte
|
|||
// a regular menu, just change the title and redraw the menubar.
|
||||
if (parentType == eMenuBarObjectType) {
|
||||
// reuse the existing menu, to avoid rebuilding the root menu bar.
|
||||
NS_ASSERTION(mMacMenu, "nsMenuX::AttributeChanged: invalid menu handle.");
|
||||
NS_ASSERTION(mNativeMenu, "nsMenuX::AttributeChanged: invalid menu handle.");
|
||||
NSString *newCocoaLabelString = nsMenuUtilsX::CreateTruncatedCocoaLabel(mLabel);
|
||||
[mMacMenu setTitle:newCocoaLabelString];
|
||||
[mNativeMenu setTitle:newCocoaLabelString];
|
||||
[newCocoaLabelString release];
|
||||
}
|
||||
else {
|
||||
|
@ -869,11 +809,18 @@ void nsMenuX::ObserveAttributeChanged(nsIDocument *aDocument, nsIContent *aConte
|
|||
}
|
||||
else {
|
||||
PRUint32 insertAfter = 0;
|
||||
if (NS_SUCCEEDED(CountVisibleBefore(&insertAfter))) {
|
||||
if (NS_SUCCEEDED(nsMenuUtilsX::CountVisibleBefore(mParent, this, &insertAfter))) {
|
||||
if (parentType == eMenuBarObjectType || parentType == eSubmenuObjectType) {
|
||||
if (parentType == eMenuBarObjectType) {
|
||||
// Before inserting we need to figure out if we should take the native
|
||||
// application menu into account.
|
||||
nsMenuBarX* mb = static_cast<nsMenuBarX*>(mParent);
|
||||
if (mb->MenuContainsAppMenu())
|
||||
insertAfter++;
|
||||
}
|
||||
NSMenu* parentMenu = (NSMenu*)mParent->NativeData();
|
||||
[parentMenu insertItem:mNativeMenuItem atIndex:insertAfter];
|
||||
[mNativeMenuItem setSubmenu:mMacMenu];
|
||||
[mNativeMenuItem setSubmenu:mNativeMenu];
|
||||
mVisible = PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,6 +137,7 @@ public:
|
|||
virtual void FreeNativeData(void * data, PRUint32 aDataType) {}
|
||||
NS_IMETHOD BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical);
|
||||
virtual nsresult ActivateNativeMenuItemAt(const nsAString& indexString) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult ForceNativeMenuReload() { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD ResetInputState() { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD SetIMEOpenState(PRBool aState) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD GetIMEOpenState(PRBool* aState) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
|
|
|
@ -46,21 +46,31 @@
|
|||
onload="onLoad();"
|
||||
title="Native Menu Test">
|
||||
|
||||
<command id="cmd_FooItem0" oncommand="executedCommandID = 'cmd_FooItem0';"/>
|
||||
<command id="cmd_FooItem1" oncommand="executedCommandID = 'cmd_FooItem1';"/>
|
||||
<command id="cmd_FooItem2" oncommand="executedCommandID = 'cmd_FooItem2';"/>
|
||||
<command id="cmd_BarItem0" oncommand="executedCommandID = 'cmd_BarItem0';"/>
|
||||
<command id="cmd_BarItem1" oncommand="executedCommandID = 'cmd_BarItem1';"/>
|
||||
<command id="cmd_BarItem2" oncommand="executedCommandID = 'cmd_BarItem2';"/>
|
||||
<command id="cmd_NewItem0" oncommand="executedCommandID = 'cmd_NewItem0';"/>
|
||||
<command id="cmd_NewItem1" oncommand="executedCommandID = 'cmd_NewItem1';"/>
|
||||
<command id="cmd_NewItem2" oncommand="executedCommandID = 'cmd_NewItem2';"/>
|
||||
<command id="cmd_NewItem3" oncommand="executedCommandID = 'cmd_NewItem3';"/>
|
||||
<command id="cmd_NewItem4" oncommand="executedCommandID = 'cmd_NewItem4';"/>
|
||||
<command id="cmd_NewItem5" oncommand="executedCommandID = 'cmd_NewItem5';"/>
|
||||
|
||||
<!-- We do not modify any menus or menu items defined here in testing. These
|
||||
serve as a baseline structure for us to test after other modifications.
|
||||
We add children to the menubar defined here and test by modifying those
|
||||
children. -->
|
||||
<menubar id="nativemenubar">
|
||||
<menu id="foo" label="Foo">
|
||||
<menupopup>
|
||||
<menuitem label="FooItem0" command="cmd_FooItem0"/>
|
||||
<menuitem label="FooItem1" command="cmd_FooItem1"/>
|
||||
<menuitem label="FooItem2" command="cmd_FooItem2"/>
|
||||
<menuseparator/>
|
||||
<menu label="Bar">
|
||||
<menupopup>
|
||||
<menuitem label="BarItem0" command="cmd_BarItem0"/>
|
||||
<menuitem label="BarItem1" command="cmd_BarItem1"/>
|
||||
<menuitem label="BarItem2" command="cmd_BarItem2"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
</menupopup>
|
||||
|
@ -78,13 +88,26 @@
|
|||
window.opener.wrappedJSObject.SimpleTest.finish();
|
||||
}
|
||||
|
||||
// We need to force a native menu reload before testing any dom changes
|
||||
// because dom changes can affect the native menu system lazily.
|
||||
function forceNativeMenuReload() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
try {
|
||||
utils.forceNativeMenuReload();
|
||||
}
|
||||
catch (e) {
|
||||
dump(e + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
var executedCommandID = "";
|
||||
|
||||
function testItem(location, targetID) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
|
||||
var correctCommandHandler = false;
|
||||
try {
|
||||
utils.activateNativeMenuItemAt(location);
|
||||
|
@ -94,18 +117,179 @@
|
|||
dump(e + "\n");
|
||||
}
|
||||
finally {
|
||||
ok(correctCommandHandler, 'Command handler not run correctly');
|
||||
executedCommandID = "";
|
||||
return correctCommandHandler;
|
||||
}
|
||||
}
|
||||
|
||||
function createXULMenuPopup() {
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
var item = document.createElementNS(XUL_NS, "menupopup");
|
||||
return item;
|
||||
}
|
||||
|
||||
function createXULMenu(aLabel) {
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
var item = document.createElementNS(XUL_NS, "menu");
|
||||
item.setAttribute("label", aLabel);
|
||||
return item;
|
||||
}
|
||||
|
||||
function createXULMenuItem(aLabel, aCommandId) {
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
var item = document.createElementNS(XUL_NS, "menuitem");
|
||||
item.setAttribute("label", aLabel);
|
||||
item.setAttribute("command", aCommandId);
|
||||
return item;
|
||||
}
|
||||
|
||||
function runBaseMenuTests() {
|
||||
return testItem("0|0", "cmd_FooItem0") &&
|
||||
testItem("0|1", "cmd_FooItem1") &&
|
||||
testItem("0|3|0", "cmd_BarItem0") &&
|
||||
testItem("0|3|1", "cmd_BarItem1");
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
var _delayedOnLoad = function() {
|
||||
testItem("0|0", "cmd_FooItem1");
|
||||
testItem("0|1", "cmd_FooItem2");
|
||||
testItem("0|3|0", "cmd_BarItem1");
|
||||
testItem("0|3|1", "cmd_BarItem2");
|
||||
onTestsFinished()
|
||||
// First let's run the base menu tests.
|
||||
ok(runBaseMenuTests());
|
||||
|
||||
// Set up some nodes that we'll use.
|
||||
var menubarNode = document.getElementById("nativemenubar");
|
||||
var newMenu0 = createXULMenu("NewMenu0");
|
||||
var newMenu1 = createXULMenu("NewMenu1");
|
||||
var newMenuPopup0 = createXULMenuPopup();
|
||||
var newMenuPopup1 = createXULMenuPopup();
|
||||
var newMenuItem0 = createXULMenuItem("NewMenuItem0", "cmd_NewItem0");
|
||||
var newMenuItem1 = createXULMenuItem("NewMenuItem1", "cmd_NewItem1");
|
||||
var newMenuItem2 = createXULMenuItem("NewMenuItem2", "cmd_NewItem2");
|
||||
var newMenuItem3 = createXULMenuItem("NewMenuItem3", "cmd_NewItem3");
|
||||
var newMenuItem4 = createXULMenuItem("NewMenuItem4", "cmd_NewItem4");
|
||||
var newMenuItem5 = createXULMenuItem("NewMenuItem5", "cmd_NewItem5");
|
||||
|
||||
// Create another submenu with hierarchy via DOM manipulation.
|
||||
// ******************
|
||||
// * Foo * NewMenu0 * <- Menu bar
|
||||
// ******************
|
||||
// ****************
|
||||
// * NewMenuItem0 * <- NewMenu0 submenu
|
||||
// ****************
|
||||
// * NewMenuItem1 *
|
||||
// ****************
|
||||
// * NewMenuItem2 *
|
||||
// *******************************
|
||||
// * NewMenu1 > * NewMenuItem3 * <- NewMenu1 submenu
|
||||
// *******************************
|
||||
// * NewMenuItem4 *
|
||||
// ****************
|
||||
// * NewMenuItem5 *
|
||||
// ****************
|
||||
menubarNode.appendChild(newMenu0);
|
||||
newMenu0.appendChild(newMenuPopup0);
|
||||
newMenuPopup0.appendChild(newMenuItem0);
|
||||
newMenuPopup0.appendChild(newMenuItem1);
|
||||
newMenuPopup0.appendChild(newMenuItem2);
|
||||
newMenuPopup0.appendChild(newMenu1);
|
||||
newMenu1.appendChild(newMenuPopup1);
|
||||
newMenuPopup1.appendChild(newMenuItem3);
|
||||
newMenuPopup1.appendChild(newMenuItem4);
|
||||
newMenuPopup1.appendChild(newMenuItem5);
|
||||
|
||||
// Error strings.
|
||||
var sa = "Command handler(s) should have activated";
|
||||
var sna = "Command handler(s) should not have activated";
|
||||
|
||||
// Run basic tests again.
|
||||
forceNativeMenuReload();
|
||||
ok(runBaseMenuTests());
|
||||
|
||||
// Test middle items.
|
||||
ok(testItem("1|1", "cmd_NewItem1"), sa);
|
||||
ok(testItem("1|3|1", "cmd_NewItem4"), sa);
|
||||
|
||||
// Hide newMenu0.
|
||||
newMenu0.setAttribute("hidden", "true");
|
||||
forceNativeMenuReload();
|
||||
ok(runBaseMenuTests(), sa); // the base menu should still be unhidden
|
||||
ok(!testItem("1|0", ""), sna);
|
||||
ok(!testItem("1|1", ""), sna);
|
||||
ok(!testItem("1|2", ""), sna);
|
||||
ok(!testItem("1|3|0", ""), sna);
|
||||
ok(!testItem("1|3|1", ""), sna);
|
||||
ok(!testItem("1|3|2", ""), sna);
|
||||
|
||||
// Show newMenu0.
|
||||
newMenu0.setAttribute("hidden", "false");
|
||||
forceNativeMenuReload();
|
||||
ok(runBaseMenuTests(), sa);
|
||||
ok(testItem("1|0", "cmd_NewItem0"), sa);
|
||||
ok(testItem("1|1", "cmd_NewItem1"), sa);
|
||||
ok(testItem("1|2", "cmd_NewItem2"), sa);
|
||||
ok(testItem("1|3|0", "cmd_NewItem3"), sa);
|
||||
ok(testItem("1|3|1", "cmd_NewItem4"), sa);
|
||||
ok(testItem("1|3|2", "cmd_NewItem5"), sa);
|
||||
|
||||
// Hide items.
|
||||
newMenuItem1.setAttribute("hidden", "true");
|
||||
newMenuItem4.setAttribute("hidden", "true");
|
||||
forceNativeMenuReload();
|
||||
ok(runBaseMenuTests(), sa);
|
||||
ok(testItem("1|0", "cmd_NewItem0"), sa);
|
||||
ok(testItem("1|1", "cmd_NewItem2"), sa);
|
||||
ok(!testItem("1|2", ""), sna);
|
||||
ok(testItem("1|2|0", "cmd_NewItem3"), sa);
|
||||
ok(testItem("1|2|1", "cmd_NewItem5"), sa);
|
||||
ok(!testItem("1|2|2", ""), sna);
|
||||
|
||||
// Show items.
|
||||
newMenuItem1.setAttribute("hidden", "false");
|
||||
newMenuItem4.setAttribute("hidden", "false");
|
||||
forceNativeMenuReload();
|
||||
ok(runBaseMenuTests(), sa);
|
||||
ok(testItem("1|0", "cmd_NewItem0"), sa);
|
||||
ok(testItem("1|1", "cmd_NewItem1"), sa);
|
||||
ok(testItem("1|2", "cmd_NewItem2"), sa);
|
||||
ok(testItem("1|3|0", "cmd_NewItem3"), sa);
|
||||
ok(testItem("1|3|1", "cmd_NewItem4"), sa);
|
||||
ok(testItem("1|3|2", "cmd_NewItem5"), sa);
|
||||
|
||||
// At this point in the tests the state of the menus has been returned
|
||||
// to the originally diagramed state.
|
||||
|
||||
// Remove menu.
|
||||
menubarNode.removeChild(newMenu0);
|
||||
forceNativeMenuReload();
|
||||
ok(runBaseMenuTests(), sa);
|
||||
ok(!testItem("1|0", ""), sna);
|
||||
ok(!testItem("1|1", ""), sna);
|
||||
ok(!testItem("1|2", ""), sna);
|
||||
ok(!testItem("1|3|0", ""), sna);
|
||||
ok(!testItem("1|3|1", ""), sna);
|
||||
ok(!testItem("1|3|2", ""), sna);
|
||||
// return state to original diagramed state
|
||||
menubarNode.appendChild(newMenu0);
|
||||
|
||||
// Test for bug 447042, make sure that adding a menu node with no children
|
||||
// to the menu bar and then adding another menu node with children works.
|
||||
// Menus with no children don't get their native menu items shown and that
|
||||
// caused internal arrays to get out of sync and an append crashed.
|
||||
var tmpMenu0 = createXULMenu("tmpMenu0");
|
||||
menubarNode.removeChild(newMenu0);
|
||||
menubarNode.appendChild(tmpMenu0);
|
||||
menubarNode.appendChild(newMenu0);
|
||||
forceNativeMenuReload();
|
||||
ok(runBaseMenuTests());
|
||||
ok(testItem("1|0", "cmd_NewItem0"), sa);
|
||||
ok(testItem("1|1", "cmd_NewItem1"), sa);
|
||||
ok(testItem("1|2", "cmd_NewItem2"), sa);
|
||||
ok(testItem("1|3|0", "cmd_NewItem3"), sa);
|
||||
ok(testItem("1|3|1", "cmd_NewItem4"), sa);
|
||||
ok(testItem("1|3|2", "cmd_NewItem5"), sa);
|
||||
// return state to original diagramed state
|
||||
menubarNode.removeChild(tmpMenu0);
|
||||
|
||||
onTestsFinished();
|
||||
}
|
||||
|
||||
setTimeout(_delayedOnLoad, 1000);
|
||||
|
|
Загрузка…
Ссылка в новой задаче