• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 // Definition of helper functions for the ContextMenus API.
6 
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_
9 
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"
14 
15 namespace extensions {
16 namespace context_menus_api_helpers {
17 
18 namespace {
19 
20 template <typename PropertyWithEnumT>
GetParentId(const PropertyWithEnumT & property,bool is_off_the_record,const MenuItem::ExtensionKey & key)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>();
27 
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;
34   else
35     NOTREACHED();
36   return parent_id.Pass();
37 }
38 
39 }  // namespace
40 
41 extern const char kCannotFindItemError[];
42 extern const char kCheckedError[];
43 extern const char kDuplicateIDError[];
44 extern const char kGeneratedIdKey[];
45 extern const char kLauncherNotAllowedError[];
46 extern const char kOnclickDisallowedError[];
47 extern const char kParentsMustBeNormalError[];
48 extern const char kTitleNeededError[];
49 
50 std::string GetIDString(const MenuItem::Id& id);
51 
52 MenuItem* GetParent(MenuItem::Id parent_id,
53                     const MenuManager* menu_manager,
54                     std::string* error);
55 
56 template<typename PropertyWithEnumT>
GetContexts(const PropertyWithEnumT & property)57 MenuItem::ContextList GetContexts(const PropertyWithEnumT& property) {
58   MenuItem::ContextList contexts;
59   for (size_t i = 0; i < property.contexts->size(); ++i) {
60     switch (property.contexts->at(i)) {
61       case PropertyWithEnumT::CONTEXTS_TYPE_ALL:
62         contexts.Add(extensions::MenuItem::ALL);
63         break;
64       case PropertyWithEnumT::CONTEXTS_TYPE_PAGE:
65         contexts.Add(extensions::MenuItem::PAGE);
66         break;
67       case PropertyWithEnumT::CONTEXTS_TYPE_SELECTION:
68         contexts.Add(extensions::MenuItem::SELECTION);
69         break;
70       case PropertyWithEnumT::CONTEXTS_TYPE_LINK:
71         contexts.Add(extensions::MenuItem::LINK);
72         break;
73       case PropertyWithEnumT::CONTEXTS_TYPE_EDITABLE:
74         contexts.Add(extensions::MenuItem::EDITABLE);
75         break;
76       case PropertyWithEnumT::CONTEXTS_TYPE_IMAGE:
77         contexts.Add(extensions::MenuItem::IMAGE);
78         break;
79       case PropertyWithEnumT::CONTEXTS_TYPE_VIDEO:
80         contexts.Add(extensions::MenuItem::VIDEO);
81         break;
82       case PropertyWithEnumT::CONTEXTS_TYPE_AUDIO:
83         contexts.Add(extensions::MenuItem::AUDIO);
84         break;
85       case PropertyWithEnumT::CONTEXTS_TYPE_FRAME:
86         contexts.Add(extensions::MenuItem::FRAME);
87         break;
88       case PropertyWithEnumT::CONTEXTS_TYPE_LAUNCHER:
89         // Not available for <webview>.
90         contexts.Add(extensions::MenuItem::LAUNCHER);
91         break;
92       case PropertyWithEnumT::CONTEXTS_TYPE_NONE:
93         NOTREACHED();
94     }
95   }
96   return contexts;
97 }
98 
99 template<typename PropertyWithEnumT>
GetType(const PropertyWithEnumT & property,MenuItem::Type default_type)100 MenuItem::Type GetType(const PropertyWithEnumT& property,
101                        MenuItem::Type default_type) {
102   switch (property.type) {
103     case PropertyWithEnumT::TYPE_NONE:
104       return default_type;
105     case PropertyWithEnumT::TYPE_NORMAL:
106       return extensions::MenuItem::NORMAL;
107     case PropertyWithEnumT::TYPE_CHECKBOX:
108       return extensions::MenuItem::CHECKBOX;
109     case PropertyWithEnumT::TYPE_RADIO:
110       return extensions::MenuItem::RADIO;
111     case PropertyWithEnumT::TYPE_SEPARATOR:
112       return extensions::MenuItem::SEPARATOR;
113   }
114   return extensions::MenuItem::NORMAL;
115 }
116 
117 // Creates and adds a menu item from |create_properties|.
118 template<typename PropertyWithEnumT>
CreateMenuItem(const PropertyWithEnumT & create_properties,Profile * profile,const Extension * extension,const MenuItem::Id & item_id,std::string * error)119 bool CreateMenuItem(const PropertyWithEnumT& create_properties,
120                     Profile* profile,
121                     const Extension* extension,
122                     const MenuItem::Id& item_id,
123                     std::string* error) {
124   bool is_webview = item_id.extension_key.webview_instance_id != 0;
125   MenuManager* menu_manager = MenuManager::Get(profile);
126 
127   if (menu_manager->GetItemById(item_id)) {
128     *error = ErrorUtils::FormatErrorMessage(kDuplicateIDError,
129                                             GetIDString(item_id));
130     return false;
131   }
132 
133   if (!is_webview && BackgroundInfo::HasLazyBackgroundPage(extension) &&
134       create_properties.onclick.get()) {
135     *error = kOnclickDisallowedError;
136     return false;
137   }
138 
139   // Contexts.
140   MenuItem::ContextList contexts;
141   if (create_properties.contexts.get())
142     contexts = GetContexts(create_properties);
143   else
144     contexts.Add(MenuItem::PAGE);
145 
146   if (contexts.Contains(MenuItem::LAUNCHER)) {
147     // Launcher item is not allowed for <webview>.
148     if (!extension->is_platform_app() || is_webview) {
149       *error = kLauncherNotAllowedError;
150       return false;
151     }
152   }
153 
154   // Title.
155   std::string title;
156   if (create_properties.title.get())
157     title = *create_properties.title;
158 
159   MenuItem::Type type = GetType(create_properties, MenuItem::NORMAL);
160   if (title.empty() && type != MenuItem::SEPARATOR) {
161     *error = kTitleNeededError;
162     return false;
163   }
164 
165   // Checked state.
166   bool checked = false;
167   if (create_properties.checked.get())
168     checked = *create_properties.checked;
169 
170   // Enabled.
171   bool enabled = true;
172   if (create_properties.enabled.get())
173     enabled = *create_properties.enabled;
174 
175   scoped_ptr<MenuItem> item(
176       new MenuItem(item_id, title, checked, enabled, type, contexts));
177 
178   // URL Patterns.
179   if (!item->PopulateURLPatterns(
180           create_properties.document_url_patterns.get(),
181           create_properties.target_url_patterns.get(),
182           error)) {
183     return false;
184   }
185 
186   // Parent id.
187   bool success = true;
188   scoped_ptr<MenuItem::Id> parent_id(GetParentId(
189       create_properties, profile->IsOffTheRecord(), item_id.extension_key));
190   if (parent_id.get()) {
191     MenuItem* parent = GetParent(*parent_id, menu_manager, error);
192     if (!parent)
193       return false;
194     success = menu_manager->AddChildItem(parent->id(), item.release());
195   } else {
196     success = menu_manager->AddContextItem(extension, item.release());
197   }
198 
199   if (!success)
200     return false;
201 
202   menu_manager->WriteToStorage(extension, item_id.extension_key);
203   return true;
204 }
205 
206 // Updates a menu item from |update_properties|.
207 template<typename PropertyWithEnumT>
UpdateMenuItem(const PropertyWithEnumT & update_properties,Profile * profile,const Extension * extension,const MenuItem::Id & item_id,std::string * error)208 bool UpdateMenuItem(const PropertyWithEnumT& update_properties,
209                     Profile* profile,
210                     const Extension* extension,
211                     const MenuItem::Id& item_id,
212                     std::string* error) {
213   bool radio_item_updated = false;
214   bool is_webview = item_id.extension_key.webview_instance_id != 0;
215   MenuManager* menu_manager = MenuManager::Get(profile);
216 
217   MenuItem* item = menu_manager->GetItemById(item_id);
218   if (!item || item->extension_id() != extension->id()){
219     *error = ErrorUtils::FormatErrorMessage(
220         kCannotFindItemError, GetIDString(item_id));
221     return false;
222   }
223 
224   // Type.
225   MenuItem::Type type = GetType(update_properties, item->type());
226 
227   if (type != item->type()) {
228     if (type == MenuItem::RADIO || item->type() == MenuItem::RADIO)
229       radio_item_updated = true;
230     item->set_type(type);
231   }
232 
233   // Title.
234   if (update_properties.title.get()) {
235     std::string title(*update_properties.title);
236     if (title.empty() && item->type() != MenuItem::SEPARATOR) {
237       *error = kTitleNeededError;
238       return false;
239     }
240     item->set_title(title);
241   }
242 
243   // Checked state.
244   if (update_properties.checked.get()) {
245     bool checked = *update_properties.checked;
246     if (checked &&
247         item->type() != MenuItem::CHECKBOX &&
248         item->type() != MenuItem::RADIO) {
249       *error = kCheckedError;
250       return false;
251     }
252     if (checked != item->checked()) {
253       if (!item->SetChecked(checked)) {
254         *error = kCheckedError;
255         return false;
256       }
257       radio_item_updated = true;
258     }
259   }
260 
261   // Enabled.
262   if (update_properties.enabled.get())
263     item->set_enabled(*update_properties.enabled);
264 
265   // Contexts.
266   MenuItem::ContextList contexts;
267   if (update_properties.contexts.get()) {
268     contexts = GetContexts(update_properties);
269 
270     if (contexts.Contains(MenuItem::LAUNCHER)) {
271       // Launcher item is not allowed for <webview>.
272       if (!extension->is_platform_app() || is_webview) {
273         *error = kLauncherNotAllowedError;
274         return false;
275       }
276     }
277 
278     if (contexts != item->contexts())
279       item->set_contexts(contexts);
280   }
281 
282   // Parent id.
283   MenuItem* parent = NULL;
284   scoped_ptr<MenuItem::Id> parent_id(GetParentId(
285       update_properties, profile->IsOffTheRecord(), item_id.extension_key));
286   if (parent_id.get()) {
287     MenuItem* parent = GetParent(*parent_id, menu_manager, error);
288     if (!parent || !menu_manager->ChangeParent(item->id(), &parent->id()))
289       return false;
290   }
291 
292   // URL Patterns.
293   if (!item->PopulateURLPatterns(
294           update_properties.document_url_patterns.get(),
295           update_properties.target_url_patterns.get(), error)) {
296     return false;
297   }
298 
299   // There is no need to call ItemUpdated if ChangeParent is called because
300   // all sanitation is taken care of in ChangeParent.
301   if (!parent && radio_item_updated && !menu_manager->ItemUpdated(item->id()))
302     return false;
303 
304   menu_manager->WriteToStorage(extension, item_id.extension_key);
305   return true;
306 }
307 
308 }  // namespace context_menus_api_helpers
309 }  // namespace extensions
310 
311 #endif  // CHROME_BROWSER_EXTENSIONS_API_CONTEXT_MENUS_CONTEXT_MENUS_API_HELPERS_H_
312