Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(269)

Side by Side Diff: chrome/browser/extensions/extension_menu_manager.cc

Issue 1042003: Initial version of an experimental Extensions Context Menu API.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 10 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/extensions/extension_menu_manager.h"
6
7 #include "base/logging.h"
8 #include "base/string_util.h"
9 #include "base/utf_string_conversions.h"
10 #include "base/values.h"
11 #include "base/json/json_writer.h"
12 #include "chrome/app/chrome_dll_resource.h"
13 #include "chrome/browser/extensions/extension_message_service.h"
14 #include "chrome/browser/extensions/extension_tabs_module.h"
15 #include "chrome/browser/profile.h"
16 #include "chrome/common/extensions/extension.h"
17 #include "webkit/glue/context_menu.h"
18
19 ExtensionMenuItem::ExtensionMenuItem(const std::string& extension_id,
20 std::string title,
21 bool checked, Type type,
22 const ContextList& contexts,
23 const ContextList& enabled_contexts)
24 : extension_id_(extension_id),
25 title_(title),
26 id_(0),
27 type_(type),
28 checked_(checked),
29 contexts_(contexts),
30 enabled_contexts_(enabled_contexts),
31 parent_id_(0) {}
32
33 ExtensionMenuItem::~ExtensionMenuItem() {}
34
35 ExtensionMenuItem* ExtensionMenuItem::ChildAt(int index) const {
36 if (index < 0 || static_cast<size_t>(index) >= children_.size())
37 return NULL;
38 return children_[index].get();
39 }
40
41 bool ExtensionMenuItem::RemoveChild(int child_id) {
42 for (List::iterator i = children_.begin(); i != children_.end(); ++i) {
43 if ((*i)->id() == child_id) {
44 children_.erase(i);
45 return true;
46 } else if ((*i)->RemoveChild(child_id)) {
47 return true;
48 }
49 }
50 return false;
51 }
52
53 string16 ExtensionMenuItem::TitleWithReplacement(
54 const string16& selection) const {
55 string16 result = UTF8ToUTF16(title_);
56 // TODO(asargent) - Change this to properly handle %% escaping so you can
57 // put "%s" in titles that won't get substituted.
58 ReplaceSubstringsAfterOffset(&result, 0, ASCIIToUTF16("%s"), selection);
59 return result;
60 }
61
62 bool ExtensionMenuItem::SetChecked(bool checked) {
63 if (type_ != CHECKBOX && type_ != RADIO)
64 return false;
65 checked_ = checked;
66 return true;
67 }
68
69 void ExtensionMenuItem::AddChild(ExtensionMenuItem* item) {
70 item->parent_id_ = id_;
71 children_.push_back(linked_ptr<ExtensionMenuItem>(item));
72 }
73
74 ExtensionMenuManager::ExtensionMenuManager() : next_item_id_(1) {
75 registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
76 NotificationService::AllSources());
77 }
78
79 ExtensionMenuManager::~ExtensionMenuManager() {}
80
81 std::set<std::string> ExtensionMenuManager::ExtensionIds() {
82 std::set<std::string> id_set;
83 for (MenuItemMap::const_iterator i = context_items_.begin();
84 i != context_items_.end(); ++i) {
85 id_set.insert(i->first);
86 }
87 return id_set;
88 }
89
90 std::vector<const ExtensionMenuItem*> ExtensionMenuManager::MenuItems(
91 const std::string& extension_id) {
92 std::vector<const ExtensionMenuItem*> result;
93
94 MenuItemMap::iterator i = context_items_.find(extension_id);
95 if (i != context_items_.end()) {
96 ExtensionMenuItem::List& list = i->second;
97 ExtensionMenuItem::List::iterator j;
98 for (j = list.begin(); j != list.end(); ++j) {
99 result.push_back(j->get());
100 }
101 }
102
103 return result;
104 }
105
106 int ExtensionMenuManager::AddContextItem(ExtensionMenuItem* item) {
107 const std::string& extension_id = item->extension_id();
108 // The item must have a non-empty extension id.
109 if (extension_id.empty())
110 return 0;
111
112 DCHECK_EQ(0, item->id());
113 item->set_id(next_item_id_++);
114
115 context_items_[extension_id].push_back(linked_ptr<ExtensionMenuItem>(item));
116 items_by_id_[item->id()] = item;
117
118 if (item->type() == ExtensionMenuItem::RADIO && item->checked())
119 RadioItemSelected(item);
120
121 return item->id();
122 }
123
124 int ExtensionMenuManager::AddChildItem(int parent_id,
125 ExtensionMenuItem* child) {
126 ExtensionMenuItem* parent = GetItemById(parent_id);
127 if (!parent || parent->type() != ExtensionMenuItem::NORMAL ||
128 parent->extension_id() != child->extension_id())
129 return 0;
130 child->set_id(next_item_id_++);
131 parent->AddChild(child);
132 items_by_id_[child->id()] = child;
133 return child->id();
134 }
135
136 bool ExtensionMenuManager::RemoveContextMenuItem(int id) {
137 if (items_by_id_.find(id) == items_by_id_.end())
138 return false;
139
140 MenuItemMap::iterator i;
141 for (i = context_items_.begin(); i != context_items_.end(); ++i) {
142 ExtensionMenuItem::List& list = i->second;
143 ExtensionMenuItem::List::iterator j;
144 for (j = list.begin(); j < list.end(); ++j) {
145 // See if the current item is a match, or if one of its children was.
146 if ((*j)->id() == id) {
147 list.erase(j);
148 items_by_id_.erase(id);
149 return true;
150 } else if ((*j)->RemoveChild(id)) {
151 items_by_id_.erase(id);
152 return true;
153 }
154 }
155 }
156 NOTREACHED(); // The check at the very top should prevent getting here.
157 return false;
158 }
159
160 ExtensionMenuItem* ExtensionMenuManager::GetItemById(int id) {
161 std::map<int, ExtensionMenuItem*>::const_iterator i = items_by_id_.find(id);
162 if (i != items_by_id_.end())
163 return i->second;
164 else
165 return NULL;
166 }
167
168 void ExtensionMenuManager::RadioItemSelected(ExtensionMenuItem* item) {
169 // If this is a child item, we need to get a handle to the list from its
170 // parent. Otherwise get a handle to the top-level list.
171 ExtensionMenuItem::List* list = NULL;
172 if (item->parent_id()) {
173 ExtensionMenuItem* parent = GetItemById(item->parent_id());
174 if (!parent) {
175 NOTREACHED();
176 return;
177 }
178 list = parent->children();
179 } else {
180 if (context_items_.find(item->extension_id()) == context_items_.end()) {
181 NOTREACHED();
182 return;
183 }
184 list = &context_items_[item->extension_id()];
185 }
186
187 // Find where |item| is in the list.
188 ExtensionMenuItem::List::iterator item_location;
189 for (item_location = list->begin(); item_location != list->end();
190 ++item_location) {
191 if (item_location->get() == item)
192 break;
193 }
194 if (item_location == list->end()) {
195 NOTREACHED(); // We should have found the item.
196 return;
197 }
198
199 // Iterate backwards from |item| and uncheck any adjacent radio items.
200 ExtensionMenuItem::List::iterator i;
201 if (item_location != list->begin()) {
202 i = item_location;
203 do {
204 --i;
205 if ((*i)->type() != ExtensionMenuItem::RADIO)
206 break;
207 (*i)->SetChecked(false);
208 } while (i != list->begin());
209 }
210
211 // Now iterate forwards from |item| and uncheck any adjacent radio items.
212 for (i = item_location + 1; i != list->end(); ++i) {
213 if ((*i)->type() != ExtensionMenuItem::RADIO)
214 break;
215 (*i)->SetChecked(false);
216 }
217 }
218
219 static void AddURLProperty(DictionaryValue* dictionary,
220 const std::wstring& key, const GURL& url) {
221 if (!url.is_empty())
222 dictionary->SetString(key, url.possibly_invalid_spec());
223 }
224
225 void ExtensionMenuManager::GetItemAndIndex(int id, ExtensionMenuItem** item,
226 size_t* index) {
227 for (MenuItemMap::const_iterator i = context_items_.begin();
228 i != context_items_.end(); ++i) {
229 const ExtensionMenuItem::List& list = i->second;
230 for (size_t tmp_index = 0; tmp_index < list.size(); tmp_index++) {
231 if (list[tmp_index]->id() == id) {
232 if (item)
233 *item = list[tmp_index].get();
234 if (index)
235 *index = tmp_index;
236 return;
237 }
238 }
239 }
240 if (item)
241 *item = NULL;
242 if (index)
243 *index = 0;
244 }
245
246
247 void ExtensionMenuManager::ExecuteCommand(Profile* profile,
248 TabContents* tab_contents,
249 const ContextMenuParams& params,
250 int menuItemId) {
251 ExtensionMessageService* service = profile->GetExtensionMessageService();
252 if (!service)
253 return;
254
255 ExtensionMenuItem* item = GetItemById(menuItemId);
256 if (!item)
257 return;
258
259 if (item->type() == ExtensionMenuItem::RADIO)
260 RadioItemSelected(item);
261
262 ListValue args;
263
264 DictionaryValue* properties = new DictionaryValue();
265 properties->SetInteger(L"menuItemId", item->id());
266 if (item->parent_id())
267 properties->SetInteger(L"parentMenuItemId", item->parent_id());
268
269 switch (params.media_type) {
270 case WebKit::WebContextMenuData::MediaTypeImage:
271 properties->SetString(L"mediaType", "IMAGE");
272 break;
273 case WebKit::WebContextMenuData::MediaTypeVideo:
274 properties->SetString(L"mediaType", "VIDEO");
275 break;
276 case WebKit::WebContextMenuData::MediaTypeAudio:
277 properties->SetString(L"mediaType", "AUDIO");
278 break;
279 default: {} // Do nothing.
280 }
281
282 AddURLProperty(properties, L"linkUrl", params.unfiltered_link_url);
283 AddURLProperty(properties, L"srcUrl", params.src_url);
284 AddURLProperty(properties, L"mainFrameUrl", params.page_url);
285 AddURLProperty(properties, L"frameUrl", params.frame_url);
286
287 if (params.selection_text.length() > 0)
288 properties->SetString(L"selectionText", params.selection_text);
289
290 properties->SetBoolean(L"editable", params.is_editable);
291
292 args.Append(properties);
293
294 // Add the tab info to the argument list.
295 args.Append(ExtensionTabUtil::CreateTabValue(tab_contents));
296
297 if (item->type() == ExtensionMenuItem::CHECKBOX ||
298 item->type() == ExtensionMenuItem::RADIO) {
299 bool was_checked = item->checked();
300 properties->SetBoolean(L"wasChecked", was_checked);
301
302 // RADIO items always get set to true when you click on them, but CHECKBOX
303 // items get their state toggled.
304 bool checked =
305 (item->type() == ExtensionMenuItem::RADIO) ? true : !was_checked;
306
307 item->SetChecked(checked);
308 properties->SetBoolean(L"checked", item->checked());
309 }
310
311 std::string json_args;
312 base::JSONWriter::Write(&args, false, &json_args);
313 std::string event_name = "contextMenu/" + item->extension_id();
314 service->DispatchEventToRenderers(event_name, json_args,
315 profile->IsOffTheRecord());
316 }
317
318 void ExtensionMenuManager::Observe(NotificationType type,
319 const NotificationSource& source,
320 const NotificationDetails& details) {
321 // Remove menu items for disabled/uninstalled extensions.
322 if (type != NotificationType::EXTENSION_UNLOADED) {
323 NOTREACHED();
324 return;
325 }
326 Extension* extension = Details<Extension>(details).ptr();
327 MenuItemMap::iterator i = context_items_.find(extension->id());
328 if (i != context_items_.end()) {
329 const ExtensionMenuItem::List& list = i->second;
330 ExtensionMenuItem::List::const_iterator j;
331 for (j = list.begin(); j != list.end(); ++j)
332 items_by_id_.erase((*j)->id());
333 context_items_.erase(i);
334 }
335 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698