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