diff options
author | asargent@chromium.org <asargent@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-16 18:34:28 +0000 |
---|---|---|
committer | asargent@chromium.org <asargent@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-16 18:34:28 +0000 |
commit | 43919ac90f426c807da507d52c163972634c771e (patch) | |
tree | 29d4053f433b61e587fa532428cb3dc4b11823de /chrome | |
parent | 29f192ded5a521a42ff1f93880e0787f2500d5a0 (diff) | |
download | chromium_src-43919ac90f426c807da507d52c163972634c771e.zip chromium_src-43919ac90f426c807da507d52c163972634c771e.tar.gz chromium_src-43919ac90f426c807da507d52c163972634c771e.tar.bz2 |
Add concept of an options page to Extensions.
BUG=23801
TEST=Create an extension with an "options_page" entry in its manifest with a value the name of a html file in the extension dir.
Load that extension and go to chrome://extensions, there should be an "Options" button that takes you to the page specified in the manifest.
Review URL: http://codereview.chromium.org/271114
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29297 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/extensions/extension_browsertests_misc.cc | 38 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_ui.cc | 22 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_ui.h | 3 | ||||
-rw-r--r-- | chrome/browser/resources/extensions_ui.html | 31 | ||||
-rw-r--r-- | chrome/common/extensions/extension.cc | 10 | ||||
-rw-r--r-- | chrome/common/extensions/extension.h | 4 | ||||
-rw-r--r-- | chrome/common/extensions/extension_constants.cc | 5 | ||||
-rw-r--r-- | chrome/common/extensions/extension_constants.h | 2 | ||||
-rw-r--r-- | chrome/common/extensions/extension_unittest.cc | 13 | ||||
-rw-r--r-- | chrome/test/data/extensions/options.crx | bin | 0 -> 995 bytes |
10 files changed, 118 insertions, 10 deletions
diff --git a/chrome/browser/extensions/extension_browsertests_misc.cc b/chrome/browser/extensions/extension_browsertests_misc.cc index 63530b3..10d33c3 100644 --- a/chrome/browser/extensions/extension_browsertests_misc.cc +++ b/chrome/browser/extensions/extension_browsertests_misc.cc @@ -788,3 +788,41 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, AutoUpdate) { ASSERT_EQ("2.0", extensions->at(0)->VersionString()); } +// Used to simulate a click on the first button named 'Options'. +static const wchar_t* jscript_click_option_button = + L"(function() { " + L" var button = document.evaluate(\"//button[text()='Options']\"," + L" document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE," + L" null).snapshotItem(0);" + L" button.click();" + L" window.domAutomationController.send(0);" + L"})();"; + +// Test that an extension with an options page makes an 'Options' button appear +// on chrome://extensions, and that clicking the button opens a new tab with the +// extension's options page. +IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, OptionsPage) { + // Install an extension with an options page. + ASSERT_TRUE(InstallExtension(test_data_dir_.AppendASCII("options.crx"), 1)); + ExtensionsService* service = browser()->profile()->GetExtensionsService(); + const ExtensionList* extensions = service->extensions(); + ASSERT_EQ(1u, extensions->size()); + Extension* extension = extensions->at(0); + + // Go to the chrome://extensions page and click the Options button. + ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIExtensionsURL)); + TabStripModel* tab_strip = browser()->tabstrip_model(); + TabContents* extensions_tab = browser()->GetSelectedTabContents(); + ui_test_utils::ExecuteJavaScript(extensions_tab->render_view_host(), L"", + jscript_click_option_button); + + // If the options page hasn't already come up, wait for it. + if (tab_strip->count() == 1) { + ui_test_utils::WaitForNewTab(browser()); + } + ASSERT_EQ(2, tab_strip->count()); + + EXPECT_EQ(extension->GetResourceURL("options.html"), + tab_strip->GetTabContentsAt(1)->GetURL()); +} + diff --git a/chrome/browser/extensions/extensions_ui.cc b/chrome/browser/extensions/extensions_ui.cc index b77a751..752a47d 100644 --- a/chrome/browser/extensions/extensions_ui.cc +++ b/chrome/browser/extensions/extensions_ui.cc @@ -8,6 +8,7 @@ #include "app/resource_bundle.h" #include "base/string_util.h" #include "base/thread.h" +#include "chrome/browser/browser.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_thread.h" #include "chrome/browser/debugger/devtools_manager.h" @@ -95,6 +96,8 @@ void ExtensionsDOMHandler::RegisterMessages() { NewCallback(this, &ExtensionsDOMHandler::HandleEnableMessage)); dom_ui_->RegisterMessageCallback("uninstall", NewCallback(this, &ExtensionsDOMHandler::HandleUninstallMessage)); + dom_ui_->RegisterMessageCallback("options", + NewCallback(this, &ExtensionsDOMHandler::HandleOptionsMessage)); dom_ui_->RegisterMessageCallback("load", NewCallback(this, &ExtensionsDOMHandler::HandleLoadMessage)); dom_ui_->RegisterMessageCallback("pack", @@ -198,6 +201,22 @@ void ExtensionsDOMHandler::HandleUninstallMessage(const Value* value) { extensions_service_->UninstallExtension(extension_id, false); } +void ExtensionsDOMHandler::HandleOptionsMessage(const Value* value) { + CHECK(value->IsType(Value::TYPE_LIST)); + const ListValue* list = static_cast<const ListValue*>(value); + CHECK(list->GetSize() == 1); + std::string extension_id; + CHECK(list->GetString(0, &extension_id)); + Extension *extension = extensions_service_->GetExtensionById(extension_id); + if (!extension || extension->options_url().is_empty()) { + return; + } + Browser* browser = Browser::GetOrCreateTabbedBrowser(dom_ui_->GetProfile()); + CHECK(browser); + browser->OpenURL(extension->options_url(), GURL(), NEW_FOREGROUND_TAB, + PageTransition::LINK); +} + void ExtensionsDOMHandler::HandleLoadMessage(const Value* value) { std::string string_path; CHECK(value->IsType(Value::TYPE_LIST)); @@ -399,6 +418,9 @@ DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue( extension_data->SetString(L"description", extension->description()); extension_data->SetString(L"version", extension->version()->GetString()); extension_data->SetBoolean(L"enabled", enabled); + if (!extension->options_url().is_empty()) { + extension_data->SetString(L"options_url", extension->options_url().spec()); + } // Add list of content_script detail DictionaryValues ListValue *content_script_list = new ListValue(); diff --git a/chrome/browser/extensions/extensions_ui.h b/chrome/browser/extensions/extensions_ui.h index 908c9f3..90c0e92 100644 --- a/chrome/browser/extensions/extensions_ui.h +++ b/chrome/browser/extensions/extensions_ui.h @@ -95,6 +95,9 @@ class ExtensionsDOMHandler // Callback for "uninstall" message. void HandleUninstallMessage(const Value* value); + // Callback for "options" message. + void HandleOptionsMessage(const Value* value); + // Callback for "load" message. void HandleLoadMessage(const Value* value); diff --git a/chrome/browser/resources/extensions_ui.html b/chrome/browser/resources/extensions_ui.html index 99076f8..a228457 100644 --- a/chrome/browser/resources/extensions_ui.html +++ b/chrome/browser/resources/extensions_ui.html @@ -206,6 +206,7 @@ var extensionDataFormat = { "description": "Extension long format description", "version": "1.0.231", "enabled": "true", + "options_url": "options.html", "content_scripts": [ { "js": ["script1_file1.js", "script1_file2.js"], @@ -345,12 +346,19 @@ function handleUninstallExtension(node) { } /** - * Utility function which asks the C++ to show a platform-specific file select - * dialog, and fire |callback| with the |filePath| that resulted. |selectType| - * can be either 'file' or 'folder'. |operation| can be 'load', 'packRoot', - * or 'pem' which are signals to the C++ to do some operation-specific - * configuration. + * Handles an 'options' button getting clicked. */ +function handleOptions(node) { + chrome.send('options', [node.extensionId]); +} + +/** +* Utility function which asks the C++ to show a platform-specific file select +* dialog, and fire |callback| with the |filePath| that resulted. |selectType| +* can be either 'file' or 'folder'. |operation| can be 'load', 'packRoot', +* or 'pem' which are signals to the C++ to do some operation-specific +* configuration. +*/ function showFileDialog(selectType, operation, callback) { handleFilePathSelected = function(filePath) { callback(filePath); @@ -387,7 +395,7 @@ function showPackDialog() { /** * Hides the pack dialog. - */ + */ function hidePackDialog() { document.getElementById('dialogBackground').style.display="none" } @@ -438,7 +446,7 @@ function autoUpdate() { <input type="text" id="extensionPathText"> </div> <div> - <input type="button" value="BROWSE" + <input type="button" value="BROWSE" i18n-values="value:packDialogBrowse" onclick="selectExtensionPath();"> </div> @@ -451,7 +459,7 @@ function autoUpdate() { <input type="text" id="privateKeyPath"> </div> <div> - <input type="button" value="BROWSE" + <input type="button" value="BROWSE" i18n-values="value:packDialogBrowse" onclick="selectPrivateKeyPath();"> </div> @@ -463,7 +471,7 @@ function autoUpdate() { <div> <input type="button" value="Cancel" onclick="hidePackDialog();"> </div> - </div> + </div> </div> </div> </div> @@ -524,6 +532,11 @@ function autoUpdate() { jsvalues=".extensionId:id" onclick="handleUninstallExtension(this)" >Uninstall</button> + <button + jsdisplay="options_url" + jsvalues=".extensionId:id" + onclick="handleOptions(this)" + >Options</button> </div> </div> </div> diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index 506c390..fee0e89 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -830,6 +830,16 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_id, background_url_ = GetResourceURL(background_str); } + // Initialize options page url (optional). + if (source.HasKey(keys::kOptionsPage)) { + std::string options_str; + if (!source.GetString(keys::kOptionsPage, &options_str)) { + *error = errors::kInvalidOptionsPage; + return false; + } + options_url_ = GetResourceURL(options_str); + } + // Initialize toolstrips (optional). if (source.HasKey(keys::kToolstrips)) { ListValue* list_value; diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index 0cb120b..c96ec0a 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -195,6 +195,7 @@ class Extension { } const std::vector<PluginInfo>& plugins() const { return plugins_; } const GURL& background_url() const { return background_url_; } + const GURL& options_url() const { return options_url_; } const std::vector<ToolstripInfo>& toolstrips() const { return toolstrips_; } const std::vector<std::string>& api_permissions() const { return api_permissions_; @@ -334,6 +335,9 @@ class Extension { // loaded in the background. GURL background_url_; + // Optional URL to a page for setting options/preferences. + GURL options_url_; + // Optional list of toolstrips_ and associated properties. std::vector<ToolstripInfo> toolstrips_; diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc index 13d0547..db7c8b7 100644 --- a/chrome/common/extensions/extension_constants.cc +++ b/chrome/common/extensions/extension_constants.cc @@ -46,6 +46,7 @@ const wchar_t* kToolstrips = L"toolstrips"; const wchar_t* kType = L"type"; const wchar_t* kVersion = L"version"; const wchar_t* kUpdateURL = L"update_url"; +const wchar_t* kOptionsPage = L"options_page"; } // namespace extension_manifest_keys namespace extension_manifest_values { @@ -133,7 +134,7 @@ const char* kInvalidPrivacyBlacklists = const char* kInvalidPrivacyBlacklistsPath = "Invalid value for 'privacy_blacklists[*]'."; const char* kInvalidBackground = - "Invalid value for 'background'."; + "Invalid value for 'background_page'."; const char* kInvalidRunAt = "Invalid value for 'content_scripts[*].run_at'."; const char* kInvalidSignature = @@ -175,4 +176,6 @@ const char* kLocalesNoDefaultLocaleSpecified = "Localization used, but default_locale wasn't specified in the manifest."; const char* kLocalesNoValidLocaleNamesListed = "No valid locale name could be found in _locales directory."; +const char* kInvalidOptionsPage = + "Invalid value for 'options_page'."; } // namespace extension_manifest_errors diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h index ec8dbe4..5c8a087 100644 --- a/chrome/common/extensions/extension_constants.h +++ b/chrome/common/extensions/extension_constants.h @@ -47,6 +47,7 @@ namespace extension_manifest_keys { extern const wchar_t* kType; extern const wchar_t* kVersion; extern const wchar_t* kUpdateURL; + extern const wchar_t* kOptionsPage; } // namespace extension_manifest_keys // Some values expected in manifests. @@ -116,6 +117,7 @@ namespace extension_manifest_errors { extern const char* kInvalidDefaultLocale; extern const char* kLocalesNoDefaultLocaleSpecified; extern const char* kLocalesNoValidLocaleNamesListed; + extern const char* kInvalidOptionsPage; } // namespace extension_manifest_errors #endif // CHROME_COMMON_EXTENSIONS_EXTENSION_CONSTANTS_H_ diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc index 104ce7a..eeb4499 100644 --- a/chrome/common/extensions/extension_unittest.cc +++ b/chrome/common/extensions/extension_unittest.cc @@ -231,6 +231,12 @@ TEST(ExtensionTest, InitFromValueInvalid) { input_value->Set(keys::kBrowserAction, action); EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); EXPECT_STREQ(error.c_str(), errors::kOneUISurfaceOnly); + + // Test invalid options page url. + input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy())); + input_value->Set(keys::kOptionsPage, Value::CreateNullValue()); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_TRUE(MatchPattern(error, errors::kInvalidOptionsPage)); } TEST(ExtensionTest, InitFromValueValid) { @@ -254,6 +260,13 @@ TEST(ExtensionTest, InitFromValueValid) { EXPECT_EQ("my extension", extension.name()); EXPECT_EQ(extension.id(), extension.url().host()); EXPECT_EQ(path.value(), extension.path().value()); + + // Test with an options page. + input_value.SetString(keys::kOptionsPage, "options.html"); + EXPECT_TRUE(extension.InitFromValue(input_value, false, &error)); + EXPECT_EQ("", error); + EXPECT_EQ("chrome-extension", extension.options_url().scheme()); + EXPECT_EQ("/options.html", extension.options_url().path()); } TEST(ExtensionTest, GetResourceURLAndPath) { diff --git a/chrome/test/data/extensions/options.crx b/chrome/test/data/extensions/options.crx Binary files differnew file mode 100644 index 0000000..f286e19 --- /dev/null +++ b/chrome/test/data/extensions/options.crx |