OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/strings/utf_string_conversions.h" | 5 #include "base/strings/utf_string_conversions.h" |
6 #include "chrome/app/chrome_command_ids.h" | 6 #include "chrome/app/chrome_command_ids.h" |
7 #include "chrome/browser/extensions/context_menu_matcher.h" | 7 #include "chrome/browser/extensions/context_menu_matcher.h" |
8 #include "chrome/browser/extensions/extension_service.h" | 8 #include "chrome/browser/extensions/extension_service.h" |
9 #include "chrome/browser/extensions/extension_util.h" | 9 #include "chrome/browser/extensions/extension_util.h" |
10 #include "chrome/browser/profiles/profile.h" | 10 #include "chrome/browser/profiles/profile.h" |
11 #include "chrome/common/extensions/api/context_menus.h" | |
11 #include "content/public/common/context_menu_params.h" | 12 #include "content/public/common/context_menu_params.h" |
12 #include "extensions/browser/extension_system.h" | 13 #include "extensions/browser/extension_system.h" |
13 #include "ui/gfx/favicon_size.h" | 14 #include "ui/gfx/favicon_size.h" |
14 #include "ui/gfx/image/image.h" | 15 #include "ui/gfx/image/image.h" |
15 | 16 |
16 namespace extensions { | 17 namespace extensions { |
17 | 18 |
18 // static | 19 // static |
19 const size_t ContextMenuMatcher::kMaxExtensionItemTitleLength = 75; | 20 const size_t ContextMenuMatcher::kMaxExtensionItemTitleLength = 75; |
20 | 21 |
21 ContextMenuMatcher::ContextMenuMatcher( | 22 ContextMenuMatcher::ContextMenuMatcher( |
22 Profile* profile, | 23 Profile* profile, |
23 ui::SimpleMenuModel::Delegate* delegate, | 24 ui::SimpleMenuModel::Delegate* delegate, |
24 ui::SimpleMenuModel* menu_model, | 25 ui::SimpleMenuModel* menu_model, |
25 const base::Callback<bool(const MenuItem*)>& filter) | 26 const base::Callback<bool(const MenuItem*)>& filter) |
26 : profile_(profile), menu_model_(menu_model), delegate_(delegate), | 27 : profile_(profile), menu_model_(menu_model), delegate_(delegate), |
27 filter_(filter) { | 28 filter_(filter) { |
28 } | 29 } |
29 | 30 |
30 void ContextMenuMatcher::AppendExtensionItems( | 31 void ContextMenuMatcher::AppendExtensionItems( |
31 const MenuItem::ExtensionKey& extension_key, | 32 const MenuItem::ExtensionKey& extension_key, |
32 const base::string16& selection_text, | 33 const base::string16& selection_text, |
33 int* index) { | 34 int* index, |
35 bool is_action_menu) { | |
34 DCHECK_GE(*index, 0); | 36 DCHECK_GE(*index, 0); |
35 int max_index = | 37 int max_index = |
36 IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST - IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; | 38 IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST - IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; |
37 if (*index >= max_index) | 39 if (*index >= max_index) |
38 return; | 40 return; |
39 | 41 |
40 const Extension* extension = NULL; | 42 const Extension* extension = NULL; |
41 MenuItem::List items; | 43 MenuItem::List items; |
42 bool can_cross_incognito; | 44 bool can_cross_incognito; |
43 if (!GetRelevantExtensionTopLevelItems( | 45 if (!GetRelevantExtensionTopLevelItems( |
44 extension_key, &extension, &can_cross_incognito, items)) | 46 extension_key, &extension, &can_cross_incognito, items)) |
45 return; | 47 return; |
46 | 48 |
47 if (items.empty()) | 49 if (items.empty()) |
48 return; | 50 return; |
49 | 51 |
50 // If this is the first extension-provided menu item, and there are other | 52 // If this is the first extension-provided menu item, and there are other |
51 // items in the menu, and the last item is not a separator add a separator. | 53 // items in the menu, and the last item is not a separator add a separator. |
52 if (*index == 0 && menu_model_->GetItemCount()) | 54 if (*index == 0 && menu_model_->GetItemCount()) |
53 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); | 55 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); |
54 | 56 |
55 // Extensions (other than platform apps) are only allowed one top-level slot | 57 // Extensions (other than platform apps) are only allowed one top-level slot |
56 // (and it can't be a radio or checkbox item because we are going to put the | 58 // (and it can't be a radio or checkbox item because we are going to put the |
57 // extension icon next to it). | 59 // extension icon next to it), unless the context menu is an an action menu. |
58 // If they have more than that, we automatically push them into a submenu. | 60 // Action menus do not include the extension action, and they only include |
59 if (extension->is_platform_app()) { | 61 // items from one extension, so they are not placed within a submenu. |
60 RecursivelyAppendExtensionItems(items, can_cross_incognito, selection_text, | 62 // Otherwise, we automatically push them into a submenu if there is more than |
61 menu_model_, index); | 63 // one top-level item. |
64 if (extension->is_platform_app() || is_action_menu) { | |
65 RecursivelyAppendExtensionItems(items, | |
66 can_cross_incognito, | |
67 selection_text, | |
68 menu_model_, | |
69 index, | |
70 is_action_menu); | |
62 } else { | 71 } else { |
63 int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; | 72 int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; |
64 base::string16 title; | 73 base::string16 title; |
65 MenuItem::List submenu_items; | 74 MenuItem::List submenu_items; |
66 | 75 |
67 if (items.size() > 1 || items[0]->type() != MenuItem::NORMAL) { | 76 if (items.size() > 1 || items[0]->type() != MenuItem::NORMAL) { |
68 title = base::UTF8ToUTF16(extension->name()); | 77 title = base::UTF8ToUTF16(extension->name()); |
69 submenu_items = items; | 78 submenu_items = items; |
70 } else { | 79 } else { |
71 MenuItem* item = items[0]; | 80 MenuItem* item = items[0]; |
72 extension_item_map_[menu_id] = item->id(); | 81 extension_item_map_[menu_id] = item->id(); |
73 title = item->TitleWithReplacement(selection_text, | 82 title = item->TitleWithReplacement(selection_text, |
74 kMaxExtensionItemTitleLength); | 83 kMaxExtensionItemTitleLength); |
75 submenu_items = GetRelevantExtensionItems(item->children(), | 84 submenu_items = GetRelevantExtensionItems(item->children(), |
76 can_cross_incognito); | 85 can_cross_incognito); |
77 } | 86 } |
78 | 87 |
79 // Now add our item(s) to the menu_model_. | 88 // Now add our item(s) to the menu_model_. |
80 if (submenu_items.empty()) { | 89 if (submenu_items.empty()) { |
81 menu_model_->AddItem(menu_id, title); | 90 menu_model_->AddItem(menu_id, title); |
82 } else { | 91 } else { |
83 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate_); | 92 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate_); |
84 extension_menu_models_.push_back(submenu); | 93 extension_menu_models_.push_back(submenu); |
85 menu_model_->AddSubMenu(menu_id, title, submenu); | 94 menu_model_->AddSubMenu(menu_id, title, submenu); |
86 RecursivelyAppendExtensionItems(submenu_items, can_cross_incognito, | 95 RecursivelyAppendExtensionItems(submenu_items, |
87 selection_text, submenu, index); | 96 can_cross_incognito, |
97 selection_text, | |
98 submenu, | |
99 index, | |
100 false); // is_action_menu_top_level | |
88 } | 101 } |
89 SetExtensionIcon(extension_key.extension_id); | 102 if (!is_action_menu) |
103 SetExtensionIcon(extension_key.extension_id); | |
90 } | 104 } |
91 } | 105 } |
92 | 106 |
93 void ContextMenuMatcher::Clear() { | 107 void ContextMenuMatcher::Clear() { |
94 extension_item_map_.clear(); | 108 extension_item_map_.clear(); |
95 extension_menu_models_.clear(); | 109 extension_menu_models_.clear(); |
96 } | 110 } |
97 | 111 |
98 base::string16 ContextMenuMatcher::GetTopLevelContextMenuTitle( | 112 base::string16 ContextMenuMatcher::GetTopLevelContextMenuTitle( |
99 const MenuItem::ExtensionKey& extension_key, | 113 const MenuItem::ExtensionKey& extension_key, |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
184 result.push_back(*i); | 198 result.push_back(*i); |
185 } | 199 } |
186 return result; | 200 return result; |
187 } | 201 } |
188 | 202 |
189 void ContextMenuMatcher::RecursivelyAppendExtensionItems( | 203 void ContextMenuMatcher::RecursivelyAppendExtensionItems( |
190 const MenuItem::List& items, | 204 const MenuItem::List& items, |
191 bool can_cross_incognito, | 205 bool can_cross_incognito, |
192 const base::string16& selection_text, | 206 const base::string16& selection_text, |
193 ui::SimpleMenuModel* menu_model, | 207 ui::SimpleMenuModel* menu_model, |
194 int* index) | 208 int* index, |
195 { | 209 bool is_action_menu_top_level) { |
196 MenuItem::Type last_type = MenuItem::NORMAL; | 210 MenuItem::Type last_type = MenuItem::NORMAL; |
197 int radio_group_id = 1; | 211 int radio_group_id = 1; |
212 int num_items = 0; | |
198 | 213 |
199 for (MenuItem::List::const_iterator i = items.begin(); | 214 for (MenuItem::List::const_iterator i = items.begin(); |
200 i != items.end(); ++i) { | 215 i != items.end(); ++i) { |
201 MenuItem* item = *i; | 216 MenuItem* item = *i; |
202 | 217 |
203 // If last item was of type radio but the current one isn't, auto-insert | 218 // If last item was of type radio but the current one isn't, auto-insert |
204 // a separator. The converse case is handled below. | 219 // a separator. The converse case is handled below. |
205 if (last_type == MenuItem::RADIO && | 220 if (last_type == MenuItem::RADIO && |
206 item->type() != MenuItem::RADIO) { | 221 item->type() != MenuItem::RADIO) { |
207 menu_model->AddSeparator(ui::NORMAL_SEPARATOR); | 222 menu_model->AddSeparator(ui::NORMAL_SEPARATOR); |
208 last_type = MenuItem::SEPARATOR; | 223 last_type = MenuItem::SEPARATOR; |
209 } | 224 } |
210 | 225 |
211 int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; | 226 int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + *index; |
212 if (menu_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) | 227 ++num_items; |
228 // Action context menus have a limit for top level extension items to | |
229 // prevent control items from being pushed off the screen, since extension | |
230 // items will not be placed in a submenu. | |
231 if (menu_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST || | |
232 (is_action_menu_top_level && | |
233 num_items > api::context_menus::ACTION_MENU_TOP_LEVEL_LIMIT)) | |
213 return; | 234 return; |
235 (*index)++; | |
Devlin
2014/07/29 20:49:45
nit: ++(*index);
gpdavis
2014/07/30 20:53:02
Done.
| |
214 extension_item_map_[menu_id] = item->id(); | 236 extension_item_map_[menu_id] = item->id(); |
215 base::string16 title = item->TitleWithReplacement(selection_text, | 237 base::string16 title = item->TitleWithReplacement(selection_text, |
216 kMaxExtensionItemTitleLength); | 238 kMaxExtensionItemTitleLength); |
217 if (item->type() == MenuItem::NORMAL) { | 239 if (item->type() == MenuItem::NORMAL) { |
218 MenuItem::List children = | 240 MenuItem::List children = |
219 GetRelevantExtensionItems(item->children(), can_cross_incognito); | 241 GetRelevantExtensionItems(item->children(), can_cross_incognito); |
220 if (children.empty()) { | 242 if (children.empty()) { |
221 menu_model->AddItem(menu_id, title); | 243 menu_model->AddItem(menu_id, title); |
222 } else { | 244 } else { |
223 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate_); | 245 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate_); |
224 extension_menu_models_.push_back(submenu); | 246 extension_menu_models_.push_back(submenu); |
225 menu_model->AddSubMenu(menu_id, title, submenu); | 247 menu_model->AddSubMenu(menu_id, title, submenu); |
226 RecursivelyAppendExtensionItems(children, can_cross_incognito, | 248 RecursivelyAppendExtensionItems(children, |
227 selection_text, submenu, index); | 249 can_cross_incognito, |
250 selection_text, | |
251 submenu, | |
252 index, | |
253 false); // is_action_menu_top_level | |
228 } | 254 } |
229 } else if (item->type() == MenuItem::CHECKBOX) { | 255 } else if (item->type() == MenuItem::CHECKBOX) { |
230 menu_model->AddCheckItem(menu_id, title); | 256 menu_model->AddCheckItem(menu_id, title); |
231 } else if (item->type() == MenuItem::RADIO) { | 257 } else if (item->type() == MenuItem::RADIO) { |
232 if (i != items.begin() && | 258 if (i != items.begin() && |
233 last_type != MenuItem::RADIO) { | 259 last_type != MenuItem::RADIO) { |
234 radio_group_id++; | 260 radio_group_id++; |
235 | 261 |
236 // Auto-append a separator if needed. | 262 // Auto-append a separator if needed. |
237 menu_model->AddSeparator(ui::NORMAL_SEPARATOR); | 263 menu_model->AddSeparator(ui::NORMAL_SEPARATOR); |
(...skipping 26 matching lines...) Expand all Loading... | |
264 DCHECK_GE(index, 0); | 290 DCHECK_GE(index, 0); |
265 | 291 |
266 const SkBitmap& icon = menu_manager->GetIconForExtension(extension_id); | 292 const SkBitmap& icon = menu_manager->GetIconForExtension(extension_id); |
267 DCHECK(icon.width() == gfx::kFaviconSize); | 293 DCHECK(icon.width() == gfx::kFaviconSize); |
268 DCHECK(icon.height() == gfx::kFaviconSize); | 294 DCHECK(icon.height() == gfx::kFaviconSize); |
269 | 295 |
270 menu_model_->SetIcon(index, gfx::Image::CreateFrom1xBitmap(icon)); | 296 menu_model_->SetIcon(index, gfx::Image::CreateFrom1xBitmap(icon)); |
271 } | 297 } |
272 | 298 |
273 } // namespace extensions | 299 } // namespace extensions |
OLD | NEW |