| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/extensions/extension_context_menu_model.h" |
| |
| #include "base/bind.h" |
| #include "base/macros.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "chrome/app/chrome_command_ids.h" |
| #include "chrome/browser/extensions/chrome_extension_browser_constants.h" |
| #include "chrome/browser/extensions/context_menu_matcher.h" |
| #include "chrome/browser/extensions/extension_action.h" |
| #include "chrome/browser/extensions/extension_action_manager.h" |
| #include "chrome/browser/extensions/extension_action_runner.h" |
| #include "chrome/browser/extensions/extension_tab_util.h" |
| #include "chrome/browser/extensions/extension_uninstall_dialog.h" |
| #include "chrome/browser/extensions/extension_util.h" |
| #include "chrome/browser/extensions/menu_manager.h" |
| #include "chrome/browser/extensions/scripting_permissions_modifier.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/sessions/session_tab_helper.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/chrome_pages.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/toolbar/toolbar_actions_model.h" |
| #include "chrome/common/extensions/extension_constants.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/grit/chromium_strings.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/url_formatter/url_formatter.h" |
| #include "components/vector_icons/vector_icons.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/context_menu_params.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/extension_system.h" |
| #include "extensions/browser/management_policy.h" |
| #include "extensions/browser/uninstall_reason.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/manifest_handlers/options_page_info.h" |
| #include "extensions/common/manifest_url_handlers.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/models/menu_separator_types.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/gfx/color_palette.h" |
| #include "ui/gfx/image/image.h" |
| #include "ui/gfx/paint_vector_icon.h" |
| |
| namespace extensions { |
| |
| namespace { |
| |
| // Returns true if the given |item| is of the given |type|. |
| bool MenuItemMatchesAction(ExtensionContextMenuModel::ActionType type, |
| const MenuItem* item) { |
| if (type == ExtensionContextMenuModel::NO_ACTION) |
| return false; |
| |
| const MenuItem::ContextList& contexts = item->contexts(); |
| |
| if (contexts.Contains(MenuItem::ALL)) |
| return true; |
| if (contexts.Contains(MenuItem::PAGE_ACTION) && |
| (type == ExtensionContextMenuModel::PAGE_ACTION)) |
| return true; |
| if (contexts.Contains(MenuItem::BROWSER_ACTION) && |
| (type == ExtensionContextMenuModel::BROWSER_ACTION)) |
| return true; |
| |
| return false; |
| } |
| |
| // Returns the id for the visibility command for the given |extension|. |
| int GetVisibilityStringId( |
| Profile* profile, |
| const Extension* extension, |
| ExtensionContextMenuModel::ButtonVisibility button_visibility) { |
| DCHECK(profile); |
| int string_id = -1; |
| // We display "show" or "hide" based on the icon's visibility, and can have |
| // "transitively shown" buttons that are shown only while the button has a |
| // popup or menu visible. |
| switch (button_visibility) { |
| case (ExtensionContextMenuModel::VISIBLE): |
| string_id = IDS_EXTENSIONS_HIDE_BUTTON_IN_MENU; |
| break; |
| case (ExtensionContextMenuModel::TRANSITIVELY_VISIBLE): |
| string_id = IDS_EXTENSIONS_KEEP_BUTTON_IN_TOOLBAR; |
| break; |
| case (ExtensionContextMenuModel::OVERFLOWED): |
| string_id = IDS_EXTENSIONS_SHOW_BUTTON_IN_TOOLBAR; |
| break; |
| } |
| return string_id; |
| } |
| |
| // Returns true if the given |extension| is required to remain installed by |
| // policy. |
| bool IsExtensionRequiredByPolicy(const Extension* extension, |
| Profile* profile) { |
| ManagementPolicy* policy = ExtensionSystem::Get(profile)->management_policy(); |
| return !policy->UserMayModifySettings(extension, nullptr) || |
| policy->MustRemainInstalled(extension, nullptr); |
| } |
| |
| ExtensionContextMenuModel::ContextMenuAction CommandIdToContextMenuAction( |
| int command_id) { |
| using ContextMenuAction = ExtensionContextMenuModel::ContextMenuAction; |
| |
| switch (command_id) { |
| case ExtensionContextMenuModel::HOME_PAGE: |
| return ContextMenuAction::kHomePage; |
| case ExtensionContextMenuModel::OPTIONS: |
| return ContextMenuAction::kOptions; |
| case ExtensionContextMenuModel::TOGGLE_VISIBILITY: |
| return ContextMenuAction::kToggleVisibility; |
| case ExtensionContextMenuModel::UNINSTALL: |
| return ContextMenuAction::kUninstall; |
| case ExtensionContextMenuModel::MANAGE_EXTENSIONS: |
| return ContextMenuAction::kManageExtensions; |
| case ExtensionContextMenuModel::INSPECT_POPUP: |
| return ContextMenuAction::kInspectPopup; |
| case ExtensionContextMenuModel::PAGE_ACCESS_RUN_ON_CLICK: |
| return ContextMenuAction::kPageAccessRunOnClick; |
| case ExtensionContextMenuModel::PAGE_ACCESS_RUN_ON_SITE: |
| return ContextMenuAction::kPageAccessRunOnSite; |
| case ExtensionContextMenuModel::PAGE_ACCESS_RUN_ON_ALL_SITES: |
| return ContextMenuAction::kPageAccessRunOnAllSites; |
| case ExtensionContextMenuModel::PAGE_ACCESS_LEARN_MORE: |
| return ContextMenuAction::kPageAccessLearnMore; |
| case ExtensionContextMenuModel::PAGE_ACCESS_CANT_ACCESS: |
| case ExtensionContextMenuModel::PAGE_ACCESS_SUBMENU: |
| NOTREACHED(); |
| break; |
| default: |
| break; |
| } |
| NOTREACHED(); |
| return ContextMenuAction::kNoAction; |
| } |
| |
| // A stub for the uninstall dialog. |
| // TODO(devlin): Ideally, we would just have the uninstall dialog take a |
| // base::Callback, but that's a bunch of churn. |
| class UninstallDialogHelper : public ExtensionUninstallDialog::Delegate { |
| public: |
| // Kicks off the asynchronous process to confirm and uninstall the given |
| // |extension|. |
| static void UninstallExtension(Browser* browser, const Extension* extension) { |
| UninstallDialogHelper* helper = new UninstallDialogHelper(); |
| helper->BeginUninstall(browser, extension); |
| } |
| |
| private: |
| // This class handles its own lifetime. |
| UninstallDialogHelper() {} |
| ~UninstallDialogHelper() override {} |
| |
| void BeginUninstall(Browser* browser, const Extension* extension) { |
| uninstall_dialog_ = ExtensionUninstallDialog::Create( |
| browser->profile(), browser->window()->GetNativeWindow(), this); |
| uninstall_dialog_->ConfirmUninstall(extension, |
| UNINSTALL_REASON_USER_INITIATED, |
| UNINSTALL_SOURCE_TOOLBAR_CONTEXT_MENU); |
| } |
| |
| // ExtensionUninstallDialog::Delegate: |
| void OnExtensionUninstallDialogClosed(bool did_start_uninstall, |
| const base::string16& error) override { |
| delete this; |
| } |
| |
| std::unique_ptr<ExtensionUninstallDialog> uninstall_dialog_; |
| |
| DISALLOW_COPY_AND_ASSIGN(UninstallDialogHelper); |
| }; |
| |
| } // namespace |
| |
| ExtensionContextMenuModel::ExtensionContextMenuModel( |
| const Extension* extension, |
| Browser* browser, |
| ButtonVisibility button_visibility, |
| PopupDelegate* delegate) |
| : SimpleMenuModel(this), |
| extension_id_(extension->id()), |
| is_component_(Manifest::IsComponentLocation(extension->location())), |
| browser_(browser), |
| profile_(browser->profile()), |
| delegate_(delegate), |
| action_type_(NO_ACTION), |
| button_visibility_(button_visibility) { |
| InitMenu(extension, button_visibility); |
| } |
| |
| bool ExtensionContextMenuModel::IsCommandIdChecked(int command_id) const { |
| const Extension* extension = GetExtension(); |
| if (!extension) |
| return false; |
| |
| if (ContextMenuMatcher::IsExtensionsCustomCommandId(command_id)) |
| return extension_items_->IsCommandIdChecked(command_id); |
| |
| if (command_id == PAGE_ACCESS_RUN_ON_CLICK || |
| command_id == PAGE_ACCESS_RUN_ON_SITE || |
| command_id == PAGE_ACCESS_RUN_ON_ALL_SITES) { |
| content::WebContents* web_contents = GetActiveWebContents(); |
| return web_contents && |
| GetCurrentPageAccess(extension, web_contents) == command_id; |
| } |
| |
| return false; |
| } |
| |
| bool ExtensionContextMenuModel::IsCommandIdVisible(int command_id) const { |
| const Extension* extension = GetExtension(); |
| if (!extension) |
| return false; |
| if (ContextMenuMatcher::IsExtensionsCustomCommandId(command_id)) { |
| return extension_items_->IsCommandIdVisible(command_id); |
| } |
| |
| // Standard menu items are always visible. |
| return true; |
| } |
| |
| bool ExtensionContextMenuModel::IsCommandIdEnabled(int command_id) const { |
| const Extension* extension = GetExtension(); |
| if (!extension) |
| return false; |
| |
| if (ContextMenuMatcher::IsExtensionsCustomCommandId(command_id)) |
| return extension_items_->IsCommandIdEnabled(command_id); |
| |
| switch (command_id) { |
| case HOME_PAGE: |
| // The HOME_PAGE links to the Homepage URL. If the extension doesn't have |
| // a homepage, we just disable this menu item. We also disable for |
| // component extensions, because it doesn't make sense to link to a |
| // webstore page or chrome://extensions. |
| return ManifestURL::GetHomepageURL(extension).is_valid() && |
| !is_component_; |
| case OPTIONS: |
| return OptionsPageInfo::HasOptionsPage(extension); |
| case INSPECT_POPUP: { |
| content::WebContents* web_contents = GetActiveWebContents(); |
| return web_contents && extension_action_ && |
| extension_action_->HasPopup( |
| SessionTabHelper::IdForTab(web_contents).id()); |
| } |
| case UNINSTALL: |
| return !IsExtensionRequiredByPolicy(extension, profile_); |
| case PAGE_ACCESS_CANT_ACCESS: |
| case PAGE_ACCESS_SUBMENU: |
| case PAGE_ACCESS_RUN_ON_CLICK: |
| case PAGE_ACCESS_RUN_ON_SITE: |
| case PAGE_ACCESS_RUN_ON_ALL_SITES: |
| case PAGE_ACCESS_LEARN_MORE: { |
| content::WebContents* web_contents = GetActiveWebContents(); |
| if (!web_contents) |
| return false; |
| // TODO(devlin): This can lead to some fun race-like conditions, where the |
| // menu is constructed during navigation. Since we get the URL both here |
| // and in execution of the command, there's a chance we'll find two |
| // different URLs. This would be solved if we maintained the URL that the |
| // menu was showing for. |
| const GURL& url = web_contents->GetLastCommittedURL(); |
| return IsPageAccessCommandEnabled(*extension, url, command_id); |
| } |
| // The following, if they are present, are always enabled. |
| case TOGGLE_VISIBILITY: |
| case MANAGE_EXTENSIONS: |
| return true; |
| default: |
| NOTREACHED() << "Unknown command" << command_id; |
| } |
| return true; |
| } |
| |
| void ExtensionContextMenuModel::ExecuteCommand(int command_id, |
| int event_flags) { |
| const Extension* extension = GetExtension(); |
| if (!extension) |
| return; |
| |
| if (ContextMenuMatcher::IsExtensionsCustomCommandId(command_id)) { |
| DCHECK(extension_items_); |
| extension_items_->ExecuteCommand(command_id, GetActiveWebContents(), |
| nullptr, content::ContextMenuParams()); |
| action_taken_ = ContextMenuAction::kCustomCommand; |
| return; |
| } |
| |
| action_taken_ = CommandIdToContextMenuAction(command_id); |
| |
| switch (command_id) { |
| case HOME_PAGE: { |
| content::OpenURLParams params(ManifestURL::GetHomepageURL(extension), |
| content::Referrer(), |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui::PAGE_TRANSITION_LINK, false); |
| browser_->OpenURL(params); |
| break; |
| } |
| case OPTIONS: |
| DCHECK(OptionsPageInfo::HasOptionsPage(extension)); |
| ExtensionTabUtil::OpenOptionsPage(extension, browser_); |
| break; |
| case TOGGLE_VISIBILITY: { |
| bool currently_visible = button_visibility_ == VISIBLE; |
| ToolbarActionsModel::Get(browser_->profile()) |
| ->SetActionVisibility(extension->id(), !currently_visible); |
| break; |
| } |
| case UNINSTALL: { |
| UninstallDialogHelper::UninstallExtension(browser_, extension); |
| break; |
| } |
| case MANAGE_EXTENSIONS: { |
| chrome::ShowExtensions(browser_, extension->id()); |
| break; |
| } |
| case INSPECT_POPUP: { |
| delegate_->InspectPopup(); |
| break; |
| } |
| case PAGE_ACCESS_RUN_ON_CLICK: |
| case PAGE_ACCESS_RUN_ON_SITE: |
| case PAGE_ACCESS_RUN_ON_ALL_SITES: |
| case PAGE_ACCESS_LEARN_MORE: |
| HandlePageAccessCommand(command_id, extension); |
| break; |
| default: |
| NOTREACHED() << "Unknown option"; |
| break; |
| } |
| } |
| |
| void ExtensionContextMenuModel::OnMenuWillShow(ui::SimpleMenuModel* menu) { |
| action_taken_ = ContextMenuAction::kNoAction; |
| } |
| |
| void ExtensionContextMenuModel::MenuClosed(ui::SimpleMenuModel* menu) { |
| if (action_taken_) { |
| ContextMenuAction action = *action_taken_; |
| UMA_HISTOGRAM_ENUMERATION("Extensions.ContextMenuAction", action); |
| action_taken_ = base::nullopt; |
| } |
| } |
| |
| ExtensionContextMenuModel::~ExtensionContextMenuModel() {} |
| |
| void ExtensionContextMenuModel::InitMenu(const Extension* extension, |
| ButtonVisibility button_visibility) { |
| DCHECK(extension); |
| |
| extension_action_ = |
| ExtensionActionManager::Get(profile_)->GetExtensionAction(*extension); |
| if (extension_action_) { |
| action_type_ = extension_action_->action_type() == ActionInfo::TYPE_PAGE |
| ? PAGE_ACTION |
| : BROWSER_ACTION; |
| } |
| |
| extension_items_.reset(new ContextMenuMatcher( |
| profile_, this, this, base::Bind(MenuItemMatchesAction, action_type_))); |
| |
| std::string extension_name = extension->name(); |
| // Ampersands need to be escaped to avoid being treated like |
| // mnemonics in the menu. |
| base::ReplaceChars(extension_name, "&", "&&", &extension_name); |
| AddItem(HOME_PAGE, base::UTF8ToUTF16(extension_name)); |
| AppendExtensionItems(); |
| AddSeparator(ui::NORMAL_SEPARATOR); |
| |
| CreatePageAccessSubmenu(extension); |
| |
| if (!is_component_ || OptionsPageInfo::HasOptionsPage(extension)) |
| AddItemWithStringId(OPTIONS, IDS_EXTENSIONS_OPTIONS_MENU_ITEM); |
| |
| if (!is_component_) { |
| bool is_required_by_policy = |
| IsExtensionRequiredByPolicy(extension, profile_); |
| int message_id = is_required_by_policy ? |
| IDS_EXTENSIONS_INSTALLED_BY_ADMIN : IDS_EXTENSIONS_UNINSTALL; |
| AddItem(UNINSTALL, l10n_util::GetStringUTF16(message_id)); |
| if (is_required_by_policy) { |
| int uninstall_index = GetIndexOfCommandId(UNINSTALL); |
| SetIcon(uninstall_index, |
| gfx::Image(gfx::CreateVectorIcon(vector_icons::kBusinessIcon, 16, |
| gfx::kChromeIconGrey))); |
| } |
| } |
| |
| // Add a toggle visibility (show/hide) if the extension icon is shown on the |
| // toolbar. |
| int visibility_string_id = |
| GetVisibilityStringId(profile_, extension, button_visibility); |
| DCHECK_NE(-1, visibility_string_id); |
| AddItemWithStringId(TOGGLE_VISIBILITY, visibility_string_id); |
| |
| if (!is_component_) { |
| AddSeparator(ui::NORMAL_SEPARATOR); |
| AddItemWithStringId(MANAGE_EXTENSIONS, IDS_MANAGE_EXTENSION); |
| } |
| |
| const ActionInfo* action_info = ActionInfo::GetPageActionInfo(extension); |
| if (!action_info) |
| action_info = ActionInfo::GetBrowserActionInfo(extension); |
| if (profile_->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode) && |
| delegate_ && !is_component_ && action_info && !action_info->synthesized) { |
| AddSeparator(ui::NORMAL_SEPARATOR); |
| AddItemWithStringId(INSPECT_POPUP, IDS_EXTENSION_ACTION_INSPECT_POPUP); |
| } |
| } |
| |
| const Extension* ExtensionContextMenuModel::GetExtension() const { |
| return ExtensionRegistry::Get(profile_)->enabled_extensions().GetByID( |
| extension_id_); |
| } |
| |
| void ExtensionContextMenuModel::AppendExtensionItems() { |
| MenuManager* menu_manager = MenuManager::Get(profile_); |
| if (!menu_manager || // Null in unit tests |
| !menu_manager->MenuItems(MenuItem::ExtensionKey(extension_id_))) |
| return; |
| |
| AddSeparator(ui::NORMAL_SEPARATOR); |
| |
| int index = 0; |
| extension_items_->AppendExtensionItems(MenuItem::ExtensionKey(extension_id_), |
| base::string16(), &index, |
| true); // is_action_menu |
| } |
| |
| ExtensionContextMenuModel::MenuEntries |
| ExtensionContextMenuModel::GetCurrentPageAccess( |
| const Extension* extension, |
| content::WebContents* web_contents) const { |
| DCHECK(web_contents); |
| ScriptingPermissionsModifier modifier(profile_, extension); |
| DCHECK(modifier.CanAffectExtension()); |
| ScriptingPermissionsModifier::SiteAccess site_access = |
| modifier.GetSiteAccess(web_contents->GetLastCommittedURL()); |
| if (site_access.has_all_sites_access) |
| return PAGE_ACCESS_RUN_ON_ALL_SITES; |
| if (site_access.has_site_access) |
| return PAGE_ACCESS_RUN_ON_SITE; |
| return PAGE_ACCESS_RUN_ON_CLICK; |
| } |
| |
| bool ExtensionContextMenuModel::IsPageAccessCommandEnabled( |
| const Extension& extension, |
| const GURL& url, |
| int command_id) const { |
| // The "Can't access this site" entry is, by design, always disabled. |
| if (command_id == PAGE_ACCESS_CANT_ACCESS) |
| return false; |
| |
| ScriptingPermissionsModifier modifier(profile_, &extension); |
| DCHECK(modifier.CanAffectExtension()); |
| |
| ScriptingPermissionsModifier::SiteAccess site_access = |
| modifier.GetSiteAccess(url); |
| |
| // Verify the extension wants access to the page - that's the only time these |
| // commands should be shown. |
| DCHECK(site_access.has_site_access || site_access.withheld_site_access || |
| extension.permissions_data()->HasAPIPermission( |
| APIPermission::kActiveTab)); |
| |
| switch (command_id) { |
| case PAGE_ACCESS_SUBMENU: |
| case PAGE_ACCESS_LEARN_MORE: |
| case PAGE_ACCESS_RUN_ON_CLICK: |
| // These are always enabled. |
| return true; |
| case PAGE_ACCESS_RUN_ON_SITE: |
| // The "on this site" option is only enabled if the extension wants to |
| // always run on the site without user interaction. |
| return site_access.has_site_access || site_access.withheld_site_access; |
| case PAGE_ACCESS_RUN_ON_ALL_SITES: |
| // The "on all sites" option is only enabled if the extension wants to be |
| // able to run everywhere. |
| return site_access.has_all_sites_access || |
| site_access.withheld_all_sites_access; |
| default: |
| break; |
| } |
| |
| NOTREACHED() << "Unexpected command id: " << command_id; |
| return false; |
| } |
| |
| void ExtensionContextMenuModel::CreatePageAccessSubmenu( |
| const Extension* extension) { |
| content::WebContents* web_contents = GetActiveWebContents(); |
| if (!web_contents) |
| return; |
| |
| ScriptingPermissionsModifier modifier(profile_, extension); |
| if (!modifier.CanAffectExtension()) |
| return; |
| |
| const GURL& url = web_contents->GetLastCommittedURL(); |
| ScriptingPermissionsModifier::SiteAccess site_access = |
| modifier.GetSiteAccess(url); |
| |
| bool has_active_tab = extension->permissions_data()->HasAPIPermission( |
| APIPermission::kActiveTab); |
| bool wants_site_access = |
| site_access.has_site_access || site_access.withheld_site_access; |
| if (!wants_site_access && !has_active_tab) { |
| AddItemWithStringId(PAGE_ACCESS_CANT_ACCESS, |
| IDS_EXTENSIONS_CONTEXT_MENU_CANT_ACCESS_PAGE); |
| return; |
| } |
| |
| const int kRadioGroup = 0; |
| page_access_submenu_ = std::make_unique<ui::SimpleMenuModel>(this); |
| |
| // Add the three options for "on click", "on this site", "on all sites". |
| // Though we always add these three, some may be disabled. |
| page_access_submenu_->AddRadioItemWithStringId( |
| PAGE_ACCESS_RUN_ON_CLICK, |
| IDS_EXTENSIONS_CONTEXT_MENU_PAGE_ACCESS_RUN_ON_CLICK, kRadioGroup); |
| page_access_submenu_->AddRadioItem( |
| PAGE_ACCESS_RUN_ON_SITE, |
| l10n_util::GetStringFUTF16( |
| IDS_EXTENSIONS_CONTEXT_MENU_PAGE_ACCESS_RUN_ON_SITE, |
| url_formatter::StripWWW(base::UTF8ToUTF16( |
| url::Origin::Create(web_contents->GetLastCommittedURL()) |
| .host()))), |
| kRadioGroup); |
| page_access_submenu_->AddRadioItemWithStringId( |
| PAGE_ACCESS_RUN_ON_ALL_SITES, |
| IDS_EXTENSIONS_CONTEXT_MENU_PAGE_ACCESS_RUN_ON_ALL_SITES, kRadioGroup); |
| |
| // Add the learn more link. |
| page_access_submenu_->AddSeparator(ui::NORMAL_SEPARATOR); |
| page_access_submenu_->AddItemWithStringId( |
| PAGE_ACCESS_LEARN_MORE, |
| IDS_EXTENSIONS_CONTEXT_MENU_PAGE_ACCESS_LEARN_MORE); |
| |
| AddSubMenuWithStringId(PAGE_ACCESS_SUBMENU, |
| IDS_EXTENSIONS_CONTEXT_MENU_PAGE_ACCESS, |
| page_access_submenu_.get()); |
| } |
| |
| void ExtensionContextMenuModel::HandlePageAccessCommand( |
| int command_id, |
| const Extension* extension) const { |
| content::WebContents* web_contents = GetActiveWebContents(); |
| if (!web_contents) |
| return; |
| |
| if (command_id == PAGE_ACCESS_LEARN_MORE) { |
| content::OpenURLParams params( |
| GURL(chrome_extension_constants::kRuntimeHostPermissionsHelpURL), |
| content::Referrer(), WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui::PAGE_TRANSITION_LINK, false); |
| browser_->OpenURL(params); |
| return; |
| } |
| |
| MenuEntries current_access = GetCurrentPageAccess(extension, web_contents); |
| if (command_id == current_access) |
| return; |
| |
| auto convert_page_access = [](int command_id) { |
| switch (command_id) { |
| case PAGE_ACCESS_RUN_ON_CLICK: |
| return ExtensionActionRunner::PageAccess::RUN_ON_CLICK; |
| case PAGE_ACCESS_RUN_ON_SITE: |
| return ExtensionActionRunner::PageAccess::RUN_ON_SITE; |
| case PAGE_ACCESS_RUN_ON_ALL_SITES: |
| return ExtensionActionRunner::PageAccess::RUN_ON_ALL_SITES; |
| } |
| NOTREACHED(); |
| return ExtensionActionRunner::PageAccess::RUN_ON_CLICK; |
| }; |
| |
| ExtensionActionRunner* runner = |
| ExtensionActionRunner::GetForWebContents(web_contents); |
| if (runner) |
| runner->HandlePageAccessModified(extension, |
| convert_page_access(current_access), |
| convert_page_access(command_id)); |
| } |
| |
| content::WebContents* ExtensionContextMenuModel::GetActiveWebContents() const { |
| return browser_->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| } // namespace extensions |