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

Side by Side Diff: chrome/browser/extensions/api/context_menus/context_menus_api.cc

Issue 11778097: Revert revision 176015 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 7 years, 11 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 | Annotate | Revision Log
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/api/context_menus/context_menus_api.h"
6
7 #include <string>
8
9 #include "base/string_number_conversions.h"
10 #include "base/string_util.h"
11 #include "base/values.h"
12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/extensions/menu_manager.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/common/extensions/api/context_menus.h"
16 #include "extensions/common/error_utils.h"
17 #include "extensions/common/url_pattern_set.h"
18
19 using extensions::ErrorUtils;
20
21 namespace {
22
23 const char kGeneratedIdKey[] = "generatedId";
24
25 const char kCannotFindItemError[] = "Cannot find menu item with id *";
26 const char kOnclickDisallowedError[] = "Extensions using event pages cannot "
27 "pass an onclick parameter to chrome.contextMenus.create. Instead, use "
28 "the chrome.contextMenus.onClicked event.";
29 const char kCheckedError[] =
30 "Only items with type \"radio\" or \"checkbox\" can be checked";
31 const char kDuplicateIDError[] =
32 "Cannot create item with duplicate id *";
33 const char kIdRequiredError[] = "Extensions using event pages must pass an "
34 "id parameter to chrome.contextMenus.create";
35 const char kParentsMustBeNormalError[] =
36 "Parent items must have type \"normal\"";
37 const char kTitleNeededError[] =
38 "All menu items except for separators must have a title";
39 const char kLauncherNotAllowedError[] =
40 "Only packaged apps are allowed to use 'launcher' context";
41
42 std::string GetIDString(const extensions::MenuItem::Id& id) {
43 if (id.uid == 0)
44 return id.string_uid;
45 else
46 return base::IntToString(id.uid);
47 }
48
49 template<typename PropertyWithEnumT>
50 extensions::MenuItem::ContextList GetContexts(
51 const PropertyWithEnumT& property) {
52 extensions::MenuItem::ContextList contexts;
53 for (size_t i = 0; i < property.contexts->size(); ++i) {
54 switch (property.contexts->at(i)) {
55 case PropertyWithEnumT::CONTEXTS_ELEMENT_ALL:
56 contexts.Add(extensions::MenuItem::ALL);
57 break;
58 case PropertyWithEnumT::CONTEXTS_ELEMENT_PAGE:
59 contexts.Add(extensions::MenuItem::PAGE);
60 break;
61 case PropertyWithEnumT::CONTEXTS_ELEMENT_SELECTION:
62 contexts.Add(extensions::MenuItem::SELECTION);
63 break;
64 case PropertyWithEnumT::CONTEXTS_ELEMENT_LINK:
65 contexts.Add(extensions::MenuItem::LINK);
66 break;
67 case PropertyWithEnumT::CONTEXTS_ELEMENT_EDITABLE:
68 contexts.Add(extensions::MenuItem::EDITABLE);
69 break;
70 case PropertyWithEnumT::CONTEXTS_ELEMENT_IMAGE:
71 contexts.Add(extensions::MenuItem::IMAGE);
72 break;
73 case PropertyWithEnumT::CONTEXTS_ELEMENT_VIDEO:
74 contexts.Add(extensions::MenuItem::VIDEO);
75 break;
76 case PropertyWithEnumT::CONTEXTS_ELEMENT_AUDIO:
77 contexts.Add(extensions::MenuItem::AUDIO);
78 break;
79 case PropertyWithEnumT::CONTEXTS_ELEMENT_FRAME:
80 contexts.Add(extensions::MenuItem::FRAME);
81 break;
82 case PropertyWithEnumT::CONTEXTS_ELEMENT_LAUNCHER:
83 contexts.Add(extensions::MenuItem::LAUNCHER);
84 break;
85 case PropertyWithEnumT::CONTEXTS_ELEMENT_NONE:
86 NOTREACHED();
87 }
88 }
89 return contexts;
90 }
91
92 template<typename PropertyWithEnumT>
93 extensions::MenuItem::Type GetType(const PropertyWithEnumT& property) {
94 switch (property.type) {
95 case PropertyWithEnumT::TYPE_NONE:
96 case PropertyWithEnumT::TYPE_NORMAL:
97 return extensions::MenuItem::NORMAL;
98 case PropertyWithEnumT::TYPE_CHECKBOX:
99 return extensions::MenuItem::CHECKBOX;
100 case PropertyWithEnumT::TYPE_RADIO:
101 return extensions::MenuItem::RADIO;
102 case PropertyWithEnumT::TYPE_SEPARATOR:
103 return extensions::MenuItem::SEPARATOR;
104 }
105 return extensions::MenuItem::NORMAL;
106 }
107
108 template<typename PropertyWithEnumT>
109 scoped_ptr<extensions::MenuItem::Id> GetParentId(
110 const PropertyWithEnumT& property,
111 bool is_off_the_record,
112 std::string extension_id) {
113 scoped_ptr<extensions::MenuItem::Id> parent_id(
114 new extensions::MenuItem::Id(is_off_the_record, extension_id));
115 switch (property.parent_id_type) {
116 case PropertyWithEnumT::PARENT_ID_NONE:
117 return scoped_ptr<extensions::MenuItem::Id>().Pass();
118 case PropertyWithEnumT::PARENT_ID_INTEGER:
119 parent_id->uid = *property.parent_id_integer;
120 break;
121 case PropertyWithEnumT::PARENT_ID_STRING:
122 parent_id->string_uid = *property.parent_id_string;
123 break;
124 }
125 return parent_id.Pass();
126 }
127
128 extensions::MenuItem* GetParent(extensions::MenuItem::Id parent_id,
129 const extensions::MenuManager* menu_manager,
130 std::string* error) {
131 extensions::MenuItem* parent = menu_manager->GetItemById(parent_id);
132 if (!parent) {
133 *error = ErrorUtils::FormatErrorMessage(
134 kCannotFindItemError, GetIDString(parent_id));
135 return NULL;
136 }
137 if (parent->type() != extensions::MenuItem::NORMAL) {
138 *error = kParentsMustBeNormalError;
139 return NULL;
140 }
141
142 return parent;
143 }
144
145 } // namespace
146
147 namespace extensions {
148
149 namespace Create = api::context_menus::Create;
150 namespace Remove = api::context_menus::Remove;
151 namespace Update = api::context_menus::Update;
152
153 bool ContextMenusCreateFunction::RunImpl() {
154 MenuItem::Id id(profile()->IsOffTheRecord(), extension_id());
155 scoped_ptr<Create::Params> params(Create::Params::Create(*args_));
156 EXTENSION_FUNCTION_VALIDATE(params.get());
157
158 if (params->create_properties.id.get()) {
159 id.string_uid = *params->create_properties.id;
160 } else {
161 if (GetExtension()->has_lazy_background_page()) {
162 error_ = kIdRequiredError;
163 return false;
164 }
165
166 // The Generated Id is added by context_menus_custom_bindings.js.
167 DictionaryValue* properties = NULL;
168 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &properties));
169 EXTENSION_FUNCTION_VALIDATE(properties->GetInteger(kGeneratedIdKey,
170 &id.uid));
171 }
172
173 std::string title;
174 if (params->create_properties.title.get())
175 title = *params->create_properties.title;
176
177 MenuManager* menu_manager = profile()->GetExtensionService()->menu_manager();
178
179 if (menu_manager->GetItemById(id)) {
180 error_ = ErrorUtils::FormatErrorMessage(kDuplicateIDError,
181 GetIDString(id));
182 return false;
183 }
184
185 if (GetExtension()->has_lazy_background_page() &&
186 params->create_properties.onclick.get()) {
187 error_ = kOnclickDisallowedError;
188 return false;
189 }
190
191 MenuItem::ContextList contexts;
192 if (params->create_properties.contexts.get())
193 contexts = GetContexts(params->create_properties);
194 else
195 contexts.Add(MenuItem::PAGE);
196
197 if (contexts.Contains(MenuItem::LAUNCHER) &&
198 !GetExtension()->is_platform_app()) {
199 error_ = kLauncherNotAllowedError;
200 return false;
201 }
202
203 MenuItem::Type type = GetType(params->create_properties);
204
205 if (title.empty() && type != MenuItem::SEPARATOR) {
206 error_ = kTitleNeededError;
207 return false;
208 }
209
210 bool checked = false;
211 if (params->create_properties.checked.get())
212 checked = *params->create_properties.checked;
213
214 bool enabled = true;
215 if (params->create_properties.enabled.get())
216 enabled = *params->create_properties.enabled;
217
218 scoped_ptr<MenuItem> item(
219 new MenuItem(id, title, checked, enabled, type, contexts));
220
221 if (!item->PopulateURLPatterns(
222 params->create_properties.document_url_patterns.get(),
223 params->create_properties.target_url_patterns.get(),
224 &error_)) {
225 return false;
226 }
227
228 bool success = true;
229 scoped_ptr<MenuItem::Id> parent_id(GetParentId(params->create_properties,
230 profile()->IsOffTheRecord(),
231 extension_id()));
232 if (parent_id.get()) {
233 MenuItem* parent = GetParent(*parent_id, menu_manager, &error_);
234 if (!parent)
235 return false;
236 success = menu_manager->AddChildItem(parent->id(), item.release());
237 } else {
238 success = menu_manager->AddContextItem(GetExtension(), item.release());
239 }
240
241 if (!success)
242 return false;
243
244 menu_manager->WriteToStorage(GetExtension());
245 return true;
246 }
247
248 bool ContextMenusUpdateFunction::RunImpl() {
249 bool radio_item_updated = false;
250 MenuItem::Id item_id(profile()->IsOffTheRecord(), extension_id());
251 scoped_ptr<Update::Params> params(Update::Params::Create(*args_));
252
253 EXTENSION_FUNCTION_VALIDATE(params.get());
254 switch (params->id_type) {
255 case Update::Params::ID_STRING:
256 item_id.string_uid = *params->id_string;
257 break;
258 case Update::Params::ID_INTEGER:
259 item_id.uid = *params->id_integer;
260 break;
261 case Update::Params::ID_NONE:
262 NOTREACHED();
263 }
264
265 ExtensionService* service = profile()->GetExtensionService();
266 MenuManager* manager = service->menu_manager();
267 MenuItem* item = manager->GetItemById(item_id);
268 if (!item || item->extension_id() != extension_id()) {
269 error_ = ErrorUtils::FormatErrorMessage(
270 kCannotFindItemError, GetIDString(item_id));
271 return false;
272 }
273
274 // Type.
275 MenuItem::Type type = GetType(params->update_properties);
276
277 if (type != item->type()) {
278 if (type == MenuItem::RADIO || item->type() == MenuItem::RADIO)
279 radio_item_updated = true;
280 item->set_type(type);
281 }
282
283 // Title.
284 if (params->update_properties.title.get()) {
285 std::string title(*params->update_properties.title);
286 if (title.empty() && item->type() != MenuItem::SEPARATOR) {
287 error_ = kTitleNeededError;
288 return false;
289 }
290 item->set_title(title);
291 }
292
293 // Checked state.
294 if (params->update_properties.checked.get()) {
295 bool checked = *params->update_properties.checked;
296 if (checked &&
297 item->type() != MenuItem::CHECKBOX &&
298 item->type() != MenuItem::RADIO) {
299 error_ = kCheckedError;
300 return false;
301 }
302 if (checked != item->checked()) {
303 if (!item->SetChecked(checked)) {
304 error_ = kCheckedError;
305 return false;
306 }
307 radio_item_updated = true;
308 }
309 }
310
311 // Enabled.
312 if (params->update_properties.enabled.get())
313 item->set_enabled(*params->update_properties.enabled);
314
315 // Contexts.
316 MenuItem::ContextList contexts;
317 if (params->update_properties.contexts.get()) {
318 contexts = GetContexts(params->update_properties);
319
320 if (contexts.Contains(MenuItem::LAUNCHER) &&
321 !GetExtension()->is_platform_app()) {
322 error_ = kLauncherNotAllowedError;
323 return false;
324 }
325
326 if (contexts != item->contexts())
327 item->set_contexts(contexts);
328 }
329
330 // Parent id.
331 MenuItem* parent = NULL;
332 scoped_ptr<MenuItem::Id> parent_id(GetParentId(params->update_properties,
333 profile()->IsOffTheRecord(),
334 extension_id()));
335 if (parent_id.get()) {
336 MenuItem* parent = GetParent(*parent_id, manager, &error_);
337 if (!parent || !manager->ChangeParent(item->id(), &parent->id()))
338 return false;
339 }
340
341 // URL Patterns.
342 if (!item->PopulateURLPatterns(
343 params->update_properties.document_url_patterns.get(),
344 params->update_properties.target_url_patterns.get(), &error_)) {
345 return false;
346 }
347
348 // There is no need to call ItemUpdated if ChangeParent is called because
349 // all sanitation is taken care of in ChangeParent.
350 if (!parent && radio_item_updated && !manager->ItemUpdated(item->id()))
351 return false;
352
353 manager->WriteToStorage(GetExtension());
354 return true;
355 }
356
357 bool ContextMenusRemoveFunction::RunImpl() {
358 scoped_ptr<Remove::Params> params(Remove::Params::Create(*args_));
359 EXTENSION_FUNCTION_VALIDATE(params.get());
360
361 ExtensionService* service = profile()->GetExtensionService();
362 MenuManager* manager = service->menu_manager();
363
364 MenuItem::Id id(profile()->IsOffTheRecord(), extension_id());
365 switch (params->menu_item_id_type) {
366 case Remove::Params::MENU_ITEM_ID_STRING:
367 id.string_uid = *params->menu_item_id_string;
368 break;
369 case Remove::Params::MENU_ITEM_ID_INTEGER:
370 id.uid = *params->menu_item_id_integer;
371 break;
372 case Remove::Params::MENU_ITEM_ID_NONE:
373 NOTREACHED();
374 }
375
376 MenuItem* item = manager->GetItemById(id);
377 // Ensure one extension can't remove another's menu items.
378 if (!item || item->extension_id() != extension_id()) {
379 error_ = ErrorUtils::FormatErrorMessage(
380 kCannotFindItemError, GetIDString(id));
381 return false;
382 }
383
384 if (!manager->RemoveContextMenuItem(id))
385 return false;
386 manager->WriteToStorage(GetExtension());
387 return true;
388 }
389
390 bool ContextMenusRemoveAllFunction::RunImpl() {
391 ExtensionService* service = profile()->GetExtensionService();
392 MenuManager* manager = service->menu_manager();
393 manager->RemoveAllContextItems(GetExtension()->id());
394 manager->WriteToStorage(GetExtension());
395 return true;
396 }
397
398 } // namespace extensions
OLDNEW
« no previous file with comments | « chrome/browser/extensions/api/context_menus/context_menus_api.h ('k') | chrome/browser/extensions/api/cookies/cookies_api.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698