diff options
author | finnur@chromium.org <finnur@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-26 03:18:46 +0000 |
---|---|---|
committer | finnur@chromium.org <finnur@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-26 03:18:46 +0000 |
commit | 671e6c1cecf01e46dc0267e020971aa0f98de0a2 (patch) | |
tree | 61db92254d4030103b4f4f68b7b8a7ad342f6d93 | |
parent | 37e1bb64e696f39acb8a80021af58356af8e3bf1 (diff) | |
download | chromium_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
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 Binary files differnew file mode 100644 index 0000000..d145964 --- /dev/null +++ b/chrome/test/data/extensions/samples/print_browser_action/print_16x16.png |