• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "chrome/browser/extensions/extension_context_menu_model.h"
6 
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/extensions/manifest_url_handler.h"
26 #include "chrome/common/pref_names.h"
27 #include "chrome/common/url_constants.h"
28 #include "chrome/grit/chromium_strings.h"
29 #include "chrome/grit/generated_resources.h"
30 #include "content/public/browser/web_contents.h"
31 #include "content/public/common/context_menu_params.h"
32 #include "extensions/browser/extension_prefs.h"
33 #include "extensions/browser/extension_registry.h"
34 #include "extensions/browser/extension_system.h"
35 #include "extensions/browser/management_policy.h"
36 #include "extensions/browser/uninstall_reason.h"
37 #include "extensions/common/extension.h"
38 #include "extensions/common/feature_switch.h"
39 #include "extensions/common/manifest_handlers/options_page_info.h"
40 #include "ui/base/l10n/l10n_util.h"
41 
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;
50 
51 namespace {
52 
53 // Returns true if the given |item| is of the given |type|.
MenuItemMatchesAction(ExtensionContextMenuModel::ActionType type,const MenuItem * item)54 bool MenuItemMatchesAction(ExtensionContextMenuModel::ActionType type,
55                            const MenuItem* item) {
56   if (type == ExtensionContextMenuModel::NO_ACTION)
57     return false;
58 
59   const MenuItem::ContextList& contexts = item->contexts();
60 
61   if (contexts.Contains(MenuItem::ALL))
62     return true;
63   if (contexts.Contains(MenuItem::PAGE_ACTION) &&
64       (type == ExtensionContextMenuModel::PAGE_ACTION))
65     return true;
66   if (contexts.Contains(MenuItem::BROWSER_ACTION) &&
67       (type == ExtensionContextMenuModel::BROWSER_ACTION))
68     return true;
69 
70   return false;
71 }
72 
73 // Returns the id for the visibility command for the given |extension|, or -1
74 // if none should be shown.
GetVisibilityStringId(Profile * profile,const Extension * extension)75 int GetVisibilityStringId(Profile* profile, const Extension* extension) {
76   DCHECK(profile);
77   int string_id = -1;
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(
82             *extension)) {
83       string_id = IDS_EXTENSIONS_HIDE_BUTTON;
84     }
85   } else {
86     // With the redesign, we display "show" or "hide" based on the icon's
87     // visibility.
88     bool visible = ExtensionActionAPI::GetBrowserActionVisibility(
89                        ExtensionPrefs::Get(profile), extension->id());
90     string_id =
91         visible ? IDS_EXTENSIONS_HIDE_BUTTON : IDS_EXTENSIONS_SHOW_BUTTON;
92   }
93   return string_id;
94 }
95 
96 }  // namespace
97 
ExtensionContextMenuModel(const Extension * extension,Browser * browser,PopupDelegate * delegate)98 ExtensionContextMenuModel::ExtensionContextMenuModel(const Extension* extension,
99                                                      Browser* browser,
100                                                      PopupDelegate* delegate)
101     : SimpleMenuModel(this),
102       extension_id_(extension->id()),
103       browser_(browser),
104       profile_(browser->profile()),
105       delegate_(delegate),
106       action_type_(NO_ACTION),
107       extension_items_count_(0) {
108   InitMenu(extension);
109 
110   if (profile_->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode) &&
111       delegate_) {
112     AddSeparator(ui::NORMAL_SEPARATOR);
113     AddItemWithStringId(INSPECT_POPUP, IDS_EXTENSION_ACTION_INSPECT_POPUP);
114   }
115 }
116 
ExtensionContextMenuModel(const Extension * extension,Browser * browser)117 ExtensionContextMenuModel::ExtensionContextMenuModel(const Extension* extension,
118                                                      Browser* browser)
119     : SimpleMenuModel(this),
120       extension_id_(extension->id()),
121       browser_(browser),
122       profile_(browser->profile()),
123       delegate_(NULL),
124       action_type_(NO_ACTION),
125       extension_items_count_(0) {
126   InitMenu(extension);
127 }
128 
IsCommandIdChecked(int command_id) const129 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);
133   return false;
134 }
135 
IsCommandIdEnabled(int command_id) const136 bool ExtensionContextMenuModel::IsCommandIdEnabled(int command_id) const {
137   const Extension* extension = GetExtension();
138   if (!extension)
139     return false;
140 
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();
152     if (!web_contents)
153       return false;
154 
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);
161   }
162   return true;
163 }
164 
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator)165 bool ExtensionContextMenuModel::GetAcceleratorForCommandId(
166     int command_id, ui::Accelerator* accelerator) {
167   return false;
168 }
169 
ExecuteCommand(int command_id,int event_flags)170 void ExtensionContextMenuModel::ExecuteCommand(int command_id,
171                                                int event_flags) {
172   const Extension* extension = GetExtension();
173   if (!extension)
174     return;
175 
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());
183     return;
184   }
185 
186   switch (command_id) {
187     case NAME: {
188       OpenURLParams params(extensions::ManifestURL::GetHomepageURL(extension),
189                            Referrer(), NEW_FOREGROUND_TAB,
190                            ui::PAGE_TRANSITION_LINK, false);
191       browser_->OpenURL(params);
192       break;
193     }
194     case ALWAYS_RUN: {
195       WebContents* web_contents = GetActiveWebContents();
196       if (web_contents) {
197         extensions::ActiveScriptController::GetForWebContents(web_contents)
198             ->AlwaysRunOnVisibleOrigin(extension);
199       }
200       break;
201     }
202     case CONFIGURE:
203       DCHECK(extensions::OptionsPageInfo::HasOptionsPage(extension));
204       extensions::ExtensionTabUtil::OpenOptionsPage(extension, browser_);
205       break;
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);
212       break;
213     }
214     case UNINSTALL: {
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);
220       break;
221     }
222     case MANAGE: {
223       chrome::ShowExtensions(browser_, extension->id());
224       break;
225     }
226     case INSPECT_POPUP: {
227       delegate_->InspectPopup();
228       break;
229     }
230     default:
231      NOTREACHED() << "Unknown option";
232      break;
233   }
234 }
235 
ExtensionUninstallAccepted()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),
243                              NULL);
244   }
245   Release();
246 }
247 
ExtensionUninstallCanceled()248 void ExtensionContextMenuModel::ExtensionUninstallCanceled() {
249   Release();
250 }
251 
~ExtensionContextMenuModel()252 ExtensionContextMenuModel::~ExtensionContextMenuModel() {}
253 
InitMenu(const Extension * extension)254 void ExtensionContextMenuModel::InitMenu(const Extension* extension) {
255   DCHECK(extension);
256 
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;
264   } else {
265     action_type_ = BROWSER_ACTION;
266   }
267 
268   extension_items_.reset(new extensions::ContextMenuMatcher(
269       profile_, this, this, base::Bind(MenuItemMatchesAction, action_type_)));
270 
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);
278 
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();
284   if (web_contents &&
285       extensions::ActiveScriptController::GetForWebContents(web_contents)
286           ->WantsToRun(extension)) {
287     AddItemWithStringId(ALWAYS_RUN, IDS_EXTENSIONS_ALWAYS_RUN);
288   }
289 
290   AddItemWithStringId(CONFIGURE, IDS_EXTENSIONS_OPTIONS_MENU_ITEM);
291   AddItem(UNINSTALL, l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNINSTALL));
292 
293   // Add a toggle visibility (show/hide) if the extension icon is shown on the
294   // toolbar.
295   int visibility_string_id = GetVisibilityStringId(profile_, extension);
296   if (visibility_string_id != -1)
297     AddItemWithStringId(TOGGLE_VISIBILITY, visibility_string_id);
298 
299   AddSeparator(ui::NORMAL_SEPARATOR);
300   AddItemWithStringId(MANAGE, IDS_MANAGE_EXTENSION);
301 }
302 
GetExtension() const303 const Extension* ExtensionContextMenuModel::GetExtension() const {
304   return extensions::ExtensionRegistry::Get(profile_)
305       ->enabled_extensions()
306       .GetByID(extension_id_);
307 }
308 
AppendExtensionItems()309 void ExtensionContextMenuModel::AppendExtensionItems() {
310   extension_items_->Clear();
311 
312   MenuManager* menu_manager = MenuManager::Get(profile_);
313   if (!menu_manager ||
314       !menu_manager->MenuItems(MenuItem::ExtensionKey(extension_id_)))
315     return;
316 
317   AddSeparator(ui::NORMAL_SEPARATOR);
318 
319   extension_items_count_ = 0;
320   extension_items_->AppendExtensionItems(MenuItem::ExtensionKey(extension_id_),
321                                          base::string16(),
322                                          &extension_items_count_,
323                                          true);  // is_action_menu
324 }
325 
GetActiveWebContents() const326 content::WebContents* ExtensionContextMenuModel::GetActiveWebContents() const {
327   return browser_->tab_strip_model()->GetActiveWebContents();
328 }
329