diff options
author | oshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-07 06:57:54 +0000 |
---|---|---|
committer | oshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-07 06:57:54 +0000 |
commit | cf8c490895593de5069dd6c8204344ade8d6651e (patch) | |
tree | 079214b331277cc9034b9d5e64fb684c57b350a1 | |
parent | 0f9e5b14cb75b2ac53ad3576125ba045f5ec53e2 (diff) | |
download | chromium_src-cf8c490895593de5069dd6c8204344ade8d6651e.zip chromium_src-cf8c490895593de5069dd6c8204344ade8d6651e.tar.gz chromium_src-cf8c490895593de5069dd6c8204344ade8d6651e.tar.bz2 |
Separate chrome independent part from RVContextMenu
and move it to components/renderer_context_menu/renderer_view_context_menu_base.{h|cc}
BUG=397320
TBR=blundell@chromium.org
Review URL: https://codereview.chromium.org/432003007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@287971 0039d316-1c4b-4281-b951-d872f2087c98
6 files changed, 659 insertions, 436 deletions
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc index 7d7207c..e9bae1b 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc @@ -142,11 +142,6 @@ const int kImageSearchThumbnailMinSize = 300 * 300; const int kImageSearchThumbnailMaxWidth = 600; const int kImageSearchThumbnailMaxHeight = 600; -// The range of command IDs reserved for content's custom menus. -// TODO(oshima): These values will be injected by embedders. -const int content_context_custom_first = IDC_CONTENT_CONTEXT_CUSTOM_FIRST; -const int content_context_custom_last = IDC_CONTENT_CONTEXT_CUSTOM_LAST; - // Maps UMA enumeration to IDC. IDC could be changed so we can't use // just them and |UMA_HISTOGRAM_CUSTOM_ENUMERATION|. // Never change mapping or reuse |enum_id|. Always push back new items. @@ -262,32 +257,6 @@ int FindUMAEnumValueForCommand(int id) { return -1; } -// Increments histogram value for used items specified by |id|. -void RecordUsedItem(int id) { - int enum_id = FindUMAEnumValueForCommand(id); - if (enum_id != -1) { - const size_t kMappingSize = arraysize(kUmaEnumToControlId); - UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Used", enum_id, - kUmaEnumToControlId[kMappingSize - 1].enum_id); - } else { - NOTREACHED() << "Update kUmaEnumToControlId. Unhanded IDC: " << id; - } -} - -// Increments histogram value for visible context menu item specified by |id|. -void RecordShownItem(int id) { - int enum_id = FindUMAEnumValueForCommand(id); - if (enum_id != -1) { - const size_t kMappingSize = arraysize(kUmaEnumToControlId); - UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Shown", enum_id, - kUmaEnumToControlId[kMappingSize - 1].enum_id); - } else { - // Just warning here. It's harder to maintain list of all possibly - // visible items than executable items. - DLOG(ERROR) << "Update kUmaEnumToControlId. Unhanded IDC: " << id; - } -} - // Usually a new tab is expected where this function is used, // however users should be able to open a tab in background // or in a new window. @@ -298,98 +267,6 @@ WindowOpenDisposition ForceNewTabDispositionFromEventFlags( return disposition == CURRENT_TAB ? NEW_FOREGROUND_TAB : disposition; } -bool IsCustomItemEnabled(const std::vector<content::MenuItem>& items, int id) { - DCHECK(RenderViewContextMenu::IsContentCustomCommandId(id)); - for (size_t i = 0; i < items.size(); ++i) { - int action_id = - RenderViewContextMenu::ConvertToContentCustomCommandId(items[i].action); - if (action_id == id) - return items[i].enabled; - if (items[i].type == content::MenuItem::SUBMENU) { - if (IsCustomItemEnabled(items[i].submenu, id)) - return true; - } - } - return false; -} - -bool IsCustomItemChecked(const std::vector<content::MenuItem>& items, int id) { - DCHECK(RenderViewContextMenu::IsContentCustomCommandId(id)); - for (size_t i = 0; i < items.size(); ++i) { - int action_id = - RenderViewContextMenu::ConvertToContentCustomCommandId(items[i].action); - if (action_id == id) - return items[i].checked; - if (items[i].type == content::MenuItem::SUBMENU) { - if (IsCustomItemChecked(items[i].submenu, id)) - return true; - } - } - return false; -} - -const size_t kMaxCustomMenuDepth = 5; -const size_t kMaxCustomMenuTotalItems = 1000; - -void AddCustomItemsToMenu(const std::vector<content::MenuItem>& items, - size_t depth, - size_t* total_items, - ui::SimpleMenuModel::Delegate* delegate, - ui::SimpleMenuModel* menu_model) { - if (depth > kMaxCustomMenuDepth) { - LOG(ERROR) << "Custom menu too deeply nested."; - return; - } - for (size_t i = 0; i < items.size(); ++i) { - int command_id = - RenderViewContextMenu::ConvertToContentCustomCommandId(items[i].action); - if (!RenderViewContextMenu::IsContentCustomCommandId(command_id)) { - LOG(ERROR) << "Custom menu action value out of range."; - return; - } - if (*total_items >= kMaxCustomMenuTotalItems) { - LOG(ERROR) << "Custom menu too large (too many items)."; - return; - } - (*total_items)++; - switch (items[i].type) { - case content::MenuItem::OPTION: - menu_model->AddItem( - RenderViewContextMenu::ConvertToContentCustomCommandId( - items[i].action), - items[i].label); - break; - case content::MenuItem::CHECKABLE_OPTION: - menu_model->AddCheckItem( - RenderViewContextMenu::ConvertToContentCustomCommandId( - items[i].action), - items[i].label); - break; - case content::MenuItem::GROUP: - // TODO(viettrungluu): I don't know what this is supposed to do. - NOTREACHED(); - break; - case content::MenuItem::SEPARATOR: - menu_model->AddSeparator(ui::NORMAL_SEPARATOR); - break; - case content::MenuItem::SUBMENU: { - ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate); - AddCustomItemsToMenu(items[i].submenu, depth + 1, total_items, delegate, - submenu); - menu_model->AddSubMenu( - RenderViewContextMenu::ConvertToContentCustomCommandId( - items[i].action), - items[i].label, - submenu); - break; - } - default: - NOTREACHED(); - break; - } - } -} - // Helper function to escape "&" as "&&". void EscapeAmpersands(base::string16* text) { base::ReplaceChars(*text, base::ASCIIToUTF16("&"), base::ASCIIToUTF16("&&"), @@ -401,21 +278,9 @@ PrefService* GetPrefs(content::BrowserContext* context) { return user_prefs::UserPrefs::Get(context); } -} // namespace - -// static -const size_t RenderViewContextMenu::kMaxSelectionTextLength = 50; - -// static -int RenderViewContextMenu::ConvertToContentCustomCommandId(int id) { - return content_context_custom_first + id; -} +bool custom_id_ranges_initialized = false; -// static -bool RenderViewContextMenu::IsContentCustomCommandId(int id) { - return id >= content_context_custom_first && - id <= content_context_custom_last; -} +} // namespace // static bool RenderViewContextMenu::IsDevToolsURL(const GURL& url) { @@ -434,22 +299,21 @@ static const int kSpellcheckRadioGroup = 1; RenderViewContextMenu::RenderViewContextMenu( content::RenderFrameHost* render_frame_host, const content::ContextMenuParams& params) - : params_(params), - source_web_contents_(WebContents::FromRenderFrameHost(render_frame_host)), - render_process_id_(render_frame_host->GetProcess()->GetID()), - render_frame_id_(render_frame_host->GetRoutingID()), - browser_context_(source_web_contents_->GetBrowserContext()), - menu_model_(this), + : RenderViewContextMenuBase(render_frame_host, params), extension_items_(browser_context_, this, &menu_model_, base::Bind(MenuItemMatchesParams, params_)), protocol_handler_submenu_model_(this), protocol_handler_registry_( - ProtocolHandlerRegistryFactory::GetForBrowserContext(GetProfile())), - command_executed_(false) { - content_type_.reset(ContextMenuContentTypeFactory::Create( - source_web_contents_, params)); + ProtocolHandlerRegistryFactory::GetForBrowserContext(GetProfile())) { + if (!custom_id_ranges_initialized) { + custom_id_ranges_initialized = true; + SetContentCustomCommandIdRange(IDC_CONTENT_CONTEXT_CUSTOM_FIRST, + IDC_CONTENT_CONTEXT_CUSTOM_LAST); + } + set_content_type(ContextMenuContentTypeFactory::Create( + source_web_contents_, params)); } RenderViewContextMenu::~RenderViewContextMenu() { @@ -457,17 +321,6 @@ RenderViewContextMenu::~RenderViewContextMenu() { // Menu construction functions ------------------------------------------------- -void RenderViewContextMenu::Init() { - InitMenu(); - if (toolkit_delegate_) - toolkit_delegate_->Init(&menu_model_); -} - -void RenderViewContextMenu::Cancel() { - if (toolkit_delegate_) - toolkit_delegate_->Cancel(); -} - static bool ExtensionPatternMatch(const extensions::URLPatternSet& patterns, const GURL& url) { // No patterns means no restriction, so that implicitly matches. @@ -615,16 +468,7 @@ void RenderViewContextMenu::AppendCurrentExtensionItems() { } void RenderViewContextMenu::InitMenu() { - if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_CUSTOM)) { - AppendCustomItems(); - - const bool has_selection = !params_.selection_text.empty(); - if (has_selection) { - // We will add more items if there's a selection, so add a separator. - // TODO(lazyboy): Clean up separator logic. - menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); - } - } + RenderViewContextMenuBase::InitMenu(); if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_PAGE)) AppendPageItems(); @@ -725,6 +569,37 @@ Profile* RenderViewContextMenu::GetProfile() { return Profile::FromBrowserContext(browser_context_); } +void RenderViewContextMenu::RecordUsedItem(int id) { + int enum_id = FindUMAEnumValueForCommand(id); + if (enum_id != -1) { + const size_t kMappingSize = arraysize(kUmaEnumToControlId); + UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Used", enum_id, + kUmaEnumToControlId[kMappingSize - 1].enum_id); + } else { + NOTREACHED() << "Update kUmaEnumToControlId. Unhanded IDC: " << id; + } +} + +void RenderViewContextMenu::RecordShownItem(int id) { + int enum_id = FindUMAEnumValueForCommand(id); + if (enum_id != -1) { + const size_t kMappingSize = arraysize(kUmaEnumToControlId); + UMA_HISTOGRAM_ENUMERATION("RenderViewContextMenu.Shown", enum_id, + kUmaEnumToControlId[kMappingSize - 1].enum_id); + } else { + // Just warning here. It's harder to maintain list of all possibly + // visible items than executable items. + DLOG(ERROR) << "Update kUmaEnumToControlId. Unhanded IDC: " << id; + } +} + +#if defined(ENABLE_PLUGINS) +void RenderViewContextMenu::HandleAuthorizeAllPlugins() { + ChromePluginServiceFilter::GetInstance()->AuthorizeAllPlugins( + source_web_contents_, false, std::string()); +} +#endif + void RenderViewContextMenu::AppendPrintPreviewItems() { #if defined(ENABLE_FULL_PRINTING) if (!print_preview_menu_observer_.get()) { @@ -747,57 +622,6 @@ const Extension* RenderViewContextMenu::GetExtension() const { source_web_contents_->GetRenderViewHost()); } -void RenderViewContextMenu::AddMenuItem(int command_id, - const base::string16& title) { - menu_model_.AddItem(command_id, title); -} - -void RenderViewContextMenu::AddCheckItem(int command_id, - const base::string16& title) { - menu_model_.AddCheckItem(command_id, title); -} - -void RenderViewContextMenu::AddSeparator() { - menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); -} - -void RenderViewContextMenu::AddSubMenu(int command_id, - const base::string16& label, - ui::MenuModel* model) { - menu_model_.AddSubMenu(command_id, label, model); -} - -void RenderViewContextMenu::UpdateMenuItem(int command_id, - bool enabled, - bool hidden, - const base::string16& label) { - if (toolkit_delegate_) { - toolkit_delegate_->UpdateMenuItem(command_id, - enabled, - hidden, - label); - } -} - -RenderViewHost* RenderViewContextMenu::GetRenderViewHost() const { - return source_web_contents_->GetRenderViewHost(); -} - -WebContents* RenderViewContextMenu::GetWebContents() const { - return source_web_contents_; -} - -BrowserContext* RenderViewContextMenu::GetBrowserContext() const { - return browser_context_; -} - -bool RenderViewContextMenu::AppendCustomItems() { - size_t total_items = 0; - AddCustomItemsToMenu(params_.custom_items, 0, &total_items, this, - &menu_model_); - return total_items > 0; -} - void RenderViewContextMenu::AppendDeveloperItems() { // Show Inspect Element in DevTools itself only in case of the debug // devtools build. @@ -1126,20 +950,11 @@ void RenderViewContextMenu::AppendProtocolHandlerSubMenu() { &protocol_handler_submenu_model_); } -void RenderViewContextMenu::AppendPlatformEditableItems() { -} - // Menu delegate functions ----------------------------------------------------- bool RenderViewContextMenu::IsCommandIdEnabled(int id) const { - // If this command is is added by one of our observers, we dispatch it to the - // observer. - ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_); - RenderViewContextMenuObserver* observer; - while ((observer = it.GetNext()) != NULL) { - if (observer->IsCommandIdSupported(id)) - return observer->IsCommandIdEnabled(id); - } + if (RenderViewContextMenuBase::IsCommandIdEnabled(id)) + return true; CoreTabHelper* core_tab_helper = CoreTabHelper::FromWebContents(source_web_contents_); @@ -1162,10 +977,6 @@ bool RenderViewContextMenu::IsCommandIdEnabled(int id) const { return prefs->GetBoolean(prefs::kEnableContinuousSpellcheck); } - // Custom items. - if (IsContentCustomCommandId(id)) - return IsCustomItemEnabled(params_.custom_items, id); - // Extension items. if (ContextMenuMatcher::IsExtensionsCustomCommandId(id)) return extension_items_.IsCommandIdEnabled(id); @@ -1428,14 +1239,8 @@ bool RenderViewContextMenu::IsCommandIdEnabled(int id) const { } bool RenderViewContextMenu::IsCommandIdChecked(int id) const { - // If this command is is added by one of our observers, we dispatch it to the - // observer. - ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_); - RenderViewContextMenuObserver* observer; - while ((observer = it.GetNext()) != NULL) { - if (observer->IsCommandIdSupported(id)) - return observer->IsCommandIdChecked(id); - } + if (RenderViewContextMenuBase::IsCommandIdChecked(id)) + return true; // See if the video is set to looping. if (id == IDC_CONTENT_CONTEXT_LOOP) { @@ -1448,10 +1253,6 @@ bool RenderViewContextMenu::IsCommandIdChecked(int id) const { WebContextMenuData::MediaControls) != 0; } - // Custom items. - if (IsContentCustomCommandId(id)) - return IsCustomItemChecked(params_.custom_items, id); - // Extension items. if (ContextMenuMatcher::IsExtensionsCustomCommandId(id)) return extension_items_.IsCommandIdChecked(id); @@ -1460,34 +1261,12 @@ bool RenderViewContextMenu::IsCommandIdChecked(int id) const { } void RenderViewContextMenu::ExecuteCommand(int id, int event_flags) { + RenderViewContextMenuBase::ExecuteCommand(id, event_flags); + if (command_executed_) + return; command_executed_ = true; - RecordUsedItem(id); - - // If this command is is added by one of our observers, we dispatch it to the - // observer. - ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_); - RenderViewContextMenuObserver* observer; - while ((observer = it.GetNext()) != NULL) { - if (observer->IsCommandIdSupported(id)) - return observer->ExecuteCommand(id); - } - RenderFrameHost* render_frame_host = - RenderFrameHost::FromID(render_process_id_, render_frame_id_); - - // Process custom actions range. - if (IsContentCustomCommandId(id)) { - unsigned action = id - content_context_custom_first; - const content::CustomContextMenuContext& context = params_.custom_context; -#if defined(ENABLE_PLUGINS) - if (context.request_id && !context.is_pepper_menu) { - ChromePluginServiceFilter::GetInstance()->AuthorizeAllPlugins( - source_web_contents_, false, std::string()); - } -#endif - source_web_contents_->ExecuteCustomContextMenuCommand(action, context); - return; - } + RenderFrameHost* render_frame_host = GetRenderFrameHost(); // Process extension menu items. if (ContextMenuMatcher::IsExtensionsCustomCommandId(id)) { @@ -1915,45 +1694,27 @@ ProtocolHandlerRegistry::ProtocolHandlerList return handlers; } -void RenderViewContextMenu::MenuWillShow(ui::SimpleMenuModel* source) { - for (int i = 0; i < source->GetItemCount(); ++i) { - if (source->IsVisibleAt(i) && - source->GetTypeAt(i) != ui::MenuModel::TYPE_SEPARATOR) { - RecordShownItem(source->GetCommandIdAt(i)); - } - } - - // Ignore notifications from submenus. - if (source != &menu_model_) - return; - - content::RenderWidgetHostView* view = - source_web_contents_->GetRenderWidgetHostView(); - if (view) - view->SetShowingContextMenu(true); - +void RenderViewContextMenu::NotifyMenuShown() { content::NotificationService::current()->Notify( chrome::NOTIFICATION_RENDER_VIEW_CONTEXT_MENU_SHOWN, content::Source<RenderViewContextMenu>(this), content::NotificationService::NoDetails()); } -void RenderViewContextMenu::MenuClosed(ui::SimpleMenuModel* source) { - // Ignore notifications from submenus. - if (source != &menu_model_) - return; - - content::RenderWidgetHostView* view = - source_web_contents_->GetRenderWidgetHostView(); - if (view) - view->SetShowingContextMenu(false); - source_web_contents_->NotifyContextMenuClosed(params_.custom_context); +void RenderViewContextMenu::NotifyURLOpened( + const GURL& url, + content::WebContents* new_contents) { + RetargetingDetails details; + details.source_web_contents = source_web_contents_; + details.source_render_frame_id = GetRenderFrameHost()->GetRoutingID(); + details.target_url = url; + details.target_web_contents = new_contents; + details.not_yet_in_tabstrip = false; - if (!command_executed_) { - FOR_EACH_OBSERVER(RenderViewContextMenuObserver, - observers_, - OnMenuCancel()); - } + content::NotificationService::current()->Notify( + chrome::NOTIFICATION_RETARGETING, + content::Source<Profile>(GetProfile()), + content::Details<RetargetingDetails>(&details)); } bool RenderViewContextMenu::IsDevCommandEnabled(int id) const { @@ -1982,42 +1743,12 @@ base::string16 RenderViewContextMenu::PrintableSelectionText() { // Controller functions -------------------------------------------------------- -void RenderViewContextMenu::OpenURL( - const GURL& url, const GURL& referring_url, - WindowOpenDisposition disposition, - content::PageTransition transition) { - content::Referrer referrer = content::Referrer::SanitizeForRequest( - url, - content::Referrer(referring_url.GetAsReferrer(), - params_.referrer_policy)); - - if (params_.link_url == url && disposition != OFF_THE_RECORD) - params_.custom_context.link_followed = url; - - WebContents* new_contents = source_web_contents_->OpenURL(OpenURLParams( - url, referrer, disposition, transition, false)); - if (!new_contents) - return; - - RetargetingDetails details; - details.source_web_contents = source_web_contents_; - details.source_render_frame_id = render_frame_id_; - details.target_url = url; - details.target_web_contents = new_contents; - details.not_yet_in_tabstrip = false; - content::NotificationService::current()->Notify( - chrome::NOTIFICATION_RETARGETING, - content::Source<Profile>(GetProfile()), - content::Details<RetargetingDetails>(&details)); -} - void RenderViewContextMenu::CopyImageAt(int x, int y) { source_web_contents_->GetRenderViewHost()->CopyImageAt(x, y); } void RenderViewContextMenu::GetImageThumbnailForSearch() { - RenderFrameHost* render_frame_host = - RenderFrameHost::FromID(render_process_id_, render_frame_id_); + RenderFrameHost* render_frame_host = GetRenderFrameHost(); if (!render_frame_host) return; render_frame_host->Send(new ChromeViewMsg_RequestThumbnailForContextNode( @@ -2029,8 +1760,7 @@ void RenderViewContextMenu::GetImageThumbnailForSearch() { void RenderViewContextMenu::Inspect(int x, int y) { content::RecordAction(UserMetricsAction("DevTools_InspectElement")); - RenderFrameHost* render_frame_host = - RenderFrameHost::FromID(render_process_id_, render_frame_id_); + RenderFrameHost* render_frame_host = GetRenderFrameHost(); if (!render_frame_host) return; DevToolsWindow::InspectElement(render_frame_host->GetRenderViewHost(), x, y); diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h index 5b5932e..e234867 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu.h +++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h @@ -15,6 +15,7 @@ #include "chrome/browser/extensions/context_menu_matcher.h" #include "chrome/browser/extensions/menu_manager.h" #include "components/renderer_context_menu/context_menu_content_type.h" +#include "components/renderer_context_menu/render_view_context_menu_base.h" #include "components/renderer_context_menu/render_view_context_menu_observer.h" #include "components/renderer_context_menu/render_view_context_menu_proxy.h" #include "content/public/common/context_menu_params.h" @@ -46,99 +47,20 @@ struct WebMediaPlayerAction; struct WebPluginAction; } -class RenderViewContextMenu : public ui::SimpleMenuModel::Delegate, - public RenderViewContextMenuProxy { +class RenderViewContextMenu : public RenderViewContextMenuBase { public: - // A delegate interface to communicate with the toolkit used by - // the embedder. - class ToolkitDelegate { - public: - virtual ~ToolkitDelegate() {} - // Initialize the toolkit's menu. - virtual void Init(ui::SimpleMenuModel* menu_model) = 0; - - virtual void Cancel() = 0; - - // Updates the actual menu items controlled by the toolkit. - virtual void UpdateMenuItem(int command_id, - bool enabled, - bool hidden, - const base::string16& title) = 0; - }; - - static const size_t kMaxSelectionTextLength; - - // Convert a command ID so that it fits within the range for - // content context menu. - static int ConvertToContentCustomCommandId(int id); - - // True if the given id is the one generated for content context menu. - static bool IsContentCustomCommandId(int id); - RenderViewContextMenu(content::RenderFrameHost* render_frame_host, const content::ContextMenuParams& params); virtual ~RenderViewContextMenu(); - // Initializes the context menu. - void Init(); - - // Programmatically closes the context menu. - void Cancel(); - - const ui::SimpleMenuModel& menu_model() const { return menu_model_; } - const content::ContextMenuParams& params() const { return params_; } - - // SimpleMenuModel::Delegate implementation. + // SimpleMenuModel::Delegate: virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE; - virtual void MenuWillShow(ui::SimpleMenuModel* source) OVERRIDE; - virtual void MenuClosed(ui::SimpleMenuModel* source) OVERRIDE; - - // RenderViewContextMenuProxy implementation. - virtual void AddMenuItem(int command_id, - const base::string16& title) OVERRIDE; - virtual void AddCheckItem(int command_id, - const base::string16& title) OVERRIDE; - virtual void AddSeparator() OVERRIDE; - virtual void AddSubMenu(int command_id, - const base::string16& label, - ui::MenuModel* model) OVERRIDE; - virtual void UpdateMenuItem(int command_id, - bool enabled, - bool hidden, - const base::string16& title) OVERRIDE; - virtual content::RenderViewHost* GetRenderViewHost() const OVERRIDE; - virtual content::WebContents* GetWebContents() const OVERRIDE; - virtual content::BrowserContext* GetBrowserContext() const OVERRIDE; protected: - void set_toolkit_delegate(scoped_ptr<ToolkitDelegate> delegate) { - toolkit_delegate_ = delegate.Pass(); - } - - ToolkitDelegate* toolkit_delegate() { - return toolkit_delegate_.get(); - } - - void InitMenu(); Profile* GetProfile(); - - // Platform specific functions. - virtual bool GetAcceleratorForCommandId( - int command_id, - ui::Accelerator* accelerator) = 0; - virtual void AppendPlatformEditableItems(); - - content::ContextMenuParams params_; - content::WebContents* source_web_contents_; - // The RenderFrameHost's IDs. - int render_process_id_; - int render_frame_id_; - content::BrowserContext* browser_context_; - - ui::SimpleMenuModel menu_model_; extensions::ContextMenuMatcher extension_items_; private: @@ -154,9 +76,19 @@ class RenderViewContextMenu : public ui::SimpleMenuModel::Delegate, static bool MenuItemMatchesParams(const content::ContextMenuParams& params, const extensions::MenuItem* item); + // RenderViewContextMenuBase: + virtual void InitMenu() OVERRIDE; + virtual void RecordShownItem(int id) OVERRIDE; + virtual void RecordUsedItem(int id) OVERRIDE; +#if defined(ENABLE_PLUGINS) + virtual void HandleAuthorizeAllPlugins() OVERRIDE; +#endif + virtual void NotifyMenuShown() OVERRIDE; + virtual void NotifyURLOpened(const GURL& url, + content::WebContents* new_contents) OVERRIDE; + // Gets the extension (if any) associated with the WebContents that we're in. const extensions::Extension* GetExtension() const; - bool AppendCustomItems(); void AppendDeveloperItems(); void AppendDevtoolsForUnpackedExtensions(); @@ -181,11 +113,6 @@ class RenderViewContextMenu : public ui::SimpleMenuModel::Delegate, void AppendSpellcheckOptionsSubMenu(); void AppendProtocolHandlerSubMenu(); - // Opens the specified URL string in a new tab. - void OpenURL(const GURL& url, const GURL& referrer, - WindowOpenDisposition disposition, - content::PageTransition transition); - // Copy to the clipboard an image located at a point in the RenderView void CopyImageAt(int x, int y); @@ -231,17 +158,6 @@ class RenderViewContextMenu : public ui::SimpleMenuModel::Delegate, scoped_ptr<PrintPreviewContextMenuObserver> print_preview_menu_observer_; #endif - // Our observers. - mutable ObserverList<RenderViewContextMenuObserver> observers_; - - // Whether a command has been executed. Used to track whether menu observers - // should be notified of menu closing without execution. - bool command_executed_; - - scoped_ptr<ContextMenuContentType> content_type_; - - scoped_ptr<ToolkitDelegate> toolkit_delegate_; - DISALLOW_COPY_AND_ASSIGN(RenderViewContextMenu); }; diff --git a/components/components.gyp b/components/components.gyp index 65e7ecd..bd3a0d1 100644 --- a/components/components.gyp +++ b/components/components.gyp @@ -44,7 +44,6 @@ 'pref_registry.gypi', 'query_parser.gypi', 'rappor.gypi', - 'renderer_context_menu.gypi', 'search.gypi', 'search_provider_logos.gypi', 'signin.gypi', @@ -103,6 +102,7 @@ 'includes': [ 'autocomplete.gypi', 'gcm_driver.gypi', + 'renderer_context_menu.gypi', 'search_engines.gypi', 'sync_driver.gypi', 'invalidation.gypi', diff --git a/components/renderer_context_menu.gypi b/components/renderer_context_menu.gypi index 2ca2715..90ad5bb 100644 --- a/components/renderer_context_menu.gypi +++ b/components/renderer_context_menu.gypi @@ -9,6 +9,8 @@ 'dependencies': [ '../base/base.gyp:base', '../content/content.gyp:content_browser', + '../components/components.gyp:search_engines', + '../components/components.gyp:component_metrics_proto', ], 'include_dirs': [ '..', @@ -18,6 +20,8 @@ 'renderer_context_menu/context_menu_content_type.h', 'renderer_context_menu/context_menu_delegate.cc', 'renderer_context_menu/context_menu_delegate.h', + 'renderer_context_menu/render_view_context_menu_base.cc', + 'renderer_context_menu/render_view_context_menu_base.h', 'renderer_context_menu/render_view_context_menu_observer.cc', 'renderer_context_menu/render_view_context_menu_observer.h', 'renderer_context_menu/render_view_context_menu_proxy.h', diff --git a/components/renderer_context_menu/render_view_context_menu_base.cc b/components/renderer_context_menu/render_view_context_menu_base.cc new file mode 100644 index 0000000..3d0f89a --- /dev/null +++ b/components/renderer_context_menu/render_view_context_menu_base.cc @@ -0,0 +1,387 @@ +// Copyright 2014 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 "components/renderer_context_menu/render_view_context_menu_base.h" + +#include <algorithm> +#include <utility> + +#include "base/command_line.h" +#include "base/logging.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/menu_item.h" +#include "extensions/browser/extension_host.h" +#include "extensions/browser/extension_system.h" +#include "extensions/browser/view_type_utils.h" +#include "extensions/common/extension.h" +#include "third_party/WebKit/public/web/WebContextMenuData.h" + +using blink::WebContextMenuData; +using blink::WebString; +using blink::WebURL; +using content::BrowserContext; +using content::OpenURLParams; +using content::RenderFrameHost; +using content::RenderViewHost; +using content::WebContents; + +namespace { + +// The (inclusive) range of command IDs reserved for content's custom menus. +int content_context_custom_first = -1; +int content_context_custom_last = -1; + +bool IsCustomItemEnabledInternal(const std::vector<content::MenuItem>& items, + int id) { + DCHECK(RenderViewContextMenuBase::IsContentCustomCommandId(id)); + for (size_t i = 0; i < items.size(); ++i) { + int action_id = RenderViewContextMenuBase::ConvertToContentCustomCommandId( + items[i].action); + if (action_id == id) + return items[i].enabled; + if (items[i].type == content::MenuItem::SUBMENU) { + if (IsCustomItemEnabledInternal(items[i].submenu, id)) + return true; + } + } + return false; +} + +bool IsCustomItemCheckedInternal(const std::vector<content::MenuItem>& items, + int id) { + DCHECK(RenderViewContextMenuBase::IsContentCustomCommandId(id)); + for (size_t i = 0; i < items.size(); ++i) { + int action_id = RenderViewContextMenuBase::ConvertToContentCustomCommandId( + items[i].action); + if (action_id == id) + return items[i].checked; + if (items[i].type == content::MenuItem::SUBMENU) { + if (IsCustomItemCheckedInternal(items[i].submenu, id)) + return true; + } + } + return false; +} + +const size_t kMaxCustomMenuDepth = 5; +const size_t kMaxCustomMenuTotalItems = 1000; + +void AddCustomItemsToMenu(const std::vector<content::MenuItem>& items, + size_t depth, + size_t* total_items, + ui::SimpleMenuModel::Delegate* delegate, + ui::SimpleMenuModel* menu_model) { + if (depth > kMaxCustomMenuDepth) { + LOG(ERROR) << "Custom menu too deeply nested."; + return; + } + for (size_t i = 0; i < items.size(); ++i) { + int command_id = RenderViewContextMenuBase::ConvertToContentCustomCommandId( + items[i].action); + if (!RenderViewContextMenuBase::IsContentCustomCommandId(command_id)) { + LOG(ERROR) << "Custom menu action value out of range."; + return; + } + if (*total_items >= kMaxCustomMenuTotalItems) { + LOG(ERROR) << "Custom menu too large (too many items)."; + return; + } + (*total_items)++; + switch (items[i].type) { + case content::MenuItem::OPTION: + menu_model->AddItem( + RenderViewContextMenuBase::ConvertToContentCustomCommandId( + items[i].action), + items[i].label); + break; + case content::MenuItem::CHECKABLE_OPTION: + menu_model->AddCheckItem( + RenderViewContextMenuBase::ConvertToContentCustomCommandId( + items[i].action), + items[i].label); + break; + case content::MenuItem::GROUP: + // TODO(viettrungluu): I don't know what this is supposed to do. + NOTREACHED(); + break; + case content::MenuItem::SEPARATOR: + menu_model->AddSeparator(ui::NORMAL_SEPARATOR); + break; + case content::MenuItem::SUBMENU: { + ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate); + AddCustomItemsToMenu(items[i].submenu, depth + 1, total_items, delegate, + submenu); + menu_model->AddSubMenu( + RenderViewContextMenuBase::ConvertToContentCustomCommandId( + items[i].action), + items[i].label, + submenu); + break; + } + default: + NOTREACHED(); + break; + } + } +} + +} // namespace + +// static +void RenderViewContextMenuBase::SetContentCustomCommandIdRange( + int first, int last) { + // The range is inclusive. + content_context_custom_first = first; + content_context_custom_last = last; +} + +// static +const size_t RenderViewContextMenuBase::kMaxSelectionTextLength = 50; + +// static +int RenderViewContextMenuBase::ConvertToContentCustomCommandId(int id) { + return content_context_custom_first + id; +} + +// static +bool RenderViewContextMenuBase::IsContentCustomCommandId(int id) { + return id >= content_context_custom_first && + id <= content_context_custom_last; +} + +RenderViewContextMenuBase::RenderViewContextMenuBase( + content::RenderFrameHost* render_frame_host, + const content::ContextMenuParams& params) + : params_(params), + source_web_contents_(WebContents::FromRenderFrameHost(render_frame_host)), + browser_context_(source_web_contents_->GetBrowserContext()), + menu_model_(this), + command_executed_(false), + render_process_id_(render_frame_host->GetProcess()->GetID()), + render_frame_id_(render_frame_host->GetRoutingID()) { +} + +RenderViewContextMenuBase::~RenderViewContextMenuBase() { +} + +// Menu construction functions ------------------------------------------------- + +void RenderViewContextMenuBase::Init() { + // Command id range must have been already initializerd. + DCHECK_NE(-1, content_context_custom_first); + DCHECK_NE(-1, content_context_custom_last); + + InitMenu(); + if (toolkit_delegate_) + toolkit_delegate_->Init(&menu_model_); +} + +void RenderViewContextMenuBase::Cancel() { + if (toolkit_delegate_) + toolkit_delegate_->Cancel(); +} + +void RenderViewContextMenuBase::InitMenu() { + if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_CUSTOM)) { + AppendCustomItems(); + + const bool has_selection = !params_.selection_text.empty(); + if (has_selection) { + // We will add more items if there's a selection, so add a separator. + // TODO(lazyboy): Clean up separator logic. + menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); + } + } +} + +void RenderViewContextMenuBase::AddMenuItem(int command_id, + const base::string16& title) { + menu_model_.AddItem(command_id, title); +} + +void RenderViewContextMenuBase::AddCheckItem(int command_id, + const base::string16& title) { + menu_model_.AddCheckItem(command_id, title); +} + +void RenderViewContextMenuBase::AddSeparator() { + menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); +} + +void RenderViewContextMenuBase::AddSubMenu(int command_id, + const base::string16& label, + ui::MenuModel* model) { + menu_model_.AddSubMenu(command_id, label, model); +} + +void RenderViewContextMenuBase::UpdateMenuItem(int command_id, + bool enabled, + bool hidden, + const base::string16& label) { + if (toolkit_delegate_) { + toolkit_delegate_->UpdateMenuItem(command_id, + enabled, + hidden, + label); + } +} + +RenderViewHost* RenderViewContextMenuBase::GetRenderViewHost() const { + return source_web_contents_->GetRenderViewHost(); +} + +WebContents* RenderViewContextMenuBase::GetWebContents() const { + return source_web_contents_; +} + +BrowserContext* RenderViewContextMenuBase::GetBrowserContext() const { + return browser_context_; +} + +bool RenderViewContextMenuBase::AppendCustomItems() { + size_t total_items = 0; + AddCustomItemsToMenu(params_.custom_items, 0, &total_items, this, + &menu_model_); + return total_items > 0; +} + +// Menu delegate functions ----------------------------------------------------- + +bool RenderViewContextMenuBase::IsCommandIdEnabled(int id) const { + // If this command is is added by one of our observers, we dispatch + // it to the observer. + ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_); + RenderViewContextMenuObserver* observer; + while ((observer = it.GetNext()) != NULL) { + if (observer->IsCommandIdSupported(id)) + return observer->IsCommandIdEnabled(id); + } + + // Custom items. + if (IsContentCustomCommandId(id)) + return IsCustomItemEnabled(id); + + return false; +} + +bool RenderViewContextMenuBase::IsCommandIdChecked(int id) const { + // If this command is is added by one of our observers, we dispatch it to the + // observer. + ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_); + RenderViewContextMenuObserver* observer; + while ((observer = it.GetNext()) != NULL) { + if (observer->IsCommandIdSupported(id)) + return observer->IsCommandIdChecked(id); + } + + // Custom items. + if (IsContentCustomCommandId(id)) + return IsCustomItemChecked(id); + + return false; +} + +void RenderViewContextMenuBase::ExecuteCommand(int id, int event_flags) { + command_executed_ = true; + RecordUsedItem(id); + + // If this command is is added by one of our observers, we dispatch + // it to the observer. + ObserverListBase<RenderViewContextMenuObserver>::Iterator it(observers_); + RenderViewContextMenuObserver* observer; + while ((observer = it.GetNext()) != NULL) { + if (observer->IsCommandIdSupported(id)) + return observer->ExecuteCommand(id); + } + + // Process custom actions range. + if (IsContentCustomCommandId(id)) { + unsigned action = id - content_context_custom_first; + const content::CustomContextMenuContext& context = params_.custom_context; +#if defined(ENABLE_PLUGINS) + if (context.request_id && !context.is_pepper_menu) + HandleAuthorizeAllPlugins(); +#endif + source_web_contents_->ExecuteCustomContextMenuCommand(action, context); + return; + } + command_executed_ = false; +} + +void RenderViewContextMenuBase::MenuWillShow(ui::SimpleMenuModel* source) { + for (int i = 0; i < source->GetItemCount(); ++i) { + if (source->IsVisibleAt(i) && + source->GetTypeAt(i) != ui::MenuModel::TYPE_SEPARATOR) { + RecordShownItem(source->GetCommandIdAt(i)); + } + } + + // Ignore notifications from submenus. + if (source != &menu_model_) + return; + + content::RenderWidgetHostView* view = + source_web_contents_->GetRenderWidgetHostView(); + if (view) + view->SetShowingContextMenu(true); + + NotifyMenuShown(); +} + +void RenderViewContextMenuBase::MenuClosed(ui::SimpleMenuModel* source) { + // Ignore notifications from submenus. + if (source != &menu_model_) + return; + + content::RenderWidgetHostView* view = + source_web_contents_->GetRenderWidgetHostView(); + if (view) + view->SetShowingContextMenu(false); + source_web_contents_->NotifyContextMenuClosed(params_.custom_context); + + if (!command_executed_) { + FOR_EACH_OBSERVER(RenderViewContextMenuObserver, + observers_, + OnMenuCancel()); + } +} + +RenderFrameHost* RenderViewContextMenuBase::GetRenderFrameHost() { + return RenderFrameHost::FromID(render_process_id_, render_frame_id_); +} + +// Controller functions -------------------------------------------------------- + +void RenderViewContextMenuBase::OpenURL( + const GURL& url, const GURL& referring_url, + WindowOpenDisposition disposition, + content::PageTransition transition) { + content::Referrer referrer = content::Referrer::SanitizeForRequest( + url, + content::Referrer(referring_url.GetAsReferrer(), + params_.referrer_policy)); + + if (params_.link_url == url && disposition != OFF_THE_RECORD) + params_.custom_context.link_followed = url; + + WebContents* new_contents = source_web_contents_->OpenURL(OpenURLParams( + url, referrer, disposition, transition, false)); + if (!new_contents) + return; + + NotifyURLOpened(url, new_contents); +} + +bool RenderViewContextMenuBase::IsCustomItemChecked(int id) const { + return IsCustomItemCheckedInternal(params_.custom_items, id); +} + +bool RenderViewContextMenuBase::IsCustomItemEnabled(int id) const { + return IsCustomItemEnabledInternal(params_.custom_items, id); +} + diff --git a/components/renderer_context_menu/render_view_context_menu_base.h b/components/renderer_context_menu/render_view_context_menu_base.h new file mode 100644 index 0000000..9d4ac63 --- /dev/null +++ b/components/renderer_context_menu/render_view_context_menu_base.h @@ -0,0 +1,186 @@ +// Copyright 2014 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. + +#ifndef COMPONENTS_RENDERER_CONTEXT_MENU_RENDER_VIEW_CONTEXT_MENU_BASE_H_ +#define COMPONENTS_RENDERER_CONTEXT_MENU_RENDER_VIEW_CONTEXT_MENU_BASE_H_ + +#include <map> +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/observer_list.h" +#include "base/strings/string16.h" +#include "components/renderer_context_menu/context_menu_content_type.h" +#include "components/renderer_context_menu/render_view_context_menu_observer.h" +#include "components/renderer_context_menu/render_view_context_menu_proxy.h" +#include "content/public/common/context_menu_params.h" +#include "content/public/common/page_transition_types.h" +#include "ui/base/models/simple_menu_model.h" +#include "ui/base/window_open_disposition.h" + +namespace content { +class RenderFrameHost; +class WebContents; +} + +namespace gfx { +class Point; +} + +namespace blink { +struct WebMediaPlayerAction; +struct WebPluginAction; +} + +class RenderViewContextMenuBase : public ui::SimpleMenuModel::Delegate, + public RenderViewContextMenuProxy { + public: + // A delegate interface to communicate with the toolkit used by + // the embedder. + class ToolkitDelegate { + public: + virtual ~ToolkitDelegate() {} + // Initialize the toolkit's menu. + virtual void Init(ui::SimpleMenuModel* menu_model) = 0; + + virtual void Cancel() = 0; + + // Updates the actual menu items controlled by the toolkit. + virtual void UpdateMenuItem(int command_id, + bool enabled, + bool hidden, + const base::string16& title) = 0; + }; + + static const size_t kMaxSelectionTextLength; + + static void SetContentCustomCommandIdRange(int first, int last); + + // Convert a command ID so that it fits within the range for + // content context menu. + static int ConvertToContentCustomCommandId(int id); + + // True if the given id is the one generated for content context menu. + static bool IsContentCustomCommandId(int id); + + RenderViewContextMenuBase(content::RenderFrameHost* render_frame_host, + const content::ContextMenuParams& params); + + virtual ~RenderViewContextMenuBase(); + + // Initializes the context menu. + void Init(); + + // Programmatically closes the context menu. + void Cancel(); + + const ui::SimpleMenuModel& menu_model() const { return menu_model_; } + const content::ContextMenuParams& params() const { return params_; } + + // SimpleMenuModel::Delegate implementation. + virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; + virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; + virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE; + virtual void MenuWillShow(ui::SimpleMenuModel* source) OVERRIDE; + virtual void MenuClosed(ui::SimpleMenuModel* source) OVERRIDE; + + // RenderViewContextMenuProxy implementation. + virtual void AddMenuItem(int command_id, + const base::string16& title) OVERRIDE; + virtual void AddCheckItem(int command_id, + const base::string16& title) OVERRIDE; + virtual void AddSeparator() OVERRIDE; + virtual void AddSubMenu(int command_id, + const base::string16& label, + ui::MenuModel* model) OVERRIDE; + virtual void UpdateMenuItem(int command_id, + bool enabled, + bool hidden, + const base::string16& title) OVERRIDE; + virtual content::RenderViewHost* GetRenderViewHost() const OVERRIDE; + virtual content::WebContents* GetWebContents() const OVERRIDE; + virtual content::BrowserContext* GetBrowserContext() const OVERRIDE; + + protected: + friend class RenderViewContextMenuTest; + friend class RenderViewContextMenuPrefsTest; + + void set_content_type(ContextMenuContentType* content_type) { + content_type_.reset(content_type); + } + + void set_toolkit_delegate(scoped_ptr<ToolkitDelegate> delegate) { + toolkit_delegate_ = delegate.Pass(); + } + + ToolkitDelegate* toolkit_delegate() { + return toolkit_delegate_.get(); + } + + // TODO(oshima): Make these methods delegate. + + // Menu Construction. + virtual void InitMenu(); + + // Increments histogram value for used items specified by |id|. + virtual void RecordUsedItem(int id) = 0; + + // Increments histogram value for visible context menu item specified by |id|. + virtual void RecordShownItem(int id) = 0; + +#if defined(ENABLE_PLUGINS) + virtual void HandleAuthorizeAllPlugins() = 0; +#endif + + // Returns the accelerator for given |command_id|. + virtual bool GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) = 0; + + // Subclasses should send notification. + virtual void NotifyMenuShown() = 0; + virtual void NotifyURLOpened(const GURL& url, + content::WebContents* new_contents) = 0; + + // TODO(oshima): Remove this. + virtual void AppendPlatformEditableItems() {} + + content::RenderFrameHost* GetRenderFrameHost(); + + bool IsCustomItemChecked(int id) const; + bool IsCustomItemEnabled(int id) const; + + // Opens the specified URL string in a new tab. + void OpenURL(const GURL& url, const GURL& referrer, + WindowOpenDisposition disposition, + content::PageTransition transition); + + content::ContextMenuParams params_; + content::WebContents* source_web_contents_; + content::BrowserContext* browser_context_; + + ui::SimpleMenuModel menu_model_; + + // Our observers. + mutable ObserverList<RenderViewContextMenuObserver> observers_; + + // Whether a command has been executed. Used to track whether menu observers + // should be notified of menu closing without execution. + bool command_executed_; + + scoped_ptr<ContextMenuContentType> content_type_; + + private: + bool AppendCustomItems(); + + // The RenderFrameHost's IDs. + int render_process_id_; + int render_frame_id_; + + scoped_ptr<ToolkitDelegate> toolkit_delegate_; + + DISALLOW_COPY_AND_ASSIGN(RenderViewContextMenuBase); +}; + +#endif // COMPONENTS_RENDERER_CONTEXT_MENU_RENDER_VIEW_CONTEXT_MENU_BASE_H_ |