summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfinnur@chromium.org <finnur@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-26 03:18:46 +0000
committerfinnur@chromium.org <finnur@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-26 03:18:46 +0000
commit671e6c1cecf01e46dc0267e020971aa0f98de0a2 (patch)
tree61db92254d4030103b4f4f68b7b8a7ad342f6d93
parent37e1bb64e696f39acb8a80021af58356af8e3bf1 (diff)
downloadchromium_src-671e6c1cecf01e46dc0267e020971aa0f98de0a2.zip
chromium_src-671e6c1cecf01e46dc0267e020971aa0f98de0a2.tar.gz
chromium_src-671e6c1cecf01e46dc0267e020971aa0f98de0a2.tar.bz2
Implement Browser Actions extensions.
Browser Actions are like Page Actions, except they appear next to the Omnibox and are always visible. For details see http://code.google.com/p/chromium/wiki/BrowserActions. Added a simple browser action sample that adds a Print button to the chrome toolbar (which brings up the Print dialog for the current page). Removed |type| from PageActions, which is currently ignored and was already removed from the docs. Each extension can only have 1 browser_action. Each browser action can specify more than one icon, but only the first is used. And no API has been added yet (besides the event definition). BUG=22099 TEST=Install the sample browser action, navigate to google.com, press the print button. A print dialog should come up. Review URL: http://codereview.chromium.org/243001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@27319 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/extension_browser_event_router.cc14
-rw-r--r--chrome/browser/extensions/extension_browser_event_router.h6
-rw-r--r--chrome/browser/extensions/extension_file_util.cc20
-rw-r--r--chrome/browser/extensions/extension_page_actions_module.cc4
-rw-r--r--chrome/browser/extensions/extension_process_manager.cc3
-rw-r--r--chrome/browser/extensions/extensions_service.cc41
-rw-r--r--chrome/browser/extensions/extensions_service.h15
-rw-r--r--chrome/browser/gtk/location_bar_view_gtk.cc12
-rw-r--r--chrome/browser/gtk/location_bar_view_gtk.h6
-rw-r--r--chrome/browser/tab_contents/tab_contents.cc8
-rw-r--r--chrome/browser/tab_contents/tab_contents.h7
-rw-r--r--chrome/browser/views/browser_actions_container.cc207
-rw-r--r--chrome/browser/views/browser_actions_container.h69
-rw-r--r--chrome/browser/views/location_bar_view.cc7
-rw-r--r--chrome/browser/views/location_bar_view.h4
-rw-r--r--chrome/browser/views/toolbar_view.cc15
-rw-r--r--chrome/browser/views/toolbar_view.h2
-rw-r--r--chrome/chrome.gyp2
-rw-r--r--chrome/common/extensions/extension.cc93
-rw-r--r--chrome/common/extensions/extension.h28
-rw-r--r--chrome/common/extensions/extension_constants.cc3
-rw-r--r--chrome/common/extensions/extension_constants.h2
-rw-r--r--chrome/common/extensions/extension_unittest.cc119
-rw-r--r--chrome/common/page_action.cc6
-rw-r--r--chrome/common/page_action.h39
-rw-r--r--chrome/renderer/renderer_resources.grd2
-rw-r--r--chrome/renderer/resources/extension_process_bindings.js8
-rw-r--r--chrome/test/data/extensions/samples/print_browser_action/background.html14
-rw-r--r--chrome/test/data/extensions/samples/print_browser_action/manifest.json13
-rw-r--r--chrome/test/data/extensions/samples/print_browser_action/print_16x16.pngbin0 -> 647 bytes
30 files changed, 612 insertions, 157 deletions
diff --git a/chrome/browser/extensions/extension_browser_event_router.cc b/chrome/browser/extensions/extension_browser_event_router.cc
index b20853e..8e98d2f 100644
--- a/chrome/browser/extensions/extension_browser_event_router.cc
+++ b/chrome/browser/extensions/extension_browser_event_router.cc
@@ -359,14 +359,12 @@ void ExtensionBrowserEventRouter::PageActionExecuted(
const std::string& url,
int button) {
ListValue args;
-
args.Append(Value::CreateStringValue(page_action_id));
DictionaryValue* data = new DictionaryValue();
data->Set(tab_keys::kTabIdKey, Value::CreateIntegerValue(tab_id));
data->Set(tab_keys::kTabUrlKey, Value::CreateStringValue(url));
data->Set(page_action_keys::kButtonKey, Value::CreateIntegerValue(button));
-
args.Append(data);
std::string json_args;
@@ -375,3 +373,15 @@ void ExtensionBrowserEventRouter::PageActionExecuted(
std::string event_name = extension_id + std::string("/") + page_action_id;
DispatchEvent(profile, event_name.c_str(), json_args);
}
+
+void ExtensionBrowserEventRouter::BrowserActionExecuted(
+ Profile* profile, const std::string& extension_id, int window_id) {
+ ListValue args;
+ args.Append(Value::CreateIntegerValue(window_id));
+
+ std::string json_args;
+ JSONWriter::Write(&args, false, &json_args);
+
+ std::string event_name = std::string("browserAction/") + extension_id;
+ DispatchEvent(profile, event_name.c_str(), json_args);
+}
diff --git a/chrome/browser/extensions/extension_browser_event_router.h b/chrome/browser/extensions/extension_browser_event_router.h
index fe3e9e7..681b448 100644
--- a/chrome/browser/extensions/extension_browser_event_router.h
+++ b/chrome/browser/extensions/extension_browser_event_router.h
@@ -52,13 +52,17 @@ class ExtensionBrowserEventRouter : public TabStripModelObserver,
void TabChangedAt(TabContents* contents, int index, bool loading_only);
void TabStripEmpty();
- // PageActions.
+ // Page Action execute event.
void PageActionExecuted(Profile* profile,
const std::string& extension_id,
const std::string& page_action_id,
int tab_id,
const std::string& url,
int button);
+ // Browser Actions execute event.
+ void BrowserActionExecuted(Profile* profile,
+ const std::string& extension_id,
+ int window_id);
// NotificationObserver.
void Observe(NotificationType type,
diff --git a/chrome/browser/extensions/extension_file_util.cc b/chrome/browser/extensions/extension_file_util.cc
index ac34470..5c49e05 100644
--- a/chrome/browser/extensions/extension_file_util.cc
+++ b/chrome/browser/extensions/extension_file_util.cc
@@ -212,10 +212,10 @@ bool ValidateExtension(Extension* extension, std::string* error) {
}
// Validate icon location for page actions.
- const PageActionMap& page_actions = extension->page_actions();
- for (PageActionMap::const_iterator i(page_actions.begin());
+ const ContextualActionMap& page_actions = extension->page_actions();
+ for (ContextualActionMap::const_iterator i(page_actions.begin());
i != page_actions.end(); ++i) {
- PageAction* page_action = i->second;
+ ContextualAction* page_action = i->second;
const std::vector<std::string>& icon_paths = page_action->icon_paths();
for (std::vector<std::string>::const_iterator iter = icon_paths.begin();
iter != icon_paths.end(); ++iter) {
@@ -227,6 +227,20 @@ bool ValidateExtension(Extension* extension, std::string* error) {
}
}
+ // Validate icon location for browser actions.
+ const ContextualAction* browser_action = extension->browser_action();
+ if (browser_action) {
+ const std::vector<std::string>& icon_paths = browser_action->icon_paths();
+ for (std::vector<std::string>::const_iterator iter = icon_paths.begin();
+ iter != icon_paths.end(); ++iter) {
+ if (!file_util::PathExists(extension->GetResourcePath(*iter))) {
+ *error = StringPrintf("Could not load icon '%s' for browser action.",
+ iter->c_str());
+ return false;
+ }
+ }
+ }
+
// Check children of extension root to see if any of them start with _ and is
// not on the reserved list.
if (!CheckForIllegalFilenames(extension->path(), error)) {
diff --git a/chrome/browser/extensions/extension_page_actions_module.cc b/chrome/browser/extensions/extension_page_actions_module.cc
index d3ba09a..2e9f64d 100644
--- a/chrome/browser/extensions/extension_page_actions_module.cc
+++ b/chrome/browser/extensions/extension_page_actions_module.cc
@@ -70,7 +70,9 @@ bool PageActionFunction::SetPageActionEnabled(bool enable) {
return false;
}
- const PageAction* page_action = extension->GetPageAction(page_action_id);
+ const ContextualAction* page_action =
+ extension->GetContextualAction(page_action_id,
+ ContextualAction::PAGE_ACTION);
if (!page_action) {
error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kNoPageActionError,
page_action_id);
diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc
index a8731d9..76f9057b 100644
--- a/chrome/browser/extensions/extension_process_manager.cc
+++ b/chrome/browser/extensions/extension_process_manager.cc
@@ -107,7 +107,8 @@ void ExtensionProcessManager::RegisterExtensionProcess(
std::vector<std::string> page_action_ids;
Extension* extension = extension_service->GetExtensionById(extension_id);
- for (PageActionMap::const_iterator i = extension->page_actions().begin();
+ for (ContextualActionMap::const_iterator i =
+ extension->page_actions().begin();
i != extension->page_actions().end(); ++i) {
page_action_ids.push_back(i->first);
}
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
index dbb2f03..04ddba2 100644
--- a/chrome/browser/extensions/extensions_service.cc
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -144,20 +144,12 @@ void ExtensionsService::Init() {
GarbageCollectExtensions();
}
-std::vector<PageAction*> ExtensionsService::GetPageActions() const {
- std::vector<PageAction*> result;
-
- // TODO(finnur): Sort the page icons in some meaningful way.
- for (ExtensionList::const_iterator iter = extensions_.begin();
- iter != extensions_.end(); ++iter) {
- const PageActionMap& page_actions = (*iter)->page_actions();
- for (PageActionMap::const_iterator i(page_actions.begin());
- i != page_actions.end(); ++i) {
- result.push_back(i->second);
- }
- }
+std::vector<ContextualAction*> ExtensionsService::GetPageActions() const {
+ return GetContextualActions(ContextualAction::PAGE_ACTION);
+}
- return result;
+std::vector<ContextualAction*> ExtensionsService::GetBrowserActions() const {
+ return GetContextualActions(ContextualAction::BROWSER_ACTION);
}
void ExtensionsService::InstallExtension(const FilePath& extension_path) {
@@ -332,6 +324,29 @@ void ExtensionsService::LoadInstalledExtension(
}
}
+std::vector<ContextualAction*> ExtensionsService::GetContextualActions(
+ ContextualAction::ContextualActionType action_type) const {
+ std::vector<ContextualAction*> result;
+
+ // TODO(finnur): Sort the icons in some meaningful way.
+ for (ExtensionList::const_iterator iter = extensions_.begin();
+ iter != extensions_.end(); ++iter) {
+ if (action_type == ContextualAction::PAGE_ACTION) {
+ const ContextualActionMap* page_actions = &(*iter)->page_actions();
+ for (ContextualActionMap::const_iterator i(page_actions->begin());
+ i != page_actions->end(); ++i) {
+ result.push_back(i->second);
+ }
+ } else {
+ ContextualAction* browser_action = (*iter)->browser_action();
+ if (browser_action)
+ result.push_back(browser_action);
+ }
+ }
+
+ return result;
+}
+
void ExtensionsService::UpdateExtensionBlacklist(
const std::vector<std::string>& blacklist) {
// Use this set to indicate if an extension in the blacklist has been used.
diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h
index 153c723..12f3416 100644
--- a/chrome/browser/extensions/extensions_service.h
+++ b/chrome/browser/extensions/extensions_service.h
@@ -102,9 +102,13 @@ class ExtensionsService
return GetExtensionByIdInternal(id, true, false);
}
- // Retrieves a vector of all page actions, irrespective of which
- // extension they belong to.
- std::vector<PageAction*> GetPageActions() const;
+ // Retrieves a vector of all page actions, irrespective of which extension
+ // they belong to.
+ std::vector<ContextualAction*> GetPageActions() const;
+
+ // Retrieves a vector of all browser actions, irrespective of which extension
+ // they belong to.
+ std::vector<ContextualAction*> GetBrowserActions() const;
// Install the extension file at |extension_path|. Will install as an
// update if an older version is already installed.
@@ -232,6 +236,11 @@ class ExtensionsService
DictionaryValue* manifest, const std::string& id,
const FilePath& path, Extension::Location location);
+ // Retrieves a vector of all page actions or browser actions, irrespective of
+ // which extension they belong to.
+ std::vector<ContextualAction*> GetContextualActions(
+ ContextualAction::ContextualActionType action_type) const;
+
// The profile this ExtensionsService is part of.
Profile* profile_;
diff --git a/chrome/browser/gtk/location_bar_view_gtk.cc b/chrome/browser/gtk/location_bar_view_gtk.cc
index 2d0fa41..eb32632 100644
--- a/chrome/browser/gtk/location_bar_view_gtk.cc
+++ b/chrome/browser/gtk/location_bar_view_gtk.cc
@@ -401,14 +401,14 @@ void LocationBarViewGtk::FocusSearch() {
}
void LocationBarViewGtk::UpdatePageActions() {
- std::vector<PageAction*> page_actions;
+ std::vector<ContextualAction*> page_actions;
if (profile_->GetExtensionsService())
page_actions = profile_->GetExtensionsService()->GetPageActions();
// Initialize on the first call, or re-inialize if more extensions have been
// loaded or added after startup.
if (page_actions.size() != page_action_views_.size()) {
- page_action_views_.reset(); // Delete the old views (if any).
+ page_action_views_.reset(); // Delete the old views (if any).
for (size_t i = 0; i < page_actions.size(); ++i) {
page_action_views_.push_back(
@@ -673,7 +673,8 @@ gboolean LocationBarViewGtk::OnSecurityIconPressed(
// LocationBarViewGtk::PageActionViewGtk
LocationBarViewGtk::PageActionViewGtk::PageActionViewGtk(
- LocationBarViewGtk* owner, Profile* profile, const PageAction* page_action)
+ LocationBarViewGtk* owner, Profile* profile,
+ const ContextualAction* page_action)
: owner_(owner),
profile_(profile),
page_action_(page_action) {
@@ -705,7 +706,7 @@ LocationBarViewGtk::PageActionViewGtk::~PageActionViewGtk() {
tracker_->StopTrackingImageLoad();
image_.Destroy();
event_box_.Destroy();
- for (size_t i=0; i < pixbufs_.size(); ++i) {
+ for (size_t i = 0; i < pixbufs_.size(); ++i) {
if (pixbufs_[i])
g_object_unref(pixbufs_[i]);
}
@@ -718,7 +719,8 @@ void LocationBarViewGtk::PageActionViewGtk::UpdateVisibility(
current_tab_id_ = ExtensionTabUtil::GetTabId(contents);
current_url_ = url;
- const PageActionState* state = contents->GetPageActionState(page_action_);
+ const ContextualActionState* state =
+ contents->GetPageActionState(page_action_);
bool visible = state != NULL;
if (visible) {
// Set the tooltip.
diff --git a/chrome/browser/gtk/location_bar_view_gtk.h b/chrome/browser/gtk/location_bar_view_gtk.h
index 541126e..c0497add 100644
--- a/chrome/browser/gtk/location_bar_view_gtk.h
+++ b/chrome/browser/gtk/location_bar_view_gtk.h
@@ -28,7 +28,7 @@ class BubblePositioner;
class Browser;
class CommandUpdater;
class GtkThemeProvider;
-class PageAction;
+class ContextualAction;
class Profile;
class SkBitmap;
class TabContents;
@@ -104,7 +104,7 @@ class LocationBarViewGtk : public AutocompleteEditController,
public:
PageActionViewGtk(
LocationBarViewGtk* owner, Profile* profile,
- const PageAction* page_action);
+ const ContextualAction* page_action);
virtual ~PageActionViewGtk();
GtkWidget* widget() { return event_box_.get(); }
@@ -129,7 +129,7 @@ class LocationBarViewGtk : public AutocompleteEditController,
// The PageAction that this view represents. The PageAction is not owned by
// us, it resides in the extension of this particular profile.
- const PageAction* page_action_;
+ const ContextualAction* page_action_;
// The icons representing different states for the page action.
std::vector<GdkPixbuf*> pixbufs_;
diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc
index 9b82671..8f927ac 100644
--- a/chrome/browser/tab_contents/tab_contents.cc
+++ b/chrome/browser/tab_contents/tab_contents.cc
@@ -586,7 +586,7 @@ void TabContents::SetIsCrashed(bool state) {
NotifyNavigationStateChanged(INVALIDATE_TAB);
}
-void TabContents::SetPageActionEnabled(const PageAction* page_action,
+void TabContents::SetPageActionEnabled(const ContextualAction* page_action,
bool enable,
const std::string& title,
int icon_id) {
@@ -599,14 +599,14 @@ void TabContents::SetPageActionEnabled(const PageAction* page_action,
if (enable) {
enabled_page_actions_[page_action].reset(
- new PageActionState(title, icon_id));
+ new ContextualActionState(title, icon_id));
} else {
enabled_page_actions_.erase(page_action);
}
}
-const PageActionState* TabContents::GetPageActionState(
- const PageAction* page_action) {
+const ContextualActionState* TabContents::GetPageActionState(
+ const ContextualAction* page_action) {
if (enabled_page_actions_.end() == enabled_page_actions_.find(page_action))
return NULL;
diff --git a/chrome/browser/tab_contents/tab_contents.h b/chrome/browser/tab_contents/tab_contents.h
index 72f050f..5749e60 100644
--- a/chrome/browser/tab_contents/tab_contents.h
+++ b/chrome/browser/tab_contents/tab_contents.h
@@ -261,14 +261,15 @@ class TabContents : public PageNavigator,
// this tab. The parameter |title| (if not empty) can be used to override the
// page action title for this tab and |icon_id| specifies an icon index
// (defined in the manifest) to use instead of the first icon (for this tab).
- void SetPageActionEnabled(const PageAction* page_action, bool enable,
+ void SetPageActionEnabled(const ContextualAction* page_action, bool enable,
const std::string& title, int icon_id);
// Returns the page action state for this tab. The pair returns contains
// the title (string) for the page action and the icon index to use (int).
// If this function returns NULL it means the page action is not enabled for
// this tab.
- const PageActionState* GetPageActionState(const PageAction* page_action);
+ const ContextualActionState* GetPageActionState(
+ const ContextualAction* page_action);
// Whether the tab is in the process of being destroyed.
// Added as a tentative work-around for focus related bug #4633. This allows
@@ -1106,7 +1107,7 @@ class TabContents : public PageNavigator,
// that can be used to override the title and icon used for the page action).
// This map is cleared every time the mainframe navigates and populated by the
// PageAction extension API.
- std::map< const PageAction*, linked_ptr<PageActionState> >
+ std::map< const ContextualAction*, linked_ptr<ContextualActionState> >
enabled_page_actions_;
// Data for misc internal state ----------------------------------------------
diff --git a/chrome/browser/views/browser_actions_container.cc b/chrome/browser/views/browser_actions_container.cc
new file mode 100644
index 0000000..52e3531
--- /dev/null
+++ b/chrome/browser/views/browser_actions_container.cc
@@ -0,0 +1,207 @@
+// Copyright (c) 2006-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/views/browser_actions_container.h"
+
+#include "base/stl_util-inl.h"
+#include "chrome/browser/extensions/extension_browser_event_router.h"
+#include "chrome/browser/extensions/extensions_service.h"
+#include "chrome/browser/extensions/extension_tabs_module.h"
+#include "chrome/browser/image_loading_tracker.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/views/toolbar_view.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/notification_type.h"
+#include "chrome/common/page_action.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "views/controls/button/image_button.h"
+
+// The size of the icon for page actions.
+static const int kIconSize = 16;
+
+// The padding between icons (pixels between each icon).
+static const int kIconHorizontalPadding = 10;
+
+////////////////////////////////////////////////////////////////////////////////
+// BrowserActionImageView
+
+// The BrowserActionImageView is a specialization of the ImageButton class.
+// It acts on a ContextualAction, in this case a BrowserAction and handles
+// loading the image for the button asynchronously on the file thread to
+class BrowserActionImageView : public views::ImageButton,
+ public views::ButtonListener,
+ public ImageLoadingTracker::Observer {
+ public:
+ BrowserActionImageView(ContextualAction* browser_action,
+ Extension* extension,
+ BrowserActionsContainer* panel);
+ ~BrowserActionImageView();
+
+ // Overridden from views::ButtonListener:
+ virtual void ButtonPressed(views::Button* sender, const views::Event& event);
+
+ // Overridden from ImageLoadingTracker.
+ virtual void OnImageLoaded(SkBitmap* image, size_t index);
+
+ private:
+ // The browser action this view represents. The ContextualAction is not owned
+ // by this class.
+ ContextualAction* browser_action_;
+
+ // The icons representing different states for the browser action.
+ std::vector<SkBitmap> browser_action_icons_;
+
+ // The object that is waiting for the image loading to complete
+ // asynchronously. This object can potentially outlive the BrowserActionView,
+ // and takes care of deleting itself.
+ ImageLoadingTracker* tracker_;
+
+ // The browser action shelf.
+ BrowserActionsContainer* panel_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserActionImageView);
+};
+
+BrowserActionImageView::BrowserActionImageView(
+ ContextualAction* browser_action, Extension* extension,
+ BrowserActionsContainer* panel)
+ : ImageButton(this),
+ browser_action_(browser_action),
+ tracker_(NULL),
+ panel_(panel) {
+ // Load the images this view needs asynchronously on the file thread. We'll
+ // get a call back into OnImageLoaded if the image loads successfully. If not,
+ // the ImageView will have no image and will not appear in the browser chrome.
+ DCHECK(!browser_action->icon_paths().empty());
+ const std::vector<std::string>& icon_paths = browser_action->icon_paths();
+ browser_action_icons_.resize(icon_paths.size());
+ tracker_ = new ImageLoadingTracker(this, icon_paths.size());
+ for (std::vector<std::string>::const_iterator iter = icon_paths.begin();
+ iter != icon_paths.end(); ++iter) {
+ FilePath path = extension->GetResourcePath(*iter);
+ tracker_->PostLoadImageTask(path);
+ }
+}
+
+BrowserActionImageView::~BrowserActionImageView() {
+ if (tracker_) {
+ tracker_->StopTrackingImageLoad();
+ tracker_ = NULL; // The tracker object will be deleted when we return.
+ }
+}
+
+void BrowserActionImageView::ButtonPressed(
+ views::Button* sender, const views::Event& event) {
+ panel_->OnBrowserActionExecuted(*browser_action_);
+}
+
+void BrowserActionImageView::OnImageLoaded(SkBitmap* image, size_t index) {
+ DCHECK(index < browser_action_icons_.size());
+ browser_action_icons_[index] = *image;
+ if (index == 0) {
+ SetImage(views::CustomButton::BS_NORMAL, image);
+ SetImage(views::CustomButton::BS_HOT, image);
+ SetImage(views::CustomButton::BS_PUSHED, image);
+ SetTooltipText(ASCIIToWide(browser_action_->name()));
+ panel_->OnBrowserActionVisibilityChanged();
+ }
+
+ if (index == browser_action_icons_.size() - 1)
+ tracker_ = NULL; // The tracker object will delete itself when we return.
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// BrowserActionsContainer
+
+BrowserActionsContainer::BrowserActionsContainer(
+ Profile* profile, ToolbarView* toolbar)
+ : profile_(profile), toolbar_(toolbar) {
+ ExtensionsService* extension_service = profile->GetExtensionsService();
+ registrar_.Add(this, NotificationType::EXTENSION_LOADED,
+ Source<ExtensionsService>(extension_service));
+ registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
+ Source<ExtensionsService>(extension_service));
+ registrar_.Add(this, NotificationType::EXTENSION_UNLOADED_DISABLED,
+ Source<ExtensionsService>(extension_service));
+
+ RefreshBrowserActionViews();
+}
+
+BrowserActionsContainer::~BrowserActionsContainer() {
+ DeleteBrowserActionViews();
+}
+
+void BrowserActionsContainer::RefreshBrowserActionViews() {
+ ExtensionsService* extension_service = profile_->GetExtensionsService();
+ if (!extension_service) // The |extension_service| can be NULL in Incognito.
+ return;
+
+ std::vector<ContextualAction*> 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);
+
+ BrowserActionImageView* view =
+ new BrowserActionImageView(browser_actions[i], extension, this);
+ browser_action_views_.push_back(view);
+ AddChildView(view);
+ }
+ }
+}
+
+void BrowserActionsContainer::DeleteBrowserActionViews() {
+ if (!browser_action_views_.empty()) {
+ for (size_t i = 0; i < browser_action_views_.size(); ++i)
+ RemoveChildView(browser_action_views_[i]);
+ STLDeleteContainerPointers(browser_action_views_.begin(),
+ browser_action_views_.end());
+ browser_action_views_.clear();
+ }
+}
+
+void BrowserActionsContainer::OnBrowserActionVisibilityChanged() {
+ toolbar_->Layout();
+}
+
+void BrowserActionsContainer::OnBrowserActionExecuted(
+ const ContextualAction& browser_action) {
+ int window_id = ExtensionTabUtil::GetWindowId(toolbar_->browser());
+ ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted(
+ profile_, browser_action.extension_id(), window_id);
+}
+
+gfx::Size BrowserActionsContainer::GetPreferredSize() {
+ if (browser_action_views_.empty())
+ return gfx::Size(0, 0);
+ int width = kIconHorizontalPadding + browser_action_views_.size() *
+ (kIconSize + kIconHorizontalPadding);
+ return gfx::Size(width, kIconSize);
+}
+
+void BrowserActionsContainer::Layout() {
+ for (size_t i = 0; i < browser_action_views_.size(); ++i) {
+ views::ImageButton* view = browser_action_views_[i];
+ gfx::Size sz = view->GetPreferredSize();
+ int x = kIconHorizontalPadding + i * (kIconSize + kIconHorizontalPadding);
+ view->SetBounds(x, (height() - sz.height()) / 2, sz.width(), sz.height());
+ }
+}
+
+void BrowserActionsContainer::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::EXTENSION_LOADED ||
+ type == NotificationType::EXTENSION_UNLOADED ||
+ type == NotificationType::EXTENSION_UNLOADED_DISABLED) {
+ RefreshBrowserActionViews();
+ } else {
+ NOTREACHED() << L"Received unexpected notification";
+ }
+}
diff --git a/chrome/browser/views/browser_actions_container.h b/chrome/browser/views/browser_actions_container.h
new file mode 100644
index 0000000..73d4f13
--- /dev/null
+++ b/chrome/browser/views/browser_actions_container.h
@@ -0,0 +1,69 @@
+// 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.
+
+#ifndef CHROME_BROWSER_VIEWS_BROWSER_ACTIONS_PANEL_H_
+#define CHROME_BROWSER_VIEWS_BROWSER_ACTIONS_PANEL_H_
+
+#include <vector>
+
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "views/view.h"
+
+class ContextualAction;
+class Profile;
+class ToolbarView;
+namespace views {
+class ImageButton;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// The BrowserActionsContainer is a container view, responsible for drawing the
+// icons that represent browser actions (extensions that add icons to the
+// toolbar).
+//
+////////////////////////////////////////////////////////////////////////////////
+class BrowserActionsContainer : public views::View,
+ public NotificationObserver {
+ public:
+ BrowserActionsContainer(Profile* profile, ToolbarView* toolbar);
+ virtual ~BrowserActionsContainer();
+
+ // Update the views to reflect the state of the browser action icons.
+ void RefreshBrowserActionViews();
+
+ // Delete all browser action views.
+ void DeleteBrowserActionViews();
+
+ // Called when a browser action becomes visible/hidden.
+ void OnBrowserActionVisibilityChanged();
+
+ // Called when the user clicks on the browser action icon.
+ void OnBrowserActionExecuted(const ContextualAction& browser_action);
+
+ // Overridden from views::View:
+ virtual gfx::Size GetPreferredSize();
+ virtual void Layout();
+
+ // Overridden from NotificationObserver:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ private:
+ // The vector of browser actions (icons/image buttons for each action).
+ std::vector<views::ImageButton*> browser_action_views_;
+
+ NotificationRegistrar registrar_;
+
+ Profile* profile_;
+
+ // The toolbar that owns us.
+ ToolbarView* toolbar_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserActionsContainer);
+};
+
+#endif // CHROME_BROWSER_VIEWS_BROWSER_ACTIONS_PANEL_H_
diff --git a/chrome/browser/views/location_bar_view.cc b/chrome/browser/views/location_bar_view.cc
index 34f23f2..ad21eeb 100644
--- a/chrome/browser/views/location_bar_view.cc
+++ b/chrome/browser/views/location_bar_view.cc
@@ -644,7 +644,7 @@ void LocationBarView::DeletePageActionViews() {
}
void LocationBarView::RefreshPageActionViews() {
- std::vector<PageAction*> page_actions;
+ std::vector<ContextualAction*> page_actions;
if (profile_->GetExtensionsService())
page_actions = profile_->GetExtensionsService()->GetPageActions();
@@ -1183,7 +1183,7 @@ void LocationBarView::SecurityImageView::ShowInfoBubble() {
LocationBarView::PageActionImageView::PageActionImageView(
LocationBarView* owner,
Profile* profile,
- const PageAction* page_action,
+ const ContextualAction* page_action,
const BubblePositioner* bubble_positioner)
: LocationBarImageView(bubble_positioner),
owner_(owner),
@@ -1250,7 +1250,8 @@ void LocationBarView::PageActionImageView::UpdateVisibility(
current_tab_id_ = ExtensionTabUtil::GetTabId(contents);
current_url_ = url;
- const PageActionState* state = contents->GetPageActionState(page_action_);
+ const ContextualActionState* state =
+ contents->GetPageActionState(page_action_);
bool visible = state != NULL;
if (visible) {
// Set the tooltip.
diff --git a/chrome/browser/views/location_bar_view.h b/chrome/browser/views/location_bar_view.h
index 343e7e6..6ab77d7 100644
--- a/chrome/browser/views/location_bar_view.h
+++ b/chrome/browser/views/location_bar_view.h
@@ -347,7 +347,7 @@ class LocationBarView : public LocationBar,
public:
PageActionImageView(LocationBarView* owner,
Profile* profile,
- const PageAction* page_action,
+ const ContextualAction* page_action,
const BubblePositioner* bubble_positioner);
virtual ~PageActionImageView();
@@ -374,7 +374,7 @@ class LocationBarView : public LocationBar,
// The PageAction that this view represents. The PageAction is not owned by
// us, it resides in the extension of this particular profile.
- const PageAction* page_action_;
+ const ContextualAction* page_action_;
// The icons representing different states for the page action.
std::vector<SkBitmap> page_action_icons_;
diff --git a/chrome/browser/views/toolbar_view.cc b/chrome/browser/views/toolbar_view.cc
index c6b81b1..d917804 100644
--- a/chrome/browser/views/toolbar_view.cc
+++ b/chrome/browser/views/toolbar_view.cc
@@ -30,6 +30,7 @@
#include "chrome/browser/browser_theme_provider.h"
#include "chrome/browser/user_data_manager.h"
#include "chrome/browser/views/bookmark_menu_button.h"
+#include "chrome/browser/views/browser_actions_container.h"
#include "chrome/browser/views/event_utils.h"
#include "chrome/browser/views/go_button.h"
#include "chrome/browser/views/location_bar_view.h"
@@ -153,6 +154,7 @@ ToolbarView::ToolbarView(Browser* browser)
star_(NULL),
location_bar_(NULL),
go_(NULL),
+ browser_actions_(NULL),
page_menu_(NULL),
app_menu_(NULL),
bookmark_menu_(NULL),
@@ -513,14 +515,15 @@ void ToolbarView::Layout() {
child_y, star_->GetPreferredSize().width(), child_height);
int go_button_width = go_->GetPreferredSize().width();
+ int browser_actions_width = browser_actions_->GetPreferredSize().width();
int page_menu_width = page_menu_->GetPreferredSize().width();
int app_menu_width = app_menu_->GetPreferredSize().width();
int bookmark_menu_width = bookmark_menu_ ?
bookmark_menu_->GetPreferredSize().width() : 0;
int location_x = star_->x() + star_->width();
int available_width = width() - kPaddingRight - bookmark_menu_width -
- app_menu_width - page_menu_width - kMenuButtonOffset - go_button_width -
- location_x;
+ app_menu_width - page_menu_width - browser_actions_width -
+ kMenuButtonOffset - go_button_width - location_x;
location_bar_->SetBounds(location_x, child_y, std::max(available_width, 0),
child_height);
@@ -535,6 +538,10 @@ void ToolbarView::Layout() {
next_menu_x += bookmark_menu_width;
}
+ browser_actions_->SetBounds(
+ next_menu_x, child_y, browser_actions_width, child_height);
+ next_menu_x += browser_actions_width;
+
page_menu_->SetBounds(next_menu_x, child_y, page_menu_width, child_height);
next_menu_x += page_menu_width;
@@ -851,12 +858,13 @@ void ToolbarView::CreateCenterStack(Profile *profile) {
}
void ToolbarView::CreateRightSideControls(Profile* profile) {
+ browser_actions_ = new BrowserActionsContainer(profile, this);
+
page_menu_ = new views::MenuButton(NULL, std::wstring(), this, false);
page_menu_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_PAGE));
page_menu_->SetTooltipText(l10n_util::GetString(IDS_PAGEMENU_TOOLTIP));
page_menu_->SetID(VIEW_ID_PAGE_MENU);
-
app_menu_ = new views::MenuButton(NULL, std::wstring(), this, false);
app_menu_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_APP));
app_menu_->SetTooltipText(l10n_util::GetStringF(IDS_APPMENU_TOOLTIP,
@@ -872,6 +880,7 @@ void ToolbarView::CreateRightSideControls(Profile* profile) {
LoadRightSideControlsImages();
+ AddChildView(browser_actions_);
AddChildView(page_menu_);
AddChildView(app_menu_);
}
diff --git a/chrome/browser/views/toolbar_view.h b/chrome/browser/views/toolbar_view.h
index ed42a11..a98e57f 100644
--- a/chrome/browser/views/toolbar_view.h
+++ b/chrome/browser/views/toolbar_view.h
@@ -22,6 +22,7 @@
#include "views/view.h"
class BackForwardMenuModelViews;
+class BrowserActionsContainer;
class Browser;
class Profile;
class ToolbarStarToggle;
@@ -219,6 +220,7 @@ class ToolbarView : public views::View,
ToolbarStarToggle* star_;
LocationBarView* location_bar_;
GoButton* go_;
+ BrowserActionsContainer* browser_actions_;
views::MenuButton* page_menu_;
views::MenuButton* app_menu_;
// The bookmark menu button. This may be null.
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index d5fe44f..32b2e2d 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1997,6 +1997,8 @@
'browser/views/bookmark_menu_controller_views.h',
'browser/views/bookmark_table_view.cc',
'browser/views/bookmark_table_view.h',
+ 'browser/views/browser_actions_container.cc',
+ 'browser/views/browser_actions_container.h',
'browser/views/browser_bubble.cc',
'browser/views/browser_bubble.h',
'browser/views/browser_bubble_gtk.cc',
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index 3a73bf2..d68e91f 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -10,6 +10,7 @@
#include "base/file_util.h"
#include "base/logging.h"
#include "base/string_util.h"
+#include "base/stl_util-inl.h"
#include "base/third_party/nss/blapi.h"
#include "base/third_party/nss/sha256.h"
#include "chrome/common/chrome_constants.h"
@@ -106,9 +107,7 @@ const size_t Extension::kNumPermissions =
arraysize(Extension::kPermissionNames);
Extension::~Extension() {
- for (PageActionMap::iterator i = page_actions_.begin();
- i != page_actions_.end(); ++i)
- delete i->second;
+ STLDeleteValues(&page_actions_);
}
const std::string Extension::VersionString() const {
@@ -150,12 +149,18 @@ GURL Extension::GetResourceURL(const GURL& extension_url,
return ret_val;
}
-const PageAction* Extension::GetPageAction(std::string id) const {
- PageActionMap::const_iterator it = page_actions_.find(id);
- if (it == page_actions_.end())
- return NULL;
+const ContextualAction* Extension::GetContextualAction(
+ std::string id, ContextualAction::ContextualActionType action_type) const {
+ if (action_type == ContextualAction::BROWSER_ACTION) {
+ DCHECK(id.empty()); // Multiple browser actions are not allowed.
+ return browser_action_.get();
+ } else {
+ ContextualActionMap::const_iterator it = page_actions_.find(id);
+ if (it == page_actions_.end())
+ return NULL;
- return it->second;
+ return it->second;
+ }
}
Extension::Location Extension::ExternalExtensionInstallType(
@@ -306,13 +311,14 @@ bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script,
return true;
}
-// Helper method that loads a PageAction object from a dictionary in the
-// page_action list of the manifest.
-PageAction* Extension::LoadPageActionHelper(
+// Helper method that loads a PageAction or BrowserAction object from a
+// dictionary in the page_actions list or browser_action key of the manifest.
+ContextualAction* Extension::LoadContextualActionHelper(
const DictionaryValue* page_action, int definition_index,
- std::string* error) {
- scoped_ptr<PageAction> result(new PageAction());
+ std::string* error, ContextualAction::ContextualActionType action_type) {
+ scoped_ptr<ContextualAction> result(new ContextualAction());
result->set_extension_id(id());
+ result->set_type(action_type);
ListValue* icons;
// Read the page action |icons|.
@@ -339,14 +345,18 @@ PageAction* Extension::LoadPageActionHelper(
++icon_count;
}
- // Read the page action |id|.
- std::string id;
- if (!page_action->GetString(keys::kPageActionId, &id)) {
- *error = ExtensionErrorUtils::FormatErrorMessage(
- errors::kInvalidPageActionId, IntToString(definition_index));
- return NULL;
+ if (action_type == ContextualAction::BROWSER_ACTION) {
+ result->set_id(""); // Not needed (only 1 browser action per extension).
+ } else {
+ // Read the page action |id|.
+ std::string id;
+ if (!page_action->GetString(keys::kPageActionId, &id)) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(
+ errors::kInvalidPageActionId, IntToString(definition_index));
+ return NULL;
+ }
+ result->set_id(id);
}
- result->set_id(id);
// Read the page action |name|.
std::string name;
@@ -357,23 +367,6 @@ PageAction* Extension::LoadPageActionHelper(
}
result->set_name(name);
- // Read the page action |type|. It is optional and set to permanent if
- // missing.
- std::string type;
- if (!page_action->GetString(keys::kType, &type)) {
- result->set_type(PageAction::PERMANENT);
- } else if (!LowerCaseEqualsASCII(type, values::kPageActionTypeTab) &&
- !LowerCaseEqualsASCII(type, values::kPageActionTypePermanent)) {
- *error = ExtensionErrorUtils::FormatErrorMessage(
- errors::kInvalidPageActionTypeValue, IntToString(definition_index));
- return NULL;
- } else {
- if (LowerCaseEqualsASCII(type, values::kPageActionTypeTab))
- result->set_type(PageAction::TAB);
- else
- result->set_type(PageAction::PERMANENT);
- }
-
return result.release();
}
@@ -940,12 +933,28 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_id,
return false;
}
- PageAction* page_action =
- LoadPageActionHelper(page_action_value, i, error);
- if (!page_action)
+ ContextualAction* contextual_action =
+ LoadContextualActionHelper(page_action_value, i, error,
+ ContextualAction::PAGE_ACTION);
+ if (!contextual_action)
return false; // Failed to parse page action definition.
- page_actions_[page_action->id()] = page_action;
+ page_actions_[contextual_action->id()] = contextual_action;
+ }
+ }
+
+ if (source.HasKey(keys::kBrowserAction)) {
+ DictionaryValue* browser_action_value;
+ if (!source.GetDictionary(keys::kBrowserAction, &browser_action_value)) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(
+ errors::kInvalidBrowserAction, "");
+ return false;
}
+
+ browser_action_.reset(
+ LoadContextualActionHelper(browser_action_value, 0, error,
+ ContextualAction::BROWSER_ACTION));
+ if (!browser_action_.get())
+ return false; // Failed to parse browser action definition.
}
// Initialize the permissions (optional).
@@ -1051,7 +1060,7 @@ std::set<FilePath> Extension::GetBrowserImages() {
}
// page action icons
- for (PageActionMap::const_iterator it = page_actions().begin();
+ for (ContextualActionMap::const_iterator it = page_actions().begin();
it != page_actions().end(); ++it) {
const std::vector<std::string>& icon_paths = it->second->icon_paths();
for (std::vector<std::string>::const_iterator iter = icon_paths.begin();
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index eb7b86c..5589500 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -107,7 +107,8 @@ class Extension {
static const char kMimeType[];
Extension()
- : location_(INVALID), is_theme_(false), background_page_ready_(false) {}
+ : location_(INVALID), is_theme_(false),
+ background_page_ready_(false) {}
explicit Extension(const FilePath& path);
virtual ~Extension();
@@ -193,7 +194,8 @@ class Extension {
const std::string& public_key() const { return public_key_; }
const std::string& description() const { return description_; }
const UserScriptList& content_scripts() const { return content_scripts_; }
- const PageActionMap& page_actions() const { return page_actions_; }
+ const ContextualActionMap& page_actions() const { return page_actions_; }
+ ContextualAction* browser_action() const { return browser_action_.get(); }
const std::vector<PrivacyBlacklistInfo>& privacy_blacklists() const {
return privacy_blacklists_;
}
@@ -224,8 +226,9 @@ class Extension {
const GURL& update_url() const { return update_url_; }
const std::map<int, std::string>& icons() { return icons_; }
- // Retrieves a page action by |id|.
- const PageAction* GetPageAction(std::string id) const;
+ // Retrieves a page action or browser action by |id|.
+ const ContextualAction* GetContextualAction(
+ std::string id, ContextualAction::ContextualActionType action_type) const;
// Returns the origin of this extension. This function takes a |registry_path|
// so that the registry location can be overwritten during testing.
@@ -290,11 +293,13 @@ class Extension {
std::string* error,
UserScript* result);
- // Helper method that loads a PageAction object from a
- // dictionary in the page_action list of the manifest.
- PageAction* LoadPageActionHelper(const DictionaryValue* page_action,
- int definition_index,
- std::string* error);
+ // Helper method that loads a ContextualAction object from a
+ // dictionary in the page_action or browser_action section of the manifest.
+ ContextualAction* LoadContextualActionHelper(
+ const DictionaryValue* contextual_action,
+ int definition_index,
+ std::string* error,
+ ContextualAction::ContextualActionType action_type);
// Figures out if a source contains keys not associated with themes - we
// don't want to allow scripts and such to be bundled with themes.
@@ -330,7 +335,10 @@ class Extension {
UserScriptList content_scripts_;
// A list of page actions.
- PageActionMap page_actions_;
+ ContextualActionMap page_actions_;
+
+ // The extension's browser action, if any.
+ scoped_ptr<ContextualAction> browser_action_;
// Optional list of privacy blacklistrom.
std::vector<PrivacyBlacklistInfo> privacy_blacklists_;
diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc
index 0ae5ff5..8281d42 100644
--- a/chrome/common/extensions/extension_constants.cc
+++ b/chrome/common/extensions/extension_constants.cc
@@ -7,6 +7,7 @@
namespace extension_manifest_keys {
const wchar_t* kBackground = L"background_page";
+const wchar_t* kBrowserAction = L"browser_action";
const wchar_t* kChromeURLOverrides = L"chrome_url_overrides";
const wchar_t* kContentScripts = L"content_scripts";
const wchar_t* kCss = L"css";
@@ -53,6 +54,8 @@ const char* kPageActionTypePermanent = "permanent";
// printf because we want to unit test them and scanf is hard to make
// cross-platform.
namespace extension_manifest_errors {
+const char* kInvalidBrowserAction =
+ "Invalid value for 'browser_action'.";
const char* kInvalidChromeURLOverrides =
"Invalid value for 'chrome_url_overrides'.";
const char* kInvalidContentScript =
diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h
index 399315e8..8b321b6 100644
--- a/chrome/common/extensions/extension_constants.h
+++ b/chrome/common/extensions/extension_constants.h
@@ -8,6 +8,7 @@
// Keys used in JSON representation of extensions.
namespace extension_manifest_keys {
extern const wchar_t* kBackground;
+ extern const wchar_t* kBrowserAction;
extern const wchar_t* kChromeURLOverrides;
extern const wchar_t* kContentScripts;
extern const wchar_t* kCss;
@@ -52,6 +53,7 @@ namespace extension_manifest_values {
// Error messages returned from Extension::InitFromValue().
namespace extension_manifest_errors {
+ extern const char* kInvalidBrowserAction;
extern const char* kInvalidChromeURLOverrides;
extern const char* kInvalidContentScript;
extern const char* kInvalidContentScriptsList;
diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc
index f1ee5e1..6348315 100644
--- a/chrome/common/extensions/extension_unittest.cc
+++ b/chrome/common/extensions/extension_unittest.cc
@@ -279,21 +279,28 @@ TEST(ExtensionTest, GetResourceURLAndPath) {
TEST(ExtensionTest, LoadPageActionHelper) {
Extension extension;
std::string error_msg;
- scoped_ptr<PageAction> page_action;
+ scoped_ptr<ContextualAction> action;
DictionaryValue input;
// First try with an empty dictionary. We should get nothing back.
- ASSERT_EQ(NULL, extension.LoadPageActionHelper(&input, 0, &error_msg));
+ ASSERT_EQ(NULL, extension.LoadContextualActionHelper(
+ &input, 0, &error_msg, ContextualAction::PAGE_ACTION));
+ ASSERT_STRNE("", error_msg.c_str());
+ error_msg = "";
+
+ // Now try the same, but as a browser action. Ensure same results.
+ ASSERT_EQ(NULL, extension.LoadContextualActionHelper(
+ &input, 0, &error_msg, ContextualAction::BROWSER_ACTION));
ASSERT_STRNE("", error_msg.c_str());
error_msg = "";
// Now setup some values to use in the page action.
- const std::string id("MyPageActionId");
- const std::string name("MyPageActionName");
+ const std::string id("MyContextualActionId");
+ const std::string name("MyContextualActionName");
std::string img1("image1.png");
std::string img2("image2.png");
- // Add the page_actions dictionary.
+ // Add the dictionary for the contextual action.
input.SetString(keys::kPageActionId, id);
input.SetString(keys::kName, name);
ListValue* icons = new ListValue;
@@ -301,31 +308,46 @@ TEST(ExtensionTest, LoadPageActionHelper) {
icons->Set(1, Value::CreateStringValue(img2));
input.Set(keys::kPageActionIcons, icons);
- // Parse the page action and read back the values from the object.
- page_action.reset(extension.LoadPageActionHelper(&input, 0, &error_msg));
- ASSERT_TRUE(NULL != page_action.get());
+ // Parse as page action and read back the values from the object.
+ action.reset(extension.LoadContextualActionHelper(
+ &input, 0, &error_msg, ContextualAction::PAGE_ACTION));
+ ASSERT_TRUE(NULL != action.get());
ASSERT_STREQ("", error_msg.c_str());
- ASSERT_STREQ(id.c_str(), page_action->id().c_str());
- ASSERT_STREQ(name.c_str(), page_action->name().c_str());
- ASSERT_EQ(2u, page_action->icon_paths().size());
- ASSERT_STREQ(img1.c_str(), page_action->icon_paths()[0].c_str());
- ASSERT_STREQ(img2.c_str(), page_action->icon_paths()[1].c_str());
- // Type hasn't been set, but it defaults to PERMANENT.
- ASSERT_EQ(PageAction::PERMANENT, page_action->type());
+ ASSERT_STREQ(id.c_str(), action->id().c_str());
+ ASSERT_STREQ(name.c_str(), action->name().c_str());
+ ASSERT_EQ(2u, action->icon_paths().size());
+ ASSERT_STREQ(img1.c_str(), action->icon_paths()[0].c_str());
+ ASSERT_STREQ(img2.c_str(), action->icon_paths()[1].c_str());
+ ASSERT_EQ(ContextualAction::PAGE_ACTION, action->type());
+
+ // Now try the same, but as a browser action.
+ action.reset(extension.LoadContextualActionHelper(
+ &input, 0, &error_msg, ContextualAction::BROWSER_ACTION));
+ ASSERT_TRUE(NULL != action.get());
+ ASSERT_STREQ("", error_msg.c_str());
+ // Browser actions don't have an id, page actions do.
+ ASSERT_STREQ("", action->id().c_str());
+ ASSERT_STREQ(name.c_str(), action->name().c_str());
+ ASSERT_EQ(2u, action->icon_paths().size());
+ ASSERT_STREQ(img1.c_str(), action->icon_paths()[0].c_str());
+ ASSERT_STREQ(img2.c_str(), action->icon_paths()[1].c_str());
+ ASSERT_EQ(ContextualAction::BROWSER_ACTION, action->type());
// Explicitly set the same type and parse again.
- input.SetString(keys::kType, values::kPageActionTypePermanent);
- page_action.reset(extension.LoadPageActionHelper(&input, 0, &error_msg));
- ASSERT_TRUE(NULL != page_action.get());
+ input.SetString(keys::kType, values::kPageActionTypeTab);
+ action.reset(extension.LoadContextualActionHelper(
+ &input, 0, &error_msg, ContextualAction::BROWSER_ACTION));
+ ASSERT_TRUE(NULL != action.get());
ASSERT_STREQ("", error_msg.c_str());
- ASSERT_EQ(PageAction::PERMANENT, page_action->type());
+ ASSERT_EQ(ContextualAction::BROWSER_ACTION, action->type());
- // Explicitly set the TAB type and parse again.
- input.SetString(keys::kType, values::kPageActionTypeTab);
- page_action.reset(extension.LoadPageActionHelper(&input, 0, &error_msg));
- ASSERT_TRUE(NULL != page_action.get());
+ // Explicitly set the PAGE_ACTION type and parse again.
+ input.SetString(keys::kType, values::kPageActionTypePermanent);
+ action.reset(extension.LoadContextualActionHelper(
+ &input, 0, &error_msg, ContextualAction::PAGE_ACTION));
+ ASSERT_TRUE(NULL != action.get());
ASSERT_STREQ("", error_msg.c_str());
- ASSERT_EQ(PageAction::TAB, page_action->type());
+ ASSERT_EQ(ContextualAction::PAGE_ACTION, action->type());
// Make a deep copy of the input and remove one key at a time and see if we
// get the right error.
@@ -334,34 +356,61 @@ TEST(ExtensionTest, LoadPageActionHelper) {
// First remove id key.
copy.reset(static_cast<DictionaryValue*>(input.DeepCopy()));
copy->Remove(keys::kPageActionId, NULL);
- page_action.reset(extension.LoadPageActionHelper(copy.get(), 0, &error_msg));
- ASSERT_TRUE(NULL == page_action.get());
+ action.reset(extension.LoadContextualActionHelper(
+ copy.get(), 0, &error_msg, ContextualAction::PAGE_ACTION));
+ ASSERT_TRUE(NULL == action.get());
ASSERT_TRUE(MatchPattern(error_msg.c_str(),
errors::kInvalidPageActionId));
+ error_msg = "";
+
+ // Same test (id key), but with browser action.
+ copy.reset(static_cast<DictionaryValue*>(input.DeepCopy()));
+ copy->Remove(keys::kPageActionId, NULL);
+ action.reset(extension.LoadContextualActionHelper(
+ copy.get(), 0, &error_msg, ContextualAction::BROWSER_ACTION));
+ // Having no id is valid for browser actions.
+ ASSERT_TRUE(NULL != action.get());
+ ASSERT_STREQ("", error_msg.c_str());
+ error_msg = "";
// Then remove the name key.
copy.reset(static_cast<DictionaryValue*>(input.DeepCopy()));
copy->Remove(keys::kName, NULL);
- page_action.reset(extension.LoadPageActionHelper(copy.get(), 0, &error_msg));
- ASSERT_TRUE(NULL == page_action.get());
+ action.reset(extension.LoadContextualActionHelper(
+ copy.get(), 0, &error_msg, ContextualAction::PAGE_ACTION));
+ ASSERT_TRUE(NULL == action.get());
+ ASSERT_TRUE(MatchPattern(error_msg.c_str(),
+ errors::kInvalidName));
+ error_msg = "";
+
+ // Same test (name key), but with browser action.
+ copy.reset(static_cast<DictionaryValue*>(input.DeepCopy()));
+ copy->Remove(keys::kName, NULL);
+ action.reset(extension.LoadContextualActionHelper(
+ copy.get(), 0, &error_msg, ContextualAction::BROWSER_ACTION));
+ ASSERT_TRUE(NULL == action.get());
ASSERT_TRUE(MatchPattern(error_msg.c_str(),
errors::kInvalidName));
+ error_msg = "";
// Then remove the icon paths key.
copy.reset(static_cast<DictionaryValue*>(input.DeepCopy()));
copy->Remove(keys::kPageActionIcons, NULL);
- page_action.reset(extension.LoadPageActionHelper(copy.get(), 0, &error_msg));
- ASSERT_TRUE(NULL == page_action.get());
+ action.reset(extension.LoadContextualActionHelper(
+ copy.get(), 0, &error_msg, ContextualAction::PAGE_ACTION));
+ ASSERT_TRUE(NULL == action.get());
ASSERT_TRUE(MatchPattern(error_msg.c_str(),
errors::kInvalidPageActionIconPaths));
+ error_msg = "";
- // Then set the type to something bogus.
+ // Same test (name key), but with browser action.
copy.reset(static_cast<DictionaryValue*>(input.DeepCopy()));
- copy->SetString(keys::kType, "something_bogus");
- page_action.reset(extension.LoadPageActionHelper(copy.get(), 0, &error_msg));
- ASSERT_TRUE(NULL == page_action.get());
+ copy->Remove(keys::kPageActionIcons, NULL);
+ action.reset(extension.LoadContextualActionHelper(
+ copy.get(), 0, &error_msg, ContextualAction::BROWSER_ACTION));
+ ASSERT_TRUE(NULL == action.get());
ASSERT_TRUE(MatchPattern(error_msg.c_str(),
- errors::kInvalidPageActionTypeValue));
+ errors::kInvalidPageActionIconPaths));
}
TEST(ExtensionTest, IdIsValid) {
diff --git a/chrome/common/page_action.cc b/chrome/common/page_action.cc
index 233a735..f2b6f24 100644
--- a/chrome/common/page_action.cc
+++ b/chrome/common/page_action.cc
@@ -4,9 +4,9 @@
#include "chrome/common/page_action.h"
-PageAction::PageAction()
- : type_(PERMANENT) {
+ContextualAction::ContextualAction()
+ : type_(PAGE_ACTION) {
}
-PageAction::~PageAction() {
+ContextualAction::~ContextualAction() {
}
diff --git a/chrome/common/page_action.h b/chrome/common/page_action.h
index 31a7c3d..8594ace 100644
--- a/chrome/common/page_action.h
+++ b/chrome/common/page_action.h
@@ -11,21 +11,21 @@
#include "base/basictypes.h"
-class PageAction {
+class ContextualAction {
public:
- PageAction();
- virtual ~PageAction();
+ ContextualAction();
+ virtual ~ContextualAction();
typedef enum {
- PERMANENT = 0,
- TAB = 1,
- } PageActionType;
+ PAGE_ACTION = 0,
+ BROWSER_ACTION = 1,
+ } ContextualActionType;
std::string id() const { return id_; }
void set_id(const std::string& id) { id_ = id; }
- PageActionType type() const { return type_; }
- void set_type(PageActionType type) { type_ = type; }
+ ContextualActionType type() const { return type_; }
+ void set_type(ContextualActionType type) { type_ = type; }
std::string extension_id() const { return extension_id_; }
void set_extension_id(const std::string& extension_id) {
@@ -41,30 +41,31 @@ class PageAction {
}
private:
- // The id for the PageAction, for example: "RssPageAction".
+ // The id for the ContextualAction, for example: "RssPageAction".
+ // For BrowserActions this is blank.
std::string id_;
- // The type of the PageAction.
- PageActionType type_;
+ // The type of the ContextualAction, either PageAction or BrowserAction.
+ ContextualActionType type_;
- // The id for the extension this PageAction belongs to (as defined in the
- // extension manifest).
+ // The id for the extension this ContextualAction belongs to (as defined in
+ // the extension manifest).
std::string extension_id_;
- // The name of the PageAction.
+ // The name of the ContextualAction.
std::string name_;
// The paths to the icons that this PageIcon can show.
std::vector<std::string> icon_paths_;
};
-typedef std::map<std::string, PageAction*> PageActionMap;
+typedef std::map<std::string, ContextualAction*> ContextualActionMap;
// This class keeps track of what values each tab uses to override the default
-// values of the PageAction.
-class PageActionState {
+// values of the ContextualAction.
+class ContextualActionState {
public:
- PageActionState(std::string title, int icon_index)
+ ContextualActionState(std::string title, int icon_index)
: title_(title), icon_index_(icon_index) {
}
@@ -78,7 +79,7 @@ class PageActionState {
// The icon to use.
int icon_index_;
- DISALLOW_COPY_AND_ASSIGN(PageActionState);
+ DISALLOW_COPY_AND_ASSIGN(ContextualActionState);
};
#endif // CHROME_COMMON_PAGE_ACTION_H_
diff --git a/chrome/renderer/renderer_resources.grd b/chrome/renderer/renderer_resources.grd
index efad954..44ee4dd 100644
--- a/chrome/renderer/renderer_resources.grd
+++ b/chrome/renderer/renderer_resources.grd
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- This comment is only here because changes to resources are not picked up
-without changes to the corresponding grd file. ek1 -->
+without changes to the corresponding grd file. fb9 -->
<grit latest_public_release="0" current_release="1">
<outputs>
<output filename="grit/renderer_resources.h" type="rc_header">
diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js
index 95e520e..ff2fddd 100644
--- a/chrome/renderer/resources/extension_process_bindings.js
+++ b/chrome/renderer/resources/extension_process_bindings.js
@@ -193,6 +193,13 @@ var chrome = chrome || {};
}
}
+ // Browser action events send {windowpId}.
+ function setupBrowserActionEvent(extensionId) {
+ var eventName = "browserAction/" + extensionId;
+ chrome.browserAction = chrome.browserAction || {};
+ chrome.browserAction.onClicked = new chrome.Event(eventName);
+ }
+
function setupToolstripEvents(renderViewId) {
chrome.toolstrip = chrome.toolstrip || {};
chrome.toolstrip.onExpanded =
@@ -317,6 +324,7 @@ var chrome = chrome || {};
}
setupPageActionEvents(extensionId);
+ setupBrowserActionEvent(extensionId);
setupToolstripEvents(GetRenderViewId());
});
})();
diff --git a/chrome/test/data/extensions/samples/print_browser_action/background.html b/chrome/test/data/extensions/samples/print_browser_action/background.html
new file mode 100644
index 0000000..75501ea
--- /dev/null
+++ b/chrome/test/data/extensions/samples/print_browser_action/background.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+<script>
+ // Called when the user clicks on the browser action.
+ chrome.browserAction.onClicked.addListener(function(windowId) {
+ chrome.tabs.getSelected(windowId, function(tab) {
+ var action_url = "javascript:window.print();";
+ chrome.tabs.update(tab.id, {url: action_url});
+ });
+ });
+</script>
+</head>
+</html>
+
diff --git a/chrome/test/data/extensions/samples/print_browser_action/manifest.json b/chrome/test/data/extensions/samples/print_browser_action/manifest.json
new file mode 100644
index 0000000..697c94e
--- /dev/null
+++ b/chrome/test/data/extensions/samples/print_browser_action/manifest.json
@@ -0,0 +1,13 @@
+{
+ "name": "Print this page browser action extension",
+ "description": "This extension adds a print button to the toolstrip",
+ "version": "1.0",
+ "background_page": "background.html",
+ "permissions": [
+ "tabs", "http://*/*", "https://*/*"
+ ],
+ "browser_action": {
+ "name": "Print this page",
+ "icons": ["print_16x16.png"]
+ }
+} \ No newline at end of file
diff --git a/chrome/test/data/extensions/samples/print_browser_action/print_16x16.png b/chrome/test/data/extensions/samples/print_browser_action/print_16x16.png
new file mode 100644
index 0000000..d145964
--- /dev/null
+++ b/chrome/test/data/extensions/samples/print_browser_action/print_16x16.png
Binary files differ