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 "chrome/browser/extensions/context_menu_matcher.h" | 5 #include "chrome/browser/extensions/context_menu_matcher.h" |
6 | 6 |
7 #include "base/strings/utf_string_conversions.h" | 7 #include "base/strings/utf_string_conversions.h" |
8 #include "chrome/app/chrome_command_ids.h" | 8 #include "chrome/app/chrome_command_ids.h" |
9 #include "chrome/browser/extensions/extension_service.h" | 9 #include "chrome/browser/extensions/extension_service.h" |
10 #include "chrome/browser/extensions/extension_util.h" | 10 #include "chrome/browser/extensions/extension_util.h" |
11 #include "content/public/browser/browser_context.h" | 11 #include "content/public/browser/browser_context.h" |
12 #include "content/public/common/context_menu_params.h" | 12 #include "content/public/common/context_menu_params.h" |
13 #include "extensions/browser/extension_system.h" | 13 #include "extensions/browser/extension_system.h" |
14 #include "ui/gfx/favicon_size.h" | 14 #include "ui/gfx/favicon_size.h" |
15 #include "ui/gfx/image/image.h" | 15 #include "ui/gfx/image/image.h" |
16 | 16 |
17 #if defined(ENABLE_EXTENSIONS) | |
18 #include "chrome/common/extensions/api/context_menus.h" | |
19 #endif | |
20 | |
17 namespace extensions { | 21 namespace extensions { |
18 | 22 |
19 namespace { | 23 namespace { |
20 | 24 |
25 int GetActionMenuTopLevelLimit() { | |
gpdavis
2014/08/11 18:39:01
@kalman,
Is this what you had in mind when you su
| |
26 #if defined(ENABLE_EXTENSIONS) | |
27 return api::context_menus::ACTION_MENU_TOP_LEVEL_LIMIT; | |
28 #else | |
29 return 0; | |
30 #endif | |
31 } | |
32 | |
21 // The range of command IDs reserved for extension's custom menus. | 33 // The range of command IDs reserved for extension's custom menus. |
22 // TODO(oshima): These values will be injected by embedders. | 34 // TODO(oshima): These values will be injected by embedders. |
23 int extensions_context_custom_first = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; | 35 int extensions_context_custom_first = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; |
24 int extensions_context_custom_last = IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST; | 36 int extensions_context_custom_last = IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST; |
25 | 37 |
26 } // namespace | 38 } // namespace |
27 | 39 |
28 // static | 40 // static |
29 const size_t ContextMenuMatcher::kMaxExtensionItemTitleLength = 75; | 41 const size_t ContextMenuMatcher::kMaxExtensionItemTitleLength = 75; |
30 | 42 |
(...skipping 15 matching lines...) Expand all Loading... | |
46 const base::Callback<bool(const MenuItem*)>& filter) | 58 const base::Callback<bool(const MenuItem*)>& filter) |
47 : browser_context_(browser_context), | 59 : browser_context_(browser_context), |
48 menu_model_(menu_model), | 60 menu_model_(menu_model), |
49 delegate_(delegate), | 61 delegate_(delegate), |
50 filter_(filter) { | 62 filter_(filter) { |
51 } | 63 } |
52 | 64 |
53 void ContextMenuMatcher::AppendExtensionItems( | 65 void ContextMenuMatcher::AppendExtensionItems( |
54 const MenuItem::ExtensionKey& extension_key, | 66 const MenuItem::ExtensionKey& extension_key, |
55 const base::string16& selection_text, | 67 const base::string16& selection_text, |
56 int* index) { | 68 int* index, |
69 bool is_action_menu) { | |
57 DCHECK_GE(*index, 0); | 70 DCHECK_GE(*index, 0); |
58 int max_index = | 71 int max_index = |
59 extensions_context_custom_last - extensions_context_custom_first; | 72 extensions_context_custom_last - extensions_context_custom_first; |
60 if (*index >= max_index) | 73 if (*index >= max_index) |
61 return; | 74 return; |
62 | 75 |
63 const Extension* extension = NULL; | 76 const Extension* extension = NULL; |
64 MenuItem::List items; | 77 MenuItem::List items; |
65 bool can_cross_incognito; | 78 bool can_cross_incognito; |
66 if (!GetRelevantExtensionTopLevelItems( | 79 if (!GetRelevantExtensionTopLevelItems( |
67 extension_key, &extension, &can_cross_incognito, items)) | 80 extension_key, &extension, &can_cross_incognito, items)) |
68 return; | 81 return; |
69 | 82 |
70 if (items.empty()) | 83 if (items.empty()) |
71 return; | 84 return; |
72 | 85 |
73 // If this is the first extension-provided menu item, and there are other | 86 // If this is the first extension-provided menu item, and there are other |
74 // items in the menu, and the last item is not a separator add a separator. | 87 // items in the menu, and the last item is not a separator add a separator. |
75 if (*index == 0 && menu_model_->GetItemCount()) | 88 if (*index == 0 && menu_model_->GetItemCount()) |
76 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); | 89 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); |
77 | 90 |
78 // Extensions (other than platform apps) are only allowed one top-level slot | 91 // Extensions (other than platform apps) are only allowed one top-level slot |
79 // (and it can't be a radio or checkbox item because we are going to put the | 92 // (and it can't be a radio or checkbox item because we are going to put the |
80 // extension icon next to it). | 93 // extension icon next to it), unless the context menu is an an action menu. |
81 // If they have more than that, we automatically push them into a submenu. | 94 // Action menus do not include the extension action, and they only include |
82 if (extension->is_platform_app()) { | 95 // items from one extension, so they are not placed within a submenu. |
83 RecursivelyAppendExtensionItems(items, can_cross_incognito, selection_text, | 96 // Otherwise, we automatically push them into a submenu if there is more than |
84 menu_model_, index); | 97 // one top-level item. |
98 if (extension->is_platform_app() || is_action_menu) { | |
99 RecursivelyAppendExtensionItems(items, | |
100 can_cross_incognito, | |
101 selection_text, | |
102 menu_model_, | |
103 index, | |
104 is_action_menu); | |
85 } else { | 105 } else { |
86 int menu_id = ConvertToExtensionsCustomCommandId(*index); | 106 int menu_id = ConvertToExtensionsCustomCommandId(*index); |
87 (*index)++; | 107 (*index)++; |
88 base::string16 title; | 108 base::string16 title; |
89 MenuItem::List submenu_items; | 109 MenuItem::List submenu_items; |
90 | 110 |
91 if (items.size() > 1 || items[0]->type() != MenuItem::NORMAL) { | 111 if (items.size() > 1 || items[0]->type() != MenuItem::NORMAL) { |
92 title = base::UTF8ToUTF16(extension->name()); | 112 title = base::UTF8ToUTF16(extension->name()); |
93 submenu_items = items; | 113 submenu_items = items; |
94 } else { | 114 } else { |
95 MenuItem* item = items[0]; | 115 MenuItem* item = items[0]; |
96 extension_item_map_[menu_id] = item->id(); | 116 extension_item_map_[menu_id] = item->id(); |
97 title = item->TitleWithReplacement(selection_text, | 117 title = item->TitleWithReplacement(selection_text, |
98 kMaxExtensionItemTitleLength); | 118 kMaxExtensionItemTitleLength); |
99 submenu_items = GetRelevantExtensionItems(item->children(), | 119 submenu_items = GetRelevantExtensionItems(item->children(), |
100 can_cross_incognito); | 120 can_cross_incognito); |
101 } | 121 } |
102 | 122 |
103 // Now add our item(s) to the menu_model_. | 123 // Now add our item(s) to the menu_model_. |
104 if (submenu_items.empty()) { | 124 if (submenu_items.empty()) { |
105 menu_model_->AddItem(menu_id, title); | 125 menu_model_->AddItem(menu_id, title); |
106 } else { | 126 } else { |
107 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate_); | 127 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate_); |
108 extension_menu_models_.push_back(submenu); | 128 extension_menu_models_.push_back(submenu); |
109 menu_model_->AddSubMenu(menu_id, title, submenu); | 129 menu_model_->AddSubMenu(menu_id, title, submenu); |
110 RecursivelyAppendExtensionItems(submenu_items, can_cross_incognito, | 130 RecursivelyAppendExtensionItems(submenu_items, |
111 selection_text, submenu, index); | 131 can_cross_incognito, |
132 selection_text, | |
133 submenu, | |
134 index, | |
135 false); // is_action_menu_top_level | |
112 } | 136 } |
113 SetExtensionIcon(extension_key.extension_id); | 137 if (!is_action_menu) |
138 SetExtensionIcon(extension_key.extension_id); | |
114 } | 139 } |
115 } | 140 } |
116 | 141 |
117 void ContextMenuMatcher::Clear() { | 142 void ContextMenuMatcher::Clear() { |
118 extension_item_map_.clear(); | 143 extension_item_map_.clear(); |
119 extension_menu_models_.clear(); | 144 extension_menu_models_.clear(); |
120 } | 145 } |
121 | 146 |
122 base::string16 ContextMenuMatcher::GetTopLevelContextMenuTitle( | 147 base::string16 ContextMenuMatcher::GetTopLevelContextMenuTitle( |
123 const MenuItem::ExtensionKey& extension_key, | 148 const MenuItem::ExtensionKey& extension_key, |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
208 result.push_back(*i); | 233 result.push_back(*i); |
209 } | 234 } |
210 return result; | 235 return result; |
211 } | 236 } |
212 | 237 |
213 void ContextMenuMatcher::RecursivelyAppendExtensionItems( | 238 void ContextMenuMatcher::RecursivelyAppendExtensionItems( |
214 const MenuItem::List& items, | 239 const MenuItem::List& items, |
215 bool can_cross_incognito, | 240 bool can_cross_incognito, |
216 const base::string16& selection_text, | 241 const base::string16& selection_text, |
217 ui::SimpleMenuModel* menu_model, | 242 ui::SimpleMenuModel* menu_model, |
218 int* index) | 243 int* index, |
219 { | 244 bool is_action_menu_top_level) { |
220 MenuItem::Type last_type = MenuItem::NORMAL; | 245 MenuItem::Type last_type = MenuItem::NORMAL; |
221 int radio_group_id = 1; | 246 int radio_group_id = 1; |
247 int num_items = 0; | |
222 | 248 |
223 for (MenuItem::List::const_iterator i = items.begin(); | 249 for (MenuItem::List::const_iterator i = items.begin(); |
224 i != items.end(); ++i) { | 250 i != items.end(); ++i) { |
225 MenuItem* item = *i; | 251 MenuItem* item = *i; |
226 | 252 |
227 // If last item was of type radio but the current one isn't, auto-insert | 253 // If last item was of type radio but the current one isn't, auto-insert |
228 // a separator. The converse case is handled below. | 254 // a separator. The converse case is handled below. |
229 if (last_type == MenuItem::RADIO && | 255 if (last_type == MenuItem::RADIO && |
230 item->type() != MenuItem::RADIO) { | 256 item->type() != MenuItem::RADIO) { |
231 menu_model->AddSeparator(ui::NORMAL_SEPARATOR); | 257 menu_model->AddSeparator(ui::NORMAL_SEPARATOR); |
232 last_type = MenuItem::SEPARATOR; | 258 last_type = MenuItem::SEPARATOR; |
233 } | 259 } |
234 | 260 |
235 int menu_id = ConvertToExtensionsCustomCommandId(*index); | 261 int menu_id = ConvertToExtensionsCustomCommandId(*index); |
236 (*index)++; | 262 ++(*index); |
237 if (menu_id >= extensions_context_custom_last) | 263 ++num_items; |
264 // Action context menus have a limit for top level extension items to | |
265 // prevent control items from being pushed off the screen, since extension | |
266 // items will not be placed in a submenu. | |
267 if (menu_id >= extensions_context_custom_last || | |
268 (is_action_menu_top_level && num_items >= GetActionMenuTopLevelLimit())) | |
238 return; | 269 return; |
270 | |
239 extension_item_map_[menu_id] = item->id(); | 271 extension_item_map_[menu_id] = item->id(); |
240 base::string16 title = item->TitleWithReplacement(selection_text, | 272 base::string16 title = item->TitleWithReplacement(selection_text, |
241 kMaxExtensionItemTitleLength); | 273 kMaxExtensionItemTitleLength); |
242 if (item->type() == MenuItem::NORMAL) { | 274 if (item->type() == MenuItem::NORMAL) { |
243 MenuItem::List children = | 275 MenuItem::List children = |
244 GetRelevantExtensionItems(item->children(), can_cross_incognito); | 276 GetRelevantExtensionItems(item->children(), can_cross_incognito); |
245 if (children.empty()) { | 277 if (children.empty()) { |
246 menu_model->AddItem(menu_id, title); | 278 menu_model->AddItem(menu_id, title); |
247 } else { | 279 } else { |
248 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate_); | 280 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate_); |
249 extension_menu_models_.push_back(submenu); | 281 extension_menu_models_.push_back(submenu); |
250 menu_model->AddSubMenu(menu_id, title, submenu); | 282 menu_model->AddSubMenu(menu_id, title, submenu); |
251 RecursivelyAppendExtensionItems(children, can_cross_incognito, | 283 RecursivelyAppendExtensionItems(children, |
252 selection_text, submenu, index); | 284 can_cross_incognito, |
285 selection_text, | |
286 submenu, | |
287 index, | |
288 false); // is_action_menu_top_level | |
253 } | 289 } |
254 } else if (item->type() == MenuItem::CHECKBOX) { | 290 } else if (item->type() == MenuItem::CHECKBOX) { |
255 menu_model->AddCheckItem(menu_id, title); | 291 menu_model->AddCheckItem(menu_id, title); |
256 } else if (item->type() == MenuItem::RADIO) { | 292 } else if (item->type() == MenuItem::RADIO) { |
257 if (i != items.begin() && | 293 if (i != items.begin() && |
258 last_type != MenuItem::RADIO) { | 294 last_type != MenuItem::RADIO) { |
259 radio_group_id++; | 295 radio_group_id++; |
260 | 296 |
261 // Auto-append a separator if needed. | 297 // Auto-append a separator if needed. |
262 menu_model->AddSeparator(ui::NORMAL_SEPARATOR); | 298 menu_model->AddSeparator(ui::NORMAL_SEPARATOR); |
(...skipping 26 matching lines...) Expand all Loading... | |
289 DCHECK_GE(index, 0); | 325 DCHECK_GE(index, 0); |
290 | 326 |
291 const SkBitmap& icon = menu_manager->GetIconForExtension(extension_id); | 327 const SkBitmap& icon = menu_manager->GetIconForExtension(extension_id); |
292 DCHECK(icon.width() == gfx::kFaviconSize); | 328 DCHECK(icon.width() == gfx::kFaviconSize); |
293 DCHECK(icon.height() == gfx::kFaviconSize); | 329 DCHECK(icon.height() == gfx::kFaviconSize); |
294 | 330 |
295 menu_model_->SetIcon(index, gfx::Image::CreateFrom1xBitmap(icon)); | 331 menu_model_->SetIcon(index, gfx::Image::CreateFrom1xBitmap(icon)); |
296 } | 332 } |
297 | 333 |
298 } // namespace extensions | 334 } // namespace extensions |
OLD | NEW |