diff options
Diffstat (limited to 'widget/cocoa/nsMenuGroupOwnerX.mm')
-rw-r--r-- | widget/cocoa/nsMenuGroupOwnerX.mm | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/widget/cocoa/nsMenuGroupOwnerX.mm b/widget/cocoa/nsMenuGroupOwnerX.mm new file mode 100644 index 0000000000..661a52bd80 --- /dev/null +++ b/widget/cocoa/nsMenuGroupOwnerX.mm @@ -0,0 +1,261 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsMenuGroupOwnerX.h" +#include "nsMenuBarX.h" +#include "nsMenuX.h" +#include "nsMenuItemX.h" +#include "nsMenuUtilsX.h" +#include "nsCocoaUtils.h" +#include "nsCocoaWindow.h" + +#include "nsCOMPtr.h" +#include "nsString.h" +#include "nsObjCExceptions.h" +#include "nsThreadUtils.h" + +#include "mozilla/dom/Element.h" +#include "nsIWidget.h" +#include "nsIDocument.h" +#include "nsIDOMDocument.h" +#include "nsIDOMElement.h" + +#include "nsINode.h" + +using namespace mozilla; + +NS_IMPL_ISUPPORTS(nsMenuGroupOwnerX, nsIMutationObserver) + + +nsMenuGroupOwnerX::nsMenuGroupOwnerX() +: mCurrentCommandID(eCommand_ID_Last) +{ + mInfoSet = [[NSMutableSet setWithCapacity:10] retain]; +} + + +nsMenuGroupOwnerX::~nsMenuGroupOwnerX() +{ + MOZ_ASSERT(mContentToObserverTable.Count() == 0, "have outstanding mutation observers!\n"); + + // The MenuItemInfo objects in mInfoSet may live longer than we do. So when + // we get destroyed we need to invalidate all their mMenuGroupOwner pointers. + NSEnumerator* counter = [mInfoSet objectEnumerator]; + MenuItemInfo* info; + while ((info = (MenuItemInfo*) [counter nextObject])) { + [info setMenuGroupOwner:nil]; + } + [mInfoSet release]; +} + + +nsresult nsMenuGroupOwnerX::Create(nsIContent* aContent) +{ + if (!aContent) + return NS_ERROR_INVALID_ARG; + + mContent = aContent; + + return NS_OK; +} + + +// +// nsIMutationObserver +// + + +void nsMenuGroupOwnerX::CharacterDataWillChange(nsIDocument* aDocument, + nsIContent* aContent, + CharacterDataChangeInfo* aInfo) +{ +} + + +void nsMenuGroupOwnerX::CharacterDataChanged(nsIDocument* aDocument, + nsIContent* aContent, + CharacterDataChangeInfo* aInfo) +{ +} + + +void nsMenuGroupOwnerX::ContentAppended(nsIDocument* aDocument, + nsIContent* aContainer, + nsIContent* aFirstNewContent, + int32_t /* unused */) +{ + for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) { + ContentInserted(aDocument, aContainer, cur, 0); + } +} + + +void nsMenuGroupOwnerX::NodeWillBeDestroyed(const nsINode * aNode) +{ +} + + +void nsMenuGroupOwnerX::AttributeWillChange(nsIDocument* aDocument, + dom::Element* aContent, + int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType, + const nsAttrValue* aNewValue) +{ +} + +void nsMenuGroupOwnerX::NativeAnonymousChildListChange(nsIDocument* aDocument, + nsIContent* aContent, + bool aIsRemove) +{ +} + +void nsMenuGroupOwnerX::AttributeChanged(nsIDocument* aDocument, + dom::Element* aElement, + int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType, + const nsAttrValue* aOldValue) +{ + nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); + nsChangeObserver* obs = LookupContentChangeObserver(aElement); + if (obs) + obs->ObserveAttributeChanged(aDocument, aElement, aAttribute); +} + + +void nsMenuGroupOwnerX::ContentRemoved(nsIDocument * aDocument, + nsIContent * aContainer, + nsIContent * aChild, + int32_t aIndexInContainer, + nsIContent * aPreviousSibling) +{ + if (!aContainer) { + return; + } + + nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); + nsChangeObserver* obs = LookupContentChangeObserver(aContainer); + if (obs) + obs->ObserveContentRemoved(aDocument, aChild, aIndexInContainer); + else if (aContainer != mContent) { + // 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); + if (obs) + obs->ObserveContentRemoved(aDocument, aChild, aIndexInContainer); + } + } +} + + +void nsMenuGroupOwnerX::ContentInserted(nsIDocument * aDocument, + nsIContent * aContainer, + nsIContent * aChild, + int32_t /* unused */) +{ + if (!aContainer) { + return; + } + + nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); + nsChangeObserver* obs = LookupContentChangeObserver(aContainer); + if (obs) + obs->ObserveContentInserted(aDocument, aContainer, aChild); + else if (aContainer != mContent) { + // 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); + if (obs) + obs->ObserveContentInserted(aDocument, aContainer, aChild); + } + } +} + + +void nsMenuGroupOwnerX::ParentChainChanged(nsIContent *aContent) +{ +} + + +// For change management, we don't use a |nsSupportsHashtable| because +// we know that the lifetime of all these items is bounded by the +// lifetime of the menubar. No need to add any more strong refs to the +// picture because the containment hierarchy already uses strong refs. +void nsMenuGroupOwnerX::RegisterForContentChanges(nsIContent *aContent, + nsChangeObserver *aMenuObject) +{ + if (!mContentToObserverTable.Contains(aContent)) { + aContent->AddMutationObserver(this); + } + mContentToObserverTable.Put(aContent, aMenuObject); +} + + +void nsMenuGroupOwnerX::UnregisterForContentChanges(nsIContent *aContent) +{ + if (mContentToObserverTable.Contains(aContent)) { + aContent->RemoveMutationObserver(this); + } + mContentToObserverTable.Remove(aContent); +} + + +nsChangeObserver* nsMenuGroupOwnerX::LookupContentChangeObserver(nsIContent* aContent) +{ + nsChangeObserver * result; + if (mContentToObserverTable.Get(aContent, &result)) + return result; + else + return nullptr; +} + + +// Given a menu item, creates a unique 4-character command ID and +// maps it to the item. Returns the id for use by the client. +uint32_t nsMenuGroupOwnerX::RegisterForCommand(nsMenuItemX* inMenuItem) +{ + // no real need to check for uniqueness. We always start afresh with each + // window at 1. Even if we did get close to the reserved Apple command id's, + // those don't start until at least ' ', which is integer 538976288. If + // we have that many menu items in one window, I think we have other + // problems. + + // make id unique + ++mCurrentCommandID; + + mCommandToMenuObjectTable.Put(mCurrentCommandID, inMenuItem); + + return mCurrentCommandID; +} + + +// Removes the mapping between the given 4-character command ID +// and its associated menu item. +void nsMenuGroupOwnerX::UnregisterCommand(uint32_t inCommandID) +{ + mCommandToMenuObjectTable.Remove(inCommandID); +} + + +nsMenuItemX* nsMenuGroupOwnerX::GetMenuItemForCommandID(uint32_t inCommandID) +{ + nsMenuItemX * result; + if (mCommandToMenuObjectTable.Get(inCommandID, &result)) + return result; + else + return nullptr; +} + +void nsMenuGroupOwnerX::AddMenuItemInfoToSet(MenuItemInfo* info) +{ + [mInfoSet addObject:info]; +} |