diff options
author | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-16 21:53:46 +0000 |
---|---|---|
committer | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-16 21:53:46 +0000 |
commit | 744ef17706b3c77d087809ec119bbc2f7bd17c89 (patch) | |
tree | 24eb889c256f49dd3d24610b68c5e09b90a5c21d /chrome | |
parent | 0db22afeba278540d2457ebfead171a8a54feb3c (diff) | |
download | chromium_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')
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&t13n_langs=en"></script> + <script type="text/javascript" src="http://www.google.com/coop/cse/brand?form=cse-search-box&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 Binary files differnew file mode 100755 index 0000000..9a79a46 --- /dev/null +++ b/chrome/test/data/extensions/api_test/page_action/icon.png 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 Binary files differnew file mode 100644 index 0000000..d145964 --- /dev/null +++ b/chrome/test/data/extensions/samples/test_page_action/print_16x16.png |