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

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

Issue 10664037: Moved ExtensionMenuManager and ExtensionMenuItem into extensions namespace (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Merged with latest master Created 8 years, 5 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
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 <algorithm>
8
9 #include "base/json/json_writer.h"
10 #include "base/logging.h"
11 #include "base/stl_util.h"
12 #include "base/string_util.h"
13 #include "base/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/extensions/extension_event_names.h"
16 #include "chrome/browser/extensions/extension_event_router.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/extensions/extension_system.h"
19 #include "chrome/browser/extensions/extension_tab_helper.h"
20 #include "chrome/browser/extensions/extension_tab_util.h"
21 #include "chrome/browser/extensions/state_store.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/ui/tab_contents/tab_contents.h"
24 #include "chrome/common/chrome_notification_types.h"
25 #include "chrome/common/extensions/extension.h"
26 #include "content/public/browser/notification_details.h"
27 #include "content/public/browser/notification_source.h"
28 #include "content/public/browser/web_contents.h"
29 #include "content/public/common/context_menu_params.h"
30 #include "ui/base/text/text_elider.h"
31 #include "ui/gfx/favicon_size.h"
32
33 using content::WebContents;
34
35 namespace {
36
37 // Keys for serialization to and from Value to store in the preferences.
38 const char kContextMenusKey[] = "context_menus";
39
40 const char kCheckedKey[] = "checked";
41 const char kContextsKey[] = "contexts";
42 const char kDocumentURLPatternsKey[] = "document_url_patterns";
43 const char kEnabledKey[] = "enabled";
44 const char kIncognitoKey[] = "incognito";
45 const char kParentUIDKey[] = "parent_uid";
46 const char kStringUIDKey[] = "string_uid";
47 const char kTargetURLPatternsKey[] = "target_url_patterns";
48 const char kTitleKey[] = "title";
49 const char kTypeKey[] = "type";
50
51 void SetIdKeyValue(base::DictionaryValue* properties,
52 const char* key,
53 const ExtensionMenuItem::Id& id) {
54 if (id.uid == 0)
55 properties->SetString(key, id.string_uid);
56 else
57 properties->SetInteger(key, id.uid);
58 }
59
60 ExtensionMenuItem::List MenuItemsFromValue(const std::string& extension_id,
61 base::Value* value) {
62 ExtensionMenuItem::List items;
63
64 base::ListValue* list = NULL;
65 if (!value || !value->GetAsList(&list))
66 return items;
67
68 for (size_t i = 0; i < list->GetSize(); ++i) {
69 base::DictionaryValue* dict = NULL;
70 if (!list->GetDictionary(i, &dict))
71 continue;
72 ExtensionMenuItem* item = ExtensionMenuItem::Populate(
73 extension_id, *dict, NULL);
74 if (!item)
75 continue;
76 items.push_back(item);
77 }
78 return items;
79 }
80
81 scoped_ptr<base::Value> MenuItemsToValue(
82 const ExtensionMenuItem::List& items) {
83 scoped_ptr<base::ListValue> list(new ListValue());
84 for (size_t i = 0; i < items.size(); ++i)
85 list->Append(items[i]->ToValue().release());
86 return scoped_ptr<Value>(list.release());
87 }
88
89 } // namespace
90
91 ExtensionMenuItem::ExtensionMenuItem(const Id& id,
92 const std::string& title,
93 bool checked,
94 bool enabled,
95 Type type,
96 const ContextList& contexts)
97 : id_(id),
98 title_(title),
99 type_(type),
100 checked_(checked),
101 enabled_(enabled),
102 contexts_(contexts),
103 parent_id_(0) {
104 }
105
106 ExtensionMenuItem::~ExtensionMenuItem() {
107 STLDeleteElements(&children_);
108 }
109
110 ExtensionMenuItem* ExtensionMenuItem::ReleaseChild(const Id& child_id,
111 bool recursive) {
112 for (List::iterator i = children_.begin(); i != children_.end(); ++i) {
113 ExtensionMenuItem* child = NULL;
114 if ((*i)->id() == child_id) {
115 child = *i;
116 children_.erase(i);
117 return child;
118 } else if (recursive) {
119 child = (*i)->ReleaseChild(child_id, recursive);
120 if (child)
121 return child;
122 }
123 }
124 return NULL;
125 }
126
127 void ExtensionMenuItem::GetFlattenedSubtree(ExtensionMenuItem::List* list) {
128 list->push_back(this);
129 for (List::iterator i = children_.begin(); i != children_.end(); ++i)
130 (*i)->GetFlattenedSubtree(list);
131 }
132
133 std::set<ExtensionMenuItem::Id> ExtensionMenuItem::RemoveAllDescendants() {
134 std::set<Id> result;
135 for (List::iterator i = children_.begin(); i != children_.end(); ++i) {
136 ExtensionMenuItem* child = *i;
137 result.insert(child->id());
138 std::set<Id> removed = child->RemoveAllDescendants();
139 result.insert(removed.begin(), removed.end());
140 }
141 STLDeleteElements(&children_);
142 return result;
143 }
144
145 string16 ExtensionMenuItem::TitleWithReplacement(
146 const string16& selection, size_t max_length) const {
147 string16 result = UTF8ToUTF16(title_);
148 // TODO(asargent) - Change this to properly handle %% escaping so you can
149 // put "%s" in titles that won't get substituted.
150 ReplaceSubstringsAfterOffset(&result, 0, ASCIIToUTF16("%s"), selection);
151
152 if (result.length() > max_length)
153 result = ui::TruncateString(result, max_length);
154 return result;
155 }
156
157 bool ExtensionMenuItem::SetChecked(bool checked) {
158 if (type_ != CHECKBOX && type_ != RADIO)
159 return false;
160 checked_ = checked;
161 return true;
162 }
163
164 void ExtensionMenuItem::AddChild(ExtensionMenuItem* item) {
165 item->parent_id_.reset(new Id(id_));
166 children_.push_back(item);
167 }
168
169 scoped_ptr<DictionaryValue> ExtensionMenuItem::ToValue() const {
170 scoped_ptr<DictionaryValue> value(new DictionaryValue);
171 // Should only be called for extensions with event pages, which only have
172 // string IDs for items.
173 DCHECK_EQ(0, id_.uid);
174 value->SetString(kStringUIDKey, id_.string_uid);
175 value->SetBoolean(kIncognitoKey, id_.incognito);
176 value->SetInteger(kTypeKey, type_);
177 if (type_ != SEPARATOR)
178 value->SetString(kTitleKey, title_);
179 if (type_ == CHECKBOX || type_ == RADIO)
180 value->SetBoolean(kCheckedKey, checked_);
181 value->SetBoolean(kEnabledKey, enabled_);
182 value->Set(kContextsKey, contexts_.ToValue().release());
183 if (parent_id_.get()) {
184 DCHECK_EQ(0, parent_id_->uid);
185 value->SetString(kParentUIDKey, parent_id_->string_uid);
186 }
187 value->Set(kDocumentURLPatternsKey,
188 document_url_patterns_.ToValue().release());
189 value->Set(kTargetURLPatternsKey, target_url_patterns_.ToValue().release());
190 return value.Pass();
191 }
192
193 // static
194 ExtensionMenuItem* ExtensionMenuItem::Populate(const std::string& extension_id,
195 const DictionaryValue& value,
196 std::string* error) {
197 bool incognito = false;
198 if (!value.GetBoolean(kIncognitoKey, &incognito))
199 return NULL;
200 Id id(incognito, extension_id);
201 if (!value.GetString(kStringUIDKey, &id.string_uid))
202 return NULL;
203 int type_int;
204 Type type;
205 if (!value.GetInteger(kTypeKey, &type_int))
206 return NULL;
207 type = static_cast<Type>(type_int);
208 std::string title;
209 if (type != SEPARATOR && !value.GetString(kTitleKey, &title))
210 return NULL;
211 bool checked;
212 if ((type == CHECKBOX || type == RADIO) &&
213 !value.GetBoolean(kCheckedKey, &checked)) {
214 return NULL;
215 }
216 bool enabled = true;
217 if (!value.GetBoolean(kEnabledKey, &enabled))
218 return NULL;
219 ContextList contexts;
220 Value* contexts_value = NULL;
221 if (!value.Get(kContextsKey, &contexts_value))
222 return NULL;
223 if (!contexts.Populate(*contexts_value))
224 return NULL;
225
226 scoped_ptr<ExtensionMenuItem> result(new ExtensionMenuItem(
227 id, title, checked, enabled, type, contexts));
228
229 if (!result->PopulateURLPatterns(
230 value, kDocumentURLPatternsKey, kTargetURLPatternsKey, error))
231 return NULL;
232
233 // parent_id is filled in from the value, but it might not be valid. It's left
234 // to be validated upon being added (via AddChildItem) to the menu manager.
235 scoped_ptr<Id> parent_id(new Id(incognito, extension_id));
236 if (value.HasKey(kParentUIDKey)) {
237 if (!value.GetString(kParentUIDKey, &parent_id->string_uid))
238 return NULL;
239 result->parent_id_.swap(parent_id);
240 }
241 return result.release();
242 }
243
244 bool ExtensionMenuItem::PopulateURLPatterns(
245 const DictionaryValue& properties,
246 const char* document_url_patterns_key,
247 const char* target_url_patterns_key,
248 std::string* error) {
249 if (properties.HasKey(document_url_patterns_key)) {
250 ListValue* list = NULL;
251 if (!properties.GetList(document_url_patterns_key, &list))
252 return false;
253 if (!document_url_patterns_.Populate(
254 *list, URLPattern::SCHEME_ALL, true, error)) {
255 return false;
256 }
257 }
258 if (properties.HasKey(target_url_patterns_key)) {
259 ListValue* list = NULL;
260 if (!properties.GetList(target_url_patterns_key, &list))
261 return false;
262 if (!target_url_patterns_.Populate(
263 *list, URLPattern::SCHEME_ALL, true, error)) {
264 return false;
265 }
266 }
267 return true;
268 }
269
270 ExtensionMenuManager::ExtensionMenuManager(Profile* profile)
271 : profile_(profile) {
272 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
273 content::Source<Profile>(profile));
274 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
275 content::Source<Profile>(profile));
276
277 extensions::StateStore* store = ExtensionSystem::Get(profile_)->state_store();
278 if (store)
279 store->RegisterKey(kContextMenusKey);
280 }
281
282 ExtensionMenuManager::~ExtensionMenuManager() {
283 MenuItemMap::iterator i;
284 for (i = context_items_.begin(); i != context_items_.end(); ++i) {
285 STLDeleteElements(&(i->second));
286 }
287 }
288
289 std::set<std::string> ExtensionMenuManager::ExtensionIds() {
290 std::set<std::string> id_set;
291 for (MenuItemMap::const_iterator i = context_items_.begin();
292 i != context_items_.end(); ++i) {
293 id_set.insert(i->first);
294 }
295 return id_set;
296 }
297
298 const ExtensionMenuItem::List* ExtensionMenuManager::MenuItems(
299 const std::string& extension_id) {
300 MenuItemMap::iterator i = context_items_.find(extension_id);
301 if (i != context_items_.end()) {
302 return &(i->second);
303 }
304 return NULL;
305 }
306
307 bool ExtensionMenuManager::AddContextItem(
308 const extensions::Extension* extension,
309 ExtensionMenuItem* item) {
310 const std::string& extension_id = item->extension_id();
311 // The item must have a non-empty extension id, and not have already been
312 // added.
313 if (extension_id.empty() || ContainsKey(items_by_id_, item->id()))
314 return false;
315
316 DCHECK_EQ(extension->id(), extension_id);
317
318 bool first_item = !ContainsKey(context_items_, extension_id);
319 context_items_[extension_id].push_back(item);
320 items_by_id_[item->id()] = item;
321
322 if (item->type() == ExtensionMenuItem::RADIO) {
323 if (item->checked())
324 RadioItemSelected(item);
325 else
326 SanitizeRadioList(context_items_[extension_id]);
327 }
328
329 // If this is the first item for this extension, start loading its icon.
330 if (first_item)
331 icon_manager_.LoadIcon(extension);
332
333 return true;
334 }
335
336 bool ExtensionMenuManager::AddChildItem(const ExtensionMenuItem::Id& parent_id,
337 ExtensionMenuItem* child) {
338 ExtensionMenuItem* parent = GetItemById(parent_id);
339 if (!parent || parent->type() != ExtensionMenuItem::NORMAL ||
340 parent->incognito() != child->incognito() ||
341 parent->extension_id() != child->extension_id() ||
342 ContainsKey(items_by_id_, child->id()))
343 return false;
344 parent->AddChild(child);
345 items_by_id_[child->id()] = child;
346
347 if (child->type() == ExtensionMenuItem::RADIO)
348 SanitizeRadioList(parent->children());
349 return true;
350 }
351
352 bool ExtensionMenuManager::DescendantOf(
353 ExtensionMenuItem* item,
354 const ExtensionMenuItem::Id& ancestor_id) {
355 // Work our way up the tree until we find the ancestor or NULL.
356 ExtensionMenuItem::Id* id = item->parent_id();
357 while (id != NULL) {
358 DCHECK(*id != item->id()); // Catch circular graphs.
359 if (*id == ancestor_id)
360 return true;
361 ExtensionMenuItem* next = GetItemById(*id);
362 if (!next) {
363 NOTREACHED();
364 return false;
365 }
366 id = next->parent_id();
367 }
368 return false;
369 }
370
371 bool ExtensionMenuManager::ChangeParent(
372 const ExtensionMenuItem::Id& child_id,
373 const ExtensionMenuItem::Id* parent_id) {
374 ExtensionMenuItem* child = GetItemById(child_id);
375 ExtensionMenuItem* new_parent = parent_id ? GetItemById(*parent_id) : NULL;
376 if ((parent_id && (child_id == *parent_id)) || !child ||
377 (!new_parent && parent_id != NULL) ||
378 (new_parent && (DescendantOf(new_parent, child_id) ||
379 child->incognito() != new_parent->incognito() ||
380 child->extension_id() != new_parent->extension_id())))
381 return false;
382
383 ExtensionMenuItem::Id* old_parent_id = child->parent_id();
384 if (old_parent_id != NULL) {
385 ExtensionMenuItem* old_parent = GetItemById(*old_parent_id);
386 if (!old_parent) {
387 NOTREACHED();
388 return false;
389 }
390 ExtensionMenuItem* taken =
391 old_parent->ReleaseChild(child_id, false /* non-recursive search*/);
392 DCHECK(taken == child);
393 SanitizeRadioList(old_parent->children());
394 } else {
395 // This is a top-level item, so we need to pull it out of our list of
396 // top-level items.
397 MenuItemMap::iterator i = context_items_.find(child->extension_id());
398 if (i == context_items_.end()) {
399 NOTREACHED();
400 return false;
401 }
402 ExtensionMenuItem::List& list = i->second;
403 ExtensionMenuItem::List::iterator j = std::find(list.begin(), list.end(),
404 child);
405 if (j == list.end()) {
406 NOTREACHED();
407 return false;
408 }
409 list.erase(j);
410 SanitizeRadioList(list);
411 }
412
413 if (new_parent) {
414 new_parent->AddChild(child);
415 SanitizeRadioList(new_parent->children());
416 } else {
417 context_items_[child->extension_id()].push_back(child);
418 child->parent_id_.reset(NULL);
419 SanitizeRadioList(context_items_[child->extension_id()]);
420 }
421 return true;
422 }
423
424 bool ExtensionMenuManager::RemoveContextMenuItem(
425 const ExtensionMenuItem::Id& id) {
426 if (!ContainsKey(items_by_id_, id))
427 return false;
428
429 ExtensionMenuItem* menu_item = GetItemById(id);
430 DCHECK(menu_item);
431 std::string extension_id = menu_item->extension_id();
432 MenuItemMap::iterator i = context_items_.find(extension_id);
433 if (i == context_items_.end()) {
434 NOTREACHED();
435 return false;
436 }
437
438 bool result = false;
439 std::set<ExtensionMenuItem::Id> items_removed;
440 ExtensionMenuItem::List& list = i->second;
441 ExtensionMenuItem::List::iterator j;
442 for (j = list.begin(); j < list.end(); ++j) {
443 // See if the current top-level item is a match.
444 if ((*j)->id() == id) {
445 items_removed = (*j)->RemoveAllDescendants();
446 items_removed.insert(id);
447 delete *j;
448 list.erase(j);
449 result = true;
450 SanitizeRadioList(list);
451 break;
452 } else {
453 // See if the item to remove was found as a descendant of the current
454 // top-level item.
455 ExtensionMenuItem* child = (*j)->ReleaseChild(id, true /* recursive */);
456 if (child) {
457 items_removed = child->RemoveAllDescendants();
458 items_removed.insert(id);
459 SanitizeRadioList(GetItemById(*child->parent_id())->children());
460 delete child;
461 result = true;
462 break;
463 }
464 }
465 }
466 DCHECK(result); // The check at the very top should have prevented this.
467
468 // Clear entries from the items_by_id_ map.
469 std::set<ExtensionMenuItem::Id>::iterator removed_iter;
470 for (removed_iter = items_removed.begin();
471 removed_iter != items_removed.end();
472 ++removed_iter) {
473 items_by_id_.erase(*removed_iter);
474 }
475
476 if (list.empty()) {
477 context_items_.erase(extension_id);
478 icon_manager_.RemoveIcon(extension_id);
479 }
480 return result;
481 }
482
483 void ExtensionMenuManager::RemoveAllContextItems(
484 const std::string& extension_id) {
485 ExtensionMenuItem::List::iterator i;
486 for (i = context_items_[extension_id].begin();
487 i != context_items_[extension_id].end(); ++i) {
488 ExtensionMenuItem* item = *i;
489 items_by_id_.erase(item->id());
490
491 // Remove descendants from this item and erase them from the lookup cache.
492 std::set<ExtensionMenuItem::Id> removed_ids = item->RemoveAllDescendants();
493 std::set<ExtensionMenuItem::Id>::const_iterator j;
494 for (j = removed_ids.begin(); j != removed_ids.end(); ++j) {
495 items_by_id_.erase(*j);
496 }
497 }
498 STLDeleteElements(&context_items_[extension_id]);
499 context_items_.erase(extension_id);
500 icon_manager_.RemoveIcon(extension_id);
501 }
502
503 ExtensionMenuItem* ExtensionMenuManager::GetItemById(
504 const ExtensionMenuItem::Id& id) const {
505 std::map<ExtensionMenuItem::Id, ExtensionMenuItem*>::const_iterator i =
506 items_by_id_.find(id);
507 if (i != items_by_id_.end())
508 return i->second;
509 else
510 return NULL;
511 }
512
513 void ExtensionMenuManager::RadioItemSelected(ExtensionMenuItem* item) {
514 // If this is a child item, we need to get a handle to the list from its
515 // parent. Otherwise get a handle to the top-level list.
516 const ExtensionMenuItem::List* list = NULL;
517 if (item->parent_id()) {
518 ExtensionMenuItem* parent = GetItemById(*item->parent_id());
519 if (!parent) {
520 NOTREACHED();
521 return;
522 }
523 list = &(parent->children());
524 } else {
525 if (context_items_.find(item->extension_id()) == context_items_.end()) {
526 NOTREACHED();
527 return;
528 }
529 list = &context_items_[item->extension_id()];
530 }
531
532 // Find where |item| is in the list.
533 ExtensionMenuItem::List::const_iterator item_location;
534 for (item_location = list->begin(); item_location != list->end();
535 ++item_location) {
536 if (*item_location == item)
537 break;
538 }
539 if (item_location == list->end()) {
540 NOTREACHED(); // We should have found the item.
541 return;
542 }
543
544 // Iterate backwards from |item| and uncheck any adjacent radio items.
545 ExtensionMenuItem::List::const_iterator i;
546 if (item_location != list->begin()) {
547 i = item_location;
548 do {
549 --i;
550 if ((*i)->type() != ExtensionMenuItem::RADIO)
551 break;
552 (*i)->SetChecked(false);
553 } while (i != list->begin());
554 }
555
556 // Now iterate forwards from |item| and uncheck any adjacent radio items.
557 for (i = item_location + 1; i != list->end(); ++i) {
558 if ((*i)->type() != ExtensionMenuItem::RADIO)
559 break;
560 (*i)->SetChecked(false);
561 }
562 }
563
564 static void AddURLProperty(DictionaryValue* dictionary,
565 const std::string& key, const GURL& url) {
566 if (!url.is_empty())
567 dictionary->SetString(key, url.possibly_invalid_spec());
568 }
569
570 void ExtensionMenuManager::ExecuteCommand(
571 Profile* profile,
572 WebContents* web_contents,
573 const content::ContextMenuParams& params,
574 const ExtensionMenuItem::Id& menu_item_id) {
575 ExtensionEventRouter* event_router = profile->GetExtensionEventRouter();
576 if (!event_router)
577 return;
578
579 ExtensionMenuItem* item = GetItemById(menu_item_id);
580 if (!item)
581 return;
582
583 // ExtensionService/Extension can be NULL in unit tests :(
584 ExtensionService* service =
585 ExtensionSystem::Get(profile_)->extension_service();
586 const extensions::Extension* extension = service ?
587 service->extensions()->GetByID(menu_item_id.extension_id) : NULL;
588
589 if (item->type() == ExtensionMenuItem::RADIO)
590 RadioItemSelected(item);
591
592 ListValue args;
593
594 DictionaryValue* properties = new DictionaryValue();
595 SetIdKeyValue(properties, "menuItemId", item->id());
596 if (item->parent_id())
597 SetIdKeyValue(properties, "parentMenuItemId", item->id());
598
599 switch (params.media_type) {
600 case WebKit::WebContextMenuData::MediaTypeImage:
601 properties->SetString("mediaType", "image");
602 break;
603 case WebKit::WebContextMenuData::MediaTypeVideo:
604 properties->SetString("mediaType", "video");
605 break;
606 case WebKit::WebContextMenuData::MediaTypeAudio:
607 properties->SetString("mediaType", "audio");
608 break;
609 default: {} // Do nothing.
610 }
611
612 AddURLProperty(properties, "linkUrl", params.unfiltered_link_url);
613 AddURLProperty(properties, "srcUrl", params.src_url);
614 AddURLProperty(properties, "pageUrl", params.page_url);
615 AddURLProperty(properties, "frameUrl", params.frame_url);
616
617 if (params.selection_text.length() > 0)
618 properties->SetString("selectionText", params.selection_text);
619
620 properties->SetBoolean("editable", params.is_editable);
621
622 args.Append(properties);
623
624 // Add the tab info to the argument list.
625 // Note: web_contents only NULL in unit tests :(
626 if (web_contents)
627 args.Append(ExtensionTabUtil::CreateTabValue(web_contents));
628 else
629 args.Append(new DictionaryValue());
630
631 if (item->type() == ExtensionMenuItem::CHECKBOX ||
632 item->type() == ExtensionMenuItem::RADIO) {
633 bool was_checked = item->checked();
634 properties->SetBoolean("wasChecked", was_checked);
635
636 // RADIO items always get set to true when you click on them, but CHECKBOX
637 // items get their state toggled.
638 bool checked =
639 (item->type() == ExtensionMenuItem::RADIO) ? true : !was_checked;
640
641 item->SetChecked(checked);
642 properties->SetBoolean("checked", item->checked());
643
644 if (extension)
645 WriteToStorage(extension);
646 }
647
648 TabContents* tab_contents = web_contents ?
649 TabContents::FromWebContents(web_contents) : NULL;
650 if (tab_contents && extension) {
651 tab_contents->extension_tab_helper()->active_tab_permission_manager()->
652 GrantIfRequested(extension);
653 }
654
655 std::string json_args;
656 base::JSONWriter::Write(&args, &json_args);
657 event_router->DispatchEventToExtension(
658 item->extension_id(), extension_event_names::kOnContextMenus,
659 json_args, profile, GURL(),
660 ExtensionEventRouter::USER_GESTURE_ENABLED);
661 event_router->DispatchEventToExtension(
662 item->extension_id(), extension_event_names::kOnContextMenuClicked,
663 json_args, profile, GURL(),
664 ExtensionEventRouter::USER_GESTURE_ENABLED);
665 }
666
667 void ExtensionMenuManager::SanitizeRadioList(
668 const ExtensionMenuItem::List& item_list) {
669 ExtensionMenuItem::List::const_iterator i = item_list.begin();
670 while (i != item_list.end()) {
671 if ((*i)->type() != ExtensionMenuItem::RADIO) {
672 ++i;
673 break;
674 }
675
676 // Uncheck any checked radio items in the run, and at the end reset
677 // the appropriate one to checked. If no check radio items were found,
678 // then check the first radio item in the run.
679 ExtensionMenuItem::List::const_iterator last_checked = item_list.end();
680 ExtensionMenuItem::List::const_iterator radio_run_iter;
681 for (radio_run_iter = i; radio_run_iter != item_list.end();
682 ++radio_run_iter) {
683 if ((*radio_run_iter)->type() != ExtensionMenuItem::RADIO) {
684 break;
685 }
686
687 if ((*radio_run_iter)->checked()) {
688 last_checked = radio_run_iter;
689 (*radio_run_iter)->SetChecked(false);
690 }
691 }
692
693 if (last_checked != item_list.end())
694 (*last_checked)->SetChecked(true);
695 else
696 (*i)->SetChecked(true);
697
698 i = radio_run_iter;
699 }
700 }
701
702 bool ExtensionMenuManager::ItemUpdated(const ExtensionMenuItem::Id& id) {
703 if (!ContainsKey(items_by_id_, id))
704 return false;
705
706 ExtensionMenuItem* menu_item = GetItemById(id);
707 DCHECK(menu_item);
708
709 if (menu_item->parent_id()) {
710 SanitizeRadioList(GetItemById(*menu_item->parent_id())->children());
711 } else {
712 std::string extension_id = menu_item->extension_id();
713 MenuItemMap::iterator i = context_items_.find(extension_id);
714 if (i == context_items_.end()) {
715 NOTREACHED();
716 return false;
717 }
718 SanitizeRadioList(i->second);
719 }
720
721 return true;
722 }
723
724 void ExtensionMenuManager::WriteToStorage(
725 const extensions::Extension* extension) {
726 if (!extension->has_lazy_background_page())
727 return;
728 const ExtensionMenuItem::List* top_items = MenuItems(extension->id());
729 ExtensionMenuItem::List all_items;
730 if (top_items) {
731 for (ExtensionMenuItem::List::const_iterator i = top_items->begin();
732 i != top_items->end(); ++i) {
733 (*i)->GetFlattenedSubtree(&all_items);
734 }
735 }
736
737 extensions::StateStore* store = ExtensionSystem::Get(profile_)->state_store();
738 if (store)
739 store->SetExtensionValue(extension->id(), kContextMenusKey,
740 MenuItemsToValue(all_items));
741 }
742
743 void ExtensionMenuManager::ReadFromStorage(const std::string& extension_id,
744 scoped_ptr<base::Value> value) {
745 const extensions::Extension* extension =
746 ExtensionSystem::Get(profile_)->extension_service()->extensions()->
747 GetByID(extension_id);
748 if (!extension)
749 return;
750
751 ExtensionMenuItem::List items = MenuItemsFromValue(extension_id, value.get());
752 for (size_t i = 0; i < items.size(); ++i) {
753 if (items[i]->parent_id()) {
754 // Parent IDs are stored in the parent_id field for convenience, but
755 // they have not yet been validated. Separate them out here.
756 // Because of the order in which we store items in the prefs, parents will
757 // precede children, so we should already know about any parent items.
758 scoped_ptr<ExtensionMenuItem::Id> parent_id;
759 parent_id.swap(items[i]->parent_id_);
760 AddChildItem(*parent_id, items[i]);
761 } else {
762 AddContextItem(extension, items[i]);
763 }
764 }
765 }
766
767 void ExtensionMenuManager::Observe(
768 int type,
769 const content::NotificationSource& source,
770 const content::NotificationDetails& details) {
771 if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
772 // Remove menu items for disabled/uninstalled extensions.
773 const extensions::Extension* extension =
774 content::Details<extensions::UnloadedExtensionInfo>(details)->extension;
775 if (ContainsKey(context_items_, extension->id())) {
776 RemoveAllContextItems(extension->id());
777 }
778 } else if (type == chrome::NOTIFICATION_EXTENSION_LOADED) {
779 const extensions::Extension* extension =
780 content::Details<const extensions::Extension>(details).ptr();
781 extensions::StateStore* store =
782 ExtensionSystem::Get(profile_)->state_store();
783 if (store && extension->has_lazy_background_page()) {
784 store->GetExtensionValue(extension->id(), kContextMenusKey,
785 base::Bind(&ExtensionMenuManager::ReadFromStorage,
786 AsWeakPtr(), extension->id()));
787 }
788 }
789 }
790
791 const SkBitmap& ExtensionMenuManager::GetIconForExtension(
792 const std::string& extension_id) {
793 return icon_manager_.GetIcon(extension_id);
794 }
795
796 ExtensionMenuItem::Id::Id()
797 : incognito(false), extension_id(""), uid(0), string_uid("") {
798 }
799
800 ExtensionMenuItem::Id::Id(bool incognito, const std::string& extension_id)
801 : incognito(incognito), extension_id(extension_id), uid(0),
802 string_uid("") {
803 }
804
805 ExtensionMenuItem::Id::~Id() {
806 }
807
808 bool ExtensionMenuItem::Id::operator==(const Id& other) const {
809 return (incognito == other.incognito &&
810 extension_id == other.extension_id &&
811 uid == other.uid &&
812 string_uid == other.string_uid);
813 }
814
815 bool ExtensionMenuItem::Id::operator!=(const Id& other) const {
816 return !(*this == other);
817 }
818
819 bool ExtensionMenuItem::Id::operator<(const Id& other) const {
820 if (incognito < other.incognito)
821 return true;
822 if (incognito == other.incognito) {
823 if (extension_id < other.extension_id)
824 return true;
825 if (extension_id == other.extension_id) {
826 if (uid < other.uid)
827 return true;
828 if (uid == other.uid)
829 return string_uid < other.string_uid;
830 }
831 }
832 return false;
833 }
OLDNEW
« no previous file with comments | « chrome/browser/extensions/extension_menu_manager.h ('k') | chrome/browser/extensions/extension_menu_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698