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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/extensions/extension_menu_manager.cc
===================================================================
--- chrome/browser/extensions/extension_menu_manager.cc (revision 0)
+++ chrome/browser/extensions/extension_menu_manager.cc (revision 0)
@@ -0,0 +1,335 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/extension_menu_manager.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "base/json/json_writer.h"
+#include "chrome/app/chrome_dll_resource.h"
+#include "chrome/browser/extensions/extension_message_service.h"
+#include "chrome/browser/extensions/extension_tabs_module.h"
+#include "chrome/browser/profile.h"
+#include "chrome/common/extensions/extension.h"
+#include "webkit/glue/context_menu.h"
+
+ExtensionMenuItem::ExtensionMenuItem(const std::string& extension_id,
+ std::string title,
+ bool checked, Type type,
+ const ContextList& contexts,
+ const ContextList& enabled_contexts)
+ : extension_id_(extension_id),
+ title_(title),
+ id_(0),
+ type_(type),
+ checked_(checked),
+ contexts_(contexts),
+ enabled_contexts_(enabled_contexts),
+ parent_id_(0) {}
+
+ExtensionMenuItem::~ExtensionMenuItem() {}
+
+ExtensionMenuItem* ExtensionMenuItem::ChildAt(int index) const {
+ if (index < 0 || static_cast<size_t>(index) >= children_.size())
+ return NULL;
+ return children_[index].get();
+}
+
+bool ExtensionMenuItem::RemoveChild(int child_id) {
+ for (List::iterator i = children_.begin(); i != children_.end(); ++i) {
+ if ((*i)->id() == child_id) {
+ children_.erase(i);
+ return true;
+ } else if ((*i)->RemoveChild(child_id)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+string16 ExtensionMenuItem::TitleWithReplacement(
+ const string16& selection) const {
+ string16 result = UTF8ToUTF16(title_);
+ // TODO(asargent) - Change this to properly handle %% escaping so you can
+ // put "%s" in titles that won't get substituted.
+ ReplaceSubstringsAfterOffset(&result, 0, ASCIIToUTF16("%s"), selection);
+ return result;
+}
+
+bool ExtensionMenuItem::SetChecked(bool checked) {
+ if (type_ != CHECKBOX && type_ != RADIO)
+ return false;
+ checked_ = checked;
+ return true;
+}
+
+void ExtensionMenuItem::AddChild(ExtensionMenuItem* item) {
+ item->parent_id_ = id_;
+ children_.push_back(linked_ptr<ExtensionMenuItem>(item));
+}
+
+ExtensionMenuManager::ExtensionMenuManager() : next_item_id_(1) {
+ registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
+ NotificationService::AllSources());
+}
+
+ExtensionMenuManager::~ExtensionMenuManager() {}
+
+std::set<std::string> ExtensionMenuManager::ExtensionIds() {
+ std::set<std::string> id_set;
+ for (MenuItemMap::const_iterator i = context_items_.begin();
+ i != context_items_.end(); ++i) {
+ id_set.insert(i->first);
+ }
+ return id_set;
+}
+
+std::vector<const ExtensionMenuItem*> ExtensionMenuManager::MenuItems(
+ const std::string& extension_id) {
+ std::vector<const ExtensionMenuItem*> result;
+
+ MenuItemMap::iterator i = context_items_.find(extension_id);
+ if (i != context_items_.end()) {
+ ExtensionMenuItem::List& list = i->second;
+ ExtensionMenuItem::List::iterator j;
+ for (j = list.begin(); j != list.end(); ++j) {
+ result.push_back(j->get());
+ }
+ }
+
+ return result;
+}
+
+int ExtensionMenuManager::AddContextItem(ExtensionMenuItem* item) {
+ const std::string& extension_id = item->extension_id();
+ // The item must have a non-empty extension id.
+ if (extension_id.empty())
+ return 0;
+
+ DCHECK_EQ(0, item->id());
+ item->set_id(next_item_id_++);
+
+ context_items_[extension_id].push_back(linked_ptr<ExtensionMenuItem>(item));
+ items_by_id_[item->id()] = item;
+
+ if (item->type() == ExtensionMenuItem::RADIO && item->checked())
+ RadioItemSelected(item);
+
+ return item->id();
+}
+
+int ExtensionMenuManager::AddChildItem(int parent_id,
+ ExtensionMenuItem* child) {
+ ExtensionMenuItem* parent = GetItemById(parent_id);
+ if (!parent || parent->type() != ExtensionMenuItem::NORMAL ||
+ parent->extension_id() != child->extension_id())
+ return 0;
+ child->set_id(next_item_id_++);
+ parent->AddChild(child);
+ items_by_id_[child->id()] = child;
+ return child->id();
+}
+
+bool ExtensionMenuManager::RemoveContextMenuItem(int id) {
+ if (items_by_id_.find(id) == items_by_id_.end())
+ return false;
+
+ MenuItemMap::iterator i;
+ for (i = context_items_.begin(); i != context_items_.end(); ++i) {
+ ExtensionMenuItem::List& list = i->second;
+ ExtensionMenuItem::List::iterator j;
+ for (j = list.begin(); j < list.end(); ++j) {
+ // See if the current item is a match, or if one of its children was.
+ if ((*j)->id() == id) {
+ list.erase(j);
+ items_by_id_.erase(id);
+ return true;
+ } else if ((*j)->RemoveChild(id)) {
+ items_by_id_.erase(id);
+ return true;
+ }
+ }
+ }
+ NOTREACHED(); // The check at the very top should prevent getting here.
+ return false;
+}
+
+ExtensionMenuItem* ExtensionMenuManager::GetItemById(int id) {
+ std::map<int, ExtensionMenuItem*>::const_iterator i = items_by_id_.find(id);
+ if (i != items_by_id_.end())
+ return i->second;
+ else
+ return NULL;
+}
+
+void ExtensionMenuManager::RadioItemSelected(ExtensionMenuItem* item) {
+ // If this is a child item, we need to get a handle to the list from its
+ // parent. Otherwise get a handle to the top-level list.
+ ExtensionMenuItem::List* list = NULL;
+ if (item->parent_id()) {
+ ExtensionMenuItem* parent = GetItemById(item->parent_id());
+ if (!parent) {
+ NOTREACHED();
+ return;
+ }
+ list = parent->children();
+ } else {
+ if (context_items_.find(item->extension_id()) == context_items_.end()) {
+ NOTREACHED();
+ return;
+ }
+ list = &context_items_[item->extension_id()];
+ }
+
+ // Find where |item| is in the list.
+ ExtensionMenuItem::List::iterator item_location;
+ for (item_location = list->begin(); item_location != list->end();
+ ++item_location) {
+ if (item_location->get() == item)
+ break;
+ }
+ if (item_location == list->end()) {
+ NOTREACHED(); // We should have found the item.
+ return;
+ }
+
+ // Iterate backwards from |item| and uncheck any adjacent radio items.
+ ExtensionMenuItem::List::iterator i;
+ if (item_location != list->begin()) {
+ i = item_location;
+ do {
+ --i;
+ if ((*i)->type() != ExtensionMenuItem::RADIO)
+ break;
+ (*i)->SetChecked(false);
+ } while (i != list->begin());
+ }
+
+ // Now iterate forwards from |item| and uncheck any adjacent radio items.
+ for (i = item_location + 1; i != list->end(); ++i) {
+ if ((*i)->type() != ExtensionMenuItem::RADIO)
+ break;
+ (*i)->SetChecked(false);
+ }
+}
+
+static void AddURLProperty(DictionaryValue* dictionary,
+ const std::wstring& key, const GURL& url) {
+ if (!url.is_empty())
+ dictionary->SetString(key, url.possibly_invalid_spec());
+}
+
+void ExtensionMenuManager::GetItemAndIndex(int id, ExtensionMenuItem** item,
+ size_t* index) {
+ for (MenuItemMap::const_iterator i = context_items_.begin();
+ i != context_items_.end(); ++i) {
+ const ExtensionMenuItem::List& list = i->second;
+ for (size_t tmp_index = 0; tmp_index < list.size(); tmp_index++) {
+ if (list[tmp_index]->id() == id) {
+ if (item)
+ *item = list[tmp_index].get();
+ if (index)
+ *index = tmp_index;
+ return;
+ }
+ }
+ }
+ if (item)
+ *item = NULL;
+ if (index)
+ *index = 0;
+}
+
+
+void ExtensionMenuManager::ExecuteCommand(Profile* profile,
+ TabContents* tab_contents,
+ const ContextMenuParams& params,
+ int menuItemId) {
+ ExtensionMessageService* service = profile->GetExtensionMessageService();
+ if (!service)
+ return;
+
+ ExtensionMenuItem* item = GetItemById(menuItemId);
+ if (!item)
+ return;
+
+ if (item->type() == ExtensionMenuItem::RADIO)
+ RadioItemSelected(item);
+
+ ListValue args;
+
+ DictionaryValue* properties = new DictionaryValue();
+ properties->SetInteger(L"menuItemId", item->id());
+ if (item->parent_id())
+ properties->SetInteger(L"parentMenuItemId", item->parent_id());
+
+ switch (params.media_type) {
+ case WebKit::WebContextMenuData::MediaTypeImage:
+ properties->SetString(L"mediaType", "IMAGE");
+ break;
+ case WebKit::WebContextMenuData::MediaTypeVideo:
+ properties->SetString(L"mediaType", "VIDEO");
+ break;
+ case WebKit::WebContextMenuData::MediaTypeAudio:
+ properties->SetString(L"mediaType", "AUDIO");
+ break;
+ default: {} // Do nothing.
+ }
+
+ AddURLProperty(properties, L"linkUrl", params.unfiltered_link_url);
+ AddURLProperty(properties, L"srcUrl", params.src_url);
+ AddURLProperty(properties, L"mainFrameUrl", params.page_url);
+ AddURLProperty(properties, L"frameUrl", params.frame_url);
+
+ if (params.selection_text.length() > 0)
+ properties->SetString(L"selectionText", params.selection_text);
+
+ properties->SetBoolean(L"editable", params.is_editable);
+
+ args.Append(properties);
+
+ // Add the tab info to the argument list.
+ args.Append(ExtensionTabUtil::CreateTabValue(tab_contents));
+
+ if (item->type() == ExtensionMenuItem::CHECKBOX ||
+ item->type() == ExtensionMenuItem::RADIO) {
+ bool was_checked = item->checked();
+ properties->SetBoolean(L"wasChecked", was_checked);
+
+ // RADIO items always get set to true when you click on them, but CHECKBOX
+ // items get their state toggled.
+ bool checked =
+ (item->type() == ExtensionMenuItem::RADIO) ? true : !was_checked;
+
+ item->SetChecked(checked);
+ properties->SetBoolean(L"checked", item->checked());
+ }
+
+ std::string json_args;
+ base::JSONWriter::Write(&args, false, &json_args);
+ std::string event_name = "contextMenu/" + item->extension_id();
+ service->DispatchEventToRenderers(event_name, json_args,
+ profile->IsOffTheRecord());
+}
+
+void ExtensionMenuManager::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ // Remove menu items for disabled/uninstalled extensions.
+ if (type != NotificationType::EXTENSION_UNLOADED) {
+ NOTREACHED();
+ return;
+ }
+ Extension* extension = Details<Extension>(details).ptr();
+ MenuItemMap::iterator i = context_items_.find(extension->id());
+ if (i != context_items_.end()) {
+ const ExtensionMenuItem::List& list = i->second;
+ ExtensionMenuItem::List::const_iterator j;
+ for (j = list.begin(); j != list.end(); ++j)
+ items_by_id_.erase((*j)->id());
+ context_items_.erase(i);
+ }
+}
Property changes on: chrome\browser\extensions\extension_menu_manager.cc
___________________________________________________________________
Added: svn:eol-style
+ LF

Powered by Google App Engine
This is Rietveld 408576698