Fix crash in Mac OS X native menus when attribute change notifications come in an order we don't expect. Allow for any attribute change notification order. b=433858 r=mstange sr=roc

This commit is contained in:
Josh Aas 2009-01-23 10:45:03 -05:00
parent 4f8f3b0027
commit 904c2da266
5 changed files with 46 additions and 45 deletions

View File

@ -226,14 +226,10 @@ nsresult nsMenuBarX::InsertMenuAtIndex(nsMenuX* aMenu, PRUint32 aIndex)
nsIContent* menuContent = aMenu->Content();
if (menuContent->GetChildCount() > 0 &&
!nsMenuUtilsX::NodeIsHiddenOrCollapsed(menuContent)) {
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;
int insertionIndex = nsMenuUtilsX::CalculateNativeInsertionPoint(this, aMenu);
if (MenuContainsAppMenu())
insertAfter++;
[mNativeMenu insertItem:aMenu->NativeMenuItem() atIndex:insertAfter];
insertionIndex++;
[mNativeMenu insertItem:aMenu->NativeMenuItem() atIndex:insertionIndex];
}
return NS_OK;

View File

@ -62,7 +62,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);
int CalculateNativeInsertionPoint(nsMenuObjectX* aParent, nsMenuObjectX* aChild);
}
#endif // nsMenuUtilsX_h_

View File

@ -45,8 +45,6 @@
#include "nsCocoaWindow.h"
#include "nsWidgetAtoms.h"
#import <Carbon/Carbon.h>
nsEventStatus nsMenuUtilsX::DispatchCommandTo(nsIContent* aTargetContent)
{
NS_PRECONDITION(aTargetContent, "null ptr");
@ -199,41 +197,40 @@ PRBool nsMenuUtilsX::NodeIsHiddenOrCollapsed(nsIContent* inContent)
// 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)
// before the given child. This will not count the application menu.
int nsMenuUtilsX::CalculateNativeInsertionPoint(nsMenuObjectX* aParent,
nsMenuObjectX* aChild)
{
NS_ASSERTION(outVisibleBefore, "bad index param in nsMenuX::CountVisibleBefore");
nsMenuObjectTypeX parentType = aParentMenu->MenuObjectType();
int insertionPoint = 0;
nsMenuObjectTypeX parentType = aParent->MenuObjectType();
if (parentType == eMenuBarObjectType) {
*outVisibleBefore = 0;
nsMenuBarX* menubarParent = static_cast<nsMenuBarX*>(aParentMenu);
nsMenuBarX* menubarParent = static_cast<nsMenuBarX*>(aParent);
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);
}
}
return insertionPoint; // we found ourselves, break out
if (currMenu && [currMenu->NativeMenuItem() menu])
insertionPoint++;
}
}
else if (parentType == eSubmenuObjectType) {
*outVisibleBefore = 0;
nsMenuX* menuParent = static_cast<nsMenuX*>(aParentMenu);
nsMenuX* menuParent = static_cast<nsMenuX*>(aParent);
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 insertionPoint; // we found ourselves, break out
NSMenuItem* nativeItem = nil;
nsMenuObjectTypeX currItemType = currItem->MenuObjectType();
if (currItemType == eSubmenuObjectType)
nativeItem = static_cast<nsMenuX*>(currItem)->NativeMenuItem();
else
nativeItem = (NSMenuItem*)(currItem->NativeData());
if ([nativeItem menu])
insertionPoint++;
}
}
return NS_ERROR_FAILURE;
return insertionPoint;
}

View File

@ -806,21 +806,19 @@ void nsMenuX::ObserveAttributeChanged(nsIDocument *aDocument, nsIContent *aConte
}
}
else {
PRUint32 insertAfter = 0;
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:mNativeMenu];
mVisible = PR_TRUE;
if (parentType == eMenuBarObjectType || parentType == eSubmenuObjectType) {
int insertionIndex = nsMenuUtilsX::CalculateNativeInsertionPoint(mParent, this);
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())
insertionIndex++;
}
NSMenu* parentMenu = (NSMenu*)mParent->NativeData();
[parentMenu insertItem:mNativeMenuItem atIndex:insertionIndex];
[mNativeMenuItem setSubmenu:mNativeMenu];
mVisible = PR_TRUE;
}
}
}

View File

@ -292,6 +292,16 @@
menubarNode.removeChild(tmpMenu0);
delete tmpMenu0;
// This test is basically a crash test for bug 433858.
newMenuItem1.setAttribute("hidden", "true");
newMenuItem2.setAttribute("hidden", "true");
newMenu1.setAttribute("hidden", "true");
forceUpdateNativeMenuAt("1");
newMenuItem1.setAttribute("hidden", "false");
newMenuItem2.setAttribute("hidden", "false");
newMenu1.setAttribute("hidden", "false");
forceUpdateNativeMenuAt("1");
onTestsFinished();
}