diff options
Diffstat (limited to 'chrome')
19 files changed, 258 insertions, 30 deletions
diff --git a/chrome/app/chrome_dll_resource.h b/chrome/app/chrome_dll_resource.h index f99438b..91a802d 100644 --- a/chrome/app/chrome_dll_resource.h +++ b/chrome/app/chrome_dll_resource.h @@ -216,3 +216,10 @@ #define IDC_HISTORY_MENU_VISITED 46100 // OSX only #define IDC_HISTORY_MENU_CLOSED 46200 // OSX only +// Extensions menu +// Dynamic items from extensions are filled in between _FIRST and _LAST. If we +// end up with more than 997 browser actions registered, we have other problems. +#define IDC_SHOW_EXTENSIONS_SUBMENU 47000 +#define IDC_MANAGE_EXTENSIONS 47001 +#define IDC_BROWSER_ACTION_FIRST 47002 +#define IDC_BROWSER_ACTION_LAST 47999 diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 3e9c6b6..b83ca67 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -943,6 +943,12 @@ each locale. --> <message name="IDS_SHOW_DOWNLOADS" desc="The show downloads menu in the app menu"> &Downloads </message> + <message name="IDS_SHOW_EXTENSIONS" desc="The show extensions menu in the app menu"> + &Extensions + </message> + <message name="IDS_MANAGE_EXTENSIONS" desc="The manage extensions menu item in the extensions submenu"> + &Manage extensions + </message> <message name="IDS_OPTIONS" desc="The text label of the Options menu item"> &Options </message> diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc index a2e2b243..7450df5 100644 --- a/chrome/browser/browser.cc +++ b/chrome/browser/browser.cc @@ -25,7 +25,9 @@ #include "chrome/browser/download/download_shelf.h" #include "chrome/browser/download/download_started_animation.h" #include "chrome/browser/extensions/crashed_extension_infobar.h" +#include "chrome/browser/extensions/extension_browser_event_router.h" #include "chrome/browser/extensions/extension_disabled_infobar_delegate.h" +#include "chrome/browser/extensions/extension_tabs_module.h" #include "chrome/browser/find_bar.h" #include "chrome/browser/find_bar_controller.h" #include "chrome/browser/google_url_tracker.h" @@ -187,6 +189,8 @@ Browser::Browser(Type type, Profile* profile) NotificationService::AllSources()); registrar_.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED, NotificationService::AllSources()); + registrar_.Add(this, NotificationType::EXTENSION_LOADED, + NotificationService::AllSources()); registrar_.Add(this, NotificationType::EXTENSION_UNLOADED, NotificationService::AllSources()); registrar_.Add(this, NotificationType::EXTENSION_PROCESS_CRASHED, @@ -1261,6 +1265,11 @@ void Browser::OpenHelpTab() { false, NULL); } +void Browser::OpenExtensionsTab() { + AddTabWithURL(GURL(chrome::kChromeUIExtensionsURL), GURL(), + PageTransition::AUTO_BOOKMARK, true, -1, false, NULL); +} + #if defined(OS_CHROMEOS) void Browser::ShowControlPanel() { GURL url("http://localhost:8080"); @@ -1492,6 +1501,7 @@ void Browser::ExecuteCommandWithDisposition( case IDC_IMPORT_SETTINGS: OpenImportSettingsDialog(); break; case IDC_ABOUT: OpenAboutChromeDialog(); break; case IDC_HELP_PAGE: OpenHelpTab(); break; + case IDC_MANAGE_EXTENSIONS: OpenExtensionsTab(); break; #if defined(OS_CHROMEOS) case IDC_CONTROL_PANEL: ShowControlPanel(); break; #endif @@ -1506,6 +1516,28 @@ void Browser::ExecuteCommandWithDisposition( // Browser, CommandUpdater::CommandUpdaterDelegate implementation: void Browser::ExecuteCommand(int id) { + if (id >= IDC_BROWSER_ACTION_FIRST && id <= IDC_BROWSER_ACTION_LAST) { + ExtensionsService* service = profile_->GetExtensionsService(); + DCHECK(service); // No browser action command should have been created + // in this window. + + // Go find the browser action in question. + std::vector<ExtensionAction*> browser_actions = + service->GetBrowserActions(); + for (size_t i = 0; i < browser_actions.size(); ++i) { + if (browser_actions[i]->command_id() == id) { + int window_id = ExtensionTabUtil::GetWindowId(this); + ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted( + profile_, browser_actions[i]->extension_id(), window_id); + return; + } + } + + // Could not find the command in question. Perhaps it went away while the + // menu was open? More likely, it is a bug. + LOG(WARNING) << "Unknown browser action executed: " << id; + } + ExecuteCommandWithDisposition(id, CURRENT_TAB); } @@ -2164,6 +2196,16 @@ void Browser::Observe(NotificationType type, break; } + case NotificationType::EXTENSION_LOADED: { + // Enable the browser action for the extension, if it has one. + Extension* extension = Details<Extension>(details).ptr(); + if (extension->browser_action()) { + command_updater_.UpdateCommandEnabled( + extension->browser_action()->command_id(), true); + } + break; + } + case NotificationType::EXTENSION_UNLOADED: { window()->GetLocationBar()->InvalidatePageActions(); @@ -2177,6 +2219,13 @@ void Browser::Observe(NotificationType type, return; } } + + // Disable the browser action for the extension, if it has one. + if (extension->browser_action()) { + command_updater_.UpdateCommandEnabled( + extension->browser_action()->command_id(), false); + } + break; } @@ -2328,6 +2377,7 @@ void Browser::InitCommandState() { command_updater_.UpdateCommandEnabled(IDC_SHOW_EXTENSION_SHELF, true); command_updater_.UpdateCommandEnabled(IDC_SHOW_DOWNLOADS, true); command_updater_.UpdateCommandEnabled(IDC_HELP_PAGE, true); + command_updater_.UpdateCommandEnabled(IDC_MANAGE_EXTENSIONS, true); #if defined(OS_CHROMEOS) command_updater_.UpdateCommandEnabled(IDC_CONTROL_PANEL, true); #endif diff --git a/chrome/browser/browser.h b/chrome/browser/browser.h index 2120a05..8ec5557 100644 --- a/chrome/browser/browser.h +++ b/chrome/browser/browser.h @@ -397,6 +397,7 @@ class Browser : public TabStripModelDelegate, void OpenImportSettingsDialog(); void OpenAboutChromeDialog(); void OpenHelpTab(); + void OpenExtensionsTab(); #if defined(OS_CHROMEOS) void ShowControlPanel(); #endif diff --git a/chrome/browser/extensions/browser_action_test.cc b/chrome/browser/extensions/browser_action_test.cc new file mode 100644 index 0000000..f597f07 --- /dev/null +++ b/chrome/browser/extensions/browser_action_test.cc @@ -0,0 +1,67 @@ +// Copyright (c) 2009 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/browser.h" +#include "chrome/browser/browser_window.h" +#include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/views/browser_actions_container.h" +#include "chrome/browser/views/toolbar_view.h" +#include "chrome/common/extensions/extension_action.h" +#include "chrome/test/ui_test_utils.h" + +static void TestAction(Browser* browser) { + // Navigate to a page we have permission to modify. + ui_test_utils::NavigateToURL(browser, + GURL("http://localhost:1337/files/extensions/test_file.txt")); + + // Send the command. + ExtensionsService* service = browser->profile()->GetExtensionsService(); + browser->ExecuteCommand(service->GetBrowserActions()[0]->command_id()); + + // Verify the command worked. + TabContents* tab = browser->GetSelectedTabContents(); + bool result = false; + ui_test_utils::ExecuteJavaScriptAndExtractBool( + tab->render_view_host(), L"", + L"setInterval(function(){" + L" if(document.body.bgColor == 'red'){" + L" window.domAutomationController.send(true)}}, 100)", + &result); + ASSERT_TRUE(result); +} + +IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, BrowserAction) { + StartHTTPServer(); + + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("samples") + .AppendASCII("make_page_red"))); + + // Test that there is a browser action in the toolbar. + BrowserActionsContainer* browser_actions = + browser()->window()->GetBrowserWindowTesting()->GetToolbarView()-> + browser_actions(); + ASSERT_EQ(1, browser_actions->num_browser_actions()); + + TestAction(browser()); +} + +IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, BrowserActionNoIcon) { + StartHTTPServer(); + + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("samples") + .AppendASCII("make_page_red_no_icon"))); + + // Test that there is a *not* a browser action in the toolbar. + BrowserActionsContainer* browser_actions = + browser()->window()->GetBrowserWindowTesting()->GetToolbarView()-> + browser_actions(); + ASSERT_EQ(0, browser_actions->num_browser_actions()); + + TestAction(browser()); +} diff --git a/chrome/browser/views/browser_actions_container.cc b/chrome/browser/views/browser_actions_container.cc index 2499692..e1d12f5 100644 --- a/chrome/browser/views/browser_actions_container.cc +++ b/chrome/browser/views/browser_actions_container.cc @@ -140,14 +140,14 @@ void BrowserActionsContainer::RefreshBrowserActionViews() { std::vector<ExtensionAction*> browser_actions; browser_actions = extension_service->GetBrowserActions(); - if (browser_action_views_.size() != browser_actions.size()) { - DeleteBrowserActionViews(); - - for (size_t i = 0; i < browser_actions.size(); ++i) { - Extension* extension = extension_service->GetExtensionById( - browser_actions[i]->extension_id()); - DCHECK(extension); + DeleteBrowserActionViews(); + for (size_t i = 0; i < browser_actions.size(); ++i) { + Extension* extension = extension_service->GetExtensionById( + browser_actions[i]->extension_id()); + DCHECK(extension); + // Only show browser actions that have an icon. + if (browser_actions[i]->icon_paths().size() > 0) { BrowserActionImageView* view = new BrowserActionImageView(browser_actions[i], extension, this); browser_action_views_.push_back(view); diff --git a/chrome/browser/views/browser_actions_container.h b/chrome/browser/views/browser_actions_container.h index 85b2c6c..1da8559 100644 --- a/chrome/browser/views/browser_actions_container.h +++ b/chrome/browser/views/browser_actions_container.h @@ -31,6 +31,8 @@ class BrowserActionsContainer : public views::View, BrowserActionsContainer(Profile* profile, ToolbarView* toolbar); virtual ~BrowserActionsContainer(); + int num_browser_actions() { return browser_action_views_.size(); } + // Update the views to reflect the state of the browser action icons. void RefreshBrowserActionViews(); diff --git a/chrome/browser/views/toolbar_view.cc b/chrome/browser/views/toolbar_view.cc index 430bfef..7e08acf 100644 --- a/chrome/browser/views/toolbar_view.cc +++ b/chrome/browser/views/toolbar_view.cc @@ -1044,8 +1044,8 @@ void ToolbarView::CreateDevToolsMenuContents() { #endif void ToolbarView::CreateAppMenu() { - if (app_menu_contents_.get()) - return; + // We always rebuild the app menu so that we can get the current state of the + // extension system. app_menu_contents_.reset(new views::SimpleMenuModel(this)); app_menu_contents_->AddItemWithStringId(IDC_NEW_TAB, IDS_NEW_TAB); @@ -1056,7 +1056,8 @@ void ToolbarView::CreateAppMenu() { // We will create the child menu items for this once the asynchronous call is // done. See OnGetProfilesDone(). const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - if (command_line.HasSwitch(switches::kEnableUserDataDirProfiles)) { + if (command_line.HasSwitch(switches::kEnableUserDataDirProfiles) && + !profiles_menu_contents_.get()) { profiles_helper_->GetProfiles(NULL); profiles_menu_contents_.reset(new views::SimpleMenuModel(this)); app_menu_contents_->AddSubMenuWithStringId(IDS_PROFILE_MENU, @@ -1073,6 +1074,39 @@ void ToolbarView::CreateAppMenu() { IDS_BOOKMARK_MANAGER); app_menu_contents_->AddItemWithStringId(IDC_SHOW_DOWNLOADS, IDS_SHOW_DOWNLOADS); + + // Create the extensions item or submenu. + // If there are any browser actions, we create an "Extensions" submenu, of + // which "Manage extensions" is the first entry. If there are no browser + // actions, we just create an "Extensions" menu item which does the same thing + // as "Manage extensions". + ExtensionsService* extensions_service = + browser_->profile()->GetExtensionsService(); + if (extensions_service && extensions_service->extensions_enabled()) { + std::vector<ExtensionAction*> browser_actions = + browser_->profile()->GetExtensionsService()->GetBrowserActions(); + if (browser_actions.size() == 0) { + app_menu_contents_->AddItemWithStringId(IDC_MANAGE_EXTENSIONS, + IDS_SHOW_EXTENSIONS); + } else { + extension_menu_contents_.reset(new views::SimpleMenuModel(this)); + app_menu_contents_->AddSubMenuWithStringId( + IDS_SHOW_EXTENSIONS, extension_menu_contents_.get()); + + extension_menu_contents_->AddItemWithStringId(IDC_MANAGE_EXTENSIONS, + IDS_MANAGE_EXTENSIONS); + for (size_t i = 0; i < browser_actions.size(); ++i) { + if (browser_actions[i]->command_id() > IDC_BROWSER_ACTION_LAST) { + NOTREACHED() << "Too many browser actions."; + } else { + extension_menu_contents_->AddItem( + browser_actions[i]->command_id(), + UTF8ToUTF16(browser_actions[i]->name())); + } + } + } + } + app_menu_contents_->AddSeparator(); #ifdef CHROME_PERSONALIZATION if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableSync)) { diff --git a/chrome/browser/views/toolbar_view.h b/chrome/browser/views/toolbar_view.h index a98e57f..c7080b8 100644 --- a/chrome/browser/views/toolbar_view.h +++ b/chrome/browser/views/toolbar_view.h @@ -103,6 +103,7 @@ class ToolbarView : public views::View, // Accessors... Browser* browser() const { return browser_; } + BrowserActionsContainer* browser_actions() const { return browser_actions_; } ToolbarStarToggle* star_button() const { return star_; } GoButton* go_button() const { return go_; } LocationBarView* location_bar() const { return location_bar_; } @@ -246,6 +247,7 @@ class ToolbarView : public views::View, scoped_ptr<EncodingMenuModel> encoding_menu_contents_; scoped_ptr<views::SimpleMenuModel> devtools_menu_contents_; scoped_ptr<views::SimpleMenuModel> app_menu_contents_; + scoped_ptr<views::SimpleMenuModel> extension_menu_contents_; // TODO(beng): build these into MenuButton. scoped_ptr<views::Menu2> page_menu_menu_; diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 0694d8a..1b3338a 100755 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -64,6 +64,7 @@ 'browser/extensions/extension_toolstrip_apitest.cc', ], 'browser_tests_sources_win_specific': [ + 'browser/extensions/browser_action_test.cc', 'browser/extensions/extension_devtools_browsertest.cc', 'browser/extensions/extension_devtools_browsertest.h', 'browser/extensions/extension_devtools_browsertests.cc', diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index 8aaefa4..07fa897 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -320,29 +320,34 @@ ExtensionAction* Extension::LoadExtensionActionHelper( result->set_extension_id(id()); result->set_type(action_type); - ListValue* icons; + ListValue* icons = NULL; // Read the page action |icons|. if (!page_action->HasKey(keys::kPageActionIcons) || !page_action->GetList(keys::kPageActionIcons, &icons) || icons->GetSize() == 0) { - *error = ExtensionErrorUtils::FormatErrorMessage( - errors::kInvalidPageActionIconPaths, IntToString(definition_index)); - return NULL; - } - - int icon_count = 0; - for (ListValue::const_iterator iter = icons->begin(); - iter != icons->end(); ++iter) { - std::string path; - if (!(*iter)->GetAsString(&path) || path.empty()) { + // Icons are only required for page actions. + if (action_type == ExtensionAction::PAGE_ACTION) { *error = ExtensionErrorUtils::FormatErrorMessage( - errors::kInvalidPageActionIconPath, - IntToString(definition_index), IntToString(icon_count)); + errors::kInvalidPageActionIconPaths, IntToString(definition_index)); return NULL; } + } - result->AddIconPath(path); - ++icon_count; + int icon_count = 0; + if (icons) { + for (ListValue::const_iterator iter = icons->begin(); + iter != icons->end(); ++iter) { + std::string path; + if (!(*iter)->GetAsString(&path) || path.empty()) { + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidPageActionIconPath, + IntToString(definition_index), IntToString(icon_count)); + return NULL; + } + + result->AddIconPath(path); + ++icon_count; + } } if (action_type == ExtensionAction::BROWSER_ACTION) { diff --git a/chrome/common/extensions/extension_action.cc b/chrome/common/extensions/extension_action.cc index 2d3291e..dee9eed 100644 --- a/chrome/common/extensions/extension_action.cc +++ b/chrome/common/extensions/extension_action.cc @@ -2,10 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "chrome/app/chrome_dll_resource.h" #include "chrome/common/extensions/extension_action.h" +int ExtensionAction::next_command_id_ = IDC_BROWSER_ACTION_FIRST; + ExtensionAction::ExtensionAction() - : type_(PAGE_ACTION) { + : type_(PAGE_ACTION), command_id_(next_command_id_++) { } ExtensionAction::~ExtensionAction() { diff --git a/chrome/common/extensions/extension_action.h b/chrome/common/extensions/extension_action.h index 445f98e..53f6124 100644 --- a/chrome/common/extensions/extension_action.h +++ b/chrome/common/extensions/extension_action.h @@ -21,6 +21,8 @@ class ExtensionAction { BROWSER_ACTION = 1, } ExtensionActionType; + int command_id() const { return command_id_; } + std::string id() const { return id_; } void set_id(const std::string& id) { id_ = id; } @@ -41,6 +43,8 @@ class ExtensionAction { } private: + static int next_command_id_; + // The id for the ExtensionAction, for example: "RssPageAction". // For BrowserActions this is blank. std::string id_; @@ -57,6 +61,10 @@ class ExtensionAction { // The paths to the icons that this PageIcon can show. std::vector<std::string> icon_paths_; + + // An integer for use with the browser's command system. These should always + // be in the range [IDC_BROWSER_ACTION_FIRST, IDC_BROWSER_ACTION_LAST]. + int command_id_; }; typedef std::map<std::string, ExtensionAction*> ExtensionActionMap; diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc index 43e116f..fc024ab 100644 --- a/chrome/common/extensions/extension_unittest.cc +++ b/chrome/common/extensions/extension_unittest.cc @@ -403,14 +403,13 @@ TEST(ExtensionTest, LoadPageActionHelper) { errors::kInvalidPageActionIconPaths)); error_msg = ""; - // Same test (name key), but with browser action. + // Same test (name key), but with browser action (icons are not required for + // browser actions). copy.reset(static_cast<DictionaryValue*>(input.DeepCopy())); copy->Remove(keys::kPageActionIcons, NULL); action.reset(extension.LoadExtensionActionHelper( copy.get(), 0, &error_msg, ExtensionAction::BROWSER_ACTION)); - ASSERT_TRUE(NULL == action.get()); - ASSERT_TRUE(MatchPattern(error_msg.c_str(), - errors::kInvalidPageActionIconPaths)); + ASSERT_TRUE(NULL != action.get()); } TEST(ExtensionTest, IdIsValid) { diff --git a/chrome/test/data/extensions/samples/make_page_red/background.html b/chrome/test/data/extensions/samples/make_page_red/background.html new file mode 100755 index 0000000..0843595 --- /dev/null +++ b/chrome/test/data/extensions/samples/make_page_red/background.html @@ -0,0 +1,10 @@ +<html> +<head> +<script> + // Called when the user clicks on the browser action. + chrome.browserAction.onClicked.addListener(function(windowId) { + chrome.tabs.executeScript(null, {code:"document.body.bgColor='red'"}); + }); +</script> +</head> +</html> diff --git a/chrome/test/data/extensions/samples/make_page_red/icon.png b/chrome/test/data/extensions/samples/make_page_red/icon.png Binary files differnew file mode 100755 index 0000000..9a79a46 --- /dev/null +++ b/chrome/test/data/extensions/samples/make_page_red/icon.png diff --git a/chrome/test/data/extensions/samples/make_page_red/manifest.json b/chrome/test/data/extensions/samples/make_page_red/manifest.json new file mode 100755 index 0000000..6bb35c6 --- /dev/null +++ b/chrome/test/data/extensions/samples/make_page_red/manifest.json @@ -0,0 +1,12 @@ +{ + "name": "A browser action with no icon that makes the page red", + "version": "1.0", + "background_page": "background.html", + "permissions": [ + "tabs", "http://*/*" + ], + "browser_action": { + "name": "Make this page red", + "icons": ["icon.png"] + } +}
\ No newline at end of file diff --git a/chrome/test/data/extensions/samples/make_page_red_no_icon/background.html b/chrome/test/data/extensions/samples/make_page_red_no_icon/background.html new file mode 100644 index 0000000..0843595 --- /dev/null +++ b/chrome/test/data/extensions/samples/make_page_red_no_icon/background.html @@ -0,0 +1,10 @@ +<html> +<head> +<script> + // Called when the user clicks on the browser action. + chrome.browserAction.onClicked.addListener(function(windowId) { + chrome.tabs.executeScript(null, {code:"document.body.bgColor='red'"}); + }); +</script> +</head> +</html> diff --git a/chrome/test/data/extensions/samples/make_page_red_no_icon/manifest.json b/chrome/test/data/extensions/samples/make_page_red_no_icon/manifest.json new file mode 100644 index 0000000..ceffb65 --- /dev/null +++ b/chrome/test/data/extensions/samples/make_page_red_no_icon/manifest.json @@ -0,0 +1,11 @@ +{ + "name": "A browser action with no icon that makes the page red", + "version": "1.0", + "background_page": "background.html", + "permissions": [ + "tabs", "http://*/*" + ], + "browser_action": { + "name": "Make this page red" + } +}
\ No newline at end of file |