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 "mozilla/dom/Document.h" 22 23#include "nsINode.h" 24 25using namespace mozilla; 26 27NS_IMPL_ISUPPORTS(nsMenuGroupOwnerX, nsIMutationObserver) 28 29nsMenuGroupOwnerX::nsMenuGroupOwnerX(mozilla::dom::Element* aElement, nsMenuBarX* aMenuBarIfMenuBar) 30 : mContent(aElement), mMenuBar(aMenuBarIfMenuBar) { 31 mRepresentedObject = [[MOZMenuItemRepresentedObject alloc] initWithMenuGroupOwner:this]; 32} 33 34nsMenuGroupOwnerX::~nsMenuGroupOwnerX() { 35 MOZ_ASSERT(mContentToObserverTable.Count() == 0, "have outstanding mutation observers!\n"); 36 [mRepresentedObject setMenuGroupOwner:nullptr]; 37 [mRepresentedObject release]; 38} 39 40// 41// nsIMutationObserver 42// 43 44void nsMenuGroupOwnerX::CharacterDataWillChange(nsIContent* aContent, 45 const CharacterDataChangeInfo&) {} 46 47void nsMenuGroupOwnerX::CharacterDataChanged(nsIContent* aContent, const CharacterDataChangeInfo&) { 48} 49 50void nsMenuGroupOwnerX::ContentAppended(nsIContent* aFirstNewContent) { 51 for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) { 52 ContentInserted(cur); 53 } 54} 55 56void nsMenuGroupOwnerX::NodeWillBeDestroyed(const nsINode* aNode) {} 57 58void nsMenuGroupOwnerX::AttributeWillChange(dom::Element* aElement, int32_t aNameSpaceID, 59 nsAtom* aAttribute, int32_t aModType) {} 60 61void nsMenuGroupOwnerX::NativeAnonymousChildListChange(nsIContent* aContent, bool aIsRemove) {} 62 63void nsMenuGroupOwnerX::AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID, 64 nsAtom* aAttribute, int32_t aModType, 65 const nsAttrValue* aOldValue) { 66 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); 67 nsChangeObserver* obs = LookupContentChangeObserver(aElement); 68 if (obs) { 69 obs->ObserveAttributeChanged(aElement->OwnerDoc(), aElement, aAttribute); 70 } 71} 72 73void nsMenuGroupOwnerX::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling) { 74 nsIContent* container = aChild->GetParent(); 75 if (!container) { 76 return; 77 } 78 79 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); 80 nsChangeObserver* obs = LookupContentChangeObserver(container); 81 if (obs) { 82 obs->ObserveContentRemoved(aChild->OwnerDoc(), container, aChild, aPreviousSibling); 83 } else if (container != mContent) { 84 // We do a lookup on the parent container in case things were removed 85 // under a "menupopup" item. That is basically a wrapper for the contents 86 // of a "menu" node. 87 nsCOMPtr<nsIContent> parent = container->GetParent(); 88 if (parent) { 89 obs = LookupContentChangeObserver(parent); 90 if (obs) { 91 obs->ObserveContentRemoved(aChild->OwnerDoc(), container, aChild, aPreviousSibling); 92 } 93 } 94 } 95} 96 97void nsMenuGroupOwnerX::ContentInserted(nsIContent* aChild) { 98 nsIContent* container = aChild->GetParent(); 99 if (!container) { 100 return; 101 } 102 103 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); 104 nsChangeObserver* obs = LookupContentChangeObserver(container); 105 if (obs) { 106 obs->ObserveContentInserted(aChild->OwnerDoc(), container, aChild); 107 } else if (container != mContent) { 108 // We do a lookup on the parent container in case things were removed 109 // under a "menupopup" item. That is basically a wrapper for the contents 110 // of a "menu" node. 111 nsCOMPtr<nsIContent> parent = container->GetParent(); 112 if (parent) { 113 obs = LookupContentChangeObserver(parent); 114 if (obs) { 115 obs->ObserveContentInserted(aChild->OwnerDoc(), container, aChild); 116 } 117 } 118 } 119} 120 121void nsMenuGroupOwnerX::ParentChainChanged(nsIContent* aContent) {} 122 123// For change management, we don't use a |nsSupportsHashtable| because 124// we know that the lifetime of all these items is bounded by the 125// lifetime of the menubar. No need to add any more strong refs to the 126// picture because the containment hierarchy already uses strong refs. 127void nsMenuGroupOwnerX::RegisterForContentChanges(nsIContent* aContent, 128 nsChangeObserver* aMenuObject) { 129 if (!mContentToObserverTable.Contains(aContent)) { 130 aContent->AddMutationObserver(this); 131 } 132 mContentToObserverTable.InsertOrUpdate(aContent, aMenuObject); 133} 134 135void nsMenuGroupOwnerX::UnregisterForContentChanges(nsIContent* aContent) { 136 if (mContentToObserverTable.Contains(aContent)) { 137 aContent->RemoveMutationObserver(this); 138 } 139 mContentToObserverTable.Remove(aContent); 140} 141 142nsChangeObserver* nsMenuGroupOwnerX::LookupContentChangeObserver(nsIContent* aContent) { 143 nsChangeObserver* result; 144 if (mContentToObserverTable.Get(aContent, &result)) { 145 return result; 146 } 147 return nullptr; 148} 149 150// Given a menu item, creates a unique 4-character command ID and 151// maps it to the item. Returns the id for use by the client. 152uint32_t nsMenuGroupOwnerX::RegisterForCommand(nsMenuItemX* aMenuItem) { 153 // no real need to check for uniqueness. We always start afresh with each 154 // window at 1. Even if we did get close to the reserved Apple command id's, 155 // those don't start until at least ' ', which is integer 538976288. If 156 // we have that many menu items in one window, I think we have other 157 // problems. 158 159 // make id unique 160 ++mCurrentCommandID; 161 162 mCommandToMenuObjectTable.InsertOrUpdate(mCurrentCommandID, aMenuItem); 163 164 return mCurrentCommandID; 165} 166 167// Removes the mapping between the given 4-character command ID 168// and its associated menu item. 169void nsMenuGroupOwnerX::UnregisterCommand(uint32_t aCommandID) { 170 mCommandToMenuObjectTable.Remove(aCommandID); 171} 172 173nsMenuItemX* nsMenuGroupOwnerX::GetMenuItemForCommandID(uint32_t aCommandID) { 174 nsMenuItemX* result; 175 if (mCommandToMenuObjectTable.Get(aCommandID, &result)) { 176 return result; 177 } 178 return nullptr; 179} 180 181@implementation MOZMenuItemRepresentedObject { 182 nsMenuGroupOwnerX* mMenuGroupOwner; // weak, cleared by nsMenuGroupOwnerX's destructor 183} 184 185- (id)initWithMenuGroupOwner:(nsMenuGroupOwnerX*)aMenuGroupOwner { 186 self = [super init]; 187 mMenuGroupOwner = aMenuGroupOwner; 188 return self; 189} 190 191- (void)setMenuGroupOwner:(nsMenuGroupOwnerX*)aMenuGroupOwner { 192 mMenuGroupOwner = aMenuGroupOwner; 193} 194 195- (nsMenuGroupOwnerX*)menuGroupOwner { 196 return mMenuGroupOwner; 197} 198 199@end 200