diff options
Diffstat (limited to 'chrome/browser/extensions/extension_context_menu_api.cc')
-rw-r--r-- | chrome/browser/extensions/extension_context_menu_api.cc | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/chrome/browser/extensions/extension_context_menu_api.cc b/chrome/browser/extensions/extension_context_menu_api.cc new file mode 100644 index 0000000..5652245 --- /dev/null +++ b/chrome/browser/extensions/extension_context_menu_api.cc @@ -0,0 +1,355 @@ +// 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_context_menu_api.h" + +#include <string> + +#include "base/values.h" +#include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/profile.h" +#include "chrome/common/extensions/extension_error_utils.h" + +const wchar_t kCheckedKey[] = L"checked"; +const wchar_t kContextsKey[] = L"contexts"; +const wchar_t kDocumentUrlPatternsKey[] = L"documentUrlPatterns"; +const wchar_t kGeneratedIdKey[] = L"generatedId"; +const wchar_t kParentIdKey[] = L"parentId"; +const wchar_t kTargetUrlPatternsKey[] = L"targetUrlPatterns"; +const wchar_t kTitleKey[] = L"title"; +const wchar_t kTypeKey[] = L"type"; + +const char kCannotFindItemError[] = "Cannot find menu item with id *"; +const char kCheckedError[] = + "Only items with type \"radio\" or \"checkbox\" can be checked"; +const char kInvalidURLPatternError[] = "Invalid url pattern '*'"; +const char kInvalidValueError[] = "Invalid value for *"; +const char kInvalidTypeStringError[] = "Invalid type string '*'"; +const char kParentsMustBeNormalError[] = + "Parent items must have type \"normal\""; +const char kTitleNeededError[] = + "All menu items except for separators must have a title"; + + +bool ExtensionContextMenuFunction::ParseContexts( + const DictionaryValue& properties, + const wchar_t* key, + ExtensionMenuItem::ContextList* result) { + ListValue* list = NULL; + if (!properties.GetList(key, &list)) { + return true; + } + ExtensionMenuItem::ContextList tmp_result; + + std::string value; + for (size_t i = 0; i < list->GetSize(); i++) { + if (!list->GetString(i, &value)) + return false; + + if (value == "all") { + tmp_result.Add(ExtensionMenuItem::ALL); + } else if (value == "page") { + tmp_result.Add(ExtensionMenuItem::PAGE); + } else if (value == "selection") { + tmp_result.Add(ExtensionMenuItem::SELECTION); + } else if (value == "link") { + tmp_result.Add(ExtensionMenuItem::LINK); + } else if (value == "editable") { + tmp_result.Add(ExtensionMenuItem::EDITABLE); + } else if (value == "image") { + tmp_result.Add(ExtensionMenuItem::IMAGE); + } else if (value == "video") { + tmp_result.Add(ExtensionMenuItem::VIDEO); + } else if (value == "audio") { + tmp_result.Add(ExtensionMenuItem::AUDIO); + } else { + error_ = ExtensionErrorUtils::FormatErrorMessage(kInvalidValueError, + WideToASCII(key)); + return false; + } + } + *result = tmp_result; + return true; +} + +bool ExtensionContextMenuFunction::ParseType( + const DictionaryValue& properties, + const ExtensionMenuItem::Type& default_value, + ExtensionMenuItem::Type* result) { + DCHECK(result); + if (!properties.HasKey(kTypeKey)) { + *result = default_value; + return true; + } + + std::string type_string; + if (!properties.GetString(kTypeKey, &type_string)) + return false; + + if (type_string == "normal") { + *result = ExtensionMenuItem::NORMAL; + } else if (type_string == "checkbox") { + *result = ExtensionMenuItem::CHECKBOX; + } else if (type_string == "radio") { + *result = ExtensionMenuItem::RADIO; + } else if (type_string == "separator") { + *result = ExtensionMenuItem::SEPARATOR; + } else { + error_ = ExtensionErrorUtils::FormatErrorMessage(kInvalidTypeStringError, + type_string); + return false; + } + return true; +} + +bool ExtensionContextMenuFunction::ParseChecked( + ExtensionMenuItem::Type type, + const DictionaryValue& properties, + bool default_value, + bool* checked) { + if (!properties.HasKey(kCheckedKey)) { + *checked = default_value; + return true; + } + if (!properties.GetBoolean(kCheckedKey, checked)) + return false; + if (checked && type != ExtensionMenuItem::CHECKBOX && + type != ExtensionMenuItem::RADIO) { + error_ = kCheckedError; + return false; + } + return true; +} + +bool ExtensionContextMenuFunction::ParseURLPatterns( + const DictionaryValue& properties, + const wchar_t* key, + ExtensionExtent* result) { + if (!properties.HasKey(key)) + return true; + ListValue* list = NULL; + if (!properties.GetList(key, &list)) + return false; + for (ListValue::iterator i = list->begin(); i != list->end(); ++i) { + std::string tmp; + if (!(*i)->GetAsString(&tmp)) + return false; + + URLPattern pattern(ExtensionMenuManager::kAllowedSchemes); + if (!pattern.Parse(tmp)) { + error_ = ExtensionErrorUtils::FormatErrorMessage(kInvalidURLPatternError, + tmp); + return false; + } + result->AddPattern(pattern); + } + return true; +} + +bool ExtensionContextMenuFunction::SetURLPatterns( + const DictionaryValue& properties, + ExtensionMenuItem* item) { + // Process the documentUrlPattern value. + ExtensionExtent document_url_patterns; + if (!ParseURLPatterns(properties, kDocumentUrlPatternsKey, + &document_url_patterns)) + return false; + + if (!document_url_patterns.is_empty()) { + item->set_document_url_patterns(document_url_patterns); + } + + // Process the targetUrlPattern value. + ExtensionExtent target_url_patterns; + if (!ParseURLPatterns(properties, kTargetUrlPatternsKey, + &target_url_patterns)) + return false; + + if (!target_url_patterns.is_empty()) { + item->set_target_url_patterns(target_url_patterns); + } + + return true; +} + + +bool ExtensionContextMenuFunction::GetParent( + const DictionaryValue& properties, + const ExtensionMenuManager& manager, + ExtensionMenuItem** result) { + if (!properties.HasKey(kParentIdKey)) + return true; + ExtensionMenuItem::Id parent_id(extension_id(), 0); + if (properties.HasKey(kParentIdKey) && + !properties.GetInteger(kParentIdKey, &parent_id.second)) + return false; + + ExtensionMenuItem* parent = manager.GetItemById(parent_id); + if (!parent) { + error_ = "Cannot find menu item with id " + IntToString(parent_id.second); + return false; + } + if (parent->type() != ExtensionMenuItem::NORMAL) { + error_ = kParentsMustBeNormalError; + return false; + } + *result = parent; + return true; +} + +bool CreateContextMenuFunction::RunImpl() { + DictionaryValue* properties; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &properties)); + EXTENSION_FUNCTION_VALIDATE(properties != NULL); + + ExtensionMenuItem::Id id(extension_id(), 0); + EXTENSION_FUNCTION_VALIDATE(properties->GetInteger(kGeneratedIdKey, + &id.second)); + std::string title; + if (properties->HasKey(kTitleKey) && + !properties->GetString(kTitleKey, &title)) + return false; + + ExtensionMenuManager* menu_manager = + profile()->GetExtensionsService()->menu_manager(); + + ExtensionMenuItem::ContextList contexts(ExtensionMenuItem::PAGE); + if (!ParseContexts(*properties, kContextsKey, &contexts)) + return false; + + ExtensionMenuItem::Type type; + if (!ParseType(*properties, ExtensionMenuItem::NORMAL, &type)) + return false; + + if (title.empty() && type != ExtensionMenuItem::SEPARATOR) { + error_ = kTitleNeededError; + return false; + } + + bool checked; + if (!ParseChecked(type, *properties, false, &checked)) + return false; + + scoped_ptr<ExtensionMenuItem> item( + new ExtensionMenuItem(id, title, checked, type, contexts)); + + if (!SetURLPatterns(*properties, item.get())) + return false; + + bool success = true; + if (properties->HasKey(kParentIdKey)) { + ExtensionMenuItem::Id parent_id(extension_id(), 0); + EXTENSION_FUNCTION_VALIDATE(properties->GetInteger(kParentIdKey, + &parent_id.second)); + ExtensionMenuItem* parent = menu_manager->GetItemById(parent_id); + if (!parent) { + error_ = ExtensionErrorUtils::FormatErrorMessage( + kCannotFindItemError, IntToString(parent_id.second)); + return false; + } + if (parent->type() != ExtensionMenuItem::NORMAL) { + error_ = kParentsMustBeNormalError; + return false; + } + success = menu_manager->AddChildItem(parent_id, item.release()); + } else { + success = menu_manager->AddContextItem(GetExtension(), item.release()); + } + + if (!success) + return false; + + return true; +} + +bool UpdateContextMenuFunction::RunImpl() { + ExtensionMenuItem::Id item_id(extension_id(), 0); + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &item_id.second)); + + ExtensionsService* service = profile()->GetExtensionsService(); + ExtensionMenuManager* manager = service->menu_manager(); + ExtensionMenuItem* item = manager->GetItemById(item_id); + if (!item || item->extension_id() != extension_id()) { + error_ = ExtensionErrorUtils::FormatErrorMessage( + kCannotFindItemError, IntToString(item_id.second)); + return false; + } + + DictionaryValue *properties = NULL; + EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &properties)); + EXTENSION_FUNCTION_VALIDATE(properties != NULL); + + ExtensionMenuManager* menu_manager = + profile()->GetExtensionsService()->menu_manager(); + + // Type. + ExtensionMenuItem::Type type; + if (!ParseType(*properties, item->type(), &type)) + return false; + if (type != item->type()) + item->set_type(type); + + // Title. + if (properties->HasKey(kTitleKey)) { + std::string title; + EXTENSION_FUNCTION_VALIDATE(properties->GetString(kTitleKey, &title)); + if (title.empty() && type != ExtensionMenuItem::SEPARATOR) { + error_ = kTitleNeededError; + return false; + } + item->set_title(title); + } + + // Checked state. + bool checked; + if (!ParseChecked(item->type(), *properties, item->checked(), &checked)) + return false; + if (checked != item->checked()) { + if (!item->SetChecked(checked)) + return false; + } + + // Contexts. + ExtensionMenuItem::ContextList contexts(item->contexts()); + if (!ParseContexts(*properties, kContextsKey, &contexts)) + return false; + if (contexts != item->contexts()) + item->set_contexts(contexts); + + // Parent id. + ExtensionMenuItem* parent = NULL; + if (!GetParent(*properties, *menu_manager, &parent)) + return false; + if (parent && !menu_manager->ChangeParent(item->id(), &parent->id())) + return false; + + if (!SetURLPatterns(*properties, item)) + return false; + + return true; +} + +bool RemoveContextMenuFunction::RunImpl() { + ExtensionMenuItem::Id id(extension_id(), 0); + EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &id.second)); + ExtensionsService* service = profile()->GetExtensionsService(); + ExtensionMenuManager* manager = service->menu_manager(); + + ExtensionMenuItem* item = manager->GetItemById(id); + // Ensure one extension can't remove another's menu items. + if (!item || item->extension_id() != extension_id()) { + error_ = ExtensionErrorUtils::FormatErrorMessage( + kCannotFindItemError, IntToString(id.second)); + return false; + } + + return manager->RemoveContextMenuItem(id); +} + +bool RemoveAllContextMenusFunction::RunImpl() { + ExtensionsService* service = profile()->GetExtensionsService(); + ExtensionMenuManager* manager = service->menu_manager(); + manager->RemoveAllContextItems(extension_id()); + return true; +} |