diff options
author | csilv@chromium.org <csilv@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-26 00:55:05 +0000 |
---|---|---|
committer | csilv@chromium.org <csilv@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-26 00:55:05 +0000 |
commit | 2c1d54def26b25261727faef5bfc90779d40c6b1 (patch) | |
tree | e7fea4cd49639aa13f03d4fe37e5b78e66e884af | |
parent | 1ad935e4d363f03cca2d3f80155aa301788446ea (diff) | |
download | chromium_src-2c1d54def26b25261727faef5bfc90779d40c6b1.zip chromium_src-2c1d54def26b25261727faef5bfc90779d40c6b1.tar.gz chromium_src-2c1d54def26b25261727faef5bfc90779d40c6b1.tar.bz2 |
Revert 158691 - Give platform apps control over launcher right-click context menu.
This moves most of the extension context menu related code from RenderViewContextMenu to a seperate class, which is then used in two other places as well.
Reverted due to failures on Mac testers in: ExtensionContextMenuBrowserLazyTest.EventPage
http://build.chromium.org/p/chromium.mac/builders/Mac%2010.7%20Tests%20(dbg)(1)/builds/1693/steps/browser_tests/logs/EventPage
BUG=143222
Review URL: https://chromiumcodereview.appspot.com/10918103
TBR=mek@chromium.org
Review URL: https://codereview.chromium.org/10984034
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@158705 0039d316-1c4b-4281-b951-d872f2087c98
15 files changed, 274 insertions, 473 deletions
diff --git a/chrome/browser/extensions/api/context_menu/context_menu_api.cc b/chrome/browser/extensions/api/context_menu/context_menu_api.cc index f090885..102d5ee 100644 --- a/chrome/browser/extensions/api/context_menu/context_menu_api.cc +++ b/chrome/browser/extensions/api/context_menu/context_menu_api.cc @@ -75,9 +75,6 @@ extensions::MenuItem::ContextList GetContexts( case PropertyWithEnumT::CONTEXTS_ELEMENT_FRAME: contexts.Add(extensions::MenuItem::FRAME); break; - case PropertyWithEnumT::CONTEXTS_ELEMENT_LAUNCHER: - contexts.Add(extensions::MenuItem::LAUNCHER); - break; case PropertyWithEnumT::CONTEXTS_ELEMENT_NONE: NOTREACHED(); } diff --git a/chrome/browser/extensions/context_menu_matcher.cc b/chrome/browser/extensions/context_menu_matcher.cc deleted file mode 100644 index 5c7b7a2..0000000 --- a/chrome/browser/extensions/context_menu_matcher.cc +++ /dev/null @@ -1,231 +0,0 @@ -// 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 "base/utf_string_conversions.h" -#include "chrome/app/chrome_command_ids.h" -#include "chrome/browser/extensions/context_menu_matcher.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/profiles/profile.h" -#include "content/public/common/context_menu_params.h" -#include "ui/gfx/favicon_size.h" - -namespace extensions { - -// static -const size_t ContextMenuMatcher::kMaxExtensionItemTitleLength = 75; - -ContextMenuMatcher::ContextMenuMatcher( - Profile* profile, - ui::SimpleMenuModel::Delegate* delegate, - ui::SimpleMenuModel* menu_model, - const base::Callback<bool(const MenuItem*)>& filter) - : profile_(profile), menu_model_(menu_model), delegate_(delegate), - filter_(filter) { -} - -void ContextMenuMatcher::AppendExtensionItems(const std::string& extension_id, - const string16& selection_text, - int* index) -{ - ExtensionService* service = profile_->GetExtensionService(); - MenuManager* manager = service->menu_manager(); - const Extension* extension = service->GetExtensionById(extension_id, false); - DCHECK_GE(*index, 0); - int max_index = - IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST - IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; - if (!extension || *index >= max_index) - return; - - // Find matching items. - const MenuItem::List* all_items = manager->MenuItems(extension_id); - if (!all_items || all_items->empty()) - return; - bool can_cross_incognito = service->CanCrossIncognito(extension); - MenuItem::List items = GetRelevantExtensionItems(*all_items, - can_cross_incognito); - - if (items.empty()) - return; - - // If this is the first extension-provided menu item, and there are other - // items in the menu, and the last item is not a separator add a separator. - if (*index == 0 && menu_model_->GetItemCount() && - menu_model_->GetTypeAt(menu_model_->GetItemCount() - 1) != - ui::MenuModel::TYPE_SEPARATOR) - menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); - - // Extensions (other than platform apps) are only allowed one top-level slot - // (and it can't be a radio or checkbox item because we are going to put the - // extension icon next to it). - // If they have more than that, we automatically push them into a submenu. - if (extension->is_platform_app()) { - RecursivelyAppendExtensionItems(items, can_cross_incognito, selection_text, - menu_model_, index); - } else { - int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; - string16 title; - MenuItem::List submenu_items; - - if (items.size() > 1 || items[0]->type() != MenuItem::NORMAL) { - title = UTF8ToUTF16(extension->name()); - submenu_items = items; - } else { - MenuItem* item = items[0]; - extension_item_map_[menu_id] = item->id(); - title = item->TitleWithReplacement(selection_text, - kMaxExtensionItemTitleLength); - submenu_items = GetRelevantExtensionItems(item->children(), - can_cross_incognito); - } - - // Now add our item(s) to the menu_model_. - if (submenu_items.empty()) { - menu_model_->AddItem(menu_id, title); - } else { - ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate_); - extension_menu_models_.push_back(submenu); - menu_model_->AddSubMenu(menu_id, title, submenu); - RecursivelyAppendExtensionItems(submenu_items, can_cross_incognito, - selection_text, submenu, index); - } - SetExtensionIcon(extension_id); - } -} - -void ContextMenuMatcher::Clear() { - extension_item_map_.clear(); - extension_menu_models_.clear(); -} - -bool ContextMenuMatcher::IsCommandIdChecked(int command_id) const { - MenuItem* item = GetExtensionMenuItem(command_id); - if (!item) - return false; - return item->checked(); -} - -bool ContextMenuMatcher::IsCommandIdEnabled(int command_id) const { - MenuItem* item = GetExtensionMenuItem(command_id); - if (!item) - return true; - return item->enabled(); -} - -void ContextMenuMatcher::ExecuteCommand(int command_id, - const content::ContextMenuParams& params) { - MenuManager* manager = profile_->GetExtensionService()->menu_manager(); - MenuItem* item = GetExtensionMenuItem(command_id); - if (!item) - return; - - manager->ExecuteCommand(profile_, NULL, params, item->id()); -} - -MenuItem::List ContextMenuMatcher::GetRelevantExtensionItems( - const MenuItem::List& items, - bool can_cross_incognito) { - MenuItem::List result; - for (MenuItem::List::const_iterator i = items.begin(); - i != items.end(); ++i) { - const MenuItem* item = *i; - - if (!filter_.Run(item)) - continue; - - if (item->id().incognito == profile_->IsOffTheRecord() || - can_cross_incognito) - result.push_back(*i); - } - return result; -} - -void ContextMenuMatcher::RecursivelyAppendExtensionItems( - const MenuItem::List& items, - bool can_cross_incognito, - const string16& selection_text, - ui::SimpleMenuModel* menu_model, - int* index) -{ - MenuItem::Type last_type = MenuItem::NORMAL; - int radio_group_id = 1; - - for (MenuItem::List::const_iterator i = items.begin(); - i != items.end(); ++i) { - MenuItem* item = *i; - - // If last item was of type radio but the current one isn't, auto-insert - // a separator. The converse case is handled below. - if (last_type == MenuItem::RADIO && - item->type() != MenuItem::RADIO) { - menu_model->AddSeparator(ui::NORMAL_SEPARATOR); - last_type = MenuItem::SEPARATOR; - } - - int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; - if (menu_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) - return; - extension_item_map_[menu_id] = item->id(); - string16 title = item->TitleWithReplacement(selection_text, - kMaxExtensionItemTitleLength); - if (item->type() == MenuItem::NORMAL) { - MenuItem::List children = - GetRelevantExtensionItems(item->children(), can_cross_incognito); - if (children.empty()) { - menu_model->AddItem(menu_id, title); - } else { - ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(delegate_); - extension_menu_models_.push_back(submenu); - menu_model->AddSubMenu(menu_id, title, submenu); - RecursivelyAppendExtensionItems(children, can_cross_incognito, - selection_text, submenu, index); - } - } else if (item->type() == MenuItem::CHECKBOX) { - menu_model->AddCheckItem(menu_id, title); - } else if (item->type() == MenuItem::RADIO) { - if (i != items.begin() && - last_type != MenuItem::RADIO) { - radio_group_id++; - - // Auto-append a separator if needed. - if (last_type != MenuItem::SEPARATOR) - menu_model->AddSeparator(ui::NORMAL_SEPARATOR); - } - - menu_model->AddRadioItem(menu_id, title, radio_group_id); - } else if (item->type() == MenuItem::SEPARATOR) { - if (i != items.begin() && last_type != MenuItem::SEPARATOR) { - menu_model->AddSeparator(ui::NORMAL_SEPARATOR); - } - } - last_type = item->type(); - } -} - -MenuItem* ContextMenuMatcher::GetExtensionMenuItem(int id) const { - MenuManager* manager = profile_->GetExtensionService()->menu_manager(); - std::map<int, MenuItem::Id>::const_iterator i = - extension_item_map_.find(id); - if (i != extension_item_map_.end()) { - MenuItem* item = manager->GetItemById(i->second); - if (item) - return item; - } - return NULL; -} - -void ContextMenuMatcher::SetExtensionIcon(const std::string& extension_id) { - ExtensionService* service = profile_->GetExtensionService(); - MenuManager* menu_manager = service->menu_manager(); - - int index = menu_model_->GetItemCount() - 1; - DCHECK_GE(index, 0); - - const SkBitmap& icon = menu_manager->GetIconForExtension(extension_id); - DCHECK(icon.width() == gfx::kFaviconSize); - DCHECK(icon.height() == gfx::kFaviconSize); - - menu_model_->SetIcon(index, gfx::Image(icon)); -} - -} // namespace extensions diff --git a/chrome/browser/extensions/context_menu_matcher.h b/chrome/browser/extensions/context_menu_matcher.h deleted file mode 100644 index 7d497f6..0000000 --- a/chrome/browser/extensions/context_menu_matcher.h +++ /dev/null @@ -1,86 +0,0 @@ -// 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. - -#ifndef CHROME_BROWSER_EXTENSIONS_CONTEXT_MENU_MATCHER_H_ -#define CHROME_BROWSER_EXTENSIONS_CONTEXT_MENU_MATCHER_H_ - -#include <map> - -#include "base/callback.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/scoped_vector.h" -#include "chrome/browser/extensions/menu_manager.h" -#include "ui/base/models/simple_menu_model.h" - -class ExtensionContextMenuBrowserTest; -class Profile; - -namespace extensions { - -// This class contains code that is shared between the various places where -// context menu items added by the extension or app should be shown. -class ContextMenuMatcher { - public: - static const size_t kMaxExtensionItemTitleLength; - - // The |filter| will be called on possibly matching menu items, and its - // result is used to determine which items to actually append to the menu. - ContextMenuMatcher(Profile* profile, - ui::SimpleMenuModel::Delegate* delegate, - ui::SimpleMenuModel* menu_model, - const base::Callback<bool(const MenuItem*)>& filter); - - // This is a helper function to append items for one particular extension. - // The |index| parameter is used for assigning id's, and is incremented for - // each item actually added. - void AppendExtensionItems(const std::string& extension_id, - const string16& selection_text, - int* index); - - void Clear(); - - bool IsCommandIdChecked(int command_id) const; - bool IsCommandIdEnabled(int command_id) const; - void ExecuteCommand(int command_id, - const content::ContextMenuParams& params); - - private: - friend class ::ExtensionContextMenuBrowserTest; - - MenuItem::List GetRelevantExtensionItems( - const MenuItem::List& items, - bool can_cross_incognito); - - // Used for recursively adding submenus of extension items. - void RecursivelyAppendExtensionItems( - const MenuItem::List& items, - bool can_cross_incognito, - const string16& selection_text, - ui::SimpleMenuModel* menu_model, - int* index); - - // Attempts to get an MenuItem given the id of a context menu item. - extensions::MenuItem* GetExtensionMenuItem(int id) const; - - // This will set the icon on the most recently-added item in the menu_model_. - void SetExtensionIcon(const std::string& extension_id); - - Profile* profile_; - ui::SimpleMenuModel* menu_model_; - ui::SimpleMenuModel::Delegate* delegate_; - - base::Callback<bool(const MenuItem*)> filter_; - - // Maps the id from a context menu item to the MenuItem's internal id. - std::map<int, extensions::MenuItem::Id> extension_item_map_; - - // Keep track of and clean up menu models for submenus. - ScopedVector<ui::SimpleMenuModel> extension_menu_models_; - - DISALLOW_COPY_AND_ASSIGN(ContextMenuMatcher); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_CONTEXT_MENU_MATCHER_H_ diff --git a/chrome/browser/extensions/extension_context_menu_browsertest.cc b/chrome/browser/extensions/extension_context_menu_browsertest.cc index 10bf72e..442d2ab 100644 --- a/chrome/browser/extensions/extension_context_menu_browsertest.cc +++ b/chrome/browser/extensions/extension_context_menu_browsertest.cc @@ -39,6 +39,36 @@ class TestRenderViewContextMenu : public RenderViewContextMenu { virtual ~TestRenderViewContextMenu() {} + bool HasExtensionItemWithLabel(const std::string& label) { + string16 label16 = UTF8ToUTF16(label); + std::map<int, MenuItem::Id>::iterator i; + for (i = extension_item_map_.begin(); i != extension_item_map_.end(); ++i) { + const MenuItem::Id& id = i->second; + string16 tmp_label; + EXPECT_TRUE(GetItemLabel(id, &tmp_label)); + if (tmp_label == label16) + return true; + } + return false; + } + + // Looks in the menu for an extension item with |id|, and if it is found and + // has a label, that is put in |result| and we return true. Otherwise returns + // false. + bool GetItemLabel(const MenuItem::Id& id, string16* result) { + int command_id = 0; + if (!FindCommandId(id, &command_id)) + return false; + + MenuModel* model = NULL; + int index = -1; + if (!GetMenuModelAndItemIndex(command_id, &model, &index)) { + return false; + } + *result = model->GetLabelAt(index); + return true; + } + // Searches for an menu item with |command_id|. If it's found, the return // value is true and the model and index where it appears in that model are // returned in |found_model| and |found_index|. Otherwise returns false. @@ -65,8 +95,17 @@ class TestRenderViewContextMenu : public RenderViewContextMenu { return false; } - extensions::ContextMenuMatcher& extension_items() { - return extension_items_; + // Given an extension menu item id, tries to find the corresponding command id + // in the menu. + bool FindCommandId(const MenuItem::Id& id, int* command_id) { + std::map<int, MenuItem::Id>::const_iterator i; + for (i = extension_item_map_.begin(); i != extension_item_map_.end(); ++i) { + if (i->second == id) { + *command_id = i->first; + return true; + } + } + return false; } protected: @@ -158,7 +197,7 @@ class ExtensionContextMenuBrowserTest : public ExtensionBrowserTest { const std::string& label) { scoped_ptr<TestRenderViewContextMenu> menu( CreateMenu(browser(), page_url, link_url, frame_url)); - return MenuHasExtensionItemWithLabel(menu.get(), label); + return menu->HasExtensionItemWithLabel(label); } // This creates an extension that starts |enabled| and then switches to @@ -194,56 +233,6 @@ class ExtensionContextMenuBrowserTest : public ExtensionBrowserTest { ASSERT_TRUE(update.WaitUntilSatisfied()); ASSERT_EQ(!enabled, menu->IsCommandIdEnabled(command_id)); } - - bool MenuHasExtensionItemWithLabel(TestRenderViewContextMenu* menu, - const std::string& label) { - string16 label16 = UTF8ToUTF16(label); - std::map<int, MenuItem::Id>::iterator i; - for (i = menu->extension_items().extension_item_map_.begin(); - i != menu->extension_items().extension_item_map_.end(); ++i) { - const MenuItem::Id& id = i->second; - string16 tmp_label; - EXPECT_TRUE(GetItemLabel(menu, id, &tmp_label)); - if (tmp_label == label16) - return true; - } - return false; - } - - // Looks in the menu for an extension item with |id|, and if it is found and - // has a label, that is put in |result| and we return true. Otherwise returns - // false. - bool GetItemLabel(TestRenderViewContextMenu* menu, - const MenuItem::Id& id, - string16* result) { - int command_id = 0; - if (!FindCommandId(menu, id, &command_id)) - return false; - - MenuModel* model = NULL; - int index = -1; - if (!menu->GetMenuModelAndItemIndex(command_id, &model, &index)) { - return false; - } - *result = model->GetLabelAt(index); - return true; - } - - // Given an extension menu item id, tries to find the corresponding command id - // in the menu. - bool FindCommandId(TestRenderViewContextMenu* menu, - const MenuItem::Id& id, - int* command_id) { - std::map<int, MenuItem::Id>::const_iterator i; - for (i = menu->extension_items().extension_item_map_.begin(); - i != menu->extension_items().extension_item_map_.end(); ++i) { - if (i->second == id) { - *command_id = i->first; - return true; - } - } - return false; - } }; // Tests adding a simple context menu item. @@ -313,7 +302,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, LongTitle) { ASSERT_TRUE(listener.WaitUntilSatisfied()); // Make sure we have an item registered with a long title. - size_t limit = extensions::ContextMenuMatcher::kMaxExtensionItemTitleLength; + size_t limit = RenderViewContextMenu::kMaxExtensionItemTitleLength; MenuItem::List items = GetItems(); ASSERT_EQ(1u, items.size()); MenuItem* item = items.at(0); @@ -326,7 +315,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, LongTitle) { CreateMenu(browser(), url, GURL(), GURL())); string16 label; - ASSERT_TRUE(GetItemLabel(menu.get(), item->id(), &label)); + ASSERT_TRUE(menu->GetItemLabel(item->id(), &label)); ASSERT_TRUE(label.size() <= limit); } @@ -591,7 +580,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserLazyTest, EventPage) { MenuItem::Id id(false, extension->id()); id.string_uid = "checkbox1"; int command_id = -1; - ASSERT_TRUE(FindCommandId(menu.get(), id, &command_id)); + ASSERT_TRUE(menu->FindCommandId(id, &command_id)); EXPECT_FALSE(menu->IsCommandIdChecked(command_id)); // Executing the checkbox also fires the onClicked event. diff --git a/chrome/browser/extensions/menu_manager.h b/chrome/browser/extensions/menu_manager.h index 035a16d..9ffe0fc 100644 --- a/chrome/browser/extensions/menu_manager.h +++ b/chrome/browser/extensions/menu_manager.h @@ -71,7 +71,6 @@ class MenuItem { VIDEO = 64, AUDIO = 128, FRAME = 256, - LAUNCHER = 512 }; // An item can be only one of these types. diff --git a/chrome/browser/tab_contents/render_view_context_menu.cc b/chrome/browser/tab_contents/render_view_context_menu.cc index 87ee142..d7863bf 100644 --- a/chrome/browser/tab_contents/render_view_context_menu.cc +++ b/chrome/browser/tab_contents/render_view_context_menu.cc @@ -227,6 +227,8 @@ bool ShouldShowTranslateItem(const GURL& page_url) { } // namespace // static +const size_t RenderViewContextMenu::kMaxExtensionItemTitleLength = 75; +// static const size_t RenderViewContextMenu::kMaxSelectionTextLength = 50; // static @@ -251,8 +253,6 @@ RenderViewContextMenu::RenderViewContextMenu( source_web_contents_(web_contents), profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())), ALLOW_THIS_IN_INITIALIZER_LIST(menu_model_(this)), - extension_items_(profile_, this, &menu_model_, - base::Bind(MenuItemMatchesParams, params_)), external_(false), ALLOW_THIS_IN_INITIALIZER_LIST(speech_input_submenu_model_(this)), ALLOW_THIS_IN_INITIALIZER_LIST(protocol_handler_submenu_model_(this)), @@ -338,21 +338,177 @@ static const GURL& GetDocumentURL(const content::ContextMenuParams& params) { return params.frame_url.is_empty() ? params.page_url : params.frame_url; } +// Given a list of items, returns the ones that match given the contents +// of |params| and the profile. // static -bool RenderViewContextMenu::MenuItemMatchesParams( +MenuItem::List RenderViewContextMenu::GetRelevantExtensionItems( + const MenuItem::List& items, const content::ContextMenuParams& params, - const extensions::MenuItem* item) { - bool match = ExtensionContextAndPatternMatch(params, item->contexts(), - item->target_url_patterns()); - if (!match) - return false; + Profile* profile, + bool can_cross_incognito) { + MenuItem::List result; + for (MenuItem::List::const_iterator i = items.begin(); + i != items.end(); ++i) { + const MenuItem* item = *i; + + if (!ExtensionContextAndPatternMatch(params, item->contexts(), + item->target_url_patterns())) + continue; + + const GURL& document_url = GetDocumentURL(params); + if (!ExtensionPatternMatch(item->document_url_patterns(), document_url)) + continue; + + if (item->id().incognito == profile->IsOffTheRecord() || + can_cross_incognito) + result.push_back(*i); + } + return result; +} + +void RenderViewContextMenu::AppendExtensionItems( + const std::string& extension_id, int* index) { + ExtensionService* service = profile_->GetExtensionService(); + MenuManager* manager = service->menu_manager(); + const Extension* extension = service->GetExtensionById(extension_id, false); + DCHECK_GE(*index, 0); + int max_index = + IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST - IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST; + if (!extension || *index >= max_index) + return; + + // Find matching items. + const MenuItem::List* all_items = manager->MenuItems(extension_id); + if (!all_items || all_items->empty()) + return; + bool can_cross_incognito = service->CanCrossIncognito(extension); + MenuItem::List items = + GetRelevantExtensionItems(*all_items, params_, profile_, + can_cross_incognito); + if (items.empty()) + return; + + // If this is the first extension-provided menu item, and there are other + // items in the menu, add a separator. + if (*index == 0 && menu_model_.GetItemCount()) + menu_model_.AddSeparator(ui::NORMAL_SEPARATOR); + + // Extensions (other than platform apps) are only allowed one top-level slot + // (and it can't be a radio or checkbox item because we are going to put the + // extension icon next to it). + // If they have more than that, we automatically push them into a submenu. + if (extension->is_platform_app()) { + RecursivelyAppendExtensionItems(items, can_cross_incognito, &menu_model_, + index); + } else { + int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; + string16 title; + MenuItem::List submenu_items; + + if (items.size() > 1 || items[0]->type() != MenuItem::NORMAL) { + title = UTF8ToUTF16(extension->name()); + submenu_items = items; + } else { + MenuItem* item = items[0]; + extension_item_map_[menu_id] = item->id(); + title = item->TitleWithReplacement(PrintableSelectionText(), + kMaxExtensionItemTitleLength); + submenu_items = GetRelevantExtensionItems(item->children(), params_, + profile_, can_cross_incognito); + } + + // Now add our item(s) to the menu_model_. + if (submenu_items.empty()) { + menu_model_.AddItem(menu_id, title); + } else { + ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(this); + extension_menu_models_.push_back(submenu); + menu_model_.AddSubMenu(menu_id, title, submenu); + RecursivelyAppendExtensionItems(submenu_items, can_cross_incognito, + submenu, index); + } + SetExtensionIcon(extension_id); + } +} + +void RenderViewContextMenu::RecursivelyAppendExtensionItems( + const MenuItem::List& items, + bool can_cross_incognito, + ui::SimpleMenuModel* menu_model, + int* index) { + string16 selection_text = PrintableSelectionText(); + MenuItem::Type last_type = MenuItem::NORMAL; + int radio_group_id = 1; + + for (MenuItem::List::const_iterator i = items.begin(); + i != items.end(); ++i) { + MenuItem* item = *i; + + // If last item was of type radio but the current one isn't, auto-insert + // a separator. The converse case is handled below. + if (last_type == MenuItem::RADIO && + item->type() != MenuItem::RADIO) { + menu_model->AddSeparator(ui::NORMAL_SEPARATOR); + last_type = MenuItem::SEPARATOR; + } + + int menu_id = IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST + (*index)++; + if (menu_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) + return; + extension_item_map_[menu_id] = item->id(); + string16 title = item->TitleWithReplacement(selection_text, + kMaxExtensionItemTitleLength); + if (item->type() == MenuItem::NORMAL) { + MenuItem::List children = + GetRelevantExtensionItems(item->children(), params_, + profile_, can_cross_incognito); + if (children.empty()) { + menu_model->AddItem(menu_id, title); + } else { + ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(this); + extension_menu_models_.push_back(submenu); + menu_model->AddSubMenu(menu_id, title, submenu); + RecursivelyAppendExtensionItems(children, can_cross_incognito, + submenu, index); + } + } else if (item->type() == MenuItem::CHECKBOX) { + menu_model->AddCheckItem(menu_id, title); + } else if (item->type() == MenuItem::RADIO) { + if (i != items.begin() && + last_type != MenuItem::RADIO) { + radio_group_id++; + + // Auto-append a separator if needed. + if (last_type != MenuItem::SEPARATOR) + menu_model->AddSeparator(ui::NORMAL_SEPARATOR); + } - const GURL& document_url = GetDocumentURL(params); - return ExtensionPatternMatch(item->document_url_patterns(), document_url); + menu_model->AddRadioItem(menu_id, title, radio_group_id); + } else if (item->type() == MenuItem::SEPARATOR) { + if (i != items.begin() && last_type != MenuItem::SEPARATOR) { + menu_model->AddSeparator(ui::NORMAL_SEPARATOR); + } + } + last_type = item->type(); + } +} + +void RenderViewContextMenu::SetExtensionIcon(const std::string& extension_id) { + ExtensionService* service = profile_->GetExtensionService(); + MenuManager* menu_manager = service->menu_manager(); + + int index = menu_model_.GetItemCount() - 1; + DCHECK_GE(index, 0); + + const SkBitmap& icon = menu_manager->GetIconForExtension(extension_id); + DCHECK(icon.width() == gfx::kFaviconSize); + DCHECK(icon.height() == gfx::kFaviconSize); + + menu_model_.SetIcon(index, gfx::Image(icon)); } void RenderViewContextMenu::AppendAllExtensionItems() { - extension_items_.Clear(); + extension_item_map_.clear(); ExtensionService* service = profile_->GetExtensionService(); if (!service) return; // In unit-tests, we may not have an ExtensionService. @@ -381,8 +537,7 @@ void RenderViewContextMenu::AppendAllExtensionItems() { std::vector<std::pair<std::string, std::string> >::const_iterator i; for (i = sorted_ids.begin(); i != sorted_ids.end(); ++i) { - extension_items_.AppendExtensionItems(i->second, PrintableSelectionText(), - &index); + AppendExtensionItems(i->second, &index); } UMA_HISTOGRAM_TIMES("Extensions.ContextMenus_BuildTime", base::TimeTicks::Now() - begin); @@ -513,8 +668,7 @@ void RenderViewContextMenu::AppendPlatformAppItems() { AppendCopyItem(); int index = 0; - extension_items_.AppendExtensionItems(platform_app->id(), - PrintableSelectionText(), &index); + AppendExtensionItems(platform_app->id(), &index); // Add dev tools for unpacked extensions. if (platform_app->location() == Extension::LOAD) { @@ -905,6 +1059,18 @@ void RenderViewContextMenu::AppendProtocolHandlerSubMenu() { void RenderViewContextMenu::AppendPlatformEditableItems() { } +MenuItem* RenderViewContextMenu::GetExtensionMenuItem(int id) const { + MenuManager* manager = profile_->GetExtensionService()->menu_manager(); + std::map<int, MenuItem::Id>::const_iterator i = + extension_item_map_.find(id); + if (i != extension_item_map_.end()) { + MenuItem* item = manager->GetItemById(i->second); + if (item) + return item; + } + return NULL; +} + // Menu delegate functions ----------------------------------------------------- bool RenderViewContextMenu::IsCommandIdEnabled(int id) const { @@ -944,7 +1110,11 @@ bool RenderViewContextMenu::IsCommandIdEnabled(int id) const { // Extension items. if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { - return extension_items_.IsCommandIdEnabled(id); + MenuItem* item = GetExtensionMenuItem(id); + // If this is the parent menu item, it is always enabled. + if (!item) + return true; + return item->enabled(); } if (id >= IDC_CONTENT_CONTEXT_PROTOCOL_HANDLER_FIRST && @@ -1241,7 +1411,11 @@ bool RenderViewContextMenu::IsCommandIdChecked(int id) const { // Extension items. if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { - return extension_items_.IsCommandIdChecked(id); + MenuItem* item = GetExtensionMenuItem(id); + if (item) + return item->checked(); + else + return false; } #if defined(ENABLE_INPUT_SPEECH) @@ -1282,7 +1456,13 @@ void RenderViewContextMenu::ExecuteCommand(int id, int event_flags) { // Process extension menu items. if (id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { - extension_items_.ExecuteCommand(id, params_); + MenuManager* manager = profile_->GetExtensionService()->menu_manager(); + std::map<int, MenuItem::Id>::const_iterator i = + extension_item_map_.find(id); + if (i != extension_item_map_.end()) { + manager->ExecuteCommand(profile_, source_web_contents_, params_, + i->second); + } return; } diff --git a/chrome/browser/tab_contents/render_view_context_menu.h b/chrome/browser/tab_contents/render_view_context_menu.h index b50b292..fc6a80b 100644 --- a/chrome/browser/tab_contents/render_view_context_menu.h +++ b/chrome/browser/tab_contents/render_view_context_menu.h @@ -14,7 +14,6 @@ #include "base/observer_list.h" #include "base/string16.h" #include "chrome/browser/custom_handlers/protocol_handler_registry.h" -#include "chrome/browser/extensions/context_menu_matcher.h" #include "chrome/browser/extensions/menu_manager.h" #include "chrome/browser/tab_contents/render_view_context_menu_observer.h" #include "content/public/common/context_menu_params.h" @@ -128,6 +127,7 @@ class RenderViewContextMenuProxy { class RenderViewContextMenu : public ui::SimpleMenuModel::Delegate, public RenderViewContextMenuProxy { public: + static const size_t kMaxExtensionItemTitleLength; static const size_t kMaxSelectionTextLength; RenderViewContextMenu(content::WebContents* web_contents, @@ -178,16 +178,21 @@ class RenderViewContextMenu : public ui::SimpleMenuModel::Delegate, ui::Accelerator* accelerator) = 0; virtual void AppendPlatformEditableItems(); + // Attempts to get an MenuItem given the id of a context menu item. + extensions::MenuItem* GetExtensionMenuItem(int id) const; + content::ContextMenuParams params_; content::WebContents* source_web_contents_; Profile* profile_; ui::SimpleMenuModel menu_model_; - extensions::ContextMenuMatcher extension_items_; // True if we are showing for an external tab contents. The default is false. bool external_; + // Maps the id from a context menu item to the MenuItem's internal id. + std::map<int, extensions::MenuItem::Id> extension_item_map_; + private: friend class RenderViewContextMenuTest; @@ -197,9 +202,11 @@ class RenderViewContextMenu : public ui::SimpleMenuModel::Delegate, const content::ContextMenuParams& params, extensions::MenuItem::ContextList contexts, const URLPatternSet& target_url_patterns); - static bool MenuItemMatchesParams( + static extensions::MenuItem::List GetRelevantExtensionItems( + const extensions::MenuItem::List& items, const content::ContextMenuParams& params, - const extensions::MenuItem* item); + Profile* profile, + bool can_cross_incognito); // Gets the extension (if any) associated with the WebContents that we're in. const extensions::Extension* GetExtension() const; @@ -224,6 +231,20 @@ class RenderViewContextMenu : public ui::SimpleMenuModel::Delegate, void AppendSpeechInputOptionsSubMenu(); void AppendProtocolHandlerSubMenu(); + // This is a helper function to append items for one particular extension. + // The |index| parameter is used for assigning id's, and is incremented for + // each item actually added. + void AppendExtensionItems(const std::string& extension_id, int* index); + + // Used for recursively adding submenus of extension items. + void RecursivelyAppendExtensionItems( + const std::vector<extensions::MenuItem*>& items, + bool can_cross_incognito, + ui::SimpleMenuModel* menu_model, + int* index); + // This will set the icon on the most recently-added item in the menu_model_. + void SetExtensionIcon(const std::string& extension_id); + // Opens the specified URL string in a new tab. The |frame_id| specifies the // frame in which the context menu was displayed, or 0 if the menu action is // independent of that frame (e.g. protocol handler settings). diff --git a/chrome/browser/ui/app_list/extension_app_item.cc b/chrome/browser/ui/app_list/extension_app_item.cc index 86d2358..fb9b5a7 100644 --- a/chrome/browser/ui/app_list/extension_app_item.cc +++ b/chrome/browser/ui/app_list/extension_app_item.cc @@ -5,8 +5,6 @@ #include "chrome/browser/ui/app_list/extension_app_item.h" #include "base/utf_string_conversions.h" -#include "chrome/app/chrome_command_ids.h" -#include "chrome/browser/extensions/context_menu_matcher.h" #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_sorting.h" @@ -22,7 +20,6 @@ #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_icon_set.h" -#include "content/public/common/context_menu_params.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" @@ -128,10 +125,6 @@ ExtensionSorting* GetExtensionSorting(Profile* profile) { return profile->GetExtensionService()->extension_prefs()->extension_sorting(); } -bool MenuItemHasLauncherContext(const extensions::MenuItem* item) { - return item->contexts().Contains(extensions::MenuItem::LAUNCHER); -} - } // namespace ExtensionAppItem::ExtensionAppItem(Profile* profile, @@ -234,9 +227,6 @@ bool ExtensionAppItem::IsCommandIdChecked(int command_id) const { if (command_id >= LAUNCH_TYPE_START && command_id < LAUNCH_TYPE_LAST) { return static_cast<int>(GetExtensionLaunchType(profile_, extension_id_)) + LAUNCH_TYPE_START == command_id; - } else if (command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && - command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { - return extension_menu_items_->IsCommandIdChecked(command_id); } return false; } @@ -257,9 +247,6 @@ bool ExtensionAppItem::IsCommandIdEnabled(int command_id) const { } else if (command_id == DETAILS) { const Extension* extension = GetExtension(); return extension && extension->from_webstore(); - } else if (command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && - command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { - return extension_menu_items_->IsCommandIdEnabled(command_id); } return true; } @@ -290,10 +277,6 @@ void ExtensionAppItem::ExecuteCommand(int command_id) { StartExtensionUninstall(); } else if (command_id == DETAILS) { ShowExtensionDetails(); - } else if (command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST && - command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST) { - extension_menu_items_->ExecuteCommand(command_id, - content::ContextMenuParams()); } } @@ -324,16 +307,9 @@ ui::MenuModel* ExtensionAppItem::GetContextMenuModel() { if (!context_menu_model_.get()) { context_menu_model_.reset(new ui::SimpleMenuModel(this)); - extension_menu_items_.reset(new extensions::ContextMenuMatcher( - profile_, this, context_menu_model_.get(), - base::Bind(MenuItemHasLauncherContext))); - context_menu_model_->AddItem(LAUNCH, UTF8ToUTF16(title())); // Talk extension isn't an app and so doesn't support most launch options. if (!IsTalkExtension()) { - int index = 0; - extension_menu_items_->AppendExtensionItems(extension_id_, string16(), - &index); context_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR); context_menu_model_->AddItemWithStringId( TOGGLE_PIN, diff --git a/chrome/browser/ui/app_list/extension_app_item.h b/chrome/browser/ui/app_list/extension_app_item.h index 4ae4ab6..c73fedd 100644 --- a/chrome/browser/ui/app_list/extension_app_item.h +++ b/chrome/browser/ui/app_list/extension_app_item.h @@ -19,7 +19,6 @@ class Profile; class SkBitmap; namespace extensions { -class ContextMenuMatcher; class Extension; } @@ -77,7 +76,6 @@ class ExtensionAppItem : public ChromeAppListItem, scoped_ptr<extensions::IconImage> icon_; scoped_ptr<ui::SimpleMenuModel> context_menu_model_; - scoped_ptr<extensions::ContextMenuMatcher> extension_menu_items_; DISALLOW_COPY_AND_ASSIGN(ExtensionAppItem); }; diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc index 3078cd6..11a9368 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc @@ -404,13 +404,6 @@ ash::LauncherID ChromeLauncherController::GetLauncherIDForAppID( return 0; } -std::string ChromeLauncherController::GetAppIDForLauncherID( - ash::LauncherID id) { - DCHECK(id_to_item_controller_map_.find(id) != - id_to_item_controller_map_.end()); - return id_to_item_controller_map_[id]->app_id(); -} - void ChromeLauncherController::SetAppImage(const std::string& id, const gfx::ImageSkia& image) { // TODO: need to get this working for shortcuts. diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h index 027344c..d022c2e 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h @@ -167,7 +167,6 @@ class ChromeLauncherController std::string GetAppID(TabContents* tab); ash::LauncherID GetLauncherIDForAppID(const std::string& app_id); - std::string GetAppIDForLauncherID(ash::LauncherID id); // Sets the image for an app tab. This is intended to be invoked from the // AppIconLoader. diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc index 1df17dfe..ca1b3ca 100644 --- a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc +++ b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc @@ -7,31 +7,18 @@ #include "ash/launcher/launcher_context_menu.h" #include "ash/shell.h" #include "base/command_line.h" -#include "chrome/browser/extensions/context_menu_matcher.h" #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" #include "chrome/common/chrome_switches.h" -#include "content/public/common/context_menu_params.h" #include "grit/ash_strings.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" -namespace { - -bool MenuItemHasLauncherContext(const extensions::MenuItem* item) { - return item->contexts().Contains(extensions::MenuItem::LAUNCHER); -} - -} // namespace - LauncherContextMenu::LauncherContextMenu(ChromeLauncherController* controller, const ash::LauncherItem* item) : ui::SimpleMenuModel(NULL), controller_(controller), - item_(item ? *item : ash::LauncherItem()), - extension_items_(new extensions::ContextMenuMatcher( - controller->profile(), this, this, - base::Bind(MenuItemHasLauncherContext))) { + item_(item ? *item : ash::LauncherItem()) { set_delegate(this); if (is_valid_item()) { @@ -80,14 +67,6 @@ LauncherContextMenu::LauncherContextMenu(ChromeLauncherController* controller, } AddSeparator(ui::NORMAL_SEPARATOR); } - std::string app_id = controller->GetAppIDForLauncherID(item_.id); - if (!app_id.empty()) { - int index = 0; - extension_items_->AppendExtensionItems( - app_id, string16(), &index); - if (index > 0) - AddSeparator(ui::NORMAL_SEPARATOR); - } AddCheckItemWithStringId( MENU_AUTO_HIDE, ash::LauncherContextMenu::GetAutoHideResourceStringId()); if (CommandLine::ForCurrentProcess()->HasSwitch( @@ -118,7 +97,7 @@ bool LauncherContextMenu::IsCommandIdChecked(int command_id) const { case MENU_AUTO_HIDE: return ash::LauncherContextMenu::IsAutoHideMenuHideChecked(); default: - return extension_items_->IsCommandIdChecked(command_id); + return false; } } @@ -128,7 +107,7 @@ bool LauncherContextMenu::IsCommandIdEnabled(int command_id) const { return item_.type == ash::TYPE_PLATFORM_APP || controller_->IsPinnable(item_.id); default: - return extension_items_->IsCommandIdEnabled(command_id); + return true; } } @@ -177,8 +156,5 @@ void LauncherContextMenu::ExecuteCommand(int command_id) { break; case MENU_ALIGNMENT_MENU: break; - default: - extension_items_->ExecuteCommand(command_id, - content::ContextMenuParams()); } } diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu.h b/chrome/browser/ui/ash/launcher/launcher_context_menu.h index 9d9ad6a..20385ca 100644 --- a/chrome/browser/ui/ash/launcher/launcher_context_menu.h +++ b/chrome/browser/ui/ash/launcher/launcher_context_menu.h @@ -8,15 +8,10 @@ #include "ash/launcher/launcher_alignment_menu.h" #include "ash/launcher/launcher_types.h" #include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" #include "ui/base/models/simple_menu_model.h" class ChromeLauncherController; -namespace extensions { -class ContextMenuMatcher; -} - // Context menu shown for a launcher item. class LauncherContextMenu : public ui::SimpleMenuModel, public ui::SimpleMenuModel::Delegate { @@ -63,8 +58,6 @@ class LauncherContextMenu : public ui::SimpleMenuModel, ash::LauncherAlignmentMenu alignment_menu_; - scoped_ptr<extensions::ContextMenuMatcher> extension_items_; - DISALLOW_COPY_AND_ASSIGN(LauncherContextMenu); }; diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index f2de3fc..3bbde0d 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -356,8 +356,6 @@ 'browser/extensions/bundle_installer.h', 'browser/extensions/component_loader.cc', 'browser/extensions/component_loader.h', - 'browser/extensions/context_menu_matcher.cc', - 'browser/extensions/context_menu_matcher.h', 'browser/extensions/convert_user_script.cc', 'browser/extensions/convert_user_script.h', 'browser/extensions/convert_web_app.cc', diff --git a/chrome/common/extensions/api/context_menus.json b/chrome/common/extensions/api/context_menus.json index 364bac4..5a333eb 100644 --- a/chrome/common/extensions/api/context_menus.json +++ b/chrome/common/extensions/api/context_menus.json @@ -44,8 +44,7 @@ }, "pageUrl": { "type": "string", - "optional": true, - "description": "The URL of the page where the menu item was clicked. This property is not set if the click occured in a context where there is no current page, such as in a launcher context menu." + "description": "The URL of the page where the menu item was clicked." }, "frameUrl": { "type": "string", @@ -116,7 +115,7 @@ "type": "array", "items": { "type": "string", - "enum": ["all", "page", "frame", "selection", "link", "editable", "image", "video", "audio", "launcher"] + "enum": ["all", "page", "frame", "selection", "link", "editable", "image", "video", "audio"] }, "minItems": 1, "optional": true, @@ -210,7 +209,7 @@ "type": "array", "items": { "type": "string", - "enum": ["all", "page", "frame", "selection", "link", "editable", "image", "video", "audio", "launcher"] + "enum": ["all", "page", "frame", "selection", "link", "editable", "image", "video", "audio"] }, "minItems": 1, "optional": true |