1 // Copyright 2014 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.
5 // Definition of helper functions for the ContextMenus API.
7 #ifndef CHROME_BROWSER_EXTENSIONS_API_CONTEXT_MENUS_CONTEXT_MENUS_API_HELPERS_H_
8 #define CHROME_BROWSER_EXTENSIONS_API_CONTEXT_MENUS_CONTEXT_MENUS_API_HELPERS_H_
10 #include "chrome/browser/extensions/menu_manager.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "extensions/common/error_utils.h"
13 #include "extensions/common/manifest_handlers/background_info.h"
15 namespace extensions {
16 namespace context_menus_api_helpers {
20 template <typename PropertyWithEnumT>
21 scoped_ptr<extensions::MenuItem::Id> GetParentId(
22 const PropertyWithEnumT& property,
23 bool is_off_the_record,
24 const MenuItem::ExtensionKey& key) {
25 if (!property.parent_id)
26 return scoped_ptr<extensions::MenuItem::Id>();
28 scoped_ptr<extensions::MenuItem::Id> parent_id(
29 new extensions::MenuItem::Id(is_off_the_record, key));
30 if (property.parent_id->as_integer)
31 parent_id->uid = *property.parent_id->as_integer;
32 else if (property.parent_id->as_string)
33 parent_id->string_uid = *property.parent_id->as_string;
36 return parent_id.Pass();
41 extern const char kActionNotAllowedError[];
42 extern const char kCannotFindItemError[];
43 extern const char kCheckedError[];
44 extern const char kDuplicateIDError[];
45 extern const char kGeneratedIdKey[];
46 extern const char kLauncherNotAllowedError[];
47 extern const char kOnclickDisallowedError[];
48 extern const char kParentsMustBeNormalError[];
49 extern const char kTitleNeededError[];
51 std::string GetIDString(const MenuItem::Id& id);
53 MenuItem* GetParent(MenuItem::Id parent_id,
54 const MenuManager* menu_manager,
57 template<typename PropertyWithEnumT>
58 MenuItem::ContextList GetContexts(const PropertyWithEnumT& property) {
59 MenuItem::ContextList contexts;
60 for (size_t i = 0; i < property.contexts->size(); ++i) {
61 switch (property.contexts->at(i)) {
62 case PropertyWithEnumT::CONTEXTS_TYPE_ALL:
63 contexts.Add(extensions::MenuItem::ALL);
65 case PropertyWithEnumT::CONTEXTS_TYPE_PAGE:
66 contexts.Add(extensions::MenuItem::PAGE);
68 case PropertyWithEnumT::CONTEXTS_TYPE_SELECTION:
69 contexts.Add(extensions::MenuItem::SELECTION);
71 case PropertyWithEnumT::CONTEXTS_TYPE_LINK:
72 contexts.Add(extensions::MenuItem::LINK);
74 case PropertyWithEnumT::CONTEXTS_TYPE_EDITABLE:
75 contexts.Add(extensions::MenuItem::EDITABLE);
77 case PropertyWithEnumT::CONTEXTS_TYPE_IMAGE:
78 contexts.Add(extensions::MenuItem::IMAGE);
80 case PropertyWithEnumT::CONTEXTS_TYPE_VIDEO:
81 contexts.Add(extensions::MenuItem::VIDEO);
83 case PropertyWithEnumT::CONTEXTS_TYPE_AUDIO:
84 contexts.Add(extensions::MenuItem::AUDIO);
86 case PropertyWithEnumT::CONTEXTS_TYPE_FRAME:
87 contexts.Add(extensions::MenuItem::FRAME);
89 case PropertyWithEnumT::CONTEXTS_TYPE_LAUNCHER:
90 // Not available for <webview>.
91 contexts.Add(extensions::MenuItem::LAUNCHER);
93 case PropertyWithEnumT::CONTEXTS_TYPE_BROWSER_ACTION:
94 // Not available for <webview>.
95 contexts.Add(extensions::MenuItem::BROWSER_ACTION);
97 case PropertyWithEnumT::CONTEXTS_TYPE_PAGE_ACTION:
98 // Not available for <webview>.
99 contexts.Add(extensions::MenuItem::PAGE_ACTION);
101 case PropertyWithEnumT::CONTEXTS_TYPE_NONE:
108 template<typename PropertyWithEnumT>
109 MenuItem::Type GetType(const PropertyWithEnumT& property,
110 MenuItem::Type default_type) {
111 switch (property.type) {
112 case PropertyWithEnumT::TYPE_NONE:
114 case PropertyWithEnumT::TYPE_NORMAL:
115 return extensions::MenuItem::NORMAL;
116 case PropertyWithEnumT::TYPE_CHECKBOX:
117 return extensions::MenuItem::CHECKBOX;
118 case PropertyWithEnumT::TYPE_RADIO:
119 return extensions::MenuItem::RADIO;
120 case PropertyWithEnumT::TYPE_SEPARATOR:
121 return extensions::MenuItem::SEPARATOR;
123 return extensions::MenuItem::NORMAL;
126 // Creates and adds a menu item from |create_properties|.
127 template<typename PropertyWithEnumT>
128 bool CreateMenuItem(const PropertyWithEnumT& create_properties,
130 const Extension* extension,
131 const MenuItem::Id& item_id,
132 std::string* error) {
133 bool is_webview = item_id.extension_key.webview_instance_id != 0;
134 MenuManager* menu_manager = MenuManager::Get(profile);
136 if (menu_manager->GetItemById(item_id)) {
137 *error = ErrorUtils::FormatErrorMessage(kDuplicateIDError,
138 GetIDString(item_id));
142 if (!is_webview && BackgroundInfo::HasLazyBackgroundPage(extension) &&
143 create_properties.onclick.get()) {
144 *error = kOnclickDisallowedError;
149 MenuItem::ContextList contexts;
150 if (create_properties.contexts.get())
151 contexts = GetContexts(create_properties);
153 contexts.Add(MenuItem::PAGE);
155 if (contexts.Contains(MenuItem::LAUNCHER)) {
156 // Launcher item is not allowed for <webview>.
157 if (!extension->is_platform_app() || is_webview) {
158 *error = kLauncherNotAllowedError;
163 if (contexts.Contains(MenuItem::BROWSER_ACTION) ||
164 contexts.Contains(MenuItem::PAGE_ACTION)) {
165 // Action items are not allowed for <webview>.
166 if (!extension->is_extension() || is_webview) {
167 *error = kActionNotAllowedError;
174 if (create_properties.title.get())
175 title = *create_properties.title;
177 MenuItem::Type type = GetType(create_properties, MenuItem::NORMAL);
178 if (title.empty() && type != MenuItem::SEPARATOR) {
179 *error = kTitleNeededError;
184 bool checked = false;
185 if (create_properties.checked.get())
186 checked = *create_properties.checked;
190 if (create_properties.enabled.get())
191 enabled = *create_properties.enabled;
193 scoped_ptr<MenuItem> item(
194 new MenuItem(item_id, title, checked, enabled, type, contexts));
197 if (!item->PopulateURLPatterns(
198 create_properties.document_url_patterns.get(),
199 create_properties.target_url_patterns.get(),
206 scoped_ptr<MenuItem::Id> parent_id(GetParentId(
207 create_properties, profile->IsOffTheRecord(), item_id.extension_key));
208 if (parent_id.get()) {
209 MenuItem* parent = GetParent(*parent_id, menu_manager, error);
212 success = menu_manager->AddChildItem(parent->id(), item.release());
214 success = menu_manager->AddContextItem(extension, item.release());
220 menu_manager->WriteToStorage(extension, item_id.extension_key);
224 // Updates a menu item from |update_properties|.
225 template<typename PropertyWithEnumT>
226 bool UpdateMenuItem(const PropertyWithEnumT& update_properties,
228 const Extension* extension,
229 const MenuItem::Id& item_id,
230 std::string* error) {
231 bool radio_item_updated = false;
232 bool is_webview = item_id.extension_key.webview_instance_id != 0;
233 MenuManager* menu_manager = MenuManager::Get(profile);
235 MenuItem* item = menu_manager->GetItemById(item_id);
236 if (!item || item->extension_id() != extension->id()){
237 *error = ErrorUtils::FormatErrorMessage(
238 kCannotFindItemError, GetIDString(item_id));
243 MenuItem::Type type = GetType(update_properties, item->type());
245 if (type != item->type()) {
246 if (type == MenuItem::RADIO || item->type() == MenuItem::RADIO)
247 radio_item_updated = true;
248 item->set_type(type);
252 if (update_properties.title.get()) {
253 std::string title(*update_properties.title);
254 if (title.empty() && item->type() != MenuItem::SEPARATOR) {
255 *error = kTitleNeededError;
258 item->set_title(title);
262 if (update_properties.checked.get()) {
263 bool checked = *update_properties.checked;
265 item->type() != MenuItem::CHECKBOX &&
266 item->type() != MenuItem::RADIO) {
267 *error = kCheckedError;
270 if (checked != item->checked()) {
271 if (!item->SetChecked(checked)) {
272 *error = kCheckedError;
275 radio_item_updated = true;
280 if (update_properties.enabled.get())
281 item->set_enabled(*update_properties.enabled);
284 MenuItem::ContextList contexts;
285 if (update_properties.contexts.get()) {
286 contexts = GetContexts(update_properties);
288 if (contexts.Contains(MenuItem::LAUNCHER)) {
289 // Launcher item is not allowed for <webview>.
290 if (!extension->is_platform_app() || is_webview) {
291 *error = kLauncherNotAllowedError;
296 if (contexts != item->contexts())
297 item->set_contexts(contexts);
301 MenuItem* parent = NULL;
302 scoped_ptr<MenuItem::Id> parent_id(GetParentId(
303 update_properties, profile->IsOffTheRecord(), item_id.extension_key));
304 if (parent_id.get()) {
305 MenuItem* parent = GetParent(*parent_id, menu_manager, error);
306 if (!parent || !menu_manager->ChangeParent(item->id(), &parent->id()))
311 if (!item->PopulateURLPatterns(
312 update_properties.document_url_patterns.get(),
313 update_properties.target_url_patterns.get(), error)) {
317 // There is no need to call ItemUpdated if ChangeParent is called because
318 // all sanitation is taken care of in ChangeParent.
319 if (!parent && radio_item_updated && !menu_manager->ItemUpdated(item->id()))
322 menu_manager->WriteToStorage(extension, item_id.extension_key);
326 } // namespace context_menus_api_helpers
327 } // namespace extensions
329 #endif // CHROME_BROWSER_EXTENSIONS_API_CONTEXT_MENUS_CONTEXT_MENUS_API_HELPERS_H_