diff options
author | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-26 12:35:28 +0000 |
---|---|---|
committer | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-26 12:35:28 +0000 |
commit | 3349b59b789b2cd6c7c9164ed62fe7613878403b (patch) | |
tree | c54548a67b04447ab76fe34f3aa97d168aa23f83 | |
parent | 8b90ad02b9716374fe62b4f499c9ec488964c5a1 (diff) | |
download | chromium_src-3349b59b789b2cd6c7c9164ed62fe7613878403b.zip chromium_src-3349b59b789b2cd6c7c9164ed62fe7613878403b.tar.gz chromium_src-3349b59b789b2cd6c7c9164ed62fe7613878403b.tar.bz2 |
Make extension page actions be automatically converted into browser actions
BUG=124605
TEST=browser_tests --gtest_filter=*BrowserAction* (new tests), --gtest_filter=*PageAction*
Review URL: http://codereview.chromium.org/10234001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@134097 0039d316-1c4b-4281-b951-d872f2087c98
10 files changed, 271 insertions, 33 deletions
diff --git a/chrome/browser/extensions/api/extension_action/extension_actions_api.cc b/chrome/browser/extensions/api/extension_action/extension_actions_api.cc index 68769cf..a6c6a4b 100644 --- a/chrome/browser/extensions/api/extension_action/extension_actions_api.cc +++ b/chrome/browser/extensions/api/extension_action/extension_actions_api.cc @@ -145,6 +145,11 @@ bool ExtensionActionFunction::ParseCSSColorString( } bool ExtensionActionFunction::SetVisible(bool visible) { + // If --browser-actions-for-all is enabled there will be a browser_action + // here instead of a page action. Until we decide to do with that, just + // ignore. + if (!GetExtension()->page_action()) + return true; extension_action_->SetIsVisible(tab_id_, visible); NotifyChange(); return true; @@ -171,7 +176,10 @@ bool ExtensionActionSetIconFunction::RunExtensionAction() { IPC::ReadParam(&bitmap_pickle, &iter, &bitmap)); extension_action_->SetIcon(tab_id_, bitmap); } else if (details_->GetInteger("iconIndex", &icon_index)) { - EXTENSION_FUNCTION_VALIDATE(GetExtension()->page_action()); + // If --browser-actions-for-all is enabled there might legimitely be an + // iconIndex set. Until we decide what to do with that, ignore. + if (!GetExtension()->page_action()) + return true; if (icon_index < 0 || static_cast<size_t>(icon_index) >= extension_action_->icon_paths()->size()) { diff --git a/chrome/browser/extensions/api/extension_action/page_as_browser_action_apitest.cc b/chrome/browser/extensions/api/extension_action/page_as_browser_action_apitest.cc new file mode 100644 index 0000000..c941923 --- /dev/null +++ b/chrome/browser/extensions/api/extension_action/page_as_browser_action_apitest.cc @@ -0,0 +1,174 @@ +// Copyright (c) 2012 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 "base/command_line.h" +#include "chrome/browser/extensions/browser_action_test_util.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_browser_event_router.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/sessions/restore_tab_helper.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/ui/omnibox/location_bar.h" +#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_action.h" +#include "chrome/test/base/ui_test_utils.h" +#include "content/public/browser/web_contents.h" + +// These are a mash-up of the tests from from page_actions_apitest.cc and +// browser_actions_apitest.cc. + +namespace { + +class PageAsBrowserActionApiTest : public ExtensionApiTest { + public: + PageAsBrowserActionApiTest() {} + virtual ~PageAsBrowserActionApiTest() {} + + void SetUpCommandLine(CommandLine* command_line) OVERRIDE { + ExtensionApiTest::SetUpCommandLine(command_line); + command_line->AppendSwitch(switches::kEnableBrowserActionsForAll); + } + + protected: + BrowserActionTestUtil GetBrowserActionsBar() { + return BrowserActionTestUtil(browser()); + } +}; + +IN_PROC_BROWSER_TEST_F(PageAsBrowserActionApiTest, Basic) { + ASSERT_TRUE(test_server()->Start()); + ASSERT_TRUE(RunExtensionTest("page_action/basics")) << message_; + const Extension* extension = GetSingleLoadedExtension(); + ASSERT_TRUE(extension) << message_; + + // Test that there is a browser action in the toolbar. + ASSERT_EQ(1, GetBrowserActionsBar().NumberOfBrowserActions()); + + { + // 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. + int tab_id = ExtensionTabUtil::GetTabId(browser()->GetSelectedWebContents()); + ExtensionAction* action = extension->browser_action(); + ASSERT_TRUE(action); + EXPECT_EQ("Modified", action->GetTitle(tab_id)); + + { + // Simulate the page action being clicked. + ResultCatcher catcher; + ExtensionService* service = browser()->profile()->GetExtensionService(); + service->toolbar_model()->ExecuteBrowserAction( + extension->id(), browser()); + 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. + EXPECT_FALSE(action->GetIcon(tab_id).isNull()); +} + +// Test that calling chrome.pageAction.setPopup() can enable a popup. +IN_PROC_BROWSER_TEST_F(PageAsBrowserActionApiTest, AddPopup) { + // Load the extension, which has no default popup. + ASSERT_TRUE(RunExtensionTest("page_action/add_popup")) << message_; + const Extension* extension = GetSingleLoadedExtension(); + ASSERT_TRUE(extension) << message_; + + int tab_id = ExtensionTabUtil::GetTabId(browser()->GetSelectedWebContents()); + + ExtensionAction* page_action = extension->browser_action(); + ASSERT_TRUE(page_action) + << "Page action test extension should have a page action."; + + ASSERT_FALSE(page_action->HasPopup(tab_id)); + + // Simulate the page action being clicked. The resulting event should + // install a page action popup. + { + ResultCatcher catcher; + ExtensionService* service = browser()->profile()->GetExtensionService(); + service->toolbar_model()->ExecuteBrowserAction( + extension->id(), browser()); + ASSERT_TRUE(catcher.GetNextResult()); + } + + ASSERT_TRUE(page_action->HasPopup(tab_id)) + << "Clicking on the page action should have caused a popup to be added."; + + ASSERT_STREQ("/a_popup.html", + page_action->GetPopupUrl(tab_id).path().c_str()); + + // Now change the popup from a_popup.html to a_second_popup.html . + // Load a page which removes the popup using chrome.pageAction.setPopup(). + { + ResultCatcher catcher; + ui_test_utils::NavigateToURL( + browser(), + GURL(extension->GetResourceURL("change_popup.html"))); + ASSERT_TRUE(catcher.GetNextResult()); + } + + ASSERT_TRUE(page_action->HasPopup(tab_id)); + ASSERT_STREQ("/another_popup.html", + page_action->GetPopupUrl(tab_id).path().c_str()); +} + +// Test that calling chrome.pageAction.setPopup() can remove a popup. +IN_PROC_BROWSER_TEST_F(PageAsBrowserActionApiTest, RemovePopup) { + // Load the extension, which has a page action with a default popup. + ASSERT_TRUE(RunExtensionTest("page_action/remove_popup")) << message_; + const Extension* extension = GetSingleLoadedExtension(); + ASSERT_TRUE(extension) << message_; + + int tab_id = ExtensionTabUtil::GetTabId(browser()->GetSelectedWebContents()); + + ExtensionAction* page_action = extension->browser_action(); + ASSERT_TRUE(page_action) + << "Page action test extension should have a page action."; + + ASSERT_TRUE(page_action->HasPopup(tab_id)) + << "Expect a page action popup before the test removes it."; + + // Load a page which removes the popup using chrome.pageAction.setPopup(). + { + ResultCatcher catcher; + ui_test_utils::NavigateToURL( + browser(), + GURL(extension->GetResourceURL("remove_popup.html"))); + ASSERT_TRUE(catcher.GetNextResult()); + } + + ASSERT_FALSE(page_action->HasPopup(tab_id)) + << "Page action popup should have been removed."; +} + +IN_PROC_BROWSER_TEST_F(PageAsBrowserActionApiTest, Getters) { + ASSERT_TRUE(RunExtensionTest("page_action/getters")) << message_; + const Extension* extension = GetSingleLoadedExtension(); + ASSERT_TRUE(extension) << message_; + + ResultCatcher catcher; + ui_test_utils::NavigateToURL(browser(), + GURL(extension->GetResourceURL("update.html"))); + ASSERT_TRUE(catcher.GetNextResult()); +} + +} diff --git a/chrome/browser/extensions/extension_browser_event_router.cc b/chrome/browser/extensions/extension_browser_event_router.cc index 6f726d5..86448a9 100644 --- a/chrome/browser/extensions/extension_browser_event_router.cc +++ b/chrome/browser/extensions/extension_browser_event_router.cc @@ -6,9 +6,10 @@ #include "base/json/json_writer.h" #include "base/values.h" +#include "chrome/browser/extensions/api/extension_action/extension_page_actions_api_constants.h" #include "chrome/browser/extensions/extension_event_names.h" #include "chrome/browser/extensions/extension_event_router.h" -#include "chrome/browser/extensions/api/extension_action/extension_page_actions_api_constants.h" +#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/extensions/extension_tabs_module_constants.h" #include "chrome/browser/extensions/extension_window_controller.h" @@ -636,8 +637,7 @@ void ExtensionBrowserEventRouter::BrowserActionExecuted( int tab_id = 0; if (!ExtensionTabUtil::GetDefaultTab(browser, &tab_contents, &tab_id)) return; - DispatchEventWithTab(profile, extension_id, "browserAction.onClicked", - tab_contents->web_contents(), true); + ExtensionActionExecuted(profile, extension_id, tab_contents); } void ExtensionBrowserEventRouter::PageActionExecuted( @@ -654,8 +654,7 @@ void ExtensionBrowserEventRouter::PageActionExecuted( NULL, NULL, &tab_contents, NULL)) { return; } - DispatchEventWithTab(profile, extension_id, "pageAction.onClicked", - tab_contents->web_contents(), true); + ExtensionActionExecuted(profile, extension_id, tab_contents); } void ExtensionBrowserEventRouter::CommandExecuted( @@ -672,3 +671,33 @@ void ExtensionBrowserEventRouter::CommandExecuted( "experimental.keybinding.onCommand", json_args); } + +void ExtensionBrowserEventRouter::ExtensionActionExecuted( + Profile* profile, + const std::string& extension_id, + TabContentsWrapper* tab_contents) { + const Extension* extension = + profile->GetExtensionService()->GetExtensionById(extension_id, false); + if (!extension) + return; + + const char* event_name = NULL; + switch (extension->extension_action_api_type()) { + case ExtensionAction::TYPE_NONE: + break; + case ExtensionAction::TYPE_BROWSER: + event_name = "browserAction.onClicked"; + break; + case ExtensionAction::TYPE_PAGE: + event_name = "pageAction.onClicked"; + break; + } + + if (event_name) { + DispatchEventWithTab(profile, + extension_id, + event_name, + tab_contents->web_contents(), + true); + } +} diff --git a/chrome/browser/extensions/extension_browser_event_router.h b/chrome/browser/extensions/extension_browser_event_router.h index b79a6e4..bd7f6fb 100644 --- a/chrome/browser/extensions/extension_browser_event_router.h +++ b/chrome/browser/extensions/extension_browser_event_router.h @@ -214,6 +214,12 @@ class ExtensionBrowserEventRouter : public TabStripModelObserver, // found, NULL if not. TabEntry* GetTabEntry(const content::WebContents* contents); + // Called when either a browser or page action is executed. Figures out which + // event to send based on what the extension wants. + void ExtensionActionExecuted(Profile* profile, + const std::string& extension_id, + TabContentsWrapper* tab_contents); + std::map<int, TabEntry> tab_entries_; // The main profile that owns this event router. diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 28b5843..bea5b91 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -2749,6 +2749,7 @@ 'browser/extensions/api/dns/dns_apitest.cc', 'browser/extensions/api/extension_action/browser_action_apitest.cc', 'browser/extensions/api/extension_action/page_action_apitest.cc', + 'browser/extensions/api/extension_action/page_as_browser_action_apitest.cc', 'browser/extensions/api/identity/identity_apitest.cc', 'browser/extensions/api/offscreen_tabs/offscreen_tabs_apitest.cc', 'browser/extensions/api/permissions/permissions_apitest.cc', diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index 5d5045c..f313c43 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -936,10 +936,9 @@ bool Extension::LoadGlobsHelper( return true; } -ExtensionAction* Extension::LoadExtensionActionHelper( +scoped_ptr<ExtensionAction> Extension::LoadExtensionActionHelper( const DictionaryValue* extension_action, string16* error) { - scoped_ptr<ExtensionAction> result(new ExtensionAction()); - result->set_extension_id(id()); + scoped_ptr<ExtensionAction> result(new ExtensionAction(id())); // Page actions are hidden by default, and browser actions ignore // visibility. @@ -954,7 +953,7 @@ ExtensionAction* Extension::LoadExtensionActionHelper( std::string path; if (!(*iter)->GetAsString(&path) || path.empty()) { *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); - return NULL; + return scoped_ptr<ExtensionAction>(); } result->icon_paths()->push_back(path); @@ -965,7 +964,7 @@ ExtensionAction* Extension::LoadExtensionActionHelper( if (extension_action->HasKey(keys::kPageActionId)) { if (!extension_action->GetString(keys::kPageActionId, &id)) { *error = ASCIIToUTF16(errors::kInvalidPageActionId); - return NULL; + return scoped_ptr<ExtensionAction>(); } result->set_id(id); } @@ -978,7 +977,7 @@ ExtensionAction* Extension::LoadExtensionActionHelper( &default_icon) || default_icon.empty()) { *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); - return NULL; + return scoped_ptr<ExtensionAction>(); } result->set_default_icon_path(default_icon); } @@ -989,12 +988,12 @@ ExtensionAction* Extension::LoadExtensionActionHelper( if (extension_action->HasKey(keys::kPageActionDefaultTitle)) { if (!extension_action->GetString(keys::kPageActionDefaultTitle, &title)) { *error = ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle); - return NULL; + return scoped_ptr<ExtensionAction>(); } } else if (manifest_version_ == 1 && extension_action->HasKey(keys::kName)) { if (!extension_action->GetString(keys::kName, &title)) { *error = ASCIIToUTF16(errors::kInvalidPageActionName); - return NULL; + return scoped_ptr<ExtensionAction>(); } } result->SetTitle(ExtensionAction::kDefaultTabId, title); @@ -1011,7 +1010,7 @@ ExtensionAction* Extension::LoadExtensionActionHelper( errors::kInvalidPageActionOldAndNewKeys, keys::kPageActionDefaultPopup, keys::kPageActionPopup); - return NULL; + return scoped_ptr<ExtensionAction>(); } popup_key = keys::kPageActionPopup; } @@ -1027,11 +1026,11 @@ ExtensionAction* Extension::LoadExtensionActionHelper( if (!popup->GetString(keys::kPageActionPopupPath, &url_str)) { *error = ExtensionErrorUtils::FormatErrorMessageUTF16( errors::kInvalidPageActionPopupPath, "<missing>"); - return NULL; + return scoped_ptr<ExtensionAction>(); } } else { *error = ASCIIToUTF16(errors::kInvalidPageActionPopup); - return NULL; + return scoped_ptr<ExtensionAction>(); } if (!url_str.empty()) { @@ -1040,7 +1039,7 @@ ExtensionAction* Extension::LoadExtensionActionHelper( if (!url.is_valid()) { *error = ExtensionErrorUtils::FormatErrorMessageUTF16( errors::kInvalidPageActionPopupPath, url_str); - return NULL; + return scoped_ptr<ExtensionAction>(); } result->SetPopupUrl(ExtensionAction::kDefaultTabId, url); } else { @@ -1049,7 +1048,7 @@ ExtensionAction* Extension::LoadExtensionActionHelper( } } - return result.release(); + return result.Pass(); } // static @@ -2300,10 +2299,16 @@ bool Extension::LoadPageAction(string16* error) { // If page_action_value is not NULL, then there was a valid page action. if (page_action_value) { - page_action_.reset( - LoadExtensionActionHelper(page_action_value, error)); + page_action_ = LoadExtensionActionHelper(page_action_value, error); if (!page_action_.get()) return false; // Failed to parse page action definition. + extension_action_api_type_ = ExtensionAction::TYPE_PAGE; + + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableBrowserActionsForAll)) { + browser_action_ = page_action_.Pass(); + // extension_action_api_type_ stays the same; that's the point. + } } return true; @@ -2318,10 +2323,10 @@ bool Extension::LoadBrowserAction(string16* error) { return false; } - browser_action_.reset( - LoadExtensionActionHelper(browser_action_value, error)); + browser_action_ = LoadExtensionActionHelper(browser_action_value, error); if (!browser_action_.get()) return false; // Failed to parse browser action definition. + extension_action_api_type_ = ExtensionAction::TYPE_BROWSER; return true; } @@ -2812,6 +2817,7 @@ Extension::Extension(const FilePath& path, incognito_split_mode_(false), offline_enabled_(false), converted_from_user_script_(false), + extension_action_api_type_(ExtensionAction::TYPE_NONE), background_page_is_persistent_(true), allow_background_js_access_(true), manifest_(manifest.release()), diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index 020ed03b..e38ba6f 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -20,6 +20,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/synchronization/lock.h" +#include "chrome/common/extensions/extension_action.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_icon_set.h" #include "chrome/common/extensions/extension_permission_set.h" @@ -30,7 +31,6 @@ #include "ui/base/accelerators/accelerator.h" #include "ui/gfx/size.h" -class ExtensionAction; class ExtensionResource; class FileBrowserHandler; class SkBitmap; @@ -577,6 +577,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> { const UserScriptList& content_scripts() const { return content_scripts_; } ExtensionAction* page_action() const { return page_action_.get(); } ExtensionAction* browser_action() const { return browser_action_.get(); } + ExtensionAction::Type extension_action_api_type() const { + return extension_action_api_type_; + } const FileBrowserHandlerList* file_browser_handlers() const { return file_browser_handlers_.get(); } @@ -830,7 +833,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // Helper method to load an ExtensionAction from the page_action or // browser_action entries in the manifest. - ExtensionAction* LoadExtensionActionHelper( + scoped_ptr<ExtensionAction> LoadExtensionActionHelper( const base::DictionaryValue* extension_action, string16* error); // Helper method that loads the OAuth2 info from the 'oauth2' manifest key. @@ -929,6 +932,12 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // The extension's browser action, if any. scoped_ptr<ExtensionAction> browser_action_; + // The "API type" of whichever extension action (|page_action_| or + // |browser_action_|) refers to. This flag is needed by code which must deal + // with browser actions that were automatically converted from page actions, + // since the extensions will still call the pageAction APIs. + ExtensionAction::Type extension_action_api_type_; + // The extension's file browser actions, if any. scoped_ptr<FileBrowserHandlerList> file_browser_handlers_; diff --git a/chrome/common/extensions/extension_action.cc b/chrome/common/extensions/extension_action.cc index 10d70bf..e1695ba 100644 --- a/chrome/common/extensions/extension_action.cc +++ b/chrome/common/extensions/extension_action.cc @@ -52,7 +52,8 @@ const int kCenterAlignThreshold = 20; const int ExtensionAction::kDefaultTabId = -1; -ExtensionAction::ExtensionAction() { +ExtensionAction::ExtensionAction(const std::string& extension_id) + : extension_id_(extension_id) { } ExtensionAction::~ExtensionAction() { diff --git a/chrome/common/extensions/extension_action.h b/chrome/common/extensions/extension_action.h index 3e2495d..6f40f9a 100644 --- a/chrome/common/extensions/extension_action.h +++ b/chrome/common/extensions/extension_action.h @@ -30,14 +30,18 @@ class ExtensionAction { // parameter. static const int kDefaultTabId; - ExtensionAction(); + // The types of extension actions. + enum Type { + TYPE_NONE, + TYPE_BROWSER, + TYPE_PAGE, + }; + + explicit ExtensionAction(const std::string& extension_id); ~ExtensionAction(); // extension id std::string extension_id() const { return extension_id_; } - void set_extension_id(const std::string& extension_id) { - extension_id_ = extension_id; - } // action id -- only used with legacy page actions API std::string id() const { return id_; } @@ -168,7 +172,7 @@ class ExtensionAction { // The id for the extension this action belongs to (as defined in the // extension manifest). - std::string extension_id_; + const std::string extension_id_; // Each of these data items can have both a global state (stored with the key // kDefaultTabId), or tab-specific state (stored with the tab_id as the key). diff --git a/chrome/common/extensions/extension_action_unittest.cc b/chrome/common/extensions/extension_action_unittest.cc index b9588bf..8cc2aac 100644 --- a/chrome/common/extensions/extension_action_unittest.cc +++ b/chrome/common/extensions/extension_action_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -33,7 +33,7 @@ static SkBitmap LoadIcon(const std::string& filename) { } TEST(ExtensionActionTest, TabSpecificState) { - ExtensionAction action; + ExtensionAction action(""); // title ASSERT_EQ("", action.GetTitle(1)); |