Backed out changeset b4a0f8afc02e (bug 1857946) for causing bc failures at browser...
[gecko.git] / widget / cocoa / nsMenuGroupOwnerX.mm
blob45d074e12f92e12d8d54d9ca0012ac640fcd52f9
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"
8 #include "nsMenuX.h"
9 #include "nsMenuItemX.h"
10 #include "nsMenuUtilsX.h"
11 #include "nsCocoaUtils.h"
12 #include "nsCocoaWindow.h"
14 #include "nsCOMPtr.h"
15 #include "nsString.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"
23 #include "nsINode.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) {
32   mRepresentedObject =
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()) {
55     ContentInserted(cur);
56   }
59 void nsMenuGroupOwnerX::NodeWillBeDestroyed(nsINode* aNode) {}
61 void nsMenuGroupOwnerX::AttributeWillChange(dom::Element* aElement,
62                                             int32_t aNameSpaceID,
63                                             nsAtom* aAttribute,
64                                             int32_t aModType) {}
66 void nsMenuGroupOwnerX::AttributeChanged(dom::Element* aElement,
67                                          int32_t aNameSpaceID,
68                                          nsAtom* aAttribute, int32_t aModType,
69                                          const nsAttrValue* aOldValue) {
70   nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
71   nsChangeObserver* obs = LookupContentChangeObserver(aElement);
72   if (obs) {
73     obs->ObserveAttributeChanged(aElement->OwnerDoc(), aElement, aAttribute);
74   }
77 void nsMenuGroupOwnerX::ContentRemoved(nsIContent* aChild,
78                                        nsIContent* aPreviousSibling) {
79   nsIContent* container = aChild->GetParent();
80   if (!container) {
81     return;
82   }
84   nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
85   nsChangeObserver* obs = LookupContentChangeObserver(container);
86   if (obs) {
87     obs->ObserveContentRemoved(aChild->OwnerDoc(), container, aChild,
88                                aPreviousSibling);
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
92     // of a "menu" node.
93     nsCOMPtr<nsIContent> parent = container->GetParent();
94     if (parent) {
95       obs = LookupContentChangeObserver(parent);
96       if (obs) {
97         obs->ObserveContentRemoved(aChild->OwnerDoc(), container, aChild,
98                                    aPreviousSibling);
99       }
100     }
101   }
104 void nsMenuGroupOwnerX::ContentInserted(nsIContent* aChild) {
105   nsIContent* container = aChild->GetParent();
106   if (!container) {
107     return;
108   }
110   nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
111   nsChangeObserver* obs = LookupContentChangeObserver(container);
112   if (obs) {
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
117     // of a "menu" node.
118     nsCOMPtr<nsIContent> parent = container->GetParent();
119     if (parent) {
120       obs = LookupContentChangeObserver(parent);
121       if (obs) {
122         obs->ObserveContentInserted(aChild->OwnerDoc(), container, aChild);
123       }
124     }
125   }
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);
144   }
145   mContentToObserverTable.InsertOrUpdate(aContent, aMenuObject);
148 void nsMenuGroupOwnerX::UnregisterForContentChanges(nsIContent* aContent) {
149   if (mContentToObserverTable.Contains(aContent)) {
150     aContent->RemoveMutationObserver(this);
151   }
152   mContentToObserverTable.Remove(aContent);
155 void nsMenuGroupOwnerX::RegisterForLocaleChanges() {
156   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
157   if (obs) {
158     obs->AddObserver(this, "intl:app-locales-changed", false);
159   }
162 void nsMenuGroupOwnerX::UnregisterForLocaleChanges() {
163   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
164   if (obs) {
165     obs->RemoveObserver(this, "intl:app-locales-changed");
166   }
169 NS_IMETHODIMP
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();
175   }
176   return NS_OK;
179 nsChangeObserver* nsMenuGroupOwnerX::LookupContentChangeObserver(
180     nsIContent* aContent) {
181   nsChangeObserver* result;
182   if (mContentToObserverTable.Get(aContent, &result)) {
183     return result;
184   }
185   return nullptr;
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
195   // problems.
197   // make id unique
198   ++mCurrentCommandID;
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) {
212   nsMenuItemX* result;
213   if (mCommandToMenuObjectTable.Get(aCommandID, &result)) {
214     return result;
215   }
216   return nullptr;
219 @implementation MOZMenuItemRepresentedObject {
220   nsMenuGroupOwnerX*
221       mMenuGroupOwner;  // weak, cleared by nsMenuGroupOwnerX's destructor
224 - (id)initWithMenuGroupOwner:(nsMenuGroupOwnerX*)aMenuGroupOwner {
225   self = [super init];
226   mMenuGroupOwner = aMenuGroupOwner;
227   return self;
230 - (void)setMenuGroupOwner:(nsMenuGroupOwnerX*)aMenuGroupOwner {
231   mMenuGroupOwner = aMenuGroupOwner;
234 - (nsMenuGroupOwnerX*)menuGroupOwner {
235   return mMenuGroupOwner;
238 @end