|
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/. */ |
|
5 |
|
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" |
|
13 |
|
14 #include "nsCOMPtr.h" |
|
15 #include "nsString.h" |
|
16 #include "nsObjCExceptions.h" |
|
17 #include "nsThreadUtils.h" |
|
18 |
|
19 #include "mozilla/dom/Element.h" |
|
20 #include "nsIWidget.h" |
|
21 #include "nsIDocument.h" |
|
22 #include "nsIDOMDocument.h" |
|
23 #include "nsIDOMElement.h" |
|
24 |
|
25 #include "nsINode.h" |
|
26 |
|
27 using namespace mozilla; |
|
28 |
|
29 NS_IMPL_ISUPPORTS(nsMenuGroupOwnerX, nsIMutationObserver) |
|
30 |
|
31 |
|
32 nsMenuGroupOwnerX::nsMenuGroupOwnerX() |
|
33 : mCurrentCommandID(eCommand_ID_Last), |
|
34 mDocument(nullptr) |
|
35 { |
|
36 } |
|
37 |
|
38 |
|
39 nsMenuGroupOwnerX::~nsMenuGroupOwnerX() |
|
40 { |
|
41 // make sure we unregister ourselves as a document observer |
|
42 if (mDocument) |
|
43 mDocument->RemoveMutationObserver(this); |
|
44 } |
|
45 |
|
46 |
|
47 nsresult nsMenuGroupOwnerX::Create(nsIContent* aContent) |
|
48 { |
|
49 if (!aContent) |
|
50 return NS_ERROR_INVALID_ARG; |
|
51 |
|
52 mContent = aContent; |
|
53 |
|
54 nsIDocument* doc = aContent->OwnerDoc(); |
|
55 if (!doc) |
|
56 return NS_ERROR_FAILURE; |
|
57 doc->AddMutationObserver(this); |
|
58 mDocument = doc; |
|
59 |
|
60 return NS_OK; |
|
61 } |
|
62 |
|
63 |
|
64 // |
|
65 // nsIMutationObserver |
|
66 // |
|
67 |
|
68 |
|
69 void nsMenuGroupOwnerX::CharacterDataWillChange(nsIDocument* aDocument, |
|
70 nsIContent* aContent, |
|
71 CharacterDataChangeInfo* aInfo) |
|
72 { |
|
73 } |
|
74 |
|
75 |
|
76 void nsMenuGroupOwnerX::CharacterDataChanged(nsIDocument* aDocument, |
|
77 nsIContent* aContent, |
|
78 CharacterDataChangeInfo* aInfo) |
|
79 { |
|
80 } |
|
81 |
|
82 |
|
83 void nsMenuGroupOwnerX::ContentAppended(nsIDocument* aDocument, |
|
84 nsIContent* aContainer, |
|
85 nsIContent* aFirstNewContent, |
|
86 int32_t /* unused */) |
|
87 { |
|
88 for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) { |
|
89 ContentInserted(aDocument, aContainer, cur, 0); |
|
90 } |
|
91 } |
|
92 |
|
93 |
|
94 void nsMenuGroupOwnerX::NodeWillBeDestroyed(const nsINode * aNode) |
|
95 { |
|
96 // our menu bar node is being destroyed |
|
97 mDocument = nullptr; |
|
98 } |
|
99 |
|
100 |
|
101 void nsMenuGroupOwnerX::AttributeWillChange(nsIDocument* aDocument, |
|
102 dom::Element* aContent, |
|
103 int32_t aNameSpaceID, |
|
104 nsIAtom* aAttribute, |
|
105 int32_t aModType) |
|
106 { |
|
107 } |
|
108 |
|
109 |
|
110 void nsMenuGroupOwnerX::AttributeChanged(nsIDocument* aDocument, |
|
111 dom::Element* aElement, |
|
112 int32_t aNameSpaceID, |
|
113 nsIAtom* aAttribute, |
|
114 int32_t aModType) |
|
115 { |
|
116 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); |
|
117 nsChangeObserver* obs = LookupContentChangeObserver(aElement); |
|
118 if (obs) |
|
119 obs->ObserveAttributeChanged(aDocument, aElement, aAttribute); |
|
120 } |
|
121 |
|
122 |
|
123 void nsMenuGroupOwnerX::ContentRemoved(nsIDocument * aDocument, |
|
124 nsIContent * aContainer, |
|
125 nsIContent * aChild, |
|
126 int32_t aIndexInContainer, |
|
127 nsIContent * aPreviousSibling) |
|
128 { |
|
129 if (!aContainer) { |
|
130 return; |
|
131 } |
|
132 |
|
133 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); |
|
134 nsChangeObserver* obs = LookupContentChangeObserver(aContainer); |
|
135 if (obs) |
|
136 obs->ObserveContentRemoved(aDocument, aChild, aIndexInContainer); |
|
137 else if (aContainer != mContent) { |
|
138 // We do a lookup on the parent container in case things were removed |
|
139 // under a "menupopup" item. That is basically a wrapper for the contents |
|
140 // of a "menu" node. |
|
141 nsCOMPtr<nsIContent> parent = aContainer->GetParent(); |
|
142 if (parent) { |
|
143 obs = LookupContentChangeObserver(parent); |
|
144 if (obs) |
|
145 obs->ObserveContentRemoved(aDocument, aChild, aIndexInContainer); |
|
146 } |
|
147 } |
|
148 } |
|
149 |
|
150 |
|
151 void nsMenuGroupOwnerX::ContentInserted(nsIDocument * aDocument, |
|
152 nsIContent * aContainer, |
|
153 nsIContent * aChild, |
|
154 int32_t /* unused */) |
|
155 { |
|
156 if (!aContainer) { |
|
157 return; |
|
158 } |
|
159 |
|
160 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); |
|
161 nsChangeObserver* obs = LookupContentChangeObserver(aContainer); |
|
162 if (obs) |
|
163 obs->ObserveContentInserted(aDocument, aContainer, aChild); |
|
164 else if (aContainer != mContent) { |
|
165 // We do a lookup on the parent container in case things were removed |
|
166 // under a "menupopup" item. That is basically a wrapper for the contents |
|
167 // of a "menu" node. |
|
168 nsCOMPtr<nsIContent> parent = aContainer->GetParent(); |
|
169 if (parent) { |
|
170 obs = LookupContentChangeObserver(parent); |
|
171 if (obs) |
|
172 obs->ObserveContentInserted(aDocument, aContainer, aChild); |
|
173 } |
|
174 } |
|
175 } |
|
176 |
|
177 |
|
178 void nsMenuGroupOwnerX::ParentChainChanged(nsIContent *aContent) |
|
179 { |
|
180 } |
|
181 |
|
182 |
|
183 // For change management, we don't use a |nsSupportsHashtable| because |
|
184 // we know that the lifetime of all these items is bounded by the |
|
185 // lifetime of the menubar. No need to add any more strong refs to the |
|
186 // picture because the containment hierarchy already uses strong refs. |
|
187 void nsMenuGroupOwnerX::RegisterForContentChanges(nsIContent *aContent, |
|
188 nsChangeObserver *aMenuObject) |
|
189 { |
|
190 mContentToObserverTable.Put(aContent, aMenuObject); |
|
191 } |
|
192 |
|
193 |
|
194 void nsMenuGroupOwnerX::UnregisterForContentChanges(nsIContent *aContent) |
|
195 { |
|
196 mContentToObserverTable.Remove(aContent); |
|
197 } |
|
198 |
|
199 |
|
200 nsChangeObserver* nsMenuGroupOwnerX::LookupContentChangeObserver(nsIContent* aContent) |
|
201 { |
|
202 nsChangeObserver * result; |
|
203 if (mContentToObserverTable.Get(aContent, &result)) |
|
204 return result; |
|
205 else |
|
206 return nullptr; |
|
207 } |
|
208 |
|
209 |
|
210 // Given a menu item, creates a unique 4-character command ID and |
|
211 // maps it to the item. Returns the id for use by the client. |
|
212 uint32_t nsMenuGroupOwnerX::RegisterForCommand(nsMenuItemX* inMenuItem) |
|
213 { |
|
214 // no real need to check for uniqueness. We always start afresh with each |
|
215 // window at 1. Even if we did get close to the reserved Apple command id's, |
|
216 // those don't start until at least ' ', which is integer 538976288. If |
|
217 // we have that many menu items in one window, I think we have other |
|
218 // problems. |
|
219 |
|
220 // make id unique |
|
221 ++mCurrentCommandID; |
|
222 |
|
223 mCommandToMenuObjectTable.Put(mCurrentCommandID, inMenuItem); |
|
224 |
|
225 return mCurrentCommandID; |
|
226 } |
|
227 |
|
228 |
|
229 // Removes the mapping between the given 4-character command ID |
|
230 // and its associated menu item. |
|
231 void nsMenuGroupOwnerX::UnregisterCommand(uint32_t inCommandID) |
|
232 { |
|
233 mCommandToMenuObjectTable.Remove(inCommandID); |
|
234 } |
|
235 |
|
236 |
|
237 nsMenuItemX* nsMenuGroupOwnerX::GetMenuItemForCommandID(uint32_t inCommandID) |
|
238 { |
|
239 nsMenuItemX * result; |
|
240 if (mCommandToMenuObjectTable.Get(inCommandID, &result)) |
|
241 return result; |
|
242 else |
|
243 return nullptr; |
|
244 } |