summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authoraa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-24 04:35:08 +0000
committeraa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-24 04:35:08 +0000
commit92c6f9b9eac950c487f580709e3aab03843d15bc (patch)
tree4f25ceede36cf3655de577a48281910752f67262 /chrome
parent20ee7a74a7587986ed5db9b13aac2a78a5f0bdfc (diff)
downloadchromium_src-92c6f9b9eac950c487f580709e3aab03843d15bc.zip
chromium_src-92c6f9b9eac950c487f580709e3aab03843d15bc.tar.gz
chromium_src-92c6f9b9eac950c487f580709e3aab03843d15bc.tar.bz2
Refactor BrowserActions, and add support for
tab-specific state. Future changelists will move Page Actions over to ExtensionAction2, then replace ExtensionAction and ExtensionActionState with ExtensionAction2. Also, fix a bug in setIcon({path:...}) where it would work only the first time. BUG=24669,24472 Review URL: http://codereview.chromium.org/306044 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29997 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/extensions/browser_action_apitest.cc57
-rw-r--r--chrome/browser/extensions/extension_browser_actions_api.cc111
-rw-r--r--chrome/browser/extensions/extension_browser_actions_api.h36
-rw-r--r--chrome/browser/extensions/extension_file_util.cc8
-rw-r--r--chrome/browser/extensions/extensions_service.cc37
-rw-r--r--chrome/browser/extensions/extensions_service.h11
-rw-r--r--chrome/browser/gtk/browser_actions_toolbar_gtk.cc176
-rw-r--r--chrome/browser/gtk/browser_actions_toolbar_gtk.h12
-rw-r--r--chrome/browser/gtk/browser_toolbar_gtk.cc3
-rw-r--r--chrome/browser/gtk/location_bar_view_gtk.cc5
-rw-r--r--chrome/browser/tab_contents/tab_contents.cc26
-rw-r--r--chrome/browser/views/browser_actions_container.cc201
-rw-r--r--chrome/browser/views/browser_actions_container.h46
-rw-r--r--chrome/browser/views/location_bar_view.cc16
-rw-r--r--chrome/browser/views/toolbar_view.cc3
-rwxr-xr-xchrome/chrome.gyp3
-rwxr-xr-xchrome/common/extensions/api/extension_api.json31
-rw-r--r--chrome/common/extensions/extension.cc101
-rw-r--r--chrome/common/extensions/extension.h17
-rw-r--r--chrome/common/extensions/extension_action.cc16
-rw-r--r--chrome/common/extensions/extension_action.h6
-rwxr-xr-xchrome/common/extensions/extension_action2.cc31
-rwxr-xr-xchrome/common/extensions/extension_action2.h163
-rw-r--r--chrome/common/extensions/extension_action2_unittest.cc127
-rw-r--r--chrome/renderer/extensions/extension_process_bindings.cc7
-rw-r--r--chrome/renderer/resources/extension_process_bindings.js6
-rw-r--r--chrome/test/data/extensions/api_test/browser_action/update.html2
-rw-r--r--chrome/test/data/extensions/api_test/browser_action_tab_specific_state/background.html37
-rwxr-xr-xchrome/test/data/extensions/api_test/browser_action_tab_specific_state/manifest.json12
29 files changed, 952 insertions, 355 deletions
diff --git a/chrome/browser/extensions/browser_action_apitest.cc b/chrome/browser/extensions/browser_action_apitest.cc
index 13b73d8..3e47ad1 100644
--- a/chrome/browser/extensions/browser_action_apitest.cc
+++ b/chrome/browser/extensions/browser_action_apitest.cc
@@ -13,7 +13,7 @@
#include "chrome/browser/views/browser_actions_container.h"
#include "chrome/browser/views/extensions/extension_popup.h"
#include "chrome/browser/views/toolbar_view.h"
-#include "chrome/common/extensions/extension_action.h"
+#include "chrome/common/extensions/extension_action2.h"
#include "chrome/test/ui_test_utils.h"
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, BrowserAction) {
@@ -35,20 +35,19 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, BrowserAction) {
ASSERT_TRUE(catcher.GetNextResult());
// Test that we received the changes.
- ExtensionActionState* action_state = extension->browser_action_state();
- ASSERT_EQ("Modified", action_state->title());
- ASSERT_EQ("badge", action_state->badge_text());
+ ExtensionAction2* action = extension->browser_action();
+ ASSERT_EQ("Modified", action->GetTitle(ExtensionAction2::kDefaultTabId));
+ ASSERT_EQ("badge", action->GetBadgeText(ExtensionAction2::kDefaultTabId));
ASSERT_EQ(SkColorSetARGB(255, 255, 255, 255),
- action_state->badge_background_color());
+ action->GetBadgeBackgroundColor(ExtensionAction2::kDefaultTabId));
// Simulate the browser action being clicked.
ui_test_utils::NavigateToURL(browser(),
GURL("http://localhost:1337/files/extensions/test_file.txt"));
- ExtensionAction* browser_action = service->GetBrowserActions(false)[0];
int window_id = ExtensionTabUtil::GetWindowId(browser());
ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted(
- browser()->profile(), browser_action->extension_id(), browser());
+ browser()->profile(), action->extension_id(), browser());
// Verify the command worked.
TabContents* tab = browser()->GetSelectedTabContents();
@@ -97,6 +96,50 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, DynamicBrowserAction) {
// TODO(aa): Would be nice here to actually compare that the pixels change.
}
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TabSpecificBrowserActionState) {
+ ASSERT_TRUE(RunExtensionTest("browser_action_tab_specific_state"))
+ << message_;
+
+ // Test that there is a browser action in the toolbar and that it has no icon.
+ BrowserActionsContainer* browser_actions =
+ browser()->window()->GetBrowserWindowTesting()->GetToolbarView()->
+ browser_actions();
+ ASSERT_EQ(1, browser_actions->num_browser_actions());
+ ASSERT_FALSE(browser_actions->GetBrowserActionViewAt(0)->button()->icon()
+ .empty());
+
+ // Execute the action, its title should change
+ std::wstring text;
+ ResultCatcher catcher;
+ browser_actions->TestExecuteBrowserAction(0);
+ ASSERT_TRUE(catcher.GetNextResult());
+ ASSERT_TRUE(
+ browser_actions->GetBrowserActionViewAt(0)->button()->GetTooltipText(
+ 0, 0, &text));
+ ASSERT_EQ(L"Showing icon 2", text);
+
+ // open a new tab, the title should go back
+ browser()->NewTab();
+ ASSERT_TRUE(
+ browser_actions->GetBrowserActionViewAt(0)->button()->GetTooltipText(
+ 0, 0, &text));
+ ASSERT_EQ(L"hi!", text);
+
+ // go back to first tab, changed title should reappear
+ browser()->SelectTabContentsAt(0, true);
+ ASSERT_TRUE(
+ browser_actions->GetBrowserActionViewAt(0)->button()->GetTooltipText(
+ 0, 0, &text));
+ ASSERT_EQ(L"Showing icon 2", text);
+
+ // reload that tab, default title should come back
+ ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
+ ASSERT_TRUE(
+ browser_actions->GetBrowserActionViewAt(0)->button()->GetTooltipText(
+ 0, 0, &text));
+ ASSERT_EQ(L"hi!", text);
+}
+
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, BrowserActionPopup) {
ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("popup")));
diff --git a/chrome/browser/extensions/extension_browser_actions_api.cc b/chrome/browser/extensions/extension_browser_actions_api.cc
index 6c83ee0..92e0951 100644
--- a/chrome/browser/extensions/extension_browser_actions_api.cc
+++ b/chrome/browser/extensions/extension_browser_actions_api.cc
@@ -17,97 +17,59 @@ const char kIconIndexOutOfBounds[] =
"Browser action icon index out of bounds.";
}
-bool BrowserActionSetIconFunction::RunImpl() {
+bool BrowserActionFunction::RunImpl() {
EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_DICTIONARY));
- const DictionaryValue* args = static_cast<const DictionaryValue*>(args_);
+ details_ = static_cast<DictionaryValue*>(args_);
+
+ if (details_->HasKey(L"tabId"))
+ EXTENSION_FUNCTION_VALIDATE(details_->GetInteger(L"tabId", &tab_id_));
Extension* extension = dispatcher()->GetExtension();
- if (!extension->browser_action()) {
+ browser_action_ = extension->browser_action();
+ if (!browser_action_) {
error_ = kNoBrowserActionError;
return false;
}
- // setIcon can take a variant argument: either a canvas ImageData, or an
- // icon index.
- BinaryValue* binary;
- int icon_index;
- if (args->GetBinary(L"imageData", &binary)) {
- IPC::Message bitmap_pickle(binary->GetBuffer(), binary->GetSize());
- void* iter = NULL;
- scoped_ptr<SkBitmap> bitmap(new SkBitmap);
- EXTENSION_FUNCTION_VALIDATE(
- IPC::ReadParam(&bitmap_pickle, &iter, bitmap.get()));
- extension->browser_action_state()->set_icon(bitmap.release());
- } else if (args->GetInteger(L"iconIndex", &icon_index)) {
- if (icon_index < 0 || static_cast<size_t>(icon_index) >=
-
- extension->browser_action()->icon_paths().size()) {
- error_ = kIconIndexOutOfBounds;
- return false;
- }
- extension->browser_action_state()->set_icon_index(icon_index);
- extension->browser_action_state()->set_icon(NULL);
- } else {
- EXTENSION_FUNCTION_VALIDATE(false);
- }
+ if (!RunBrowserAction())
+ return false;
NotificationService::current()->Notify(
NotificationType::EXTENSION_BROWSER_ACTION_UPDATED,
- Source<ExtensionAction>(extension->browser_action()),
- Details<ExtensionActionState>(extension->browser_action_state()));
+ Source<ExtensionAction2>(browser_action_),
+ NotificationService::NoDetails());
return true;
}
-bool BrowserActionSetTitleFunction::RunImpl() {
- EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_DICTIONARY));
- DictionaryValue* details = static_cast<DictionaryValue*>(args_);
+bool BrowserActionSetIconFunction::RunBrowserAction() {
+ BinaryValue* binary = NULL;
+ EXTENSION_FUNCTION_VALIDATE(details_->GetBinary(L"imageData", &binary));
+ IPC::Message bitmap_pickle(binary->GetBuffer(), binary->GetSize());
+ void* iter = NULL;
+ SkBitmap bitmap;
+ EXTENSION_FUNCTION_VALIDATE(
+ IPC::ReadParam(&bitmap_pickle, &iter, &bitmap));
+ browser_action_->SetIcon(tab_id_, bitmap);
+ return true;
+}
+bool BrowserActionSetTitleFunction::RunBrowserAction() {
std::string title;
- EXTENSION_FUNCTION_VALIDATE(details->GetString(L"title", &title));
-
- Extension* extension = dispatcher()->GetExtension();
- if (!extension->browser_action()) {
- error_ = kNoBrowserActionError;
- return false;
- }
-
- extension->browser_action_state()->set_title(title);
-
- NotificationService::current()->Notify(
- NotificationType::EXTENSION_BROWSER_ACTION_UPDATED,
- Source<ExtensionAction>(extension->browser_action()),
- Details<ExtensionActionState>(extension->browser_action_state()));
+ EXTENSION_FUNCTION_VALIDATE(details_->GetString(L"title", &title));
+ browser_action_->SetTitle(tab_id_, title);
return true;
}
-bool BrowserActionSetBadgeTextFunction::RunImpl() {
- EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_DICTIONARY));
- DictionaryValue* details = static_cast<DictionaryValue*>(args_);
-
+bool BrowserActionSetBadgeTextFunction::RunBrowserAction() {
std::string badge_text;
- EXTENSION_FUNCTION_VALIDATE(details->GetString(L"text", &badge_text));
-
- Extension* extension = dispatcher()->GetExtension();
- if (!extension->browser_action()) {
- error_ = kNoBrowserActionError;
- return false;
- }
-
- extension->browser_action_state()->set_badge_text(badge_text);
-
- NotificationService::current()->Notify(
- NotificationType::EXTENSION_BROWSER_ACTION_UPDATED,
- Source<ExtensionAction>(extension->browser_action()),
- Details<ExtensionActionState>(extension->browser_action_state()));
+ EXTENSION_FUNCTION_VALIDATE(details_->GetString(L"text", &badge_text));
+ browser_action_->SetBadgeText(tab_id_, badge_text);
return true;
}
-bool BrowserActionSetBadgeBackgroundColorFunction::RunImpl() {
- EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_DICTIONARY));
- DictionaryValue* details = static_cast<DictionaryValue*>(args_);
-
+bool BrowserActionSetBadgeBackgroundColorFunction::RunBrowserAction() {
ListValue* list = NULL;
- EXTENSION_FUNCTION_VALIDATE(details->GetList(L"color", &list));
+ EXTENSION_FUNCTION_VALIDATE(details_->GetList(L"color", &list));
EXTENSION_FUNCTION_VALIDATE(list->GetSize() == 4);
int color_array[4] = {0};
@@ -117,18 +79,7 @@ bool BrowserActionSetBadgeBackgroundColorFunction::RunImpl() {
SkColor color = SkColorSetARGB(color_array[3], color_array[0], color_array[1],
color_array[2]);
+ browser_action_->SetBadgeBackgroundColor(tab_id_, color);
- Extension* extension = dispatcher()->GetExtension();
- if (!extension->browser_action()) {
- error_ = kNoBrowserActionError;
- return false;
- }
-
- extension->browser_action_state()->set_badge_background_color(color);
-
- NotificationService::current()->Notify(
- NotificationType::EXTENSION_BROWSER_ACTION_UPDATED,
- Source<ExtensionAction>(extension->browser_action()),
- Details<ExtensionActionState>(extension->browser_action_state()));
return true;
}
diff --git a/chrome/browser/extensions/extension_browser_actions_api.h b/chrome/browser/extensions/extension_browser_actions_api.h
index 8d92b8a..3b6d768 100644
--- a/chrome/browser/extensions/extension_browser_actions_api.h
+++ b/chrome/browser/extensions/extension_browser_actions_api.h
@@ -6,25 +6,47 @@
#define CHROME_BROWSER_EXTENSIONS_EXTENSION_BROWSER_ACTIONS_API_H_
#include "chrome/browser/extensions/extension_function.h"
+#include "chrome/common/extensions/extension_action2.h"
-class BrowserActionSetIconFunction : public SyncExtensionFunction {
+class ExtensionAction;
+class ExtensionActionState;
+
+class BrowserActionFunction : public SyncExtensionFunction {
+ protected:
+ BrowserActionFunction() : tab_id_(ExtensionAction2::kDefaultTabId) {}
virtual bool RunImpl();
+ virtual bool RunBrowserAction() = 0;
+
+ // All the browser action APIs take a single argument called details that is
+ // a dictionary.
+ DictionaryValue* details_;
+
+ // The tab id the browser action function should apply to, if any, or
+ // kDefaultTabId if none was specified.
+ int tab_id_;
+
+ // The browser action for the current extension.
+ ExtensionAction2* browser_action_;
+};
+
+class BrowserActionSetIconFunction : public BrowserActionFunction {
+ virtual bool RunBrowserAction();
DECLARE_EXTENSION_FUNCTION_NAME("browserAction.setIcon")
};
-class BrowserActionSetTitleFunction : public SyncExtensionFunction {
- virtual bool RunImpl();
+class BrowserActionSetTitleFunction : public BrowserActionFunction {
+ virtual bool RunBrowserAction();
DECLARE_EXTENSION_FUNCTION_NAME("browserAction.setTitle")
};
-class BrowserActionSetBadgeTextFunction : public SyncExtensionFunction {
- virtual bool RunImpl();
+class BrowserActionSetBadgeTextFunction : public BrowserActionFunction {
+ virtual bool RunBrowserAction();
DECLARE_EXTENSION_FUNCTION_NAME("browserAction.setBadgeText")
};
class BrowserActionSetBadgeBackgroundColorFunction
- : public SyncExtensionFunction {
- virtual bool RunImpl();
+ : public BrowserActionFunction {
+ virtual bool RunBrowserAction();
DECLARE_EXTENSION_FUNCTION_NAME("browserAction.setBadgeBackgroundColor")
};
diff --git a/chrome/browser/extensions/extension_file_util.cc b/chrome/browser/extensions/extension_file_util.cc
index 23bd085..60d4981 100644
--- a/chrome/browser/extensions/extension_file_util.cc
+++ b/chrome/browser/extensions/extension_file_util.cc
@@ -242,11 +242,11 @@ bool ValidateExtension(Extension* extension, std::string* error) {
}
// Validate icon location for browser actions.
- const ExtensionAction* browser_action = extension->browser_action();
+ ExtensionAction2* 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) {
+ std::vector<std::string>* icon_paths = browser_action->icon_paths();
+ for (std::vector<std::string>::iterator iter = icon_paths->begin();
+ iter != icon_paths->end(); ++iter) {
if (extension->GetResource(*iter).GetFilePath().empty()) {
*error = StringPrintf("Could not load icon '%s' for browser action.",
iter->c_str());
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
index 5b87bd1..354d7d2 100644
--- a/chrome/browser/extensions/extensions_service.cc
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -141,12 +141,16 @@ void ExtensionsService::Init() {
}
std::vector<ExtensionAction*> ExtensionsService::GetPageActions() const {
- return GetExtensionActions(ExtensionAction::PAGE_ACTION, true);
-}
+ std::vector<ExtensionAction*> result;
+
+ // TODO(finnur): Sort the icons in some meaningful way.
+ for (ExtensionList::const_iterator iter = extensions_.begin();
+ iter != extensions_.end(); ++iter) {
+ if ((*iter)->page_action())
+ result.push_back((*iter)->page_action());
+ }
-std::vector<ExtensionAction*> ExtensionsService::GetBrowserActions(
- bool include_popups) const {
- return GetExtensionActions(ExtensionAction::BROWSER_ACTION, include_popups);
+ return result;
}
void ExtensionsService::InstallExtension(const FilePath& extension_path) {
@@ -366,29 +370,6 @@ void ExtensionsService::NotifyExtensionUnloaded(Extension* extension) {
}
}
-std::vector<ExtensionAction*> ExtensionsService::GetExtensionActions(
- ExtensionAction::ExtensionActionType action_type,
- bool include_popups) const {
- std::vector<ExtensionAction*> result;
-
- // TODO(finnur): Sort the icons in some meaningful way.
- for (ExtensionList::const_iterator iter = extensions_.begin();
- iter != extensions_.end(); ++iter) {
- if (action_type == ExtensionAction::PAGE_ACTION) {
- ExtensionAction* page_action = (*iter)->page_action();
- if (page_action && (include_popups || !page_action->is_popup())) {
- result.push_back(page_action);
- }
- } else {
- ExtensionAction* browser_action = (*iter)->browser_action();
- if (browser_action && (include_popups || !browser_action->is_popup()))
- 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 4235c22..0dad4cd 100644
--- a/chrome/browser/extensions/extensions_service.h
+++ b/chrome/browser/extensions/extensions_service.h
@@ -106,10 +106,6 @@ class ExtensionsService
// they belong to.
std::vector<ExtensionAction*> GetPageActions() const;
- // Retrieves a vector of all browser actions, irrespective of which extension
- // they belong to.
- std::vector<ExtensionAction*> GetBrowserActions(bool include_popups) const;
-
// Install the extension file at |extension_path|. Will install as an
// update if an older version is already installed.
// For fresh installs, this method also causes the extension to be
@@ -249,13 +245,6 @@ class ExtensionsService
// Handles sending notification that |extension| was unloaded.
void NotifyExtensionUnloaded(Extension* extension);
- // Retrieves a vector of all page actions or browser actions, irrespective of
- // which extension they belong to. If |include_popups| is false, actions that
- // are popups are excluded.
- std::vector<ExtensionAction*> GetExtensionActions(
- ExtensionAction::ExtensionActionType action_type,
- bool include_popups) const;
-
// The profile this ExtensionsService is part of.
Profile* profile_;
diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc
index b30d053..b137203 100644
--- a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc
+++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc
@@ -17,7 +17,9 @@
#include "chrome/browser/gtk/gtk_chrome_button.h"
#include "chrome/browser/gtk/gtk_theme_provider.h"
#include "chrome/browser/profile.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_action2.h"
#include "chrome/common/gtk_util.h"
#include "chrome/common/notification_details.h"
#include "chrome/common/notification_service.h"
@@ -35,28 +37,30 @@ static const int kBrowserActionButtonPadding = 3;
class BrowserActionButton : public NotificationObserver,
public ImageLoadingTracker::Observer {
public:
- BrowserActionButton(Browser* browser, Extension* extension)
- : browser_(browser),
+ BrowserActionButton(BrowserActionsToolbarGtk* toolbar,
+ Extension* extension)
+ : toolbar_(toolbar),
extension_(extension),
button_(gtk_chrome_button_new()),
- gdk_icon_(NULL) {
+ tracker_(NULL),
+ tab_specific_icon_(NULL),
+ default_icon_(NULL) {
DCHECK(extension_->browser_action());
gtk_widget_set_size_request(button_.get(), kButtonSize, kButtonSize);
- browser_action_icons_.resize(
- extension->browser_action()->icon_paths().size());
- tracker_ = new ImageLoadingTracker(this, browser_action_icons_.size());
- for (size_t i = 0; i < extension->browser_action()->icon_paths().size();
- ++i) {
- tracker_->PostLoadImageTask(
- extension->GetResource(extension->browser_action()->icon_paths()[i]),
+ UpdateState();
+
+ // The Browser Action API does not allow the default icon path to be
+ // changed at runtime, so we can load this now and cache it.
+ std::string path = extension_->browser_action()->GetDefaultIconPath();
+ if (!path.empty()) {
+ tracker_ = new ImageLoadingTracker(this, 1);
+ tracker_->PostLoadImageTask(extension_->GetResource(path),
gfx::Size(Extension::kBrowserActionIconMaxSize,
Extension::kBrowserActionIconMaxSize));
}
- OnStateUpdated();
-
// We need to hook up extension popups here. http://crbug.com/23897
g_signal_connect(button_.get(), "clicked",
G_CALLBACK(OnButtonClicked), this);
@@ -64,7 +68,7 @@ class BrowserActionButton : public NotificationObserver,
G_CALLBACK(OnExposeEvent), this);
registrar_.Add(this, NotificationType::EXTENSION_BROWSER_ACTION_UPDATED,
- Source<ExtensionAction>(extension->browser_action()));
+ Source<ExtensionAction2>(extension->browser_action()));
registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
NotificationService::AllSources());
@@ -72,11 +76,16 @@ class BrowserActionButton : public NotificationObserver,
}
~BrowserActionButton() {
- if (gdk_icon_)
- g_object_unref(gdk_icon_);
+ if (tab_specific_icon_)
+ g_object_unref(tab_specific_icon_);
+
+ if (default_icon_)
+ g_object_unref(default_icon_);
button_.Destroy();
- tracker_->StopTrackingImageLoad();
+
+ if (tracker_)
+ tracker_->StopTrackingImageLoad();
}
GtkWidget* widget() { return button_.get(); }
@@ -85,7 +94,7 @@ class BrowserActionButton : public NotificationObserver,
const NotificationSource& source,
const NotificationDetails& details) {
if (type == NotificationType::EXTENSION_BROWSER_ACTION_UPDATED)
- OnStateUpdated();
+ UpdateState();
else if (type == NotificationType::BROWSER_THEME_CHANGED)
OnThemeChanged();
else
@@ -94,71 +103,79 @@ class BrowserActionButton : public NotificationObserver,
// ImageLoadingTracker::Observer implementation.
void OnImageLoaded(SkBitmap* image, size_t index) {
- DCHECK(index < browser_action_icons_.size());
- browser_action_icons_[index] = image ? *image : SkBitmap();
-
- OnStateUpdated();
+ default_icon_ = gfx::GdkPixbufFromSkBitmap(image);
+ UpdateState();
}
- private:
- // Called when the tooltip has changed or an image has loaded.
- void OnStateUpdated() {
+ // Updates the button based on the latest state from the associated
+ // browser action.
+ void UpdateState() {
+ int tab_id = toolbar_->GetCurrentTabId();
+ if (tab_id < 0)
+ return;
+
gtk_widget_set_tooltip_text(button_.get(),
- extension_->browser_action_state()->title().c_str());
-
- SkBitmap* image = extension_->browser_action_state()->icon();
- if (!image) {
- if (static_cast<size_t>(
- extension_->browser_action_state()->icon_index()) <
- browser_action_icons_.size()) {
- image = &browser_action_icons_[
- extension_->browser_action_state()->icon_index()];
- }
+ extension_->browser_action()->GetTitle(tab_id).c_str());
+
+ SkBitmap image = extension_->browser_action()->GetIcon(tab_id);
+ if (!image.isNull()) {
+ GdkPixbuf* previous_gdk_icon = tab_specific_icon_;
+ tab_specific_icon_ = gfx::GdkPixbufFromSkBitmap(&image);
+ SetImage(tab_specific_icon_);
+ if (previous_gdk_icon)
+ g_object_unref(previous_gdk_icon);
+ } else if (default_icon_) {
+ SetImage(default_icon_);
}
+ }
- if (image && !image->empty()) {
- GdkPixbuf* current_gdk_icon = gdk_icon_;
- gdk_icon_ = gfx::GdkPixbufFromSkBitmap(image);
- gtk_button_set_image(GTK_BUTTON(button_.get()),
- gtk_image_new_from_pixbuf(gdk_icon_));
- if (current_gdk_icon)
- g_object_unref(current_gdk_icon);
- }
+ private:
+ void SetImage(GdkPixbuf* image) {
+ gtk_button_set_image(GTK_BUTTON(button_.get()),
+ gtk_image_new_from_pixbuf(image));
}
void OnThemeChanged() {
gtk_chrome_button_set_use_gtk_rendering(GTK_CHROME_BUTTON(button_.get()),
- GtkThemeProvider::GetFrom(browser_->profile())->UseGtkTheme());
+ GtkThemeProvider::GetFrom(
+ toolbar_->browser()->profile())->UseGtkTheme());
}
static void OnButtonClicked(GtkWidget* widget, BrowserActionButton* action) {
- if (action->extension_->browser_action()->is_popup()) {
- ExtensionPopupGtk::Show(
- action->extension_->browser_action()->popup_url(),
- action->browser_,
- gtk_util::GetWidgetRectRelativeToToplevel(widget));
- } else {
- ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted(
- action->browser_->profile(), action->extension_->id(),
- action->browser_);
- }
+ if (action->extension_->browser_action()->has_popup()) {
+ ExtensionPopupGtk::Show(
+ action->extension_->browser_action()->popup_url(),
+ action->toolbar_->browser(),
+ gtk_util::GetWidgetRectRelativeToToplevel(widget));
+ } else {
+ ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted(
+ action->toolbar_->browser()->profile(), action->extension_->id(),
+ action->toolbar_->browser());
+ }
}
static gboolean OnExposeEvent(GtkWidget* widget,
GdkEventExpose* event,
- BrowserActionButton* action) {
- if (action->extension_->browser_action_state()->badge_text().empty())
+ BrowserActionButton* button) {
+ int tab_id = button->toolbar_->GetCurrentTabId();
+ if (tab_id < 0)
+ return FALSE;
+
+ ExtensionAction2* action = button->extension_->browser_action();
+ if (action->GetBadgeText(tab_id).empty())
return FALSE;
gfx::CanvasPaint canvas(event, false);
gfx::Rect bounding_rect(widget->allocation);
- action->extension_->browser_action_state()->PaintBadge(&canvas,
- bounding_rect);
+ ExtensionActionState::PaintBadge(&canvas, bounding_rect,
+ action->GetBadgeText(tab_id),
+ action->GetBadgeTextColor(tab_id),
+ action->GetBadgeBackgroundColor(tab_id));
return FALSE;
}
- // The Browser that executes a command when the button is pressed.
- Browser* browser_;
+ // The toolbar containing this button.
+ BrowserActionsToolbarGtk* toolbar_;
// The extension that contains this browser action.
Extension* extension_;
@@ -169,13 +186,11 @@ class BrowserActionButton : public NotificationObserver,
// Loads the button's icons for us on the file thread.
ImageLoadingTracker* tracker_;
- // Icons for all the different states the button can be in. These will be
- // empty while they are loading.
- std::vector<SkBitmap> browser_action_icons_;
+ // If we are displaying a tab-specific icon, it will be here.
+ GdkPixbuf* tab_specific_icon_;
- // SkBitmap must be converted to GdkPixbuf before assignment to the button.
- // This stores the current icon while it is in use.
- GdkPixbuf* gdk_icon_;
+ // If the browser action has a default icon, it will be here.
+ GdkPixbuf* default_icon_;
NotificationRegistrar registrar_;
};
@@ -199,6 +214,21 @@ BrowserActionsToolbarGtk::~BrowserActionsToolbarGtk() {
hbox_.Destroy();
}
+int BrowserActionsToolbarGtk::GetCurrentTabId() {
+ TabContents* selected_tab = browser_->GetSelectedTabContents();
+ if (!selected_tab)
+ return -1;
+
+ return selected_tab->controller().session_id().id();
+}
+
+void BrowserActionsToolbarGtk::Update() {
+ for (ExtensionButtonMap::iterator iter = extension_button_map_.begin();
+ iter != extension_button_map_.end(); ++iter) {
+ iter->second->UpdateState();
+ }
+}
+
void BrowserActionsToolbarGtk::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
@@ -219,27 +249,21 @@ void BrowserActionsToolbarGtk::CreateAllButtons() {
if (!extension_service) // The |extension_service| can be NULL in Incognito.
return;
- // Get all browser actions, including those with popups.
- std::vector<ExtensionAction*> browser_actions =
- extension_service->GetBrowserActions(true);
-
- for (size_t i = 0; i < browser_actions.size(); ++i) {
+ for (size_t i = 0; i < extension_service->extensions()->size(); ++i) {
Extension* extension = extension_service->GetExtensionById(
- browser_actions[i]->extension_id());
+ extension_service->extensions()->at(i)->id());
CreateButtonForExtension(extension);
}
}
void BrowserActionsToolbarGtk::CreateButtonForExtension(Extension* extension) {
- // Only show extensions with browser actions and that have an icon.
- if (!extension->browser_action() ||
- extension->browser_action()->icon_paths().empty()) {
+ // Only show extensions with browser actions.
+ if (!extension->browser_action())
return;
- }
RemoveButtonForExtension(extension);
linked_ptr<BrowserActionButton> button(
- new BrowserActionButton(browser_, extension));
+ new BrowserActionButton(this, extension));
gtk_box_pack_end(GTK_BOX(hbox_.get()), button->widget(), FALSE, FALSE, 0);
gtk_widget_show(button->widget());
extension_button_map_[extension->id()] = button;
diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.h b/chrome/browser/gtk/browser_actions_toolbar_gtk.h
index 4210889..015b5c9 100644
--- a/chrome/browser/gtk/browser_actions_toolbar_gtk.h
+++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.h
@@ -29,6 +29,14 @@ class BrowserActionsToolbarGtk : public NotificationObserver {
int button_count() { return extension_button_map_.size(); }
+ Browser* browser() { return browser_; }
+
+ // Returns the currently selected tab ID, or -1 if there is none.
+ int GetCurrentTabId();
+
+ // Update the display of all buttons.
+ void Update();
+
// NotificationObserver implementation.
void Observe(NotificationType type,
const NotificationSource& source,
@@ -63,7 +71,9 @@ class BrowserActionsToolbarGtk : public NotificationObserver {
// Map from extension ID to BrowserActionButton, which is a wrapper for
// a chrome button and related functionality. There should be one entry
// for every extension that has a browser action.
- std::map<std::string, linked_ptr<BrowserActionButton> > extension_button_map_;
+ typedef std::map<std::string, linked_ptr<BrowserActionButton> >
+ ExtensionButtonMap;
+ ExtensionButtonMap extension_button_map_;
DISALLOW_COPY_AND_ASSIGN(BrowserActionsToolbarGtk);
};
diff --git a/chrome/browser/gtk/browser_toolbar_gtk.cc b/chrome/browser/gtk/browser_toolbar_gtk.cc
index fa5d34e..59ebd22 100644
--- a/chrome/browser/gtk/browser_toolbar_gtk.cc
+++ b/chrome/browser/gtk/browser_toolbar_gtk.cc
@@ -406,6 +406,9 @@ void BrowserToolbarGtk::SetProfile(Profile* profile) {
void BrowserToolbarGtk::UpdateTabContents(TabContents* contents,
bool should_restore_state) {
location_bar_->Update(should_restore_state ? contents : NULL);
+
+ if (actions_toolbar_.get())
+ actions_toolbar_->Update();
}
gfx::Rect BrowserToolbarGtk::GetLocationStackBounds() const {
diff --git a/chrome/browser/gtk/location_bar_view_gtk.cc b/chrome/browser/gtk/location_bar_view_gtk.cc
index 4027d4e..8aec081 100644
--- a/chrome/browser/gtk/location_bar_view_gtk.cc
+++ b/chrome/browser/gtk/location_bar_view_gtk.cc
@@ -825,6 +825,9 @@ gboolean LocationBarViewGtk::PageActionViewGtk::OnExposeEvent(
gfx::CanvasPaint canvas(event, false);
gfx::Rect bounding_rect(widget->allocation);
- state->PaintBadge(&canvas, bounding_rect);
+ ExtensionActionState::PaintBadge(&canvas, bounding_rect,
+ state->badge_text(),
+ state->badge_text_color(),
+ state->badge_background_color());
return FALSE;
}
diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc
index e33df336..657bee4 100644
--- a/chrome/browser/tab_contents/tab_contents.cc
+++ b/chrome/browser/tab_contents/tab_contents.cc
@@ -24,6 +24,7 @@
#include "chrome/browser/download/download_manager.h"
#include "chrome/browser/download/download_request_manager.h"
#include "chrome/browser/external_protocol_handler.h"
+#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/favicon_service.h"
#include "chrome/browser/form_field_history_manager.h"
#include "chrome/browser/gears_integration.h"
@@ -1410,12 +1411,27 @@ void TabContents::DidNavigateMainFramePostCommit(
fav_icon_helper_.FetchFavIcon(details.entry->url());
// Disable all page actions, unless this is an in-page navigation.
- if (!page_actions_.empty()) {
- url_canon::Replacements<char> replacements;
- replacements.ClearRef();
- if (params.url.ReplaceComponents(replacements) !=
- params.referrer.ReplaceComponents(replacements)) {
+ url_canon::Replacements<char> replacements;
+ replacements.ClearRef();
+ if (params.url.ReplaceComponents(replacements) !=
+ params.referrer.ReplaceComponents(replacements)) {
+ if (!page_actions_.empty())
page_actions_.clear();
+
+ ExtensionsService* service = profile()->GetExtensionsService();
+ if (service) {
+ for (size_t i = 0; i < service->extensions()->size(); ++i) {
+ ExtensionAction2* action =
+ service->extensions()->at(i)->browser_action();
+ if (!action)
+ continue;
+
+ action->ClearAllValuesForTab(controller().session_id().id());
+ NotificationService::current()->Notify(
+ NotificationType::EXTENSION_BROWSER_ACTION_UPDATED,
+ Source<ExtensionAction2>(action),
+ NotificationService::NoDetails());
+ }
}
}
diff --git a/chrome/browser/views/browser_actions_container.cc b/chrome/browser/views/browser_actions_container.cc
index d21984b..f3f02f0 100644
--- a/chrome/browser/views/browser_actions_container.cc
+++ b/chrome/browser/views/browser_actions_container.cc
@@ -16,6 +16,7 @@
#include "chrome/browser/views/extensions/extension_popup.h"
#include "chrome/browser/views/toolbar_view.h"
#include "chrome/common/extensions/extension_action.h"
+#include "chrome/common/extensions/extension_action2.h"
#include "chrome/common/notification_source.h"
#include "chrome/common/notification_type.h"
#include "grit/app_resources.h"
@@ -48,34 +49,20 @@ static const int kMinimumNumberOfVisibleBrowserActions = 2;
////////////////////////////////////////////////////////////////////////////////
// BrowserActionButton
-BrowserActionButton::BrowserActionButton(
- ExtensionAction* browser_action, Extension* extension,
- BrowserActionsContainer* panel)
+BrowserActionButton::BrowserActionButton(Extension* extension,
+ BrowserActionsContainer* panel)
: MenuButton(this, L"", NULL, false),
- browser_action_(browser_action),
- browser_action_state_(extension->browser_action_state()),
+ browser_action_(extension->browser_action()),
+ extension_(extension),
tracker_(NULL),
panel_(panel) {
set_alignment(TextButton::ALIGN_CENTER);
- // 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.
- if (!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) {
- tracker_->PostLoadImageTask(
- extension->GetResource(*iter),
- gfx::Size(Extension::kBrowserActionIconMaxSize,
- Extension::kBrowserActionIconMaxSize));
- }
- }
+ // No UpdateState() here because View heirarchy not setup yet. Our parent
+ // should call UpdateState() after creation.
registrar_.Add(this, NotificationType::EXTENSION_BROWSER_ACTION_UPDATED,
- Source<ExtensionAction>(browser_action_));
+ Source<ExtensionAction2>(browser_action_));
}
BrowserActionButton::~BrowserActionButton() {
@@ -95,29 +82,39 @@ void BrowserActionButton::ButtonPressed(
panel_->OnBrowserActionExecuted(this);
}
+void BrowserActionButton::LoadImage() {
+ // Load the default image from the browser action asynchronously on the file
+ // thread. We'll get a call back into OnImageLoaded if the image loads
+ // successfully.
+ std::string relative_path = browser_action()->GetDefaultIconPath();
+ if (relative_path.empty())
+ return;
+
+ tracker_ = new ImageLoadingTracker(this, 1);
+ tracker_->PostLoadImageTask(
+ extension()->GetResource(relative_path),
+ gfx::Size(Extension::kBrowserActionIconMaxSize,
+ Extension::kBrowserActionIconMaxSize));
+}
+
void BrowserActionButton::OnImageLoaded(SkBitmap* image, size_t index) {
- DCHECK(index < browser_action_icons_.size());
- browser_action_icons_[index] = image ? *image : SkBitmap();
- if (index == browser_action_icons_.size() - 1) {
- OnStateUpdated();
- tracker_ = NULL; // The tracker object will delete itself when we return.
- }
+ SetIcon(*image);
+ tracker_ = NULL; // The tracker object will delete itself when we return.
+ GetParent()->SchedulePaint();
}
-void BrowserActionButton::OnStateUpdated() {
- SkBitmap* image = browser_action_state_->icon();
- if (!image) {
- if (static_cast<size_t>(browser_action_state_->icon_index()) <
- browser_action_icons_.size()) {
- image = &browser_action_icons_[browser_action_state_->icon_index()];
- }
- }
+void BrowserActionButton::UpdateState() {
+ int tab_id = panel_->GetCurrentTabId();
+ if (tab_id < 0)
+ return;
- if (image)
- SetIcon(*image);
+ SkBitmap image = browser_action()->GetIcon(tab_id);
+ if (image.isNull())
+ LoadImage();
+ else
+ SetIcon(image);
- SetTooltipText(ASCIIToWide(browser_action_state_->title()));
- panel_->OnBrowserActionVisibilityChanged();
+ SetTooltipText(ASCIIToWide(browser_action()->GetTitle(tab_id)));
GetParent()->SchedulePaint();
}
@@ -125,14 +122,14 @@ void BrowserActionButton::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
if (type == NotificationType::EXTENSION_BROWSER_ACTION_UPDATED) {
- OnStateUpdated();
+ UpdateState();
} else {
NOTREACHED() << L"Received unexpected notification";
}
}
bool BrowserActionButton::IsPopup() {
- return browser_action_->is_popup();
+ return browser_action_->has_popup();
}
bool BrowserActionButton::Activate() {
@@ -195,11 +192,12 @@ void BrowserActionButton::PopupDidHide() {
////////////////////////////////////////////////////////////////////////////////
// BrowserActionView
-BrowserActionView::BrowserActionView(ExtensionAction* browser_action,
- Extension* extension,
- BrowserActionsContainer* panel) {
- button_ = new BrowserActionButton(browser_action, extension, panel);
+BrowserActionView::BrowserActionView(Extension* extension,
+ BrowserActionsContainer* panel)
+ : panel_(panel) {
+ button_ = new BrowserActionButton(extension, panel);
AddChildView(button_);
+ button_->UpdateState();
}
void BrowserActionView::Layout() {
@@ -208,8 +206,16 @@ void BrowserActionView::Layout() {
void BrowserActionView::PaintChildren(gfx::Canvas* canvas) {
View::PaintChildren(canvas);
- button_->browser_action_state()->PaintBadge(canvas,
- gfx::Rect(width(), height()));
+ ExtensionAction2* action = button()->browser_action();
+ int tab_id = panel_->GetCurrentTabId();
+ if (tab_id < 0)
+ return;
+
+ ExtensionActionState::PaintBadge(
+ canvas, gfx::Rect(width(), height()),
+ action->GetBadgeText(tab_id),
+ action->GetBadgeTextColor(tab_id),
+ action->GetBadgeBackgroundColor(tab_id));
}
@@ -224,6 +230,9 @@ BrowserActionsContainer::BrowserActionsContainer(
popup_button_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) {
ExtensionsService* extension_service = profile->GetExtensionsService();
+ if (!extension_service) // The |extension_service| can be NULL in Incognito.
+ return;
+
registrar_.Add(this, NotificationType::EXTENSION_LOADED,
Source<ExtensionsService>(extension_service));
registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
@@ -233,7 +242,9 @@ BrowserActionsContainer::BrowserActionsContainer(
registrar_.Add(this, NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE,
Source<Profile>(profile_));
- RefreshBrowserActionViews();
+ for (size_t i = 0; i < extension_service->extensions()->size(); ++i)
+ AddBrowserAction(extension_service->extensions()->at(i));
+
SetID(VIEW_ID_BROWSER_ACTION_TOOLBAR);
}
@@ -242,26 +253,50 @@ BrowserActionsContainer::~BrowserActionsContainer() {
DeleteBrowserActionViews();
}
+int BrowserActionsContainer::GetCurrentTabId() {
+ TabContents* tab_contents = toolbar_->browser()->GetSelectedTabContents();
+ if (!tab_contents)
+ return -1;
+
+ return tab_contents->controller().session_id().id();
+}
+
void BrowserActionsContainer::RefreshBrowserActionViews() {
- ExtensionsService* extension_service = profile_->GetExtensionsService();
- if (!extension_service) // The |extension_service| can be NULL in Incognito.
+ for (size_t i = 0; i < browser_action_views_.size(); ++i)
+ browser_action_views_[i]->button()->UpdateState();
+}
+
+void BrowserActionsContainer::AddBrowserAction(Extension* extension) {
+#if defined(DEBUG)
+ for (size_t i = 0; i < browser_action_views_.size(); ++i) {
+ DCHECK(browser_action_views_[i]->button()->extension() != extension) <<
+ "Asked to add a browser action view for an extension that already "
+ "exists.";
+ }
+#endif
+ if (!extension->browser_action())
return;
- // Get all browser actions, including those with popups.
- std::vector<ExtensionAction*> browser_actions;
- browser_actions = extension_service->GetBrowserActions(true);
+ BrowserActionView* view = new BrowserActionView(extension, this);
+ browser_action_views_.push_back(view);
+ AddChildView(view);
+}
- DeleteBrowserActionViews();
- for (size_t i = 0; i < browser_actions.size(); ++i) {
- Extension* extension = extension_service->GetExtensionById(
- browser_actions[i]->extension_id());
- DCHECK(extension);
-
- BrowserActionView* view =
- new BrowserActionView(browser_actions[i], extension, this);
- browser_action_views_.push_back(view);
- AddChildView(view);
+void BrowserActionsContainer::RemoveBrowserAction(Extension* extension) {
+ if (!extension->browser_action())
+ return;
+
+ for (std::vector<BrowserActionView*>::iterator iter =
+ browser_action_views_.begin(); iter != browser_action_views_.end();
+ ++iter) {
+ if ((*iter)->button()->extension() == extension) {
+ RemoveChildView(*iter);
+ browser_action_views_.erase(iter);
+ return;
+ }
}
+
+ NOTREACHED() << "Asked to remove a browser action view that doesn't exist.";
}
void BrowserActionsContainer::DeleteBrowserActionViews() {
@@ -306,7 +341,7 @@ void BrowserActionsContainer::TestExecuteBrowserAction(int index) {
void BrowserActionsContainer::OnBrowserActionExecuted(
BrowserActionButton* button) {
- const ExtensionAction& browser_action = button->browser_action();
+ ExtensionAction2* browser_action = button->browser_action();
// Popups just display. No notification to the extension.
// TODO(erikkay): should there be?
@@ -326,7 +361,7 @@ void BrowserActionsContainer::OnBrowserActionExecuted(
gfx::Rect rect = button->bounds();
rect.set_x(origin.x());
rect.set_y(origin.y());
- popup_ = ExtensionPopup::Show(browser_action.popup_url(),
+ popup_ = ExtensionPopup::Show(browser_action->popup_url(),
toolbar_->browser(),
rect);
popup_->set_delegate(this);
@@ -337,7 +372,7 @@ void BrowserActionsContainer::OnBrowserActionExecuted(
// Otherwise, we send the action to the extension.
ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted(
- profile_, browser_action.extension_id(), toolbar_->browser());
+ profile_, browser_action->extension_id(), toolbar_->browser());
}
gfx::Size BrowserActionsContainer::GetPreferredSize() {
@@ -367,20 +402,26 @@ void BrowserActionsContainer::Layout() {
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();
-
- // All these actions may change visibility of BrowserActions.
- OnBrowserActionVisibilityChanged();
- } else if (type == NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE) {
- if (Details<ExtensionHost>(popup_->host()) != details)
- return;
-
- HidePopup();
- } else {
- NOTREACHED() << L"Received unexpected notification";
+ switch (type.value) {
+ case NotificationType::EXTENSION_LOADED:
+ AddBrowserAction(Details<Extension>(details).ptr());
+ OnBrowserActionVisibilityChanged();
+ break;
+
+ case NotificationType::EXTENSION_UNLOADED:
+ case NotificationType::EXTENSION_UNLOADED_DISABLED:
+ RemoveBrowserAction(Details<Extension>(details).ptr());
+ OnBrowserActionVisibilityChanged();
+ break;
+
+ case NotificationType::EXTENSION_HOST_VIEW_SHOULD_CLOSE:
+ if (Details<ExtensionHost>(popup_->host()) != details)
+ return;
+
+ HidePopup();
+
+ default:
+ NOTREACHED() << L"Unexpected notification";
}
}
diff --git a/chrome/browser/views/browser_actions_container.h b/chrome/browser/views/browser_actions_container.h
index e365bcd..756de0a 100644
--- a/chrome/browser/views/browser_actions_container.h
+++ b/chrome/browser/views/browser_actions_container.h
@@ -17,8 +17,7 @@
class BrowserActionsContainer;
class Extension;
-class ExtensionAction;
-class ExtensionActionState;
+class ExtensionAction2;
class ExtensionPopup;
class Profile;
class ToolbarView;
@@ -27,20 +26,21 @@ class ToolbarView;
// BrowserActionButton
// The BrowserActionButton is a specialization of the MenuButton class.
-// It acts on a ExtensionAction, in this case a BrowserAction and handles
+// It acts on a ExtensionAction2, in this case a BrowserAction and handles
// loading the image for the button asynchronously on the file thread to
class BrowserActionButton : public views::MenuButton,
public views::ButtonListener,
public ImageLoadingTracker::Observer,
public NotificationObserver {
public:
- BrowserActionButton(ExtensionAction* browser_action,
- Extension* extension,
- BrowserActionsContainer* panel);
+ BrowserActionButton(Extension* extension, BrowserActionsContainer* panel);
~BrowserActionButton();
- const ExtensionAction& browser_action() const { return *browser_action_; }
- ExtensionActionState* browser_action_state() { return browser_action_state_; }
+ ExtensionAction2* browser_action() const { return browser_action_; }
+ Extension* extension() { return extension_; }
+
+ // Called to update the display to match the browser action's state.
+ void UpdateState();
// Overriden from views::View. Return a 0-inset so the icon can draw all the
// way to the edge of the view if it wants.
@@ -76,15 +76,15 @@ class BrowserActionButton : public views::MenuButton,
virtual void PopupDidHide();
private:
- // Called to update the display to match the browser action's state.
- void OnStateUpdated();
+ // If the image from the browser action needs to be loaded, load it.
+ void LoadImage();
- // The browser action this view represents. The ExtensionAction is not owned
+ // The browser action this view represents. The ExtensionAction2 is not owned
// by this class.
- ExtensionAction* browser_action_;
+ ExtensionAction2* browser_action_;
- // The state of our browser action. Not owned by this class.
- ExtensionActionState* browser_action_state_;
+ // The extension associated with the browser action we're displaying.
+ Extension* extension_;
// The icons representing different states for the browser action.
std::vector<SkBitmap> browser_action_icons_;
@@ -110,9 +110,7 @@ class BrowserActionButton : public views::MenuButton,
class BrowserActionView : public views::View {
public:
- BrowserActionView(ExtensionAction* browser_action, Extension* extension,
- BrowserActionsContainer* panel);
-
+ BrowserActionView(Extension* extension, BrowserActionsContainer* panel);
BrowserActionButton* button() { return button_; }
private:
@@ -121,6 +119,9 @@ class BrowserActionView : public views::View {
// Override PaintChildren so that we can paint the badge on top of children.
virtual void PaintChildren(gfx::Canvas* canvas);
+ // The container for this view.
+ BrowserActionsContainer* panel_;
+
// The button this view contains.
BrowserActionButton* button_;
};
@@ -143,6 +144,9 @@ class BrowserActionsContainer : public views::View,
// Get the number of browser actions being displayed.
int num_browser_actions() { return browser_action_views_.size(); }
+ // Returns the current tab's ID, or -1 if there is no current tab.
+ int GetCurrentTabId();
+
// Get a particular browser action view.
BrowserActionView* GetBrowserActionViewAt(int index) {
return browser_action_views_[index];
@@ -192,6 +196,14 @@ class BrowserActionsContainer : public views::View,
ExtensionPopup* TestGetPopup() { return popup_; }
private:
+ // Adds a browser action view for the extension if it needs one. DCHECK if
+ // it has already been added.
+ void AddBrowserAction(Extension* extension);
+
+ // Removes the browser action view for an extension if it has one. DCHECK if
+ // no such view.
+ void RemoveBrowserAction(Extension* extension);
+
// The vector of browser actions (icons/image buttons for each action).
std::vector<BrowserActionView*> browser_action_views_;
diff --git a/chrome/browser/views/location_bar_view.cc b/chrome/browser/views/location_bar_view.cc
index ff6034b..70ce478 100644
--- a/chrome/browser/views/location_bar_view.cc
+++ b/chrome/browser/views/location_bar_view.cc
@@ -109,8 +109,12 @@ void LocationBarView::PageActionWithBadgeView::PaintChildren(
gfx::Canvas* canvas) {
View::PaintChildren(canvas);
const ExtensionActionState* state = image_view_->GetPageActionState();
- if (state)
- state->PaintBadge(canvas, gfx::Rect(width(), height()));
+ if (state) {
+ ExtensionActionState::PaintBadge(canvas, gfx::Rect(width(), height()),
+ state->badge_text(),
+ state->badge_text_color(),
+ state->badge_background_color());
+ }
}
void LocationBarView::PageActionWithBadgeView::UpdateVisibility(
@@ -1230,8 +1234,12 @@ void LocationBarView::PageActionImageView::Paint(gfx::Canvas* canvas) {
const ExtensionActionState* state =
contents->GetPageActionState(page_action_);
- if (state)
- state->PaintBadge(canvas, gfx::Rect(width(), height()));
+ if (state) {
+ ExtensionActionState::PaintBadge(canvas, gfx::Rect(width(), height()),
+ state->badge_text(),
+ state->badge_text_color(),
+ state->badge_background_color());
+ }
}
// PageActionImageView----------------------------------------------------------
diff --git a/chrome/browser/views/toolbar_view.cc b/chrome/browser/views/toolbar_view.cc
index f5732c16..8853710 100644
--- a/chrome/browser/views/toolbar_view.cc
+++ b/chrome/browser/views/toolbar_view.cc
@@ -218,6 +218,9 @@ void ToolbarView::SetProfile(Profile* profile) {
void ToolbarView::Update(TabContents* tab, bool should_restore_state) {
if (location_bar_)
location_bar_->Update(should_restore_state ? tab : NULL);
+
+ if (browser_actions_)
+ browser_actions_->RefreshBrowserActionViews();
}
int ToolbarView::GetNextAccessibleViewIndex(int view_index, bool nav_left) {
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 18bb465..75a3490 100755
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -545,6 +545,8 @@
'common/extensions/extension_error_utils.h',
'common/extensions/extension_action.cc',
'common/extensions/extension_action.h',
+ 'common/extensions/extension_action2.cc',
+ 'common/extensions/extension_action2.h',
'common/extensions/extension_message_bundle.cc',
'common/extensions/extension_message_bundle.h',
'common/extensions/extension_resource.cc',
@@ -4618,6 +4620,7 @@
'common/common_param_traits_unittest.cc',
'common/extensions/extension_resource_unittest.cc',
'common/extensions/extension_unittest.cc',
+ 'common/extensions/extension_action2_unittest.cc',
'common/extensions/extension_message_bundle_unittest.cc',
'common/extensions/update_manifest_unittest.cc',
'common/extensions/url_pattern_unittest.cc',
diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json
index aca58f6..18da839 100755
--- a/chrome/common/extensions/api/extension_api.json
+++ b/chrome/common/extensions/api/extension_api.json
@@ -922,6 +922,11 @@
"description": "Pixel data for an image. Must be an ImageData object (for example, from a canvas element).",
"optional": true
},
+ "path": {
+ "type": "string",
+ "description": "Relative path to an image in the extension to show in the browser action.",
+ "optional": true
+ },
"iconIndex": {
"type": "integer",
"minimum": 0,
@@ -963,6 +968,11 @@
"title": {
"type": "string",
"description": "The string the browser action should display when moused over."
+ },
+ "tabId": {
+ "type": "integer",
+ "optional": true,
+ "description": "Limits the change to when a particular tab is selected. Automatically resets when the tab is closed."
}
}
}
@@ -982,16 +992,15 @@
"description": "Pixel data for an image. Must be an ImageData object (for example, from a <code>canvas</code> element).",
"optional": true
},
- "iconIndex": {
- "type": "integer",
- "minimum": 0,
- "description": "<b>Deprecated.</b> The zero-based index into the <b>icons</b> vector specified in the manifest.",
- "optional": true
- },
"path": {
"type": "string",
"description": "Relative path to an image in the extension to show in the browser action.",
"optional": true
+ },
+ "tabId": {
+ "type": "integer",
+ "optional": true,
+ "description": "Limits the change to when a particular tab is selected. Automatically resets when the tab is closed."
}
}
}
@@ -1009,6 +1018,11 @@
"text": {
"type": "string",
"description": "Any number of characters can be passed, but only about four can fit in the space."
+ },
+ "tabId": {
+ "type": "integer",
+ "optional": true,
+ "description": "Limits the change to when a particular tab is selected. Automatically resets when the tab is closed."
}
}
}
@@ -1033,6 +1047,11 @@
},
"minItems": 4,
"maxItems": 4
+ },
+ "tabId": {
+ "type": "integer",
+ "optional": true,
+ "description": "Limits the change to when a particular tab is selected. Automatically resets when the tab is closed."
}
}
}
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index 17cd0dc..202a2dc 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -385,6 +385,95 @@ ExtensionAction* Extension::LoadExtensionActionHelper(
return result.release();
}
+ExtensionAction2* Extension::LoadExtensionAction2Helper(
+ const DictionaryValue* extension_action, std::string* error) {
+ scoped_ptr<ExtensionAction2> result(new ExtensionAction2());
+ result->set_extension_id(id());
+
+ // TODO(EXTENSIONS_DEPRECATED): icons list is obsolete.
+ ListValue* icons = NULL;
+ if (extension_action->HasKey(keys::kPageActionIcons) &&
+ extension_action->GetList(keys::kPageActionIcons, &icons)) {
+ for (ListValue::const_iterator iter = icons->begin();
+ iter != icons->end(); ++iter) {
+ std::string path;
+ if (!(*iter)->GetAsString(&path) || path.empty()) {
+ *error = errors::kInvalidPageActionIconPath;
+ return NULL;
+ }
+
+ result->icon_paths()->push_back(path);
+ result->SetDefaultIcon(path);
+ }
+ }
+
+ // TODO(EXTENSIONS_DEPRECATED): Read the page action |id| (optional).
+ std::string id;
+ if (extension_action->HasKey(keys::kPageActionId)) {
+ if (!extension_action->GetString(keys::kPageActionId, &id)) {
+ *error = errors::kInvalidPageActionId;
+ return NULL;
+ }
+ result->set_id(id);
+ }
+
+ std::string default_icon;
+ // Read the page action |default_icon| (optional).
+ if (extension_action->HasKey(keys::kPageActionDefaultIcon)) {
+ if (!extension_action->GetString(keys::kPageActionDefaultIcon,
+ &default_icon) ||
+ default_icon.empty()) {
+ *error = errors::kInvalidPageActionIconPath;
+ return NULL;
+ }
+ result->SetDefaultIcon(default_icon);
+ }
+
+ // Read the page action |default_title|.
+ std::string title;
+ if (!extension_action->GetString(keys::kName, &title) &&
+ !extension_action->GetString(keys::kPageActionDefaultTitle, &title)) {
+ *error = errors::kInvalidPageActionDefaultTitle;
+ return NULL;
+ }
+ result->SetTitle(ExtensionAction2::kDefaultTabId, title);
+
+ // Read the action's |popup| (optional).
+ DictionaryValue* popup = NULL;
+ std::string url_str;
+ if (extension_action->HasKey(keys::kPageActionPopup) &&
+ !extension_action->GetDictionary(keys::kPageActionPopup, &popup) &&
+ !extension_action->GetString(keys::kPageActionPopup, &url_str)) {
+ *error = errors::kInvalidPageActionPopup;
+ return NULL;
+ }
+ if (popup) {
+ // TODO(EXTENSIONS_DEPRECATED): popup is a string only
+ if (!popup->GetString(keys::kPageActionPopupPath, &url_str)) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(
+ errors::kInvalidPageActionPopupPath, "<missing>");
+ return NULL;
+ }
+ GURL url = GetResourceURL(url_str);
+ if (!url.is_valid()) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(
+ errors::kInvalidPageActionPopupPath, url_str);
+ return NULL;
+ }
+ result->set_popup_url(url);
+ } else if (!url_str.empty()) {
+ GURL url = GetResourceURL(url_str);
+ if (!url.is_valid()) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(
+ errors::kInvalidPageActionPopupPath, url_str);
+ return NULL;
+ }
+ result->set_popup_url(url);
+ }
+
+ return result.release();
+}
+
bool Extension::ContainsNonThemeKeys(const DictionaryValue& source) {
// Generate a map of allowable keys
static std::map<std::wstring, bool> theme_keys;
@@ -975,13 +1064,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_id,
}
browser_action_.reset(
- LoadExtensionActionHelper(browser_action_value, error,
- ExtensionAction::BROWSER_ACTION));
+ LoadExtensionAction2Helper(browser_action_value, error));
if (!browser_action_.get())
return false; // Failed to parse browser action definition.
-
- browser_action_state_.reset(
- new ExtensionActionState(browser_action_->title(), 0));
}
// Initialize the permissions (optional).
@@ -1103,9 +1188,9 @@ std::set<FilePath> Extension::GetBrowserImages() {
// browser action icons
if (browser_action_.get()) {
- 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) {
+ std::vector<std::string>* icon_paths = browser_action_->icon_paths();
+ for (std::vector<std::string>::iterator iter = icon_paths->begin();
+ iter != icon_paths->end(); ++iter) {
image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(*iter)));
}
}
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index 38da546..4a9fcd9 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -16,6 +16,7 @@
#include "base/version.h"
#include "chrome/browser/extensions/user_script_master.h"
#include "chrome/common/extensions/extension_action.h"
+#include "chrome/common/extensions/extension_action2.h"
#include "chrome/common/extensions/extension_message_bundle.h"
#include "chrome/common/extensions/extension_resource.h"
#include "chrome/common/extensions/user_script.h"
@@ -191,10 +192,7 @@ class Extension {
const std::string& description() const { return description_; }
const UserScriptList& content_scripts() const { return content_scripts_; }
ExtensionAction* page_action() const { return page_action_.get(); }
- ExtensionAction* browser_action() const { return browser_action_.get(); }
- ExtensionActionState* browser_action_state() {
- return browser_action_state_.get();
- }
+ ExtensionAction2* browser_action() const { return browser_action_.get(); }
const std::vector<PrivacyBlacklistInfo>& privacy_blacklists() const {
return privacy_blacklists_;
}
@@ -288,6 +286,12 @@ class Extension {
std::string* error,
ExtensionAction::ExtensionActionType action_type);
+ // Helper method to load an ExtensionAction2 from the page_action or
+ // browser_action entries in the manifest.
+ // TODO(aa): ExtensionAction2 should replace ExtensionAction completely.
+ ExtensionAction2* LoadExtensionAction2Helper(
+ const DictionaryValue* extension_action, std::string* error);
+
// 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.
bool ContainsNonThemeKeys(const DictionaryValue& source);
@@ -325,10 +329,7 @@ class Extension {
scoped_ptr<ExtensionAction> page_action_;
// The extension's browser action, if any.
- scoped_ptr<ExtensionAction> browser_action_;
-
- // The state of the browser action. Valid iff browser_action_ is non-NULL.
- scoped_ptr<ExtensionActionState> browser_action_state_;
+ scoped_ptr<ExtensionAction2> browser_action_;
// Optional list of privacy blacklistrom.
std::vector<PrivacyBlacklistInfo> privacy_blacklists_;
diff --git a/chrome/common/extensions/extension_action.cc b/chrome/common/extensions/extension_action.cc
index de15f17..246961f 100644
--- a/chrome/common/extensions/extension_action.cc
+++ b/chrome/common/extensions/extension_action.cc
@@ -20,11 +20,19 @@ ExtensionAction::~ExtensionAction() {
}
void ExtensionActionState::PaintBadge(gfx::Canvas* canvas,
- const gfx::Rect& bounds) const {
- const std::string& text = badge_text();
+ const gfx::Rect& bounds,
+ const std::string& text,
+ SkColor text_color,
+ SkColor background_color) {
if (text.empty())
return;
+ if (SkColorGetA(text_color) == 0x00)
+ text_color = SK_ColorWHITE;
+
+ if (SkColorGetA(background_color) == 0x00)
+ background_color = SkColorSetARGB(255, 218, 0, 24); // default badge color
+
// Different platforms need slightly different constants to look good.
#if defined(OS_LINUX)
const int kTextSize = 9;
@@ -49,7 +57,7 @@ void ExtensionActionState::PaintBadge(gfx::Canvas* canvas,
SkTypeface* typeface = SkTypeface::CreateFromName("Arial", SkTypeface::kBold);
SkPaint text_paint;
text_paint.setAntiAlias(true);
- text_paint.setColor(badge_text_color());
+ text_paint.setColor(text_color);
text_paint.setFakeBoldText(true);
text_paint.setTextAlign(SkPaint::kLeft_Align);
text_paint.setTextSize(SkIntToScalar(kTextSize));
@@ -81,7 +89,7 @@ void ExtensionActionState::PaintBadge(gfx::Canvas* canvas,
SkPaint rect_paint;
rect_paint.setStyle(SkPaint::kFill_Style);
rect_paint.setAntiAlias(true);
- rect_paint.setColor(badge_background_color());
+ rect_paint.setColor(background_color);
canvas->drawRoundRect(rect, SkIntToScalar(2), SkIntToScalar(2), rect_paint);
// Overlay the gradient. It is stretchy, so we do this in three parts.
diff --git a/chrome/common/extensions/extension_action.h b/chrome/common/extensions/extension_action.h
index b819edb..6eb66c1 100644
--- a/chrome/common/extensions/extension_action.h
+++ b/chrome/common/extensions/extension_action.h
@@ -82,6 +82,10 @@ typedef std::map<std::string, ExtensionAction*> ExtensionActionMap;
// values of the ExtensionAction.
class ExtensionActionState {
public:
+ static void PaintBadge(gfx::Canvas* canvas, const gfx::Rect& bounds,
+ const std::string& text, SkColor text_color,
+ SkColor background_color);
+
ExtensionActionState(std::string title, int icon_index)
: hidden_(false), title_(title), icon_index_(icon_index),
badge_background_color_(SkColorSetARGB(255, 218, 0, 24)),
@@ -119,8 +123,6 @@ class ExtensionActionState {
bool hidden() const { return hidden_; }
void set_hidden(bool hidden) { hidden_ = hidden; }
- void PaintBadge(gfx::Canvas* canvas, const gfx::Rect& bounds) const;
-
private:
// True if the action is in the hidden state.
bool hidden_;
diff --git a/chrome/common/extensions/extension_action2.cc b/chrome/common/extensions/extension_action2.cc
new file mode 100755
index 0000000..653de95
--- /dev/null
+++ b/chrome/common/extensions/extension_action2.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/extensions/extension_action2.h"
+
+#include "base/logging.h"
+
+const int ExtensionAction2::kDefaultTabId = -1;
+
+void ExtensionAction2::SetDefaultIcon(const std::string& path) {
+ default_icon_path_ = path;
+ icon_.erase(kDefaultTabId);
+}
+
+void ExtensionAction2::SetDefaultIcon(int icon_index) {
+ if (static_cast<size_t>(icon_index) >= icon_paths_.size()) {
+ NOTREACHED();
+ return;
+ }
+
+ SetDefaultIcon(icon_paths_[icon_index]);
+}
+
+void ExtensionAction2::ClearAllValuesForTab(int tab_id) {
+ title_.erase(tab_id);
+ icon_.erase(tab_id);
+ badge_text_.erase(tab_id);
+ badge_background_color_.erase(tab_id);
+ badge_text_color_.erase(tab_id);
+}
diff --git a/chrome/common/extensions/extension_action2.h b/chrome/common/extensions/extension_action2.h
new file mode 100755
index 0000000..ae4cf34
--- /dev/null
+++ b/chrome/common/extensions/extension_action2.h
@@ -0,0 +1,163 @@
+// 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_COMMON_EXTENSIONS_EXTENSION_ACTION2_H_
+#define CHROME_COMMON_EXTENSIONS_EXTENSION_ACTION2_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+// ExtensionAction2 encapsulates the state of a browser or page action.
+// Instances can have both global and per-tab state. If a property does not have
+// a per-tab value, the global value is used instead.
+//
+// TODO(aa): This should replace ExtensionAction and ExtensionActionState.
+class ExtensionAction2 {
+ public:
+ // Use this ID to indicate the default state for properties that take a tab_id
+ // parameter.
+ static const int kDefaultTabId;
+
+ // extension id
+ std::string extension_id() const { return extension_id_; }
+ void set_extension_id(const std::string& extension_id) {
+ extension_id_ = extension_id;
+ }
+
+ // popup details
+ const GURL& popup_url() const { return popup_url_; }
+ void set_popup_url(const GURL& url) { popup_url_ = url; }
+ bool has_popup() const { return !popup_url_.is_empty(); }
+
+ // title
+ void SetTitle(int tab_id, const std::string& title) {
+ SetValue(&title_, tab_id, title);
+ }
+ std::string GetTitle(int tab_id) { return GetValue(&title_, tab_id); }
+
+ // Icons are a bit different because the default value can be set to either a
+ // bitmap or a path. However, conceptually, there is only one default icon.
+ // Setting the default icon using a path clears the bitmap and vice-versa.
+ //
+ // To get the default icon, first check for the bitmap. If it is null, check
+ // for the path.
+
+ // icon bitmap
+ void SetIcon(int tab_id, const SkBitmap& bitmap) {
+ SetValue(&icon_, tab_id, bitmap);
+ if (tab_id == kDefaultTabId)
+ default_icon_path_.clear();
+ }
+ SkBitmap GetIcon(int tab_id) { return GetValue(&icon_, tab_id); }
+
+ // icon path (relative to extension_id()'s root)
+ // For legacy code, we also support setting the path as an index into
+ // icon_paths().
+ void SetDefaultIcon(const std::string& path);
+ void SetDefaultIcon(int icon_index);
+ std::string GetDefaultIconPath() {
+ return default_icon_path_;
+ }
+
+ // badge text
+ void SetBadgeText(int tab_id, const std::string& text) {
+ SetValue(&badge_text_, tab_id, text);
+ }
+ std::string GetBadgeText(int tab_id) { return GetValue(&badge_text_, tab_id); }
+
+ // badge text color
+ void SetBadgeTextColor(int tab_id, const SkColor& text_color) {
+ SetValue(&badge_text_color_, tab_id, text_color);
+ }
+ SkColor GetBadgeTextColor(int tab_id) {
+ return GetValue(&badge_text_color_, tab_id);
+ }
+
+ // badge background color
+ void SetBadgeBackgroundColor(int tab_id, const SkColor& color) {
+ SetValue(&badge_background_color_, tab_id, color);
+ }
+ SkColor GetBadgeBackgroundColor(int tab_id) {
+ return GetValue(&badge_background_color_, tab_id);
+ }
+
+ // Remove all tab-specific state.
+ void ClearAllValuesForTab(int tab_id);
+
+ //---------------------------------------------------------------------------
+ // Legacy support
+
+ std::string id() const { return id_; }
+ void set_id(const std::string& id) { id_ = id; }
+
+ std::vector<std::string>* icon_paths() { return &icon_paths_; }
+
+ private:
+ template <class T>
+ struct ValueTraits {
+ static T CreateEmpty() {
+ return T();
+ }
+ };
+
+ template<class T>
+ void SetValue(std::map<int, T>* map, int tab_id, T val) {
+ (*map)[tab_id] = val;
+ }
+
+ template<class T>
+ T GetValue(std::map<int, T>* map, int tab_id) {
+ typename std::map<int, T>::iterator iter = map->find(tab_id);
+ if (iter != map->end()) {
+ return iter->second;
+ } else {
+ iter = map->find(kDefaultTabId);
+ return iter != map->end() ? iter->second : ValueTraits<T>::CreateEmpty();
+ }
+ }
+
+ // The id for the extension this action belongs to (as defined in the
+ // extension manifest).
+ std::string extension_id_;
+
+ // Each of these data items can have both a global state (stored with the key
+ // kDefaultTabId), or tab-specific state (stored with the tab_id as the key).
+ std::map<int, std::string> title_;
+ std::map<int, SkBitmap> icon_;
+ std::map<int, std::string> badge_text_;
+ std::map<int, SkColor> badge_background_color_;
+ std::map<int, SkColor> badge_text_color_;
+
+ std::string default_icon_path_;
+
+ // If the action has a popup, it has a URL and a height.
+ GURL popup_url_;
+
+ //---------------------------------------------------------------------------
+ // Legacy support
+
+ // The id for the ExtensionAction2, for example: "RssPageAction". This is
+ // needed for compat with an older version of the page actions API.
+ std::string id_;
+
+ // A list of paths to icons this action might show. This is needed to support
+ // the setIcon({iconIndex:...} method.
+ std::vector<std::string> icon_paths_;
+};
+
+template <>
+struct ExtensionAction2::ValueTraits<SkColor> {
+ static SkColor CreateEmpty() {
+ return 0x00000000;
+ }
+};
+
+#endif // CHROME_COMMON_EXTENSIONS_EXTENSION_ACTION2_H_
diff --git a/chrome/common/extensions/extension_action2_unittest.cc b/chrome/common/extensions/extension_action2_unittest.cc
new file mode 100644
index 0000000..7f1d78b
--- /dev/null
+++ b/chrome/common/extensions/extension_action2_unittest.cc
@@ -0,0 +1,127 @@
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/extension_action2.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "webkit/glue/image_decoder.h"
+
+static SkBitmap LoadIcon(const std::string& filename) {
+ FilePath path;
+ PathService::Get(chrome::DIR_TEST_DATA, &path);
+ path = path.AppendASCII("extensions").AppendASCII(filename);
+
+ std::string file_contents;
+ file_util::ReadFileToString(path, &file_contents);
+ const unsigned char* data =
+ reinterpret_cast<const unsigned char*>(file_contents.data());
+
+ SkBitmap bitmap;
+ webkit_glue::ImageDecoder decoder;
+ bitmap = decoder.Decode(data, file_contents.length());
+
+ return bitmap;
+}
+
+static bool BitmapsAreEqual(const SkBitmap& bitmap1, const SkBitmap& bitmap2) {
+ void* addr1 = NULL;
+ void* addr2 = NULL;
+
+ bitmap1.lockPixels();
+ addr1 = bitmap1.getAddr32(0, 0);
+ bitmap1.unlockPixels();
+
+ bitmap2.lockPixels();
+ addr2 = bitmap2.getAddr32(0, 0);
+ bitmap2.unlockPixels();
+
+ return 0 == memcmp(addr1, addr2, bitmap1.getSize());
+}
+
+TEST(ExtensionAction2Test, TabSpecificState) {
+ ExtensionAction2 action;
+
+ // title
+ ASSERT_EQ("", action.GetTitle(1));
+ action.SetTitle(ExtensionAction2::kDefaultTabId, "foo");
+ ASSERT_EQ("foo", action.GetTitle(1));
+ ASSERT_EQ("foo", action.GetTitle(100));
+ action.SetTitle(100, "bar");
+ ASSERT_EQ("foo", action.GetTitle(1));
+ ASSERT_EQ("bar", action.GetTitle(100));
+ action.SetTitle(ExtensionAction2::kDefaultTabId, "baz");
+ ASSERT_EQ("baz", action.GetTitle(1));
+ action.ClearAllValuesForTab(100);
+ ASSERT_EQ("baz", action.GetTitle(100));
+
+ // icon
+ SkBitmap icon1 = LoadIcon("icon1.png");
+ SkBitmap icon2 = LoadIcon("icon2.png");
+ ASSERT_TRUE(action.GetIcon(1).isNull());
+ action.SetIcon(ExtensionAction2::kDefaultTabId, icon1);
+ ASSERT_TRUE(BitmapsAreEqual(icon1, action.GetIcon(100)));
+ action.SetIcon(100, icon2);
+ ASSERT_TRUE(BitmapsAreEqual(icon1, action.GetIcon(1)));
+ ASSERT_TRUE(BitmapsAreEqual(icon2, action.GetIcon(100)));
+
+ // badge text
+ ASSERT_EQ("", action.GetBadgeText(1));
+ action.SetBadgeText(ExtensionAction2::kDefaultTabId, "foo");
+ ASSERT_EQ("foo", action.GetBadgeText(1));
+ ASSERT_EQ("foo", action.GetBadgeText(100));
+ action.SetBadgeText(100, "bar");
+ ASSERT_EQ("foo", action.GetBadgeText(1));
+ ASSERT_EQ("bar", action.GetBadgeText(100));
+ action.SetBadgeText(ExtensionAction2::kDefaultTabId, "baz");
+ ASSERT_EQ("baz", action.GetBadgeText(1));
+ action.ClearAllValuesForTab(100);
+ ASSERT_EQ("baz", action.GetBadgeText(100));
+
+ // badge text color
+ ASSERT_EQ(0x00000000u, action.GetBadgeTextColor(1));
+ action.SetBadgeTextColor(ExtensionAction2::kDefaultTabId, 0xFFFF0000);
+ ASSERT_EQ(0xFFFF0000u, action.GetBadgeTextColor(1));
+ ASSERT_EQ(0xFFFF0000u, action.GetBadgeTextColor(100));
+ action.SetBadgeTextColor(100, 0xFF00FF00);
+ ASSERT_EQ(0xFFFF0000u, action.GetBadgeTextColor(1));
+ ASSERT_EQ(0xFF00FF00u, action.GetBadgeTextColor(100));
+ action.SetBadgeTextColor(ExtensionAction2::kDefaultTabId, 0xFF0000FF);
+ ASSERT_EQ(0xFF0000FFu, action.GetBadgeTextColor(1));
+ action.ClearAllValuesForTab(100);
+ ASSERT_EQ(0xFF0000FFu, action.GetBadgeTextColor(100));
+
+ // badge background color
+ ASSERT_EQ(0x00000000u, action.GetBadgeBackgroundColor(1));
+ action.SetBadgeBackgroundColor(ExtensionAction2::kDefaultTabId, 0xFFFF0000);
+ ASSERT_EQ(0xFFFF0000u, action.GetBadgeBackgroundColor(1));
+ ASSERT_EQ(0xFFFF0000u, action.GetBadgeBackgroundColor(100));
+ action.SetBadgeBackgroundColor(100, 0xFF00FF00);
+ ASSERT_EQ(0xFFFF0000u, action.GetBadgeBackgroundColor(1));
+ ASSERT_EQ(0xFF00FF00u, action.GetBadgeBackgroundColor(100));
+ action.SetBadgeBackgroundColor(ExtensionAction2::kDefaultTabId, 0xFF0000FF);
+ ASSERT_EQ(0xFF0000FFu, action.GetBadgeBackgroundColor(1));
+ action.ClearAllValuesForTab(100);
+ ASSERT_EQ(0xFF0000FFu, action.GetBadgeBackgroundColor(100));
+}
+
+TEST(ExtensionAction2Test, IconOddCases) {
+ ExtensionAction2 action;
+
+ action.SetIcon(ExtensionAction2::kDefaultTabId, LoadIcon("icon1.png"));
+ action.SetDefaultIcon("foo.png");
+ ASSERT_TRUE(action.GetIcon(1).isNull());
+ ASSERT_EQ("foo.png", action.GetDefaultIconPath());
+
+ action.icon_paths()->push_back("a.png");
+ action.icon_paths()->push_back("b.png");
+ action.SetDefaultIcon(1);
+ ASSERT_TRUE(action.GetIcon(1).isNull());
+ ASSERT_EQ("b.png", action.GetDefaultIconPath());
+
+ action.SetIcon(100, LoadIcon("icon1.png"));
+ ASSERT_TRUE(!action.GetIcon(100).isNull());
+ action.SetIcon(ExtensionAction2::kDefaultTabId, LoadIcon("icon1.png"));
+ ASSERT_TRUE(!action.GetIcon(1).isNull());
+ ASSERT_EQ("", action.GetDefaultIconPath());
+}
diff --git a/chrome/renderer/extensions/extension_process_bindings.cc b/chrome/renderer/extensions/extension_process_bindings.cc
index a9360eb..428281e 100644
--- a/chrome/renderer/extensions/extension_process_bindings.cc
+++ b/chrome/renderer/extensions/extension_process_bindings.cc
@@ -416,7 +416,6 @@ class ExtensionImpl : public ExtensionBase {
// before sending the request to the browser.
static v8::Handle<v8::Value> SetExtensionActionIcon(const v8::Arguments& args) {
v8::Local<v8::Object> details = args[1]->ToObject();
- int tab_id = details->Get(v8::String::New("tabId"))->Int32Value();
v8::Local<v8::Object> image_data =
details->Get(v8::String::New("imageData"))->ToObject();
v8::Local<v8::Object> data =
@@ -453,7 +452,11 @@ class ExtensionImpl : public ExtensionBase {
DictionaryValue* dict = new DictionaryValue();
dict->Set(L"imageData", bitmap_value);
- dict->SetInteger(L"tabId", tab_id);
+
+ if (details->Has(v8::String::New("tabId"))) {
+ dict->SetInteger(L"tabId",
+ details->Get(v8::String::New("tabId"))->Int32Value());
+ }
return StartRequestCommon(args, dict);
}
diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js
index 3974867..fbe96d4 100644
--- a/chrome/renderer/resources/extension_process_bindings.js
+++ b/chrome/renderer/resources/extension_process_bindings.js
@@ -345,7 +345,7 @@ var chrome = chrome || {};
return GetL10nMessage(message_name, placeholders);
}
- var canvas_context;
+ var canvas;
function setIconCommon(details, name, parameters) {
if ("iconIndex" in details) {
sendRequest(name, [details], parameters);
@@ -364,13 +364,13 @@ var chrome = chrome || {};
}
sendCustomRequest(SetExtensionActionIcon, name, [details], parameters);
} else if ("path" in details) {
- if (!canvas_context) {
+ if (!canvas) {
var canvas = document.createElement("canvas");
canvas.width = 19;
canvas.height = 19;
- canvas_context = canvas.getContext('2d');
}
+ var canvas_context = canvas.getContext('2d');
var img = new Image();
var self = this;
img.onerror = function() {
diff --git a/chrome/test/data/extensions/api_test/browser_action/update.html b/chrome/test/data/extensions/api_test/browser_action/update.html
index 6d69bd3..a0a347c 100644
--- a/chrome/test/data/extensions/api_test/browser_action/update.html
+++ b/chrome/test/data/extensions/api_test/browser_action/update.html
@@ -4,7 +4,7 @@
// Test that we can change various properties of the browser action.
// The C++ verifies.
chrome.browserAction.setTitle({title: "Modified"});
- chrome.browserAction.setIcon({iconIndex: 1});
+ chrome.browserAction.setIcon({path: "icon2.png"});
chrome.browserAction.setBadgeText({text: "badge"});
chrome.browserAction.setBadgeBackgroundColor({color: [255,255,255,255]});
diff --git a/chrome/test/data/extensions/api_test/browser_action_tab_specific_state/background.html b/chrome/test/data/extensions/api_test/browser_action_tab_specific_state/background.html
new file mode 100644
index 0000000..7393c5d
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/browser_action_tab_specific_state/background.html
@@ -0,0 +1,37 @@
+<html>
+<head>
+<script>
+ var min = 1;
+ var max = 5;
+ var current = min;
+
+ // Called when the user clicks on the browser action.
+ chrome.browserAction.onClicked.addListener(function(tab) {
+ current++;
+ if (current > max)
+ current = min;
+
+ chrome.browserAction.setIcon({
+ path: "icon" + current + ".png",
+ tabId: tab.id
+ });
+ chrome.browserAction.setTitle({
+ title: "Showing icon " + current,
+ tabId: tab.id
+ });
+ chrome.browserAction.setBadgeText({
+ text: String(current),
+ tabId: tab.id
+ });
+ chrome.browserAction.setBadgeBackgroundColor({
+ color: [50*current,0,0,255],
+ tabId: tab.id
+ });
+
+ chrome.test.notifyPass();
+ });
+
+ chrome.test.notifyPass();
+</script>
+</head>
+</html>
diff --git a/chrome/test/data/extensions/api_test/browser_action_tab_specific_state/manifest.json b/chrome/test/data/extensions/api_test/browser_action_tab_specific_state/manifest.json
new file mode 100755
index 0000000..2e11ba8
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/browser_action_tab_specific_state/manifest.json
@@ -0,0 +1,12 @@
+{
+ "name": "Tab color",
+ "version": "1.0",
+ "background_page": "background.html",
+ "permissions": [
+ "tabs", "http://*/*"
+ ],
+ "browser_action": {
+ "default_icon": "icon1.png",
+ "default_title": "hi!"
+ }
+} \ No newline at end of file