Update broken references to image assets
[chromium-blink-merge.git] / chrome / browser / extensions / extension_context_menu_model.cc
blob4c91406d675c8f3286b990c73927a5ac27a0b716
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.
5 #include "chrome/browser/extensions/extension_context_menu_model.h"
7 #include "base/prefs/pref_service.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/app/chrome_command_ids.h"
10 #include "chrome/browser/extensions/active_script_controller.h"
11 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
12 #include "chrome/browser/extensions/context_menu_matcher.h"
13 #include "chrome/browser/extensions/extension_action.h"
14 #include "chrome/browser/extensions/extension_action_manager.h"
15 #include "chrome/browser/extensions/extension_tab_util.h"
16 #include "chrome/browser/extensions/menu_manager.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/sessions/session_tab_helper.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_window.h"
21 #include "chrome/browser/ui/chrome_pages.h"
22 #include "chrome/browser/ui/tabs/tab_strip_model.h"
23 #include "chrome/common/extensions/extension_constants.h"
24 #include "chrome/common/pref_names.h"
25 #include "chrome/common/url_constants.h"
26 #include "chrome/grit/chromium_strings.h"
27 #include "chrome/grit/generated_resources.h"
28 #include "chrome/grit/theme_resources.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/common/context_menu_params.h"
31 #include "extensions/browser/extension_prefs.h"
32 #include "extensions/browser/extension_registry.h"
33 #include "extensions/browser/extension_system.h"
34 #include "extensions/browser/management_policy.h"
35 #include "extensions/browser/uninstall_reason.h"
36 #include "extensions/common/extension.h"
37 #include "extensions/common/feature_switch.h"
38 #include "extensions/common/manifest_handlers/options_page_info.h"
39 #include "extensions/common/manifest_url_handlers.h"
40 #include "ui/base/l10n/l10n_util.h"
41 #include "ui/base/resource/resource_bundle.h"
42 #include "ui/gfx/image/image.h"
44 using content::OpenURLParams;
45 using content::Referrer;
46 using content::WebContents;
47 using extensions::Extension;
48 using extensions::ExtensionActionAPI;
49 using extensions::ExtensionPrefs;
50 using extensions::MenuItem;
51 using extensions::MenuManager;
53 namespace {
55 // Returns true if the given |item| is of the given |type|.
56 bool MenuItemMatchesAction(ExtensionContextMenuModel::ActionType type,
57 const MenuItem* item) {
58 if (type == ExtensionContextMenuModel::NO_ACTION)
59 return false;
61 const MenuItem::ContextList& contexts = item->contexts();
63 if (contexts.Contains(MenuItem::ALL))
64 return true;
65 if (contexts.Contains(MenuItem::PAGE_ACTION) &&
66 (type == ExtensionContextMenuModel::PAGE_ACTION))
67 return true;
68 if (contexts.Contains(MenuItem::BROWSER_ACTION) &&
69 (type == ExtensionContextMenuModel::BROWSER_ACTION))
70 return true;
72 return false;
75 // Returns the id for the visibility command for the given |extension|, or -1
76 // if none should be shown.
77 int GetVisibilityStringId(
78 Profile* profile,
79 const Extension* extension,
80 ExtensionContextMenuModel::ButtonVisibility button_visibility) {
81 DCHECK(profile);
82 int string_id = -1;
83 if (!extensions::FeatureSwitch::extension_action_redesign()->IsEnabled()) {
84 // Without the toolbar redesign, we only show the visibility toggle for
85 // browser actions, and only give the option to hide.
86 if (extensions::ExtensionActionManager::Get(profile)->GetBrowserAction(
87 *extension)) {
88 string_id = IDS_EXTENSIONS_HIDE_BUTTON;
90 } else {
91 // With the redesign, we display "show" or "hide" based on the icon's
92 // visibility, and can have "transitively shown" buttons that are shown
93 // only while the button has a popup or menu visible.
94 switch (button_visibility) {
95 case (ExtensionContextMenuModel::VISIBLE):
96 string_id = IDS_EXTENSIONS_HIDE_BUTTON_IN_MENU;
97 break;
98 case (ExtensionContextMenuModel::TRANSITIVELY_VISIBLE):
99 string_id = IDS_EXTENSIONS_KEEP_BUTTON_IN_TOOLBAR;
100 break;
101 case (ExtensionContextMenuModel::OVERFLOWED):
102 string_id = IDS_EXTENSIONS_SHOW_BUTTON_IN_TOOLBAR;
103 break;
106 return string_id;
109 // Returns true if the given |extension| is required to remain installed by
110 // policy.
111 bool IsExtensionRequiredByPolicy(const Extension* extension,
112 Profile* profile) {
113 extensions::ManagementPolicy* policy =
114 extensions::ExtensionSystem::Get(profile)->management_policy();
115 return !policy->UserMayModifySettings(extension, nullptr) ||
116 policy->MustRemainInstalled(extension, nullptr);
119 } // namespace
121 ExtensionContextMenuModel::ExtensionContextMenuModel(
122 const Extension* extension,
123 Browser* browser,
124 ButtonVisibility button_visibility,
125 PopupDelegate* delegate)
126 : SimpleMenuModel(this),
127 extension_id_(extension->id()),
128 is_component_(extensions::Manifest::IsComponentLocation(
129 extension->location())),
130 browser_(browser),
131 profile_(browser->profile()),
132 delegate_(delegate),
133 action_type_(NO_ACTION),
134 extension_items_count_(0) {
135 InitMenu(extension, button_visibility);
137 if (profile_->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode) &&
138 delegate_ &&
139 !is_component_) {
140 AddSeparator(ui::NORMAL_SEPARATOR);
141 AddItemWithStringId(INSPECT_POPUP, IDS_EXTENSION_ACTION_INSPECT_POPUP);
145 ExtensionContextMenuModel::ExtensionContextMenuModel(const Extension* extension,
146 Browser* browser)
147 : ExtensionContextMenuModel(extension, browser, VISIBLE, nullptr) {
150 bool ExtensionContextMenuModel::IsCommandIdChecked(int command_id) const {
151 if (command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
152 command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST)
153 return extension_items_->IsCommandIdChecked(command_id);
154 return false;
157 bool ExtensionContextMenuModel::IsCommandIdEnabled(int command_id) const {
158 const Extension* extension = GetExtension();
159 if (!extension)
160 return false;
162 if (command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
163 command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
164 return extension_items_->IsCommandIdEnabled(command_id);
165 } else if (command_id == CONFIGURE) {
166 return extensions::OptionsPageInfo::HasOptionsPage(extension);
167 } else if (command_id == NAME) {
168 // The NAME links to the Homepage URL. If the extension doesn't have a
169 // homepage, we just disable this menu item. We also disable for component
170 // extensions, because it doesn't make sense to link to a webstore page or
171 // chrome://extensions.
172 return extensions::ManifestURL::GetHomepageURL(extension).is_valid() &&
173 !is_component_;
174 } else if (command_id == INSPECT_POPUP) {
175 WebContents* web_contents = GetActiveWebContents();
176 if (!web_contents)
177 return false;
179 return extension_action_ &&
180 extension_action_->HasPopup(SessionTabHelper::IdForTab(web_contents));
181 } else if (command_id == UNINSTALL) {
182 return !IsExtensionRequiredByPolicy(extension, profile_);
184 return true;
187 bool ExtensionContextMenuModel::GetAcceleratorForCommandId(
188 int command_id, ui::Accelerator* accelerator) {
189 return false;
192 void ExtensionContextMenuModel::ExecuteCommand(int command_id,
193 int event_flags) {
194 const Extension* extension = GetExtension();
195 if (!extension)
196 return;
198 if (command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
199 command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) {
200 WebContents* web_contents =
201 browser_->tab_strip_model()->GetActiveWebContents();
202 DCHECK(extension_items_);
203 extension_items_->ExecuteCommand(
204 command_id, web_contents, content::ContextMenuParams());
205 return;
208 switch (command_id) {
209 case NAME: {
210 OpenURLParams params(extensions::ManifestURL::GetHomepageURL(extension),
211 Referrer(), NEW_FOREGROUND_TAB,
212 ui::PAGE_TRANSITION_LINK, false);
213 browser_->OpenURL(params);
214 break;
216 case ALWAYS_RUN: {
217 WebContents* web_contents = GetActiveWebContents();
218 if (web_contents) {
219 extensions::ActiveScriptController::GetForWebContents(web_contents)
220 ->AlwaysRunOnVisibleOrigin(extension);
222 break;
224 case CONFIGURE:
225 DCHECK(extensions::OptionsPageInfo::HasOptionsPage(extension));
226 extensions::ExtensionTabUtil::OpenOptionsPage(extension, browser_);
227 break;
228 case TOGGLE_VISIBILITY: {
229 ExtensionActionAPI* api = ExtensionActionAPI::Get(profile_);
230 bool visible = api->GetBrowserActionVisibility(extension->id());
231 api->SetBrowserActionVisibility(extension->id(), !visible);
232 break;
234 case UNINSTALL: {
235 AddRef(); // Balanced in OnExtensionUninstallDialogClosed().
236 extension_uninstall_dialog_.reset(
237 extensions::ExtensionUninstallDialog::Create(
238 profile_, browser_->window()->GetNativeWindow(), this));
239 extension_uninstall_dialog_->ConfirmUninstall(
240 extension, extensions::UNINSTALL_REASON_USER_INITIATED,
241 extensions::UNINSTALL_SOURCE_TOOLBAR_CONTEXT_MENU);
242 break;
244 case MANAGE: {
245 chrome::ShowExtensions(browser_, extension->id());
246 break;
248 case INSPECT_POPUP: {
249 delegate_->InspectPopup();
250 break;
252 default:
253 NOTREACHED() << "Unknown option";
254 break;
258 void ExtensionContextMenuModel::OnExtensionUninstallDialogClosed(
259 bool did_start_uninstall,
260 const base::string16& error) {
261 Release();
264 ExtensionContextMenuModel::~ExtensionContextMenuModel() {}
266 void ExtensionContextMenuModel::InitMenu(const Extension* extension,
267 ButtonVisibility button_visibility) {
268 DCHECK(extension);
270 extensions::ExtensionActionManager* extension_action_manager =
271 extensions::ExtensionActionManager::Get(profile_);
272 extension_action_ = extension_action_manager->GetBrowserAction(*extension);
273 if (!extension_action_) {
274 extension_action_ = extension_action_manager->GetPageAction(*extension);
275 if (extension_action_)
276 action_type_ = PAGE_ACTION;
277 } else {
278 action_type_ = BROWSER_ACTION;
281 extension_items_.reset(new extensions::ContextMenuMatcher(
282 profile_, this, this, base::Bind(MenuItemMatchesAction, action_type_)));
284 std::string extension_name = extension->name();
285 // Ampersands need to be escaped to avoid being treated like
286 // mnemonics in the menu.
287 base::ReplaceChars(extension_name, "&", "&&", &extension_name);
288 AddItem(NAME, base::UTF8ToUTF16(extension_name));
289 AppendExtensionItems();
290 AddSeparator(ui::NORMAL_SEPARATOR);
292 // Add the "Always Allow" item for adding persisted permissions for script
293 // injections if there is an active action for this extension. Note that this
294 // will add it to *all* extension action context menus, not just the one
295 // attached to the script injection request icon, but that's okay.
296 WebContents* web_contents = GetActiveWebContents();
297 if (web_contents &&
298 extensions::ActiveScriptController::GetForWebContents(web_contents)
299 ->WantsToRun(extension)) {
300 AddItemWithStringId(ALWAYS_RUN, IDS_EXTENSIONS_ALWAYS_RUN);
303 if (!is_component_ || extensions::OptionsPageInfo::HasOptionsPage(extension))
304 AddItemWithStringId(CONFIGURE, IDS_EXTENSIONS_OPTIONS_MENU_ITEM);
306 if (!is_component_) {
307 bool is_required_by_policy =
308 IsExtensionRequiredByPolicy(extension, profile_);
309 int message_id = is_required_by_policy ?
310 IDS_EXTENSIONS_INSTALLED_BY_ADMIN : IDS_EXTENSIONS_UNINSTALL;
311 AddItem(UNINSTALL, l10n_util::GetStringUTF16(message_id));
312 if (is_required_by_policy) {
313 int uninstall_index = GetIndexOfCommandId(UNINSTALL);
314 SetIcon(uninstall_index,
315 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
316 IDR_OMNIBOX_HTTPS_POLICY_WARNING));
320 // Add a toggle visibility (show/hide) if the extension icon is shown on the
321 // toolbar.
322 int visibility_string_id =
323 GetVisibilityStringId(profile_, extension, button_visibility);
324 if (visibility_string_id != -1)
325 AddItemWithStringId(TOGGLE_VISIBILITY, visibility_string_id);
327 if (!is_component_) {
328 AddSeparator(ui::NORMAL_SEPARATOR);
329 AddItemWithStringId(MANAGE, IDS_MANAGE_EXTENSION);
333 const Extension* ExtensionContextMenuModel::GetExtension() const {
334 return extensions::ExtensionRegistry::Get(profile_)
335 ->enabled_extensions()
336 .GetByID(extension_id_);
339 void ExtensionContextMenuModel::AppendExtensionItems() {
340 extension_items_->Clear();
342 MenuManager* menu_manager = MenuManager::Get(profile_);
343 if (!menu_manager ||
344 !menu_manager->MenuItems(MenuItem::ExtensionKey(extension_id_)))
345 return;
347 AddSeparator(ui::NORMAL_SEPARATOR);
349 extension_items_count_ = 0;
350 extension_items_->AppendExtensionItems(MenuItem::ExtensionKey(extension_id_),
351 base::string16(),
352 &extension_items_count_,
353 true); // is_action_menu
356 content::WebContents* ExtensionContextMenuModel::GetActiveWebContents() const {
357 return browser_->tab_strip_model()->GetActiveWebContents();