summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authormpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-16 21:53:46 +0000
committermpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-16 21:53:46 +0000
commit744ef17706b3c77d087809ec119bbc2f7bd17c89 (patch)
tree24eb889c256f49dd3d24610b68c5e09b90a5c21d /chrome
parent0db22afeba278540d2457ebfead171a8a54feb3c (diff)
downloadchromium_src-744ef17706b3c77d087809ec119bbc2f7bd17c89.zip
chromium_src-744ef17706b3c77d087809ec119bbc2f7bd17c89.tar.gz
chromium_src-744ef17706b3c77d087809ec119bbc2f7bd17c89.tar.bz2
Implement new page action API.
Still no actual drawing code for badges, though. BUG=24635 TEST=Load the sample test_page_action extension and click the page action to cycle through various states. Review URL: http://codereview.chromium.org/269079 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29335 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/extensions/browser_action_apitest.cc1
-rw-r--r--chrome/browser/extensions/extension_browser_actions_api.cc24
-rw-r--r--chrome/browser/extensions/extension_function_dispatcher.cc7
-rw-r--r--chrome/browser/extensions/extension_page_actions_module.cc178
-rw-r--r--chrome/browser/extensions/extension_page_actions_module.h46
-rw-r--r--chrome/browser/extensions/extension_page_actions_module_constants.cc5
-rw-r--r--chrome/browser/extensions/extension_page_actions_module_constants.h6
-rw-r--r--chrome/browser/extensions/page_action_apitest.cc66
-rw-r--r--chrome/browser/gtk/location_bar_view_gtk.cc36
-rw-r--r--chrome/browser/gtk/location_bar_view_gtk.h6
-rw-r--r--chrome/browser/tab_contents/tab_contents.cc40
-rw-r--r--chrome/browser/tab_contents/tab_contents.h23
-rw-r--r--chrome/browser/views/location_bar_view.cc17
-rwxr-xr-xchrome/chrome.gyp1
-rwxr-xr-xchrome/common/extensions/api/extension_api.json140
-rwxr-xr-xchrome/common/extensions/docs/pageAction.html1476
-rw-r--r--chrome/common/extensions/extension_action.h8
-rw-r--r--chrome/renderer/extensions/extension_process_bindings.cc18
-rw-r--r--chrome/renderer/resources/extension_process_bindings.js32
-rwxr-xr-xchrome/test/data/extensions/api_test/browser_action/manifest.json6
-rwxr-xr-xchrome/test/data/extensions/api_test/page_action/background.html12
-rwxr-xr-xchrome/test/data/extensions/api_test/page_action/icon.pngbin0 -> 2809 bytes
-rwxr-xr-xchrome/test/data/extensions/api_test/page_action/manifest.json12
-rwxr-xr-xchrome/test/data/extensions/api_test/page_action/update.html17
-rwxr-xr-xchrome/test/data/extensions/api_test/page_action/update2.html17
-rw-r--r--chrome/test/data/extensions/samples/test_page_action/background.html65
-rw-r--r--chrome/test/data/extensions/samples/test_page_action/manifest.json11
-rw-r--r--chrome/test/data/extensions/samples/test_page_action/print_16x16.pngbin0 -> 647 bytes
28 files changed, 2177 insertions, 93 deletions
diff --git a/chrome/browser/extensions/browser_action_apitest.cc b/chrome/browser/extensions/browser_action_apitest.cc
index 5a7c086..e5a4650 100644
--- a/chrome/browser/extensions/browser_action_apitest.cc
+++ b/chrome/browser/extensions/browser_action_apitest.cc
@@ -37,7 +37,6 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, BrowserAction) {
// Test that we received the changes.
ExtensionActionState* action_state = extension->browser_action_state();
ASSERT_EQ("Modified", action_state->title());
- ASSERT_EQ(1, action_state->icon_index());
ASSERT_EQ("badge", action_state->badge_text());
ASSERT_EQ(SkColorSetARGB(255, 255, 255, 255),
action_state->badge_background_color());
diff --git a/chrome/browser/extensions/extension_browser_actions_api.cc b/chrome/browser/extensions/extension_browser_actions_api.cc
index e7d875e..5cf68e8 100644
--- a/chrome/browser/extensions/extension_browser_actions_api.cc
+++ b/chrome/browser/extensions/extension_browser_actions_api.cc
@@ -18,10 +18,8 @@ const char kIconIndexOutOfBounds[] =
}
bool BrowserActionSetIconFunction::RunImpl() {
- // setIcon can take a variant argument: either a canvas ImageData, or an
- // icon index.
- EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_BINARY) ||
- args_->IsType(Value::TYPE_DICTIONARY));
+ EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_DICTIONARY));
+ const DictionaryValue* args = static_cast<const DictionaryValue*>(args_);
Extension* extension = dispatcher()->GetExtension();
if (!extension->browser_action()) {
@@ -29,28 +27,28 @@ bool BrowserActionSetIconFunction::RunImpl() {
return false;
}
- if (args_->IsType(Value::TYPE_BINARY)) {
- BinaryValue* binary = static_cast<BinaryValue*>(args_);
+ // 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 {
- int icon_index = -1;
- EXTENSION_FUNCTION_VALIDATE(
- static_cast<DictionaryValue*>(args_)->GetInteger(
- L"iconIndex", &icon_index));
+ } else if (args->GetInteger(L"iconIndex", &icon_index)) {
+ if (icon_index < 0 || static_cast<size_t>(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);
}
NotificationService::current()->Notify(
diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc
index 1870701..ffbac6c 100644
--- a/chrome/browser/extensions/extension_function_dispatcher.cc
+++ b/chrome/browser/extensions/extension_function_dispatcher.cc
@@ -101,6 +101,13 @@ void FactoryRegistry::ResetFunctions() {
// Page Actions.
RegisterFunction<EnablePageActionFunction>();
RegisterFunction<DisablePageActionFunction>();
+ RegisterFunction<PageActionShowFunction>();
+ RegisterFunction<PageActionHideFunction>();
+ RegisterFunction<PageActionSetIconFunction>();
+ RegisterFunction<PageActionSetTitleFunction>();
+ RegisterFunction<PageActionSetBadgeBackgroundColorFunction>();
+ RegisterFunction<PageActionSetBadgeTextColorFunction>();
+ RegisterFunction<PageActionSetBadgeTextFunction>();
// Browser Actions.
RegisterFunction<BrowserActionSetIconFunction>();
diff --git a/chrome/browser/extensions/extension_page_actions_module.cc b/chrome/browser/extensions/extension_page_actions_module.cc
index 51866e3..60eb86a 100644
--- a/chrome/browser/extensions/extension_page_actions_module.cc
+++ b/chrome/browser/extensions/extension_page_actions_module.cc
@@ -15,9 +15,21 @@
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_error_utils.h"
+#include "chrome/common/render_messages.h"
namespace keys = extension_page_actions_module_constants;
+namespace {
+// Errors.
+const char kNoExtensionError[] = "No extension with id: *.";
+const char kNoTabError[] = "No tab with id: *.";
+const char kNoPageActionError[] =
+ "This extension has no page action specified.";
+const char kUrlNotActiveError[] = "This url is no longer active: *.";
+const char kIconIndexOutOfBounds[] = "Page action icon index out of bounds.";
+}
+
+// TODO(EXTENSIONS_DEPRECATED): obsolete API.
bool PageActionFunction::SetPageActionEnabled(bool enable) {
EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_LIST));
const ListValue* args = static_cast<const ListValue*>(args_);
@@ -48,7 +60,7 @@ bool PageActionFunction::SetPageActionEnabled(bool enable) {
TabContents* contents = NULL;
ExtensionTabUtil::GetTabById(tab_id, profile(), NULL, NULL, &contents, NULL);
if (!contents) {
- error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kNoTabError,
+ error_ = ExtensionErrorUtils::FormatErrorMessage(kNoTabError,
IntToString(tab_id));
return false;
}
@@ -56,8 +68,7 @@ bool PageActionFunction::SetPageActionEnabled(bool enable) {
// Make sure the URL hasn't changed.
NavigationEntry* entry = contents->controller().GetActiveEntry();
if (!entry || url != entry->url().spec()) {
- error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kUrlNotActiveError,
- url);
+ error_ = ExtensionErrorUtils::FormatErrorMessage(kUrlNotActiveError, url);
return false;
}
@@ -66,22 +77,52 @@ bool PageActionFunction::SetPageActionEnabled(bool enable) {
ExtensionsService* service = profile()->GetExtensionsService();
extension = service->GetExtensionById(extension_id());
if (!extension) {
- error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kNoExtensionError,
+ error_ = ExtensionErrorUtils::FormatErrorMessage(kNoExtensionError,
extension_id());
return false;
}
const ExtensionAction* page_action = extension->page_action();
if (!page_action) {
- error_ = ExtensionErrorUtils::FormatErrorMessage(keys::kNoPageActionError,
- page_action_id);
+ error_ = kNoPageActionError;
return false;
}
// Set visibility and broadcast notifications that the UI should be updated.
contents->SetPageActionEnabled(page_action, enable, title, icon_id);
- contents->NotifyNavigationStateChanged(TabContents::INVALIDATE_PAGE_ACTIONS);
+ contents->PageActionStateChanged();
+
+ return true;
+}
+
+bool PageActionFunction::InitCommon(int tab_id) {
+ page_action_ = dispatcher()->GetExtension()->page_action();
+ if (!page_action_) {
+ error_ = kNoPageActionError;
+ return false;
+ }
+
+ // Find the TabContents that contains this tab id.
+ contents_ = NULL;
+ ExtensionTabUtil::GetTabById(tab_id, profile(), NULL, NULL, &contents_, NULL);
+ if (!contents_) {
+ error_ = ExtensionErrorUtils::FormatErrorMessage(kNoTabError,
+ IntToString(tab_id));
+ return false;
+ }
+
+ state_ = contents_->GetOrCreatePageActionState(page_action_);
+ return true;
+}
+
+bool PageActionFunction::SetHidden(bool hidden) {
+ int tab_id;
+ EXTENSION_FUNCTION_VALIDATE(args_->GetAsInteger(&tab_id));
+ if (!InitCommon(tab_id))
+ return false;
+ state_->set_hidden(hidden);
+ contents_->PageActionStateChanged();
return true;
}
@@ -92,3 +133,126 @@ bool EnablePageActionFunction::RunImpl() {
bool DisablePageActionFunction::RunImpl() {
return SetPageActionEnabled(false);
}
+
+bool PageActionShowFunction::RunImpl() {
+ return SetHidden(false);
+}
+
+bool PageActionHideFunction::RunImpl() {
+ return SetHidden(true);
+}
+
+bool PageActionSetIconFunction::RunImpl() {
+ EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_DICTIONARY));
+ const DictionaryValue* args = static_cast<const DictionaryValue*>(args_);
+
+ int tab_id;
+ EXTENSION_FUNCTION_VALIDATE(args->GetInteger(L"tabId", &tab_id));
+ if (!InitCommon(tab_id))
+ 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()));
+ state_->set_icon(bitmap.release());
+ } else if (args->GetInteger(L"iconIndex", &icon_index)) {
+ if (icon_index < 0 ||
+ static_cast<size_t>(icon_index) >= page_action_->icon_paths().size()) {
+ error_ = kIconIndexOutOfBounds;
+ return false;
+ }
+ state_->set_icon(NULL);
+ state_->set_icon_index(icon_index);
+ } else {
+ EXTENSION_FUNCTION_VALIDATE(false);
+ }
+
+ contents_->PageActionStateChanged();
+ return true;
+}
+
+bool PageActionSetTitleFunction::RunImpl() {
+ EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_DICTIONARY));
+ const DictionaryValue* args = static_cast<const DictionaryValue*>(args_);
+
+ int tab_id;
+ EXTENSION_FUNCTION_VALIDATE(args->GetInteger(L"tabId", &tab_id));
+ if (!InitCommon(tab_id))
+ return false;
+
+ std::string title;
+ EXTENSION_FUNCTION_VALIDATE(args->GetString(L"title", &title));
+
+ state_->set_title(title);
+ contents_->PageActionStateChanged();
+ return true;
+}
+
+bool PageActionSetBadgeBackgroundColorFunction::RunImpl() {
+ EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_DICTIONARY));
+ const DictionaryValue* args = static_cast<const DictionaryValue*>(args_);
+
+ int tab_id;
+ EXTENSION_FUNCTION_VALIDATE(args->GetInteger(L"tabId", &tab_id));
+ if (!InitCommon(tab_id))
+ return false;
+
+ ListValue* color_value;
+ EXTENSION_FUNCTION_VALIDATE(args->GetList(L"color", &color_value));
+ EXTENSION_FUNCTION_VALIDATE(color_value->GetSize() == 4);
+
+ int color_array[4] = {0};
+ for (size_t i = 0; i < arraysize(color_array); ++i)
+ EXTENSION_FUNCTION_VALIDATE(color_value->GetInteger(i, &color_array[i]));
+
+ SkColor color = SkColorSetARGB(color_array[0], color_array[1], color_array[2],
+ color_array[3]);
+ state_->set_badge_background_color(color);
+ contents_->PageActionStateChanged();
+ return true;
+}
+
+bool PageActionSetBadgeTextColorFunction::RunImpl() {
+ EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_DICTIONARY));
+ const DictionaryValue* args = static_cast<const DictionaryValue*>(args_);
+
+ int tab_id;
+ EXTENSION_FUNCTION_VALIDATE(args->GetInteger(L"tabId", &tab_id));
+ if (!InitCommon(tab_id))
+ return false;
+
+ ListValue* color_value;
+ EXTENSION_FUNCTION_VALIDATE(args->GetList(L"color", &color_value));
+ EXTENSION_FUNCTION_VALIDATE(color_value->GetSize() == 4);
+
+ int color_array[4] = {0};
+ for (size_t i = 0; i < arraysize(color_array); ++i)
+ EXTENSION_FUNCTION_VALIDATE(color_value->GetInteger(i, &color_array[i]));
+
+ // TODO(mpcomplete): implement text coloring.
+ return true;
+}
+
+bool PageActionSetBadgeTextFunction::RunImpl() {
+ EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_DICTIONARY));
+ const DictionaryValue* args = static_cast<const DictionaryValue*>(args_);
+
+ int tab_id;
+ EXTENSION_FUNCTION_VALIDATE(args->GetInteger(L"tabId", &tab_id));
+ if (!InitCommon(tab_id))
+ return false;
+
+ std::string text;
+ EXTENSION_FUNCTION_VALIDATE(args->GetString(L"text", &text));
+
+ state_->set_badge_text(text);
+ contents_->PageActionStateChanged();
+ return true;
+}
diff --git a/chrome/browser/extensions/extension_page_actions_module.h b/chrome/browser/extensions/extension_page_actions_module.h
index 6a85cc3..f3ac6f0 100644
--- a/chrome/browser/extensions/extension_page_actions_module.h
+++ b/chrome/browser/extensions/extension_page_actions_module.h
@@ -7,9 +7,20 @@
#include "chrome/browser/extensions/extension_function.h"
+class TabContents;
+class ExtensionAction;
+class ExtensionActionState;
+
class PageActionFunction : public SyncExtensionFunction {
protected:
bool SetPageActionEnabled(bool enable);
+
+ bool InitCommon(int tab_id);
+ bool SetHidden(bool hidden);
+
+ ExtensionAction* page_action_;
+ TabContents* contents_;
+ ExtensionActionState* state_;
};
class EnablePageActionFunction : public PageActionFunction {
@@ -22,4 +33,39 @@ class DisablePageActionFunction : public PageActionFunction {
DECLARE_EXTENSION_FUNCTION_NAME("pageActions.disableForTab")
};
+class PageActionShowFunction : public PageActionFunction {
+ virtual bool RunImpl();
+ DECLARE_EXTENSION_FUNCTION_NAME("pageAction.show")
+};
+
+class PageActionHideFunction : public PageActionFunction {
+ virtual bool RunImpl();
+ DECLARE_EXTENSION_FUNCTION_NAME("pageAction.hide")
+};
+
+class PageActionSetIconFunction : public PageActionFunction {
+ virtual bool RunImpl();
+ DECLARE_EXTENSION_FUNCTION_NAME("pageAction.setIcon")
+};
+
+class PageActionSetTitleFunction : public PageActionFunction {
+ virtual bool RunImpl();
+ DECLARE_EXTENSION_FUNCTION_NAME("pageAction.setTitle")
+};
+
+class PageActionSetBadgeBackgroundColorFunction : public PageActionFunction {
+ virtual bool RunImpl();
+ DECLARE_EXTENSION_FUNCTION_NAME("pageAction.setBadgeBackgroundColor")
+};
+
+class PageActionSetBadgeTextColorFunction : public PageActionFunction {
+ virtual bool RunImpl();
+ DECLARE_EXTENSION_FUNCTION_NAME("pageAction.setBadgeTextColor")
+};
+
+class PageActionSetBadgeTextFunction : public PageActionFunction {
+ virtual bool RunImpl();
+ DECLARE_EXTENSION_FUNCTION_NAME("pageAction.setBadgeText")
+};
+
#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_PAGE_ACTIONS_MODULE_H_
diff --git a/chrome/browser/extensions/extension_page_actions_module_constants.cc b/chrome/browser/extensions/extension_page_actions_module_constants.cc
index 48ceaae..8690c94 100644
--- a/chrome/browser/extensions/extension_page_actions_module_constants.cc
+++ b/chrome/browser/extensions/extension_page_actions_module_constants.cc
@@ -12,9 +12,4 @@ const wchar_t kTitleKey[] = L"title";
const wchar_t kIconIdKey[] = L"iconId";
const wchar_t kButtonKey[] = L"button";
-const char kNoExtensionError[] = "No extension with id: *.";
-const char kNoTabError[] = "No tab with id: *.";
-const char kNoPageActionError[] = "No PageAction with id: *.";
-const char kUrlNotActiveError[] = "This url is no longer active: *.";
-
} // namespace extension_page_actions_module_constants
diff --git a/chrome/browser/extensions/extension_page_actions_module_constants.h b/chrome/browser/extensions/extension_page_actions_module_constants.h
index 16556d6..5bb435e 100644
--- a/chrome/browser/extensions/extension_page_actions_module_constants.h
+++ b/chrome/browser/extensions/extension_page_actions_module_constants.h
@@ -16,12 +16,6 @@ extern const wchar_t kTitleKey[];
extern const wchar_t kIconIdKey[];
extern const wchar_t kButtonKey[];
-// Error messages.
-extern const char kNoExtensionError[];
-extern const char kNoTabError[];
-extern const char kNoPageActionError[];
-extern const char kUrlNotActiveError[];
-
}; // namespace extension_page_actions_module_constants
#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_PAGE_ACTIONS_MODULE_CONSTANTS_H_
diff --git a/chrome/browser/extensions/page_action_apitest.cc b/chrome/browser/extensions/page_action_apitest.cc
new file mode 100644
index 0000000..3996de7
--- /dev/null
+++ b/chrome/browser/extensions/page_action_apitest.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_window.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/extensions/extension_browser_event_router.h"
+#include "chrome/browser/extensions/extension_tabs_module.h"
+#include "chrome/browser/extensions/extensions_service.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/views/browser_actions_container.h"
+#include "chrome/browser/views/toolbar_view.h"
+#include "chrome/common/extensions/extension_action.h"
+#include "chrome/test/ui_test_utils.h"
+
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PageAction) {
+ StartHTTPServer();
+ ASSERT_TRUE(RunExtensionTest("page_action")) << message_;
+
+ ExtensionsService* service = browser()->profile()->GetExtensionsService();
+ ASSERT_EQ(1u, service->extensions()->size());
+ Extension* extension = service->extensions()->at(0);
+ ASSERT_TRUE(extension);
+
+ {
+ // Tell the extension to update the page action state.
+ ResultCatcher catcher;
+ ui_test_utils::NavigateToURL(browser(),
+ GURL(extension->GetResourceURL("update.html")));
+ ASSERT_TRUE(catcher.GetNextResult());
+ }
+
+ // Test that we received the changes.
+ const ExtensionActionState* action_state =
+ browser()->GetSelectedTabContents()->GetPageActionState(
+ extension->page_action());
+ ASSERT_TRUE(action_state);
+ EXPECT_EQ("Modified", action_state->title());
+ EXPECT_EQ("badge", action_state->badge_text());
+ EXPECT_EQ(SkColorSetARGB(255, 255, 255, 255),
+ action_state->badge_background_color());
+
+ {
+ // Simulate the page action being clicked.
+ ResultCatcher catcher;
+ int tab_id = ExtensionTabUtil::GetTabId(browser()->GetSelectedTabContents());
+ ExtensionBrowserEventRouter::GetInstance()->PageActionExecuted(
+ browser()->profile(), extension->id(), "", tab_id, "", 0);
+ EXPECT_TRUE(catcher.GetNextResult());
+ }
+
+ {
+ // Tell the extension to update the page action state again.
+ ResultCatcher catcher;
+ ui_test_utils::NavigateToURL(browser(),
+ GURL(extension->GetResourceURL("update2.html")));
+ ASSERT_TRUE(catcher.GetNextResult());
+ }
+
+ // Test that we received the changes.
+ action_state = browser()->GetSelectedTabContents()->GetPageActionState(
+ extension->page_action());
+ EXPECT_TRUE(action_state->icon());
+}
diff --git a/chrome/browser/gtk/location_bar_view_gtk.cc b/chrome/browser/gtk/location_bar_view_gtk.cc
index cd7aeefe..78bbd04 100644
--- a/chrome/browser/gtk/location_bar_view_gtk.cc
+++ b/chrome/browser/gtk/location_bar_view_gtk.cc
@@ -676,7 +676,9 @@ LocationBarViewGtk::PageActionViewGtk::PageActionViewGtk(
const ExtensionAction* page_action)
: owner_(owner),
profile_(profile),
- page_action_(page_action) {
+ page_action_(page_action),
+ last_icon_skbitmap_(NULL),
+ last_icon_pixbuf_(NULL) {
event_box_.Own(gtk_event_box_new());
// Make the event box not visible so it does not paint a background.
gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE);
@@ -709,6 +711,8 @@ LocationBarViewGtk::PageActionViewGtk::~PageActionViewGtk() {
if (pixbufs_[i])
g_object_unref(pixbufs_[i]);
}
+ if (last_icon_pixbuf_)
+ g_object_unref(last_icon_pixbuf_);
}
void LocationBarViewGtk::PageActionViewGtk::UpdateVisibility(
@@ -720,7 +724,7 @@ void LocationBarViewGtk::PageActionViewGtk::UpdateVisibility(
const ExtensionActionState* state =
contents->GetPageActionState(page_action_);
- bool visible = state != NULL;
+ bool visible = state && !state->hidden();
if (visible) {
// Set the tooltip.
if (state->title().empty())
@@ -728,14 +732,30 @@ void LocationBarViewGtk::PageActionViewGtk::UpdateVisibility(
page_action_->title().c_str());
else
gtk_widget_set_tooltip_text(event_box_.get(), state->title().c_str());
+
// Set the image.
- int index = state->icon_index();
- // The image index (if not within bounds) will be set to the first image.
- if (index < 0 || index >= static_cast<int>(pixbufs_.size()))
- index = 0;
+ SkBitmap* icon = state->icon();
+ GdkPixbuf* pixbuf = NULL;
+ if (icon) {
+ if (icon != last_icon_skbitmap_) {
+ if (last_icon_pixbuf_)
+ g_object_unref(last_icon_pixbuf_);
+ last_icon_skbitmap_ = icon;
+ last_icon_pixbuf_ = gfx::GdkPixbufFromSkBitmap(icon);
+ }
+ DCHECK(last_icon_pixbuf_);
+ pixbuf = last_icon_pixbuf_;
+ } else {
+ int index = state->icon_index();
+ // The image index (if not within bounds) will be set to the first image.
+ if (index < 0 || index >= static_cast<int>(pixbufs_.size()))
+ index = 0;
+ pixbuf = pixbufs_[index];
+ }
+
// The pixbuf might not be loaded yet.
- if (pixbufs_[index])
- gtk_image_set_from_pixbuf(GTK_IMAGE(image_.get()), pixbufs_[index]);
+ if (pixbuf)
+ gtk_image_set_from_pixbuf(GTK_IMAGE(image_.get()), pixbuf);
else
visible = false;
}
diff --git a/chrome/browser/gtk/location_bar_view_gtk.h b/chrome/browser/gtk/location_bar_view_gtk.h
index 3fe531a..32a5f9e 100644
--- a/chrome/browser/gtk/location_bar_view_gtk.h
+++ b/chrome/browser/gtk/location_bar_view_gtk.h
@@ -134,6 +134,12 @@ class LocationBarViewGtk : public AutocompleteEditController,
// The icons representing different states for the page action.
std::vector<GdkPixbuf*> pixbufs_;
+ // A cache of the last dynamically generated bitmap and the pixbuf that
+ // corresponds to it. We keep track of both so we can free old pixbufs as
+ // their icons are replaced.
+ SkBitmap* last_icon_skbitmap_;
+ GdkPixbuf* last_icon_pixbuf_;
+
// The object that is waiting for the image loading to complete
// asynchronously. It will delete itself once it is done.
ImageLoadingTracker* tracker_;
diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc
index a526207..43c7c27 100644
--- a/chrome/browser/tab_contents/tab_contents.cc
+++ b/chrome/browser/tab_contents/tab_contents.cc
@@ -609,26 +609,33 @@ void TabContents::SetPageActionEnabled(const ExtensionAction* page_action,
const std::string& title,
int icon_id) {
DCHECK(page_action);
-
- if (!enable &&
- enabled_page_actions_.end() == enabled_page_actions_.find(page_action)) {
- return; // Don't need to disable twice.
- }
-
- if (enable) {
- enabled_page_actions_[page_action].reset(
- new ExtensionActionState(title, icon_id));
- } else {
- enabled_page_actions_.erase(page_action);
- }
+ ExtensionActionState* state = GetOrCreatePageActionState(page_action);
+ state->set_hidden(!enable);
+ state->set_title(title);
+ state->set_icon_index(icon_id);
+ state->set_icon(NULL);
}
const ExtensionActionState* TabContents::GetPageActionState(
const ExtensionAction* page_action) {
- if (enabled_page_actions_.end() == enabled_page_actions_.find(page_action))
+ if (page_actions_.end() == page_actions_.find(page_action))
return NULL;
- return enabled_page_actions_[page_action].get();
+ return page_actions_[page_action].get();
+}
+
+ExtensionActionState* TabContents::GetOrCreatePageActionState(
+ const ExtensionAction* page_action) {
+ if (page_actions_.end() == page_actions_.find(page_action)) {
+ page_actions_[page_action].reset(
+ new ExtensionActionState(page_action->title(), 0));
+ }
+
+ return page_actions_[page_action].get();
+}
+
+void TabContents::PageActionStateChanged() {
+ NotifyNavigationStateChanged(TabContents::INVALIDATE_PAGE_ACTIONS);
}
void TabContents::NotifyNavigationStateChanged(unsigned changed_flags) {
@@ -1400,12 +1407,12 @@ void TabContents::DidNavigateMainFramePostCommit(
fav_icon_helper_.FetchFavIcon(details.entry->url());
// Disable all page actions, unless this is an in-page navigation.
- if (!enabled_page_actions_.empty()) {
+ if (!page_actions_.empty()) {
url_canon::Replacements<char> replacements;
replacements.ClearRef();
if (params.url.ReplaceComponents(replacements) !=
params.referrer.ReplaceComponents(replacements)) {
- enabled_page_actions_.clear();
+ page_actions_.clear();
}
}
@@ -2625,4 +2632,3 @@ void TabContents::Observe(NotificationType type,
void TabContents::set_encoding(const std::string& encoding) {
encoding_ = CharacterEncoding::GetCanonicalEncodingNameByAliasName(encoding);
}
-
diff --git a/chrome/browser/tab_contents/tab_contents.h b/chrome/browser/tab_contents/tab_contents.h
index 99db09c..47b4243 100644
--- a/chrome/browser/tab_contents/tab_contents.h
+++ b/chrome/browser/tab_contents/tab_contents.h
@@ -269,6 +269,16 @@ class TabContents : public PageNavigator,
const ExtensionActionState* GetPageActionState(
const ExtensionAction* page_action);
+ // Same as above, but creates an enable state if it doesn't exist. The return
+ // value can be updated. The caller should call PageActionStateChanged when
+ // done modifying the state.
+ ExtensionActionState* GetOrCreatePageActionState(
+ const ExtensionAction* page_action);
+
+ // Call this after updating a ExtensionActionState object returned by
+ // GetOrCreatePageActionState to notify clients about the changes.
+ void PageActionStateChanged();
+
// Whether the tab is in the process of being destroyed.
// Added as a tentative work-around for focus related bug #4633. This allows
// us not to store focus when a tab is being closed.
@@ -1111,12 +1121,13 @@ class TabContents : public PageNavigator,
// Data for Page Actions -----------------------------------------------------
- // A map of page actions that are enabled in this tab (and a state object
- // 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 ExtensionAction*, linked_ptr<ExtensionActionState> >
- enabled_page_actions_;
+ // A map of page actions that this tab knows about (and a state object that
+ // can be used to update the title, icon, visibilty, etc used for the page
+ // action). This map is cleared every time the mainframe navigates and
+ // populated by the PageAction extension API.
+ typedef std::map< const ExtensionAction*, linked_ptr<ExtensionActionState> >
+ PageActionStateMap;
+ PageActionStateMap page_actions_;
// Data for misc internal state ----------------------------------------------
diff --git a/chrome/browser/views/location_bar_view.cc b/chrome/browser/views/location_bar_view.cc
index 324cf60..437862f 100644
--- a/chrome/browser/views/location_bar_view.cc
+++ b/chrome/browser/views/location_bar_view.cc
@@ -1245,19 +1245,24 @@ void LocationBarView::PageActionImageView::UpdateVisibility(
const ExtensionActionState* state =
contents->GetPageActionState(page_action_);
- bool visible = state != NULL;
+ bool visible = state && !state->hidden();
if (visible) {
// Set the tooltip.
if (state->title().empty())
tooltip_ = page_action_->title();
else
tooltip_ = state->title();
+
// Set the image.
- int index = state->icon_index();
- // The image index (if not within bounds) will be set to the first image.
- if (index < 0 || index >= static_cast<int>(page_action_icons_.size()))
- index = 0;
- ImageView::SetImage(page_action_icons_[index]);
+ SkBitmap* icon = state->icon();
+ if (!icon) {
+ int index = state->icon_index();
+ // The image index (if not within bounds) will be set to the first image.
+ if (index < 0 || index >= static_cast<int>(page_action_icons_.size()))
+ index = 0;
+ icon = &page_action_icons_[index];
+ }
+ ImageView::SetImage(icon);
}
SetVisible(visible);
}
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index ec353d7..6f541b19 100755
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -87,6 +87,7 @@
'browser/extensions/extension_storage_apitest.cc',
'browser/extensions/extension_tabs_apitest.cc',
'browser/extensions/extension_i18n_apitest.cc',
+ 'browser/extensions/page_action_apitest.cc',
'browser/views/browser_views_accessibility_browsertest.cc',
'browser/views/find_bar_host_browsertest.cc',
# TODO(jam): http://crbug.com/15101 These tests fail on Linux and Mac.
diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json
index 7543b4f..53480d98 100755
--- a/chrome/common/extensions/api/extension_api.json
+++ b/chrome/common/extensions/api/extension_api.json
@@ -820,6 +820,146 @@
]
},
{
+ "namespace": "pageAction",
+ "types": [],
+ "functions": [
+ {
+ "name": "show",
+ "type": "function",
+ "description": "Show the page action. The page action is shown whenever the tab is selected.",
+ "parameters": [
+ {"type": "integer", "name": "tabId", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."}
+ ]
+ },
+ {
+ "name": "hide",
+ "type": "function",
+ "description": "Hide the page action.",
+ "parameters": [
+ {"type": "integer", "name": "tabId", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."}
+ ]
+ },
+ {
+ "name": "setTitle",
+ "type": "function",
+ "description": "Set the title of the page action. This is displayed in a tooltip over the page action.",
+ "parameters": [
+ {
+ "name": "details",
+ "type": "object",
+ "properties": {
+ "tabId": {"type": "integer", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."},
+ "title": {"type": "string", "description": "The tooltip string."}
+ }
+ }
+ ]
+ },
+ {
+ "name": "setIcon",
+ "type": "function",
+ "description": "Sets the icon for the page action. The icon can be specified either as the index of one of the icons that was pre-specified in the manifest, or as the pixel data from a Canvas element. Either the iconIndex or the imageData property must be specified.",
+ "parameters": [
+ {
+ "name": "details",
+ "type": "object",
+ "properties": {
+ "tabId": {"type": "integer", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."},
+ "imageData": {
+ "type": "any",
+ "description": "Pixel data for an image. Must be an ImageData object (eg from a <code>canvas</code> element).",
+ "optional": true
+ },
+ "iconIndex": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "The zero-based index into the |icons| vector specified in the manifest.",
+ "optional": true
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "setBadgeText",
+ "type": "function",
+ "description": "Sets the badge text for the page action. This is printed on top of the icon.",
+ "parameters": [
+ {
+ "name": "details",
+ "type": "object",
+ "properties": {
+ "tabId": {"type": "integer", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."},
+ "text": {"type": "string", "description": "Any number of characters can be passed, but only about four can fit in the space."}
+ }
+ }
+ ]
+ },
+ {
+ "name": "setBadgeTextColor",
+ "type": "function",
+ "description": "Sets the text color for the badge.",
+ "parameters": [
+ {
+ "name": "details",
+ "type": "object",
+ "properties": {
+ "tabId": {"type": "integer", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."},
+ "color": {
+ "type": "array",
+ "description": "An array of four integers in the range [0,255] that make up the ARGB color for the text of the badge.",
+ "items": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 255
+ },
+ "minItems": 4,
+ "maxItems": 4
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "setBadgeBackgroundColor",
+ "type": "function",
+ "description": "Sets the background color for the badge.",
+ "parameters": [
+ {
+ "name": "details",
+ "type": "object",
+ "properties": {
+ "tabId": {"type": "integer", "minimum": 0, "description": "The id of the tab for which you want to modify the page action."},
+ "color": {
+ "type": "array",
+ "description": "An array of four integers in the range [0,255] that make up the ARGB color for the text of the badge.",
+ "items": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 255
+ },
+ "minItems": 4,
+ "maxItems": 4
+ }
+ }
+ }
+ ]
+ }
+ ],
+ "events": [
+ {
+ "name": "onClicked",
+ "type": "function",
+ "description": "Fired when a page action button is clicked.",
+ "parameters": [
+ {
+ "name": "tab",
+ "$ref": "Tab"
+ }
+ ]
+ }
+ ]
+ },
+ {
"namespace": "browserAction",
"types": [],
"functions": [
diff --git a/chrome/common/extensions/docs/pageAction.html b/chrome/common/extensions/docs/pageAction.html
new file mode 100755
index 0000000..2b71b44
--- /dev/null
+++ b/chrome/common/extensions/docs/pageAction.html
@@ -0,0 +1,1476 @@
+<!DOCTYPE html><!-- This page is a placeholder for generated extensions api doc. Note:
+ 1) The <head> information in this page is significant, should be uniform
+ across api docs and should be edited only with knowledge of the
+ templating mechanism.
+ 3) All <body>.innerHTML is genereated as an rendering step. If viewed in a
+ browser, it will be re-generated from the template, json schema and
+ authored overview content.
+ 4) The <body>.innerHTML is also generated by an offline step so that this
+ page may easily be indexed by search engines.
+--><html xmlns="http://www.w3.org/1999/xhtml"><head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <link href="css/ApiRefStyles.css" rel="stylesheet" type="text/css">
+ <script type="text/javascript" src="../../../third_party/jstemplate/jstemplate_compiled.js">
+ </script>
+ <script type="text/javascript" src="js/api_page_generator.js"></script>
+ <script type="text/javascript" src="js/bootstrap.js"></script>
+ <title>chrome.pageAction</title></head><body> <div id="container">
+ <!-- SUBTEMPLATES: DO NOT MOVE FROM THIS LOCATION -->
+ <!-- In particular, sub-templates that recurse, must be used by allowing
+ jstemplate to make a copy of the template in this section which
+ are not operated on by way of the jsskip="true" -->
+ <div style="display:none">
+
+ <!-- VALUE -->
+ <div id="valueTemplate">
+ <dt>
+ <var>paramName</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span id="typeTemplate">
+ <span>
+ <a> Type</a>
+ </span>
+ <span>
+ <span>
+ array of <span><span></span></span>
+ </span>
+ <span>paramType</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd>
+ Description of this parameter from the json schema.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd>
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div> <!-- /VALUE -->
+
+ </div> <!-- /SUBTEMPLATES -->
+
+ <a name="top"> </a>
+ <!-- API HEADER -->
+ <div id="pageHeader">
+ <div id="searchbox">
+ <form action="http://www.google.com/cse" id="cse-search-box">
+ <div>
+ <input type="hidden" name="cx" value="002967670403910741006:61_cvzfqtno">
+ <input type="hidden" name="ie" value="UTF-8">
+ <input type="text" name="q" size="31">
+ <input type="submit" name="sa" value="Search">
+ </div>
+ </form>
+
+ <script type="text/javascript" src="http://www.google.com/jsapi"></script>
+ <script type="text/javascript">google.load("elements", "1", {packages: "transliteration"});</script>
+ <script type="text/javascript" src="http://www.google.com/coop/cse/t13n?form=cse-search-box&amp;t13n_langs=en"></script>
+ <script type="text/javascript" src="http://www.google.com/coop/cse/brand?form=cse-search-box&amp;lang=en"></script>
+ </div>
+ <div id="pageTitle">
+ <h1>chrome.pageAction</h1>
+ </div>
+ </div> <!-- /pageHeader -->
+
+ <div id="pageContent">
+ <!-- SIDENAV -->
+ <div id="leftNav">
+ <ul>
+ <li> <a href="index.html">Home</a></li>
+ <li> <a href="getstarted.html">Getting Started</a></li>
+ <li> <a href="overview.html">Overview</a></li>
+ <li> <a href="devguide.html"><div>Developer's Guide</div></a>
+ <ul>
+ <li><a href="toolstrip.html">Toolstrips</a></li>
+ <li><a href="pageActions.html">Page Actions</a></li>
+ <li><a href="background_pages.html">Background Pages</a></li>
+ <li><a href="content_scripts.html">Content Scripts</a></li>
+ <li><a href="events.html">Events</a></li>
+ <li><a href="tabs.html">Tabs</a></li>
+ <li><a href="windows.html">Windows</a></li>
+ <li><a href="bookmarks.html">Bookmarks</a></li>
+ <li><a href="themes.html">Themes</a></li>
+ <li><a href="npapi.html">NPAPI Plugins</a></li>
+ <li><a href="xhr.html">Cross-Origin XHR</a></li>
+ <li><a href="packaging.html">Packaging</a></li>
+ <li><a href="autoupdate.html">Autoupdate</a></li>
+ </ul>
+ </li>
+ <li><a href="tutorials.html"><div>Tutorials</div></a>
+ <ul>
+ <li><a href="tut_debugging.html">Debugging</a></li>
+ </ul>
+ </li>
+ <li>Reference
+ <ul>
+ <li> Formats
+ <ul>
+ <li><a href="manifest.html">Manifest Files</a></li>
+ <li><a href="match_patterns.html">Match Patterns</a></li>
+ <!-- <li>Packages (.crx)</li> -->
+ </ul>
+ </li>
+ <li> <a href="api_index.html">chrome.* APIs</a> </li>
+ <li> <a href="api_other.html">Other APIs</a> </li>
+ </ul>
+ </li>
+ <li><a href="http://dev.chromium.org/developers/design-documents/extensions/samples">Samples</a></li>
+ </ul>
+ </div>
+
+ <div id="mainColumn">
+ <!-- TABLE OF CONTENTS -->
+ <div id="toc">
+ <p>Contents</p>
+ <ol>
+ <li jsinstance="*0" style="display: none; ">
+ <a>h2Name</a>
+ <ol>
+ <li>
+ <a>h3Name</a>
+ </li>
+ </ol>
+ </li>
+ <div>
+ <li>
+ <a href="#apiReference">API reference: chrome.pageAction</a>
+ <ol>
+ <li style="display: none; ">
+ <a href="#properties">Properties</a>
+ <ol>
+ <li>
+ <a href="#property-anchor">propertyName</a>
+ </li>
+ </ol>
+ </li>
+ <li>
+ <a href="#methods">Methods</a>
+ <ol>
+ <li jsinstance="0">
+ <a href="#method-hide">hide</a>
+ </li><li jsinstance="1">
+ <a href="#method-setBadgeBackgroundColor">setBadgeBackgroundColor</a>
+ </li><li jsinstance="2">
+ <a href="#method-setBadgeText">setBadgeText</a>
+ </li><li jsinstance="3">
+ <a href="#method-setBadgeTextColor">setBadgeTextColor</a>
+ </li><li jsinstance="4">
+ <a href="#method-setIcon">setIcon</a>
+ </li><li jsinstance="5">
+ <a href="#method-setTitle">setTitle</a>
+ </li><li jsinstance="*6">
+ <a href="#method-show">show</a>
+ </li>
+ </ol>
+ </li>
+ <li>
+ <a href="#events">Events</a>
+ <ol>
+ <li jsinstance="*0">
+ <a href="#event-onClicked">onClicked</a>
+ </li>
+ </ol>
+ </li>
+ <li style="display: none; ">
+ <a href="#types">Types</a>
+ <ol>
+ <li>
+ <a href="#id-anchor">id</a>
+ </li>
+ </ol>
+ </li>
+ </ol>
+ </li>
+ </div>
+ </ol>
+ </div>
+ <!-- /TABLE OF CONTENTS -->
+
+ <!-- STATIC CONTENT PLACEHOLDER -->
+ <div id="static"></div>
+
+ <!-- API PAGE -->
+ <div class="apiPage">
+ <a name="apiReference"></a>
+ <h2>API reference: chrome.pageAction</h2>
+
+ <!-- PROPERTIES -->
+ <div class="apiGroup" style="display: none; ">
+ <a name="properties"></a>
+ <h3 id="properties">Properties</h3>
+
+ <div>
+ <a></a>
+ <h4>getLastError</h4>
+ <div class="summary">
+ <!-- Note: intentionally longer 80 columns -->
+ <span>chrome.extension</span><span>lastError</span>
+ </div>
+ <div>
+ </div>
+ </div>
+
+ </div> <!-- /apiGroup -->
+
+ <!-- METHODS -->
+ <div class="apiGroup" id="methods">
+ <a name="methods"></a>
+ <h3>Methods</h3>
+
+ <!-- iterates over all functions -->
+ <div class="apiItem" jsinstance="0">
+ <a name="method-hide"></a> <!-- method-anchor -->
+ <h4>hide</h4>
+
+ <div class="summary"><span style="display: none; ">void</span>
+ <!-- Note: intentionally longer 80 columns -->
+ <span>chrome.pageAction.hide</span>(<span jsinstance="*0" class="null"><span style="display: none; ">, </span><span>integer</span>
+ <var><span>tabId</span></var></span>)</div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Hide the page action.</p>
+
+ <!-- PARAMETERS -->
+ <h4>Parameters</h4>
+ <dl>
+ <div jsinstance="*0">
+ <div>
+ <dt>
+ <var>tabId</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>integer</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>The id of the tab for which you want to modify the page action.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+
+ <!-- RETURNS -->
+ <h4 style="display: none; ">Returns</h4>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+
+ <!-- CALLBACK -->
+ <div style="display: none; ">
+ <div>
+ <h4>Callback function</h4>
+ <p>
+ If you specify the <em>callback</em> parameter,
+ it should specify a function that looks like this:
+ </p>
+
+ <!-- Note: intentionally longer 80 columns -->
+ <pre>function(<span>Type param1, Type param2</span>) <span class="subdued">{...}</span>);</pre>
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </div>
+ </div>
+
+ </div> <!-- /description -->
+
+ </div><div class="apiItem" jsinstance="1">
+ <a name="method-setBadgeBackgroundColor"></a> <!-- method-anchor -->
+ <h4>setBadgeBackgroundColor</h4>
+
+ <div class="summary"><span style="display: none; ">void</span>
+ <!-- Note: intentionally longer 80 columns -->
+ <span>chrome.pageAction.setBadgeBackgroundColor</span>(<span jsinstance="*0" class="null"><span style="display: none; ">, </span><span>object</span>
+ <var><span>details</span></var></span>)</div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Sets the background color for the badge.</p>
+
+ <!-- PARAMETERS -->
+ <h4>Parameters</h4>
+ <dl>
+ <div jsinstance="*0">
+ <div>
+ <dt>
+ <var>details</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>object</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd>
+ <dl>
+ <div jsinstance="0">
+ <div>
+ <dt>
+ <var>tabId</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>integer</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>The id of the tab for which you want to modify the page action.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div><div jsinstance="*1">
+ <div>
+ <dt>
+ <var>color</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span>
+ array of <span><span>
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>integer</span>
+ </span>
+ </span></span>
+ </span>
+ <span style="display: none; ">paramType</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>An array of four integers in the range [0,255] that make up the ARGB color for the text of the badge.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+
+ <!-- RETURNS -->
+ <h4 style="display: none; ">Returns</h4>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+
+ <!-- CALLBACK -->
+ <div style="display: none; ">
+ <div>
+ <h4>Callback function</h4>
+ <p>
+ If you specify the <em>callback</em> parameter,
+ it should specify a function that looks like this:
+ </p>
+
+ <!-- Note: intentionally longer 80 columns -->
+ <pre>function(<span>Type param1, Type param2</span>) <span class="subdued">{...}</span>);</pre>
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </div>
+ </div>
+
+ </div> <!-- /description -->
+
+ </div><div class="apiItem" jsinstance="2">
+ <a name="method-setBadgeText"></a> <!-- method-anchor -->
+ <h4>setBadgeText</h4>
+
+ <div class="summary"><span style="display: none; ">void</span>
+ <!-- Note: intentionally longer 80 columns -->
+ <span>chrome.pageAction.setBadgeText</span>(<span jsinstance="*0" class="null"><span style="display: none; ">, </span><span>object</span>
+ <var><span>details</span></var></span>)</div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Sets the badge text for the page action. This is printed on top of the icon.</p>
+
+ <!-- PARAMETERS -->
+ <h4>Parameters</h4>
+ <dl>
+ <div jsinstance="*0">
+ <div>
+ <dt>
+ <var>details</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>object</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd>
+ <dl>
+ <div jsinstance="0">
+ <div>
+ <dt>
+ <var>tabId</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>integer</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>The id of the tab for which you want to modify the page action.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div><div jsinstance="*1">
+ <div>
+ <dt>
+ <var>text</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>string</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>Any number of characters can be passed, but only about four can fit in the space.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+
+ <!-- RETURNS -->
+ <h4 style="display: none; ">Returns</h4>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+
+ <!-- CALLBACK -->
+ <div style="display: none; ">
+ <div>
+ <h4>Callback function</h4>
+ <p>
+ If you specify the <em>callback</em> parameter,
+ it should specify a function that looks like this:
+ </p>
+
+ <!-- Note: intentionally longer 80 columns -->
+ <pre>function(<span>Type param1, Type param2</span>) <span class="subdued">{...}</span>);</pre>
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </div>
+ </div>
+
+ </div> <!-- /description -->
+
+ </div><div class="apiItem" jsinstance="3">
+ <a name="method-setBadgeTextColor"></a> <!-- method-anchor -->
+ <h4>setBadgeTextColor</h4>
+
+ <div class="summary"><span style="display: none; ">void</span>
+ <!-- Note: intentionally longer 80 columns -->
+ <span>chrome.pageAction.setBadgeTextColor</span>(<span jsinstance="*0" class="null"><span style="display: none; ">, </span><span>object</span>
+ <var><span>details</span></var></span>)</div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Sets the text color for the badge.</p>
+
+ <!-- PARAMETERS -->
+ <h4>Parameters</h4>
+ <dl>
+ <div jsinstance="*0">
+ <div>
+ <dt>
+ <var>details</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>object</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd>
+ <dl>
+ <div jsinstance="0">
+ <div>
+ <dt>
+ <var>tabId</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>integer</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>The id of the tab for which you want to modify the page action.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div><div jsinstance="*1">
+ <div>
+ <dt>
+ <var>color</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span>
+ array of <span><span>
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>integer</span>
+ </span>
+ </span></span>
+ </span>
+ <span style="display: none; ">paramType</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>An array of four integers in the range [0,255] that make up the ARGB color for the text of the badge.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+
+ <!-- RETURNS -->
+ <h4 style="display: none; ">Returns</h4>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+
+ <!-- CALLBACK -->
+ <div style="display: none; ">
+ <div>
+ <h4>Callback function</h4>
+ <p>
+ If you specify the <em>callback</em> parameter,
+ it should specify a function that looks like this:
+ </p>
+
+ <!-- Note: intentionally longer 80 columns -->
+ <pre>function(<span>Type param1, Type param2</span>) <span class="subdued">{...}</span>);</pre>
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </div>
+ </div>
+
+ </div> <!-- /description -->
+
+ </div><div class="apiItem" jsinstance="4">
+ <a name="method-setIcon"></a> <!-- method-anchor -->
+ <h4>setIcon</h4>
+
+ <div class="summary"><span style="display: none; ">void</span>
+ <!-- Note: intentionally longer 80 columns -->
+ <span>chrome.pageAction.setIcon</span>(<span jsinstance="*0" class="null"><span style="display: none; ">, </span><span>object</span>
+ <var><span>details</span></var></span>)</div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Sets the icon for the page action. The icon can be specified either as the index of one of the icons that was pre-specified in the manifest, or as the pixel data from a Canvas element. Either the iconIndex or the imageData property must be specified.</p>
+
+ <!-- PARAMETERS -->
+ <h4>Parameters</h4>
+ <dl>
+ <div jsinstance="*0">
+ <div>
+ <dt>
+ <var>details</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>object</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd>
+ <dl>
+ <div jsinstance="0">
+ <div>
+ <dt>
+ <var>tabId</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>integer</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>The id of the tab for which you want to modify the page action.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div><div jsinstance="1">
+ <div>
+ <dt>
+ <var>imageData</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>any</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>Pixel data for an image. Must be an ImageData object (eg from a <code>canvas</code> element).</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div><div jsinstance="*2">
+ <div>
+ <dt>
+ <var>iconIndex</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>integer</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>The zero-based index into the |icons| vector specified in the manifest.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+
+ <!-- RETURNS -->
+ <h4 style="display: none; ">Returns</h4>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+
+ <!-- CALLBACK -->
+ <div style="display: none; ">
+ <div>
+ <h4>Callback function</h4>
+ <p>
+ If you specify the <em>callback</em> parameter,
+ it should specify a function that looks like this:
+ </p>
+
+ <!-- Note: intentionally longer 80 columns -->
+ <pre>function(<span>Type param1, Type param2</span>) <span class="subdued">{...}</span>);</pre>
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </div>
+ </div>
+
+ </div> <!-- /description -->
+
+ </div><div class="apiItem" jsinstance="5">
+ <a name="method-setTitle"></a> <!-- method-anchor -->
+ <h4>setTitle</h4>
+
+ <div class="summary"><span style="display: none; ">void</span>
+ <!-- Note: intentionally longer 80 columns -->
+ <span>chrome.pageAction.setTitle</span>(<span jsinstance="*0" class="null"><span style="display: none; ">, </span><span>object</span>
+ <var><span>details</span></var></span>)</div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Set the title of the page action. This is displayed in a tooltip over the page action.</p>
+
+ <!-- PARAMETERS -->
+ <h4>Parameters</h4>
+ <dl>
+ <div jsinstance="*0">
+ <div>
+ <dt>
+ <var>details</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>object</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd>
+ <dl>
+ <div jsinstance="0">
+ <div>
+ <dt>
+ <var>tabId</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>integer</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>The id of the tab for which you want to modify the page action.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div><div jsinstance="*1">
+ <div>
+ <dt>
+ <var>title</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>string</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>The tooltip string.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+
+ <!-- RETURNS -->
+ <h4 style="display: none; ">Returns</h4>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+
+ <!-- CALLBACK -->
+ <div style="display: none; ">
+ <div>
+ <h4>Callback function</h4>
+ <p>
+ If you specify the <em>callback</em> parameter,
+ it should specify a function that looks like this:
+ </p>
+
+ <!-- Note: intentionally longer 80 columns -->
+ <pre>function(<span>Type param1, Type param2</span>) <span class="subdued">{...}</span>);</pre>
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </div>
+ </div>
+
+ </div> <!-- /description -->
+
+ </div><div class="apiItem" jsinstance="*6">
+ <a name="method-show"></a> <!-- method-anchor -->
+ <h4>show</h4>
+
+ <div class="summary"><span style="display: none; ">void</span>
+ <!-- Note: intentionally longer 80 columns -->
+ <span>chrome.pageAction.show</span>(<span jsinstance="*0" class="null"><span style="display: none; ">, </span><span>integer</span>
+ <var><span>tabId</span></var></span>)</div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Show the page action. The page action is shown whenever the tab is selected.</p>
+
+ <!-- PARAMETERS -->
+ <h4>Parameters</h4>
+ <dl>
+ <div jsinstance="*0">
+ <div>
+ <dt>
+ <var>tabId</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>integer</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>The id of the tab for which you want to modify the page action.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+
+ <!-- RETURNS -->
+ <h4 style="display: none; ">Returns</h4>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+
+ <!-- CALLBACK -->
+ <div style="display: none; ">
+ <div>
+ <h4>Callback function</h4>
+ <p>
+ If you specify the <em>callback</em> parameter,
+ it should specify a function that looks like this:
+ </p>
+
+ <!-- Note: intentionally longer 80 columns -->
+ <pre>function(<span>Type param1, Type param2</span>) <span class="subdued">{...}</span>);</pre>
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </div>
+ </div>
+
+ </div> <!-- /description -->
+
+ </div> <!-- /apiItem -->
+
+ </div> <!-- /apiGroup -->
+
+ <!-- EVENTS -->
+ <div class="apiGroup">
+ <a name="events"></a>
+ <h3 id="events">Events</h3>
+
+ <!-- iterates over all events -->
+ <div class="apiItem" jsinstance="*0">
+ <a name="event-onClicked"></a>
+ <h4>onClicked</h4>
+
+ <div class="summary">
+ <!-- Note: intentionally longer 80 columns -->
+ <span class="subdued">chrome.pageAction.</span><span>onClicked</span><span class="subdued">.addListener</span>(function(<span>Tab tab</span>) <span class="subdued">{...}</span>);
+ </div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Fired when a page action button is clicked.</p>
+
+ <!-- PARAMETERS -->
+ <h4>Parameters</h4>
+ <dl>
+ <div jsinstance="*0">
+ <div>
+ <dt>
+ <var>tab</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span>
+ <a href="tabs.html#type-Tab">Tab</a>
+ </span>
+ <span style="display: none; ">
+ <span>
+ array of <span><span></span></span>
+ </span>
+ <span>paramType</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+
+ </div> <!-- /decription -->
+
+ </div> <!-- /apiItem -->
+
+ </div> <!-- /apiGroup -->
+
+ <!-- TYPES -->
+ <div class="apiGroup" style="display: none; ">
+ <a name="types.sort(sortByName)"></a>
+ <h3 id="types">Types</h3>
+
+ <!-- iterates over all types -->
+ <div class="apiItem">
+ <a></a>
+ <h4>type name</h4>
+
+ <div>
+ </div>
+
+ </div> <!-- /apiItem -->
+
+ </div> <!-- /apiGroup -->
+
+ </div> <!-- /apiPage -->
+ </div> <!-- /mainColumn -->
+ </div> <!-- /pageContent -->
+ <div id="pageFooter" --="">
+ <p>
+ Except as otherwise <a href="http://code.google.com/policies.html#restrictions">noted</a>,
+ the content of this page is licensed under the <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons
+ Attribution 3.0 License</a>, and code samples are licensed under the
+ <a rel="license" href="http://code.google.com/google_bsd_license.html">BSD License</a>.
+ </p>
+ <p>
+ ©2009 Google
+ </p>
+
+<!-- begin analytics -->
+<script src="http://www.google-analytics.com/urchin.js" type="text/javascript"></script>
+<script src="http://www.google-analytics.com/ga.js" type="text/javascript"></script>
+
+<script type="text/javascript">
+ // chrome doc tracking
+ try {
+ var engdocs = _gat._getTracker("YT-10763712-2");
+ engdocs._trackPageview();
+ } catch(err) {}
+
+ // code.google.com site-wide tracking
+ try {
+ _uacct="UA-18071-1";
+ _uanchor=1;
+ _uff=0;
+ urchinTracker();
+ }
+ catch(e) {/* urchinTracker not available. */}
+</script>
+<!-- end analytics -->
+
+ </div> <!-- /pageFooter -->
+ </div> <!-- /container -->
+</body></html>
+
diff --git a/chrome/common/extensions/extension_action.h b/chrome/common/extensions/extension_action.h
index 31fe0e7..6decdd4 100644
--- a/chrome/common/extensions/extension_action.h
+++ b/chrome/common/extensions/extension_action.h
@@ -83,7 +83,7 @@ typedef std::map<std::string, ExtensionAction*> ExtensionActionMap;
class ExtensionActionState {
public:
ExtensionActionState(std::string title, int icon_index)
- : title_(title), icon_index_(icon_index),
+ : hidden_(false), title_(title), icon_index_(icon_index),
badge_background_color_(SkColorSetARGB(255, 218, 0, 24)) {
}
@@ -108,9 +108,15 @@ class ExtensionActionState {
SkBitmap* icon() const { return icon_.get(); }
void set_icon(SkBitmap* icon) { icon_.reset(icon); }
+ bool hidden() const { return hidden_; }
+ void set_hidden(bool hidden) { hidden_ = hidden; }
+
void PaintBadge(gfx::Canvas* canvas, const gfx::Rect& bounds);
private:
+ // True if the action is in the hidden state.
+ bool hidden_;
+
// The title text to use for tooltips and labels.
std::string title_;
diff --git a/chrome/renderer/extensions/extension_process_bindings.cc b/chrome/renderer/extensions/extension_process_bindings.cc
index 801fb9d..fffd844 100644
--- a/chrome/renderer/extensions/extension_process_bindings.cc
+++ b/chrome/renderer/extensions/extension_process_bindings.cc
@@ -211,8 +211,8 @@ class ExtensionImpl : public ExtensionBase {
return v8::FunctionTemplate::New(GetRenderViewId);
} else if (name->Equals(v8::String::New("GetL10nMessage"))) {
return v8::FunctionTemplate::New(GetL10nMessage);
- } else if (name->Equals(v8::String::New("SetBrowserActionIcon"))) {
- return v8::FunctionTemplate::New(SetBrowserActionIcon);
+ } else if (name->Equals(v8::String::New("SetExtensionActionIcon"))) {
+ return v8::FunctionTemplate::New(SetExtensionActionIcon);
}
return ExtensionBase::GetNativeFunction(name);
@@ -407,11 +407,12 @@ class ExtensionImpl : public ExtensionBase {
return StartRequestCommon(args, value_args);
}
- // A special request for setting the browser action icon. This function
+ // A special request for setting the extension action icon. This function
// accepts a canvas ImageData object, so it needs to do extra processing
// before sending the request to the browser.
- static v8::Handle<v8::Value> SetBrowserActionIcon(const v8::Arguments& args) {
+ 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 =
@@ -421,8 +422,7 @@ class ExtensionImpl : public ExtensionBase {
int data_length = data->Get(v8::String::New("length"))->Int32Value();
if (data_length != 4 * width * height) {
- NOTREACHED() <<
- "Invalid argument to browserAction.setIcon. Expecting ImageData.";
+ NOTREACHED() << "Invalid argument to setIcon. Expecting ImageData.";
return v8::Undefined();
}
@@ -447,7 +447,11 @@ class ExtensionImpl : public ExtensionBase {
Value* bitmap_value = BinaryValue::CreateWithCopiedBuffer(
static_cast<const char*>(bitmap_pickle.data()), bitmap_pickle.size());
- return StartRequestCommon(args, bitmap_value);
+ DictionaryValue* dict = new DictionaryValue();
+ dict->Set(L"imageData", bitmap_value);
+ dict->SetInteger(L"tabId", tab_id);
+
+ return StartRequestCommon(args, dict);
}
static v8::Handle<v8::Value> GetRenderViewId(const v8::Arguments& args) {
diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js
index 2941644..dc81f67 100644
--- a/chrome/renderer/resources/extension_process_bindings.js
+++ b/chrome/renderer/resources/extension_process_bindings.js
@@ -16,7 +16,7 @@ var chrome = chrome || {};
native function OpenChannelToTab();
native function GetRenderViewId();
native function GetL10nMessage();
- native function SetBrowserActionIcon();
+ native function SetExtensionActionIcon();
if (!chrome)
chrome = {};
@@ -177,7 +177,7 @@ var chrome = chrome || {};
var request = prepareRequest(args, argSchemas);
var requestId = GetNextRequestId();
requests[requestId] = request;
- return nativeFunction(functionName, args, requestId,
+ return nativeFunction(functionName, request.args, requestId,
request.callback ? true : false);
}
@@ -202,12 +202,12 @@ var chrome = chrome || {};
chrome.pageAction.onClicked = new chrome.Event(eventName);
}
- // Browser action events send {windowpId}.
- function setupBrowserActionEvent(extensionId) {
- var eventName = "browserAction/" + extensionId;
- chrome.browserAction = chrome.browserAction || {};
- chrome.browserAction.onClicked = new chrome.Event(eventName);
- }
+ // 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 || {};
@@ -333,9 +333,9 @@ var chrome = chrome || {};
return GetL10nMessage(message_name, placeholders);
}
- apiFunctions["browserAction.setIcon"].handleRequest = function(details) {
+ function setIconCommon(details, name, parameters) {
if ("iconIndex" in details) {
- sendRequest(this.name, arguments, this.definition.parameters);
+ sendRequest(name, [details], parameters);
} else if ("imageData" in details) {
// Verify that this at least looks like an ImageData element.
// Unfortunately, we cannot use instanceof because the ImageData
@@ -349,15 +349,21 @@ var chrome = chrome || {};
throw new Error(
"The imageData property must contain an ImageData object.");
}
-
- sendCustomRequest(SetBrowserActionIcon, "browserAction.setIcon",
- details, this.definition.parameters);
+ sendCustomRequest(SetExtensionActionIcon, name, [details], parameters);
} else {
throw new Error(
"Either the iconIndex or imageData property must be specified.");
}
}
+ apiFunctions["browserAction.setIcon"].handleRequest = function(details) {
+ setIconCommon(details, this.name, this.definition.parameters);
+ };
+
+ apiFunctions["pageAction.setIcon"].handleRequest = function(details) {
+ setIconCommon(details, this.name, this.definition.parameters);
+ };
+
setupBrowserActionEvent(extensionId);
setupPageActionEvents(extensionId);
setupToolstripEvents(GetRenderViewId());
diff --git a/chrome/test/data/extensions/api_test/browser_action/manifest.json b/chrome/test/data/extensions/api_test/browser_action/manifest.json
index 750c5906..9604839 100755
--- a/chrome/test/data/extensions/api_test/browser_action/manifest.json
+++ b/chrome/test/data/extensions/api_test/browser_action/manifest.json
@@ -6,7 +6,7 @@
"tabs", "http://*/*"
],
"browser_action": {
- "name": "Make this page red",
- "icons": ["icon.png", "icon2.png"]
+ "default_title": "Make this page red",
+ "default_icon": "icon.png"
}
-} \ No newline at end of file
+}
diff --git a/chrome/test/data/extensions/api_test/page_action/background.html b/chrome/test/data/extensions/api_test/page_action/background.html
new file mode 100755
index 0000000..89e8f6f
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/page_action/background.html
@@ -0,0 +1,12 @@
+<html>
+<head>
+<script>
+ // Called when the user clicks on the browser action.
+ chrome.pageAction.onClicked.addListener(function(windowId) {
+ chrome.test.notifyPass();
+ });
+
+ chrome.test.notifyPass();
+</script>
+</head>
+</html>
diff --git a/chrome/test/data/extensions/api_test/page_action/icon.png b/chrome/test/data/extensions/api_test/page_action/icon.png
new file mode 100755
index 0000000..9a79a46
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/page_action/icon.png
Binary files differ
diff --git a/chrome/test/data/extensions/api_test/page_action/manifest.json b/chrome/test/data/extensions/api_test/page_action/manifest.json
new file mode 100755
index 0000000..e020f59
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/page_action/manifest.json
@@ -0,0 +1,12 @@
+{
+ "name": "A page action with no icon that makes the page red",
+ "version": "1.0",
+ "background_page": "background.html",
+ "permissions": [
+ "tabs", "http://*/*"
+ ],
+ "page_action": {
+ "default_title": "Make this page red",
+ "default_icon": "icon.png"
+ }
+}
diff --git a/chrome/test/data/extensions/api_test/page_action/update.html b/chrome/test/data/extensions/api_test/page_action/update.html
new file mode 100755
index 0000000..ba38b7f
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/page_action/update.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+<script>
+ // Test that we can change various properties of the browser action.
+ // The C++ verifies.
+ chrome.tabs.getSelected(null, function(tab) {
+ chrome.pageAction.show(tab.id);
+ chrome.pageAction.setTitle({title: "Modified", tabId: tab.id});
+ chrome.pageAction.setBadgeText({text: "badge", tabId: tab.id});
+ chrome.pageAction.setBadgeBackgroundColor(
+ {color: [255,255,255,255], tabId: tab.id});
+
+ chrome.test.notifyPass();
+ });
+</script>
+</head>
+</html>
diff --git a/chrome/test/data/extensions/api_test/page_action/update2.html b/chrome/test/data/extensions/api_test/page_action/update2.html
new file mode 100755
index 0000000..17f3b9f
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/page_action/update2.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="27" height="23">
+<script>
+ // Test that even if we set the icon after the extension loads, it shows up.
+ chrome.tabs.getSelected(null, function(tab) {
+ chrome.pageAction.show(tab.id);
+ chrome.pageAction.setIcon({tabId: tab.id,
+ imageData:document.getElementById("canvas")
+ .getContext('2d').getImageData(0, 0, 16, 16)});
+ chrome.test.notifyPass();
+ });
+</script>
+</body>
+</html>
diff --git a/chrome/test/data/extensions/samples/test_page_action/background.html b/chrome/test/data/extensions/samples/test_page_action/background.html
new file mode 100644
index 0000000..2c5685d
--- /dev/null
+++ b/chrome/test/data/extensions/samples/test_page_action/background.html
@@ -0,0 +1,65 @@
+<html>
+<head>
+<script>
+ var lastTabId = 0;
+ chrome.tabs.onUpdated.addListener(function(tabId, p) {
+ lastTabId = tabId;
+ });
+
+ // Called when the user clicks on the browser action.
+ var clicks = 0;
+ chrome.pageAction.onClicked.addListener(function(_, info) {
+ chrome.pageAction.setIcon({iconIndex: clicks, tabId: info.tabId});
+ if (clicks % 2) {
+ chrome.pageAction.show(info.tabId);
+ } else {
+ chrome.pageAction.hide(info.tabId);
+ setTimeout(function() { chrome.pageAction.show(info.tabId); }, 1000);
+ }
+ chrome.pageAction.setTitle({title: "click:" + clicks, tabId: info.tabId});
+ chrome.pageAction.setBadgeTextColor({
+ tabId: info.tabId,
+ color: [0, 255, clicks * 50, 255]
+ });
+ chrome.pageAction.setBadgeBackgroundColor({
+ tabId: info.tabId,
+ color: [255, clicks * 50, 0, 255]
+ });
+ chrome.pageAction.setBadgeText({
+ tabId: info.tabId,
+ text: clicks + ""
+ });
+
+ // We only have 2 icons, but cycle through 3 icons to test the
+ // out-of-bounds index bug.
+ clicks++;
+ if (clicks > 3)
+ clicks = 0;
+ });
+ var i = 0;
+
+ window.setInterval(function() {
+ // Don't animate while in "click" mode.
+ if (clicks > 0) return;
+ i++;
+ chrome.pageAction.setIcon({imageData: draw(i*2, i*4), tabId: lastTabId});
+ }, 50);
+
+ function draw(starty, startx) {
+ var canvas = document.getElementById('canvas');
+ var context = canvas.getContext('2d');
+ context.clearRect(0, 0, canvas.width, canvas.height);
+ context.fillStyle = "rgba(0,200,0,255)";
+ context.fillRect(startx % 19, starty % 19, 8, 8);
+ context.fillStyle = "rgba(0,0,200,255)";
+ context.fillRect((startx + 5) % 19, (starty + 5) % 19, 8, 8);
+ context.fillStyle = "rgba(200,0,0,255)";
+ context.fillRect((startx + 10) % 19, (starty + 10) % 19, 8, 8);
+ return context.getImageData(0, 0, 19, 19);
+ }
+</script>
+</head>
+<body>
+<canvas id="canvas" width="19" height="19"></canvas>
+</body>
+</html>
diff --git a/chrome/test/data/extensions/samples/test_page_action/manifest.json b/chrome/test/data/extensions/samples/test_page_action/manifest.json
new file mode 100644
index 0000000..28e0dab
--- /dev/null
+++ b/chrome/test/data/extensions/samples/test_page_action/manifest.json
@@ -0,0 +1,11 @@
+{
+ "name": "Animated Page Action",
+ "description": "This extension adds an animated browser action to the toolbar.",
+ "version": "1.0",
+ "permissions": ["tabs"],
+ "background_page": "background.html",
+ "page_action": {
+ "default_title": "First icon",
+ "default_icon": "print_16x16.png"
+ }
+}
diff --git a/chrome/test/data/extensions/samples/test_page_action/print_16x16.png b/chrome/test/data/extensions/samples/test_page_action/print_16x16.png
new file mode 100644
index 0000000..d145964
--- /dev/null
+++ b/chrome/test/data/extensions/samples/test_page_action/print_16x16.png
Binary files differ