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