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_service.h"
16 #include "chrome/browser/extensions/extension_tab_util.h"
17 #include "chrome/browser/extensions/menu_manager.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/sessions/session_tab_helper.h"
20 #include "chrome/browser/ui/browser.h"
21 #include "chrome/browser/ui/browser_window.h"
22 #include "chrome/browser/ui/chrome_pages.h"
23 #include "chrome/browser/ui/tabs/tab_strip_model.h"
24 #include "chrome/common/extensions/extension_constants.h"
25 #include "chrome/common/pref_names.h"
26 #include "chrome/common/url_constants.h"
27 #include "chrome/grit/chromium_strings.h"
28 #include "chrome/grit/generated_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"
42 using content::OpenURLParams
;
43 using content::Referrer
;
44 using content::WebContents
;
45 using extensions::Extension
;
46 using extensions::ExtensionActionAPI
;
47 using extensions::ExtensionPrefs
;
48 using extensions::MenuItem
;
49 using extensions::MenuManager
;
53 // Returns true if the given |item| is of the given |type|.
54 bool MenuItemMatchesAction(ExtensionContextMenuModel::ActionType type
,
55 const MenuItem
* item
) {
56 if (type
== ExtensionContextMenuModel::NO_ACTION
)
59 const MenuItem::ContextList
& contexts
= item
->contexts();
61 if (contexts
.Contains(MenuItem::ALL
))
63 if (contexts
.Contains(MenuItem::PAGE_ACTION
) &&
64 (type
== ExtensionContextMenuModel::PAGE_ACTION
))
66 if (contexts
.Contains(MenuItem::BROWSER_ACTION
) &&
67 (type
== ExtensionContextMenuModel::BROWSER_ACTION
))
73 // Returns the id for the visibility command for the given |extension|, or -1
74 // if none should be shown.
75 int GetVisibilityStringId(Profile
* profile
, const Extension
* extension
) {
78 if (!extensions::FeatureSwitch::extension_action_redesign()->IsEnabled()) {
79 // Without the toolbar redesign, we only show the visibility toggle for
80 // browser actions, and only give the option to hide.
81 if (extensions::ExtensionActionManager::Get(profile
)->GetBrowserAction(
83 string_id
= IDS_EXTENSIONS_HIDE_BUTTON
;
86 // With the redesign, we display "show" or "hide" based on the icon's
88 bool visible
= ExtensionActionAPI::GetBrowserActionVisibility(
89 ExtensionPrefs::Get(profile
), extension
->id());
91 visible
? IDS_EXTENSIONS_HIDE_BUTTON
: IDS_EXTENSIONS_SHOW_BUTTON
;
98 ExtensionContextMenuModel::ExtensionContextMenuModel(const Extension
* extension
,
100 PopupDelegate
* delegate
)
101 : SimpleMenuModel(this),
102 extension_id_(extension
->id()),
104 profile_(browser
->profile()),
106 action_type_(NO_ACTION
),
107 extension_items_count_(0) {
110 if (profile_
->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode
) &&
112 AddSeparator(ui::NORMAL_SEPARATOR
);
113 AddItemWithStringId(INSPECT_POPUP
, IDS_EXTENSION_ACTION_INSPECT_POPUP
);
117 ExtensionContextMenuModel::ExtensionContextMenuModel(const Extension
* extension
,
119 : SimpleMenuModel(this),
120 extension_id_(extension
->id()),
122 profile_(browser
->profile()),
124 action_type_(NO_ACTION
),
125 extension_items_count_(0) {
129 bool ExtensionContextMenuModel::IsCommandIdChecked(int command_id
) const {
130 if (command_id
>= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST
&&
131 command_id
<= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST
)
132 return extension_items_
->IsCommandIdChecked(command_id
);
136 bool ExtensionContextMenuModel::IsCommandIdEnabled(int command_id
) const {
137 const Extension
* extension
= GetExtension();
141 if (command_id
>= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST
&&
142 command_id
<= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST
) {
143 return extension_items_
->IsCommandIdEnabled(command_id
);
144 } else if (command_id
== CONFIGURE
) {
145 return extensions::OptionsPageInfo::HasOptionsPage(extension
);
146 } else if (command_id
== NAME
) {
147 // The NAME links to the Homepage URL. If the extension doesn't have a
148 // homepage, we just disable this menu item.
149 return extensions::ManifestURL::GetHomepageURL(extension
).is_valid();
150 } else if (command_id
== INSPECT_POPUP
) {
151 WebContents
* web_contents
= GetActiveWebContents();
155 return extension_action_
&&
156 extension_action_
->HasPopup(SessionTabHelper::IdForTab(web_contents
));
157 } else if (command_id
== UNINSTALL
) {
158 // Some extension types can not be uninstalled.
159 return extensions::ExtensionSystem::Get(
160 profile_
)->management_policy()->UserMayModifySettings(extension
, NULL
);
165 bool ExtensionContextMenuModel::GetAcceleratorForCommandId(
166 int command_id
, ui::Accelerator
* accelerator
) {
170 void ExtensionContextMenuModel::ExecuteCommand(int command_id
,
172 const Extension
* extension
= GetExtension();
176 if (command_id
>= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST
&&
177 command_id
<= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST
) {
178 WebContents
* web_contents
=
179 browser_
->tab_strip_model()->GetActiveWebContents();
180 DCHECK(extension_items_
);
181 extension_items_
->ExecuteCommand(
182 command_id
, web_contents
, content::ContextMenuParams());
186 switch (command_id
) {
188 OpenURLParams
params(extensions::ManifestURL::GetHomepageURL(extension
),
189 Referrer(), NEW_FOREGROUND_TAB
,
190 ui::PAGE_TRANSITION_LINK
, false);
191 browser_
->OpenURL(params
);
195 WebContents
* web_contents
= GetActiveWebContents();
197 extensions::ActiveScriptController::GetForWebContents(web_contents
)
198 ->AlwaysRunOnVisibleOrigin(extension
);
203 DCHECK(extensions::OptionsPageInfo::HasOptionsPage(extension
));
204 extensions::ExtensionTabUtil::OpenOptionsPage(extension
, browser_
);
206 case TOGGLE_VISIBILITY
: {
207 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(profile_
);
208 bool visible
= ExtensionActionAPI::GetBrowserActionVisibility(
209 prefs
, extension
->id());
210 ExtensionActionAPI::SetBrowserActionVisibility(
211 prefs
, extension
->id(), !visible
);
215 AddRef(); // Balanced in Accepted() and Canceled()
216 extension_uninstall_dialog_
.reset(
217 extensions::ExtensionUninstallDialog::Create(
218 profile_
, browser_
->window()->GetNativeWindow(), this));
219 extension_uninstall_dialog_
->ConfirmUninstall(extension
);
223 chrome::ShowExtensions(browser_
, extension
->id());
226 case INSPECT_POPUP
: {
227 delegate_
->InspectPopup();
231 NOTREACHED() << "Unknown option";
236 void ExtensionContextMenuModel::ExtensionUninstallAccepted() {
237 if (GetExtension()) {
238 extensions::ExtensionSystem::Get(profile_
)
239 ->extension_service()
240 ->UninstallExtension(extension_id_
,
241 extensions::UNINSTALL_REASON_USER_INITIATED
,
242 base::Bind(&base::DoNothing
),
248 void ExtensionContextMenuModel::ExtensionUninstallCanceled() {
252 ExtensionContextMenuModel::~ExtensionContextMenuModel() {}
254 void ExtensionContextMenuModel::InitMenu(const Extension
* extension
) {
257 extensions::ExtensionActionManager
* extension_action_manager
=
258 extensions::ExtensionActionManager::Get(profile_
);
259 extension_action_
= extension_action_manager
->GetBrowserAction(*extension
);
260 if (!extension_action_
) {
261 extension_action_
= extension_action_manager
->GetPageAction(*extension
);
262 if (extension_action_
)
263 action_type_
= PAGE_ACTION
;
265 action_type_
= BROWSER_ACTION
;
268 extension_items_
.reset(new extensions::ContextMenuMatcher(
269 profile_
, this, this, base::Bind(MenuItemMatchesAction
, action_type_
)));
271 std::string extension_name
= extension
->name();
272 // Ampersands need to be escaped to avoid being treated like
273 // mnemonics in the menu.
274 base::ReplaceChars(extension_name
, "&", "&&", &extension_name
);
275 AddItem(NAME
, base::UTF8ToUTF16(extension_name
));
276 AppendExtensionItems();
277 AddSeparator(ui::NORMAL_SEPARATOR
);
279 // Add the "Always Allow" item for adding persisted permissions for script
280 // injections if there is an active action for this extension. Note that this
281 // will add it to *all* extension action context menus, not just the one
282 // attached to the script injection request icon, but that's okay.
283 WebContents
* web_contents
= GetActiveWebContents();
285 extensions::ActiveScriptController::GetForWebContents(web_contents
)
286 ->WantsToRun(extension
)) {
287 AddItemWithStringId(ALWAYS_RUN
, IDS_EXTENSIONS_ALWAYS_RUN
);
290 AddItemWithStringId(CONFIGURE
, IDS_EXTENSIONS_OPTIONS_MENU_ITEM
);
291 AddItem(UNINSTALL
, l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNINSTALL
));
293 // Add a toggle visibility (show/hide) if the extension icon is shown on the
295 int visibility_string_id
= GetVisibilityStringId(profile_
, extension
);
296 if (visibility_string_id
!= -1)
297 AddItemWithStringId(TOGGLE_VISIBILITY
, visibility_string_id
);
299 AddSeparator(ui::NORMAL_SEPARATOR
);
300 AddItemWithStringId(MANAGE
, IDS_MANAGE_EXTENSION
);
303 const Extension
* ExtensionContextMenuModel::GetExtension() const {
304 return extensions::ExtensionRegistry::Get(profile_
)
305 ->enabled_extensions()
306 .GetByID(extension_id_
);
309 void ExtensionContextMenuModel::AppendExtensionItems() {
310 extension_items_
->Clear();
312 MenuManager
* menu_manager
= MenuManager::Get(profile_
);
314 !menu_manager
->MenuItems(MenuItem::ExtensionKey(extension_id_
)))
317 AddSeparator(ui::NORMAL_SEPARATOR
);
319 extension_items_count_
= 0;
320 extension_items_
->AppendExtensionItems(MenuItem::ExtensionKey(extension_id_
),
322 &extension_items_count_
,
323 true); // is_action_menu
326 content::WebContents
* ExtensionContextMenuModel::GetActiveWebContents() const {
327 return browser_
->tab_strip_model()->GetActiveWebContents();