Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */
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 #import <Cocoa/Cocoa.h>
8 #include "nsStandaloneNativeMenu.h"
9 #include "nsMenuUtilsX.h"
10 #include "nsIDOMElement.h"
11 #include "nsIMutationObserver.h"
12 #include "nsGkAtoms.h"
13 #include "nsObjCExceptions.h"
16 NS_IMPL_ISUPPORTS(nsStandaloneNativeMenu, nsIMutationObserver, nsIStandaloneNativeMenu)
18 nsStandaloneNativeMenu::nsStandaloneNativeMenu()
19 : mMenu(nullptr)
20 {
21 }
23 nsStandaloneNativeMenu::~nsStandaloneNativeMenu()
24 {
25 if (mMenu) delete mMenu;
26 }
28 NS_IMETHODIMP
29 nsStandaloneNativeMenu::Init(nsIDOMElement * aDOMElement)
30 {
31 NS_ASSERTION(mMenu == nullptr, "nsNativeMenu::Init - mMenu not null!");
33 nsresult rv;
35 nsCOMPtr<nsIContent> content = do_QueryInterface(aDOMElement, &rv);
36 NS_ENSURE_SUCCESS(rv, rv);
38 nsIAtom * tag = content->Tag();
39 if (!content->IsXUL() ||
40 (tag != nsGkAtoms::menu && tag != nsGkAtoms::menupopup))
41 return NS_ERROR_FAILURE;
43 rv = nsMenuGroupOwnerX::Create(content);
44 if (NS_FAILED(rv))
45 return rv;
47 mMenu = new nsMenuX();
48 rv = mMenu->Create(this, this, content);
49 if (NS_FAILED(rv)) {
50 delete mMenu;
51 mMenu = nullptr;
52 return rv;
53 }
55 return NS_OK;
56 }
58 static void
59 UpdateMenu(nsMenuX * aMenu)
60 {
61 aMenu->MenuOpened();
62 aMenu->MenuClosed();
64 uint32_t itemCount = aMenu->GetItemCount();
65 for (uint32_t i = 0; i < itemCount; i++) {
66 nsMenuObjectX * menuObject = aMenu->GetItemAt(i);
67 if (menuObject->MenuObjectType() == eSubmenuObjectType) {
68 UpdateMenu(static_cast<nsMenuX*>(menuObject));
69 }
70 }
71 }
73 NS_IMETHODIMP
74 nsStandaloneNativeMenu::MenuWillOpen(bool * aResult)
75 {
76 NS_ASSERTION(mMenu != nullptr, "nsStandaloneNativeMenu::OnOpen - mMenu is null!");
78 // Force an update on the mMenu by faking an open/close on all of
79 // its submenus.
80 UpdateMenu(mMenu);
82 *aResult = true;
83 return NS_OK;
84 }
86 NS_IMETHODIMP
87 nsStandaloneNativeMenu::GetNativeMenu(void ** aVoidPointer)
88 {
89 if (mMenu) {
90 *aVoidPointer = mMenu->NativeData();
91 [[(NSObject *)(*aVoidPointer) retain] autorelease];
92 return NS_OK;
93 } else {
94 *aVoidPointer = nullptr;
95 return NS_ERROR_NOT_INITIALIZED;
96 }
97 }
99 static NSMenuItem *
100 NativeMenuItemWithLocation(NSMenu * currentSubmenu, NSString * locationString)
101 {
102 NSArray * indexes = [locationString componentsSeparatedByString:@"|"];
103 NSUInteger indexCount = [indexes count];
104 if (indexCount == 0)
105 return nil;
107 for (NSUInteger i = 0; i < indexCount; i++) {
108 NSInteger targetIndex = [[indexes objectAtIndex:i] integerValue];
109 NSInteger itemCount = [currentSubmenu numberOfItems];
110 if (targetIndex < itemCount) {
111 NSMenuItem* menuItem = [currentSubmenu itemAtIndex:targetIndex];
113 // If this is the last index, just return the menu item.
114 if (i == (indexCount - 1))
115 return menuItem;
117 // If this is not the last index, find the submenu and keep going.
118 if ([menuItem hasSubmenu])
119 currentSubmenu = [menuItem submenu];
120 else
121 return nil;
122 }
123 }
125 return nil;
126 }
128 NS_IMETHODIMP
129 nsStandaloneNativeMenu::ActivateNativeMenuItemAt(const nsAString& indexString)
130 {
131 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
133 if (!mMenu)
134 return NS_ERROR_NOT_INITIALIZED;
136 NSString * locationString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading())
137 length:indexString.Length()];
138 NSMenu * menu = static_cast<NSMenu *> (mMenu->NativeData());
139 NSMenuItem * item = NativeMenuItemWithLocation(menu, locationString);
141 // We can't perform an action on an item with a submenu, that will raise
142 // an obj-c exception.
143 if (item && ![item hasSubmenu]) {
144 NSMenu * parent = [item menu];
145 if (parent) {
146 // NSLog(@"Performing action for native menu item titled: %@\n",
147 // [[currentSubmenu itemAtIndex:targetIndex] title]);
148 [parent performActionForItemAtIndex:[parent indexOfItem:item]];
149 return NS_OK;
150 }
151 }
153 return NS_ERROR_FAILURE;
155 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
156 }
158 NS_IMETHODIMP
159 nsStandaloneNativeMenu::ForceUpdateNativeMenuAt(const nsAString& indexString)
160 {
161 if (!mMenu)
162 return NS_ERROR_NOT_INITIALIZED;
164 NSString* locationString = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(indexString.BeginReading())
165 length:indexString.Length()];
166 NSArray* indexes = [locationString componentsSeparatedByString:@"|"];
167 unsigned int indexCount = [indexes count];
168 if (indexCount == 0)
169 return NS_OK;
171 nsMenuX* currentMenu = mMenu;
173 // now find the correct submenu
174 for (unsigned int i = 1; currentMenu && i < indexCount; i++) {
175 int targetIndex = [[indexes objectAtIndex:i] intValue];
176 int visible = 0;
177 uint32_t length = currentMenu->GetItemCount();
178 for (unsigned int j = 0; j < length; j++) {
179 nsMenuObjectX* targetMenu = currentMenu->GetItemAt(j);
180 if (!targetMenu)
181 return NS_OK;
182 if (!nsMenuUtilsX::NodeIsHiddenOrCollapsed(targetMenu->Content())) {
183 visible++;
184 if (targetMenu->MenuObjectType() == eSubmenuObjectType && visible == (targetIndex + 1)) {
185 currentMenu = static_cast<nsMenuX*>(targetMenu);
186 // fake open/close to cause lazy update to happen
187 currentMenu->MenuOpened();
188 currentMenu->MenuClosed();
189 break;
190 }
191 }
192 }
193 }
195 return NS_OK;
196 }