michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsMenuGroupOwnerX.h" michael@0: #include "nsMenuBarX.h" michael@0: #include "nsMenuX.h" michael@0: #include "nsMenuItemX.h" michael@0: #include "nsMenuUtilsX.h" michael@0: #include "nsCocoaUtils.h" michael@0: #include "nsCocoaWindow.h" michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsString.h" michael@0: #include "nsObjCExceptions.h" michael@0: #include "nsThreadUtils.h" michael@0: michael@0: #include "mozilla/dom/Element.h" michael@0: #include "nsIWidget.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIDOMElement.h" michael@0: michael@0: #include "nsINode.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsMenuGroupOwnerX, nsIMutationObserver) michael@0: michael@0: michael@0: nsMenuGroupOwnerX::nsMenuGroupOwnerX() michael@0: : mCurrentCommandID(eCommand_ID_Last), michael@0: mDocument(nullptr) michael@0: { michael@0: } michael@0: michael@0: michael@0: nsMenuGroupOwnerX::~nsMenuGroupOwnerX() michael@0: { michael@0: // make sure we unregister ourselves as a document observer michael@0: if (mDocument) michael@0: mDocument->RemoveMutationObserver(this); michael@0: } michael@0: michael@0: michael@0: nsresult nsMenuGroupOwnerX::Create(nsIContent* aContent) michael@0: { michael@0: if (!aContent) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: mContent = aContent; michael@0: michael@0: nsIDocument* doc = aContent->OwnerDoc(); michael@0: if (!doc) michael@0: return NS_ERROR_FAILURE; michael@0: doc->AddMutationObserver(this); michael@0: mDocument = doc; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: // michael@0: // nsIMutationObserver michael@0: // michael@0: michael@0: michael@0: void nsMenuGroupOwnerX::CharacterDataWillChange(nsIDocument* aDocument, michael@0: nsIContent* aContent, michael@0: CharacterDataChangeInfo* aInfo) michael@0: { michael@0: } michael@0: michael@0: michael@0: void nsMenuGroupOwnerX::CharacterDataChanged(nsIDocument* aDocument, michael@0: nsIContent* aContent, michael@0: CharacterDataChangeInfo* aInfo) michael@0: { michael@0: } michael@0: michael@0: michael@0: void nsMenuGroupOwnerX::ContentAppended(nsIDocument* aDocument, michael@0: nsIContent* aContainer, michael@0: nsIContent* aFirstNewContent, michael@0: int32_t /* unused */) michael@0: { michael@0: for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) { michael@0: ContentInserted(aDocument, aContainer, cur, 0); michael@0: } michael@0: } michael@0: michael@0: michael@0: void nsMenuGroupOwnerX::NodeWillBeDestroyed(const nsINode * aNode) michael@0: { michael@0: // our menu bar node is being destroyed michael@0: mDocument = nullptr; michael@0: } michael@0: michael@0: michael@0: void nsMenuGroupOwnerX::AttributeWillChange(nsIDocument* aDocument, michael@0: dom::Element* aContent, michael@0: int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: } michael@0: michael@0: michael@0: void nsMenuGroupOwnerX::AttributeChanged(nsIDocument* aDocument, michael@0: dom::Element* aElement, michael@0: int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: nsChangeObserver* obs = LookupContentChangeObserver(aElement); michael@0: if (obs) michael@0: obs->ObserveAttributeChanged(aDocument, aElement, aAttribute); michael@0: } michael@0: michael@0: michael@0: void nsMenuGroupOwnerX::ContentRemoved(nsIDocument * aDocument, michael@0: nsIContent * aContainer, michael@0: nsIContent * aChild, michael@0: int32_t aIndexInContainer, michael@0: nsIContent * aPreviousSibling) michael@0: { michael@0: if (!aContainer) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: nsChangeObserver* obs = LookupContentChangeObserver(aContainer); michael@0: if (obs) michael@0: obs->ObserveContentRemoved(aDocument, aChild, aIndexInContainer); michael@0: else if (aContainer != mContent) { michael@0: // We do a lookup on the parent container in case things were removed michael@0: // under a "menupopup" item. That is basically a wrapper for the contents michael@0: // of a "menu" node. michael@0: nsCOMPtr parent = aContainer->GetParent(); michael@0: if (parent) { michael@0: obs = LookupContentChangeObserver(parent); michael@0: if (obs) michael@0: obs->ObserveContentRemoved(aDocument, aChild, aIndexInContainer); michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: void nsMenuGroupOwnerX::ContentInserted(nsIDocument * aDocument, michael@0: nsIContent * aContainer, michael@0: nsIContent * aChild, michael@0: int32_t /* unused */) michael@0: { michael@0: if (!aContainer) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: nsChangeObserver* obs = LookupContentChangeObserver(aContainer); michael@0: if (obs) michael@0: obs->ObserveContentInserted(aDocument, aContainer, aChild); michael@0: else if (aContainer != mContent) { michael@0: // We do a lookup on the parent container in case things were removed michael@0: // under a "menupopup" item. That is basically a wrapper for the contents michael@0: // of a "menu" node. michael@0: nsCOMPtr parent = aContainer->GetParent(); michael@0: if (parent) { michael@0: obs = LookupContentChangeObserver(parent); michael@0: if (obs) michael@0: obs->ObserveContentInserted(aDocument, aContainer, aChild); michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: void nsMenuGroupOwnerX::ParentChainChanged(nsIContent *aContent) michael@0: { michael@0: } michael@0: michael@0: michael@0: // For change management, we don't use a |nsSupportsHashtable| because michael@0: // we know that the lifetime of all these items is bounded by the michael@0: // lifetime of the menubar. No need to add any more strong refs to the michael@0: // picture because the containment hierarchy already uses strong refs. michael@0: void nsMenuGroupOwnerX::RegisterForContentChanges(nsIContent *aContent, michael@0: nsChangeObserver *aMenuObject) michael@0: { michael@0: mContentToObserverTable.Put(aContent, aMenuObject); michael@0: } michael@0: michael@0: michael@0: void nsMenuGroupOwnerX::UnregisterForContentChanges(nsIContent *aContent) michael@0: { michael@0: mContentToObserverTable.Remove(aContent); michael@0: } michael@0: michael@0: michael@0: nsChangeObserver* nsMenuGroupOwnerX::LookupContentChangeObserver(nsIContent* aContent) michael@0: { michael@0: nsChangeObserver * result; michael@0: if (mContentToObserverTable.Get(aContent, &result)) michael@0: return result; michael@0: else michael@0: return nullptr; michael@0: } michael@0: michael@0: michael@0: // Given a menu item, creates a unique 4-character command ID and michael@0: // maps it to the item. Returns the id for use by the client. michael@0: uint32_t nsMenuGroupOwnerX::RegisterForCommand(nsMenuItemX* inMenuItem) michael@0: { michael@0: // no real need to check for uniqueness. We always start afresh with each michael@0: // window at 1. Even if we did get close to the reserved Apple command id's, michael@0: // those don't start until at least ' ', which is integer 538976288. If michael@0: // we have that many menu items in one window, I think we have other michael@0: // problems. michael@0: michael@0: // make id unique michael@0: ++mCurrentCommandID; michael@0: michael@0: mCommandToMenuObjectTable.Put(mCurrentCommandID, inMenuItem); michael@0: michael@0: return mCurrentCommandID; michael@0: } michael@0: michael@0: michael@0: // Removes the mapping between the given 4-character command ID michael@0: // and its associated menu item. michael@0: void nsMenuGroupOwnerX::UnregisterCommand(uint32_t inCommandID) michael@0: { michael@0: mCommandToMenuObjectTable.Remove(inCommandID); michael@0: } michael@0: michael@0: michael@0: nsMenuItemX* nsMenuGroupOwnerX::GetMenuItemForCommandID(uint32_t inCommandID) michael@0: { michael@0: nsMenuItemX * result; michael@0: if (mCommandToMenuObjectTable.Get(inCommandID, &result)) michael@0: return result; michael@0: else michael@0: return nullptr; michael@0: }