summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-rw-r--r--chrome/app/chrome_dll_resource.h7
-rw-r--r--chrome/app/generated_resources.grd6
-rw-r--r--chrome/browser/browser.cc50
-rw-r--r--chrome/browser/browser.h1
-rw-r--r--chrome/browser/extensions/browser_action_test.cc67
-rw-r--r--chrome/browser/views/browser_actions_container.cc14
-rw-r--r--chrome/browser/views/browser_actions_container.h2
-rw-r--r--chrome/browser/views/toolbar_view.cc40
-rw-r--r--chrome/browser/views/toolbar_view.h2
-rwxr-xr-xchrome/chrome.gyp1
-rw-r--r--chrome/common/extensions/extension.cc35
-rw-r--r--chrome/common/extensions/extension_action.cc5
-rw-r--r--chrome/common/extensions/extension_action.h8
-rw-r--r--chrome/common/extensions/extension_unittest.cc7
-rwxr-xr-xchrome/test/data/extensions/samples/make_page_red/background.html10
-rwxr-xr-xchrome/test/data/extensions/samples/make_page_red/icon.pngbin0 -> 2809 bytes
-rwxr-xr-xchrome/test/data/extensions/samples/make_page_red/manifest.json12
-rw-r--r--chrome/test/data/extensions/samples/make_page_red_no_icon/background.html10
-rw-r--r--chrome/test/data/extensions/samples/make_page_red_no_icon/manifest.json11
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">
&amp;Downloads
</message>
+ <message name="IDS_SHOW_EXTENSIONS" desc="The show extensions menu in the app menu">
+ &amp;Extensions
+ </message>
+ <message name="IDS_MANAGE_EXTENSIONS" desc="The manage extensions menu item in the extensions submenu">
+ &amp;Manage extensions
+ </message>
<message name="IDS_OPTIONS" desc="The text label of the Options menu item">
&amp;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
new file mode 100755
index 0000000..9a79a46
--- /dev/null
+++ b/chrome/test/data/extensions/samples/make_page_red/icon.png
Binary files differ
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