| OLD | NEW |
| (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 "base/utf_string_conversions.h" | |
| 6 #include "chrome/app/chrome_command_ids.h" | |
| 7 #include "chrome/browser/extensions/context_menu_matcher.h" | |
| 8 #include "chrome/browser/extensions/extension_service.h" | |
| 9 #include "chrome/browser/profiles/profile.h" | |
| 10 #include "content/public/common/context_menu_params.h" | |
| 11 #include "ui/gfx/favicon_size.h" | |
| 12 | |
| 13 namespace extensions { | |
| 14 | |
| 15 // static | |
| 16 const size_t ContextMenuMatcher::kMaxExtensionItemTitleLength = 75; | |
| 17 | |
| 18 ContextMenuMatcher::ContextMenuMatcher( | |
| 19 Profile* profile, | |
| 20 ui::SimpleMenuModel::Delegate* delegate, | |
| 21 ui::SimpleMenuModel* menu_model, | |
| 22 const base::Callback<bool(const MenuItem*)>& filter) | |
| 23 : profile_(profile), menu_model_(menu_model), delegate_(delegate), | |
| 24 filter_(filter) { | |
| 25 } | |
| 26 | |
| 27 void ContextMenuMatcher::AppendExtensionItems(const std::string& extension_id, | |
| 28 const string16& selection_text, | |
| 29 int* index) | |
| 30 { | |
| 31 ExtensionService* service = profile_->GetExtensionService(); | |
| 32 MenuManager* manager = service->menu_manager(); | |
| 33 const Extension* extension = service->GetExtensionById(extension_id, false); | |
| 34 DCHECK_GE(*index, 0); | |
| 35 int max_index = | |
| 36 IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST - IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; | |
| 37 if (!extension || *index >= max_index) | |
| 38 return; | |
| 39 | |
| 40 // Find matching items. | |
| 41 const MenuItem::List* all_items = manager->MenuItems(extension_id); | |
| 42 if (!all_items || all_items->empty()) | |
| 43 return; | |
| 44 bool can_cross_incognito = service->CanCrossIncognito(extension); | |
| 45 MenuItem::List items = GetRelevantExtensionItems(*all_items, | |
| 46 can_cross_incognito); | |
| 47 | |
| 48 if (items.empty()) | |
| 49 return; | |
| 50 | |
| 51 // If this is the first extension-provided menu item, and there are other | |
| 52 // items in the menu, and the last item is not a separator add a separator. | |
| 53 if (*index == 0 && menu_model_->GetItemCount() && | |
| 54 menu_model_->GetTypeAt(menu_model_->GetItemCount() - 1) != | |
| 55 ui::MenuModel::TYPE_SEPARATOR) | |
| 56 menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); | |
| 57 | |
| 58 // Extensions (other than platform apps) are only allowed one top-level slot | |
| 59 // (and it can't be a radio or checkbox item because we are going to put the | |
| 60 // extension icon next to it). | |
| 61 // If they have more than that, we automatically push them into a submenu. | |
| 62 if (extension->is_platform_app()) { | |
| 63 RecursivelyAppendExtensionItems(items, can_cross_incognito, selection_text, | |
| 64 menu_model_, index); | |
| 65 } else { | |
| 66 int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; | |
| 67 string16 title; | |
| 68 MenuItem::List submenu_items; | |
| 69 | |
| 70 if (items.size() > 1 || items[0]->type() != MenuItem::NORMAL) { | |
| 71 title = UTF8ToUTF16(extension->name()); | |
| 72 submenu_items = items; | |
| 73 } else { | |
| 74 MenuItem* item = items[0]; | |
| 75 extension_item_map_[menu_id] = item->id(); | |
| 76 title = item->TitleWithReplacement(selection_text, | |
| 77 kMaxExtensionItemTitleLength); | |
| 78 submenu_items = GetRelevantExtensionItems(item->children(), | |
| 79 can_cross_incognito); | |
| 80 } | |
| 81 | |
| 82 // Now add our item(s) to the menu_model_. | |
| 83 if (submenu_items.empty()) { | |
| 84 menu_model_->AddItem(menu_id, title); | |
| 85 } else { | |
| 86 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate_); | |
| 87 extension_menu_models_.push_back(submenu); | |
| 88 menu_model_->AddSubMenu(menu_id, title, submenu); | |
| 89 RecursivelyAppendExtensionItems(submenu_items, can_cross_incognito, | |
| 90 selection_text, submenu, index); | |
| 91 } | |
| 92 SetExtensionIcon(extension_id); | |
| 93 } | |
| 94 } | |
| 95 | |
| 96 void ContextMenuMatcher::Clear() { | |
| 97 extension_item_map_.clear(); | |
| 98 extension_menu_models_.clear(); | |
| 99 } | |
| 100 | |
| 101 bool ContextMenuMatcher::IsCommandIdChecked(int command_id) const { | |
| 102 MenuItem* item = GetExtensionMenuItem(command_id); | |
| 103 if (!item) | |
| 104 return false; | |
| 105 return item->checked(); | |
| 106 } | |
| 107 | |
| 108 bool ContextMenuMatcher::IsCommandIdEnabled(int command_id) const { | |
| 109 MenuItem* item = GetExtensionMenuItem(command_id); | |
| 110 if (!item) | |
| 111 return true; | |
| 112 return item->enabled(); | |
| 113 } | |
| 114 | |
| 115 void ContextMenuMatcher::ExecuteCommand(int command_id, | |
| 116 const content::ContextMenuParams& params) { | |
| 117 MenuManager* manager = profile_->GetExtensionService()->menu_manager(); | |
| 118 MenuItem* item = GetExtensionMenuItem(command_id); | |
| 119 if (!item) | |
| 120 return; | |
| 121 | |
| 122 manager->ExecuteCommand(profile_, NULL, params, item->id()); | |
| 123 } | |
| 124 | |
| 125 MenuItem::List ContextMenuMatcher::GetRelevantExtensionItems( | |
| 126 const MenuItem::List& items, | |
| 127 bool can_cross_incognito) { | |
| 128 MenuItem::List result; | |
| 129 for (MenuItem::List::const_iterator i = items.begin(); | |
| 130 i != items.end(); ++i) { | |
| 131 const MenuItem* item = *i; | |
| 132 | |
| 133 if (!filter_.Run(item)) | |
| 134 continue; | |
| 135 | |
| 136 if (item->id().incognito == profile_->IsOffTheRecord() || | |
| 137 can_cross_incognito) | |
| 138 result.push_back(*i); | |
| 139 } | |
| 140 return result; | |
| 141 } | |
| 142 | |
| 143 void ContextMenuMatcher::RecursivelyAppendExtensionItems( | |
| 144 const MenuItem::List& items, | |
| 145 bool can_cross_incognito, | |
| 146 const string16& selection_text, | |
| 147 ui::SimpleMenuModel* menu_model, | |
| 148 int* index) | |
| 149 { | |
| 150 MenuItem::Type last_type = MenuItem::NORMAL; | |
| 151 int radio_group_id = 1; | |
| 152 | |
| 153 for (MenuItem::List::const_iterator i = items.begin(); | |
| 154 i != items.end(); ++i) { | |
| 155 MenuItem* item = *i; | |
| 156 | |
| 157 // If last item was of type radio but the current one isn't, auto-insert | |
| 158 // a separator. The converse case is handled below. | |
| 159 if (last_type == MenuItem::RADIO && | |
| 160 item->type() != MenuItem::RADIO) { | |
| 161 menu_model->AddSeparator(ui::NORMAL_SEPARATOR); | |
| 162 last_type = MenuItem::SEPARATOR; | |
| 163 } | |
| 164 | |
| 165 int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; | |
| 166 if (menu_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) | |
| 167 return; | |
| 168 extension_item_map_[menu_id] = item->id(); | |
| 169 string16 title = item->TitleWithReplacement(selection_text, | |
| 170 kMaxExtensionItemTitleLength); | |
| 171 if (item->type() == MenuItem::NORMAL) { | |
| 172 MenuItem::List children = | |
| 173 GetRelevantExtensionItems(item->children(), can_cross_incognito); | |
| 174 if (children.empty()) { | |
| 175 menu_model->AddItem(menu_id, title); | |
| 176 } else { | |
| 177 ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate_); | |
| 178 extension_menu_models_.push_back(submenu); | |
| 179 menu_model->AddSubMenu(menu_id, title, submenu); | |
| 180 RecursivelyAppendExtensionItems(children, can_cross_incognito, | |
| 181 selection_text, submenu, index); | |
| 182 } | |
| 183 } else if (item->type() == MenuItem::CHECKBOX) { | |
| 184 menu_model->AddCheckItem(menu_id, title); | |
| 185 } else if (item->type() == MenuItem::RADIO) { | |
| 186 if (i != items.begin() && | |
| 187 last_type != MenuItem::RADIO) { | |
| 188 radio_group_id++; | |
| 189 | |
| 190 // Auto-append a separator if needed. | |
| 191 if (last_type != MenuItem::SEPARATOR) | |
| 192 menu_model->AddSeparator(ui::NORMAL_SEPARATOR); | |
| 193 } | |
| 194 | |
| 195 menu_model->AddRadioItem(menu_id, title, radio_group_id); | |
| 196 } else if (item->type() == MenuItem::SEPARATOR) { | |
| 197 if (i != items.begin() && last_type != MenuItem::SEPARATOR) { | |
| 198 menu_model->AddSeparator(ui::NORMAL_SEPARATOR); | |
| 199 } | |
| 200 } | |
| 201 last_type = item->type(); | |
| 202 } | |
| 203 } | |
| 204 | |
| 205 MenuItem* ContextMenuMatcher::GetExtensionMenuItem(int id) const { | |
| 206 MenuManager* manager = profile_->GetExtensionService()->menu_manager(); | |
| 207 std::map<int, MenuItem::Id>::const_iterator i = | |
| 208 extension_item_map_.find(id); | |
| 209 if (i != extension_item_map_.end()) { | |
| 210 MenuItem* item = manager->GetItemById(i->second); | |
| 211 if (item) | |
| 212 return item; | |
| 213 } | |
| 214 return NULL; | |
| 215 } | |
| 216 | |
| 217 void ContextMenuMatcher::SetExtensionIcon(const std::string& extension_id) { | |
| 218 ExtensionService* service = profile_->GetExtensionService(); | |
| 219 MenuManager* menu_manager = service->menu_manager(); | |
| 220 | |
| 221 int index = menu_model_->GetItemCount() - 1; | |
| 222 DCHECK_GE(index, 0); | |
| 223 | |
| 224 const SkBitmap& icon = menu_manager->GetIconForExtension(extension_id); | |
| 225 DCHECK(icon.width() == gfx::kFaviconSize); | |
| 226 DCHECK(icon.height() == gfx::kFaviconSize); | |
| 227 | |
| 228 menu_model_->SetIcon(index, gfx::Image(icon)); | |
| 229 } | |
| 230 | |
| 231 } // namespace extensions | |
| OLD | NEW |