1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsMenuGroupOwnerX.h"
7 #include "nsMenuBarX.h"
9 #include "nsMenuItemX.h"
10 #include "nsMenuUtilsX.h"
11 #include "nsCocoaUtils.h"
12 #include "nsCocoaWindow.h"
16 #include "nsObjCExceptions.h"
17 #include "nsThreadUtils.h"
19 #include "mozilla/dom/Element.h"
20 #include "nsIWidget.h"
21 #include "mozilla/dom/Document.h"
25 using namespace mozilla;
27 NS_IMPL_ISUPPORTS(nsMenuGroupOwnerX, nsIObserver, nsIMutationObserver)
29 nsMenuGroupOwnerX::nsMenuGroupOwnerX(mozilla::dom::Element* aElement,
30 nsMenuBarX* aMenuBarIfMenuBar)
31 : mContent(aElement), mMenuBar(aMenuBarIfMenuBar) {
33 [[MOZMenuItemRepresentedObject alloc] initWithMenuGroupOwner:this];
36 nsMenuGroupOwnerX::~nsMenuGroupOwnerX() {
37 MOZ_ASSERT(mContentToObserverTable.Count() == 0,
38 "have outstanding mutation observers!\n");
39 [mRepresentedObject setMenuGroupOwner:nullptr];
40 [mRepresentedObject release];
44 // nsIMutationObserver
47 void nsMenuGroupOwnerX::CharacterDataWillChange(
48 nsIContent* aContent, const CharacterDataChangeInfo&) {}
50 void nsMenuGroupOwnerX::CharacterDataChanged(nsIContent* aContent,
51 const CharacterDataChangeInfo&) {}
53 void nsMenuGroupOwnerX::ContentAppended(nsIContent* aFirstNewContent) {
54 for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
59 void nsMenuGroupOwnerX::NodeWillBeDestroyed(nsINode* aNode) {}
61 void nsMenuGroupOwnerX::AttributeWillChange(dom::Element* aElement,
66 void nsMenuGroupOwnerX::AttributeChanged(dom::Element* aElement,
68 nsAtom* aAttribute, int32_t aModType,
69 const nsAttrValue* aOldValue) {
70 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
71 nsChangeObserver* obs = LookupContentChangeObserver(aElement);
73 obs->ObserveAttributeChanged(aElement->OwnerDoc(), aElement, aAttribute);
77 void nsMenuGroupOwnerX::ContentRemoved(nsIContent* aChild,
78 nsIContent* aPreviousSibling) {
79 nsIContent* container = aChild->GetParent();
84 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
85 nsChangeObserver* obs = LookupContentChangeObserver(container);
87 obs->ObserveContentRemoved(aChild->OwnerDoc(), container, aChild,
89 } else if (container != mContent) {
90 // We do a lookup on the parent container in case things were removed
91 // under a "menupopup" item. That is basically a wrapper for the contents
93 nsCOMPtr<nsIContent> parent = container->GetParent();
95 obs = LookupContentChangeObserver(parent);
97 obs->ObserveContentRemoved(aChild->OwnerDoc(), container, aChild,
104 void nsMenuGroupOwnerX::ContentInserted(nsIContent* aChild) {
105 nsIContent* container = aChild->GetParent();
110 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
111 nsChangeObserver* obs = LookupContentChangeObserver(container);
113 obs->ObserveContentInserted(aChild->OwnerDoc(), container, aChild);
114 } else if (container != mContent) {
115 // We do a lookup on the parent container in case things were removed
116 // under a "menupopup" item. That is basically a wrapper for the contents
118 nsCOMPtr<nsIContent> parent = container->GetParent();
120 obs = LookupContentChangeObserver(parent);
122 obs->ObserveContentInserted(aChild->OwnerDoc(), container, aChild);
128 void nsMenuGroupOwnerX::ParentChainChanged(nsIContent* aContent) {}
130 void nsMenuGroupOwnerX::ARIAAttributeDefaultWillChange(
131 mozilla::dom::Element* aElement, nsAtom* aAttribute, int32_t aModType) {}
133 void nsMenuGroupOwnerX::ARIAAttributeDefaultChanged(
134 mozilla::dom::Element* aElement, nsAtom* aAttribute, int32_t aModType) {}
136 // For change management, we don't use a |nsSupportsHashtable| because
137 // we know that the lifetime of all these items is bounded by the
138 // lifetime of the menubar. No need to add any more strong refs to the
139 // picture because the containment hierarchy already uses strong refs.
140 void nsMenuGroupOwnerX::RegisterForContentChanges(
141 nsIContent* aContent, nsChangeObserver* aMenuObject) {
142 if (!mContentToObserverTable.Contains(aContent)) {
143 aContent->AddMutationObserver(this);
145 mContentToObserverTable.InsertOrUpdate(aContent, aMenuObject);
148 void nsMenuGroupOwnerX::UnregisterForContentChanges(nsIContent* aContent) {
149 if (mContentToObserverTable.Contains(aContent)) {
150 aContent->RemoveMutationObserver(this);
152 mContentToObserverTable.Remove(aContent);
155 void nsMenuGroupOwnerX::RegisterForLocaleChanges() {
156 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
158 obs->AddObserver(this, "intl:app-locales-changed", false);
162 void nsMenuGroupOwnerX::UnregisterForLocaleChanges() {
163 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
165 obs->RemoveObserver(this, "intl:app-locales-changed");
170 nsMenuGroupOwnerX::Observe(nsISupports* aSubject, const char* aTopic,
171 const char16_t* aData) {
172 if (mMenuBar && !strcmp(aTopic, "intl:app-locales-changed")) {
173 // Rebuild the menu with the new locale strings.
174 mMenuBar->SetNeedsRebuild();
179 nsChangeObserver* nsMenuGroupOwnerX::LookupContentChangeObserver(
180 nsIContent* aContent) {
181 nsChangeObserver* result;
182 if (mContentToObserverTable.Get(aContent, &result)) {
188 // Given a menu item, creates a unique 4-character command ID and
189 // maps it to the item. Returns the id for use by the client.
190 uint32_t nsMenuGroupOwnerX::RegisterForCommand(nsMenuItemX* aMenuItem) {
191 // no real need to check for uniqueness. We always start afresh with each
192 // window at 1. Even if we did get close to the reserved Apple command id's,
193 // those don't start until at least ' ', which is integer 538976288. If
194 // we have that many menu items in one window, I think we have other
200 mCommandToMenuObjectTable.InsertOrUpdate(mCurrentCommandID, aMenuItem);
202 return mCurrentCommandID;
205 // Removes the mapping between the given 4-character command ID
206 // and its associated menu item.
207 void nsMenuGroupOwnerX::UnregisterCommand(uint32_t aCommandID) {
208 mCommandToMenuObjectTable.Remove(aCommandID);
211 nsMenuItemX* nsMenuGroupOwnerX::GetMenuItemForCommandID(uint32_t aCommandID) {
213 if (mCommandToMenuObjectTable.Get(aCommandID, &result)) {
219 @implementation MOZMenuItemRepresentedObject {
221 mMenuGroupOwner; // weak, cleared by nsMenuGroupOwnerX's destructor
224 - (id)initWithMenuGroupOwner:(nsMenuGroupOwnerX*)aMenuGroupOwner {
226 mMenuGroupOwner = aMenuGroupOwner;
230 - (void)setMenuGroupOwner:(nsMenuGroupOwnerX*)aMenuGroupOwner {
231 mMenuGroupOwner = aMenuGroupOwner;
234 - (nsMenuGroupOwnerX*)menuGroupOwner {
235 return mMenuGroupOwner;