diff options
author | rafaelw@chromium.org <rafaelw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-09 17:33:18 +0000 |
---|---|---|
committer | rafaelw@chromium.org <rafaelw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-09 17:33:18 +0000 |
commit | dd9d627a45031a4fe8414e79253e82fdfac7132c (patch) | |
tree | a049ade38464deffdc606ee3dec8607122e2354b /chrome/browser | |
parent | 422a0b90cbcd884221d3fabac3507dd964e5a064 (diff) | |
download | chromium_src-dd9d627a45031a4fe8414e79253e82fdfac7132c.zip chromium_src-dd9d627a45031a4fe8414e79253e82fdfac7132c.tar.gz chromium_src-dd9d627a45031a4fe8414e79253e82fdfac7132c.tar.bz2 |
Implement gallery install API
This patch implements chrome.experimental.management.install() which is only available for use by the web store. Calling with an extensionId, causes the download url to be constructed internally and downloaded and then cause the installation to bypass the normal permissions dialog.
BUG=27431,54148
Review URL: http://codereview.chromium.org/3353015
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@58956 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/download/download_util.cc | 1 | ||||
-rw-r--r-- | chrome/browser/extensions/crx_installer.cc | 47 | ||||
-rw-r--r-- | chrome/browser/extensions/crx_installer.h | 22 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_apitest.cc | 59 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_apitest.h | 14 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_gallery_install_apitest.cc | 36 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_management_api.cc | 13 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_management_api.h | 6 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_webstore_private_api.cc | 57 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_webstore_private_api.h | 10 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service.cc | 2 | ||||
-rw-r--r-- | chrome/browser/resources/webstore_app/manifest.json | 3 |
12 files changed, 226 insertions, 44 deletions
diff --git a/chrome/browser/download/download_util.cc b/chrome/browser/download/download_util.cc index 0ec1c29..161758f 100644 --- a/chrome/browser/download/download_util.cc +++ b/chrome/browser/download/download_util.cc @@ -295,6 +295,7 @@ void OpenChromeExtension(Profile* profile, installer->set_original_url(download_item.url()); installer->set_limit_web_extent_to_download_host(!is_gallery_download); installer->InstallCrx(download_item.full_path()); + installer->set_allow_silent_install(is_gallery_download); } } else { TabContents* contents = NULL; diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc index ad4a4cc..9161059 100644 --- a/chrome/browser/extensions/crx_installer.cc +++ b/chrome/browser/extensions/crx_installer.cc @@ -9,6 +9,7 @@ #include "base/file_util.h" #include "base/path_service.h" #include "base/scoped_temp_dir.h" +#include "base/singleton.h" #include "base/task.h" #include "base/utf_string_conversions.h" #include "base/version.h" @@ -31,11 +32,42 @@ #include "third_party/skia/include/core/SkBitmap.h" namespace { - // Helper function to delete files. This is used to avoid ugly casts which - // would be necessary with PostMessage since file_util::Delete is overloaded. - static void DeleteFileHelper(const FilePath& path, bool recursive) { - file_util::Delete(path, recursive); + +// Helper function to delete files. This is used to avoid ugly casts which +// would be necessary with PostMessage since file_util::Delete is overloaded. +static void DeleteFileHelper(const FilePath& path, bool recursive) { + file_util::Delete(path, recursive); +} + +struct WhitelistedInstallData { + WhitelistedInstallData() {} + std::list<std::string> ids; +}; + +} + +// static +void CrxInstaller::SetWhitelistedInstallId(const std::string& id) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + Singleton<WhitelistedInstallData>::get()->ids.push_back(id); +} + +// static +bool CrxInstaller::ClearWhitelistedInstallId(const std::string& id) { + std::list<std::string>& ids = Singleton<WhitelistedInstallData>::get()->ids; + std::list<std::string>::iterator iter = ids.begin(); + for (; iter != ids.end(); ++iter) { + if (*iter == id) { + break; + } } + + if (iter != ids.end()) { + ids.erase(iter); + return true; + } + + return false; } CrxInstaller::CrxInstaller(const FilePath& install_directory, @@ -49,7 +81,8 @@ CrxInstaller::CrxInstaller(const FilePath& install_directory, create_app_shortcut_(false), frontend_(frontend), client_(client), - apps_require_extension_mime_type_(false) { + apps_require_extension_mime_type_(false), + allow_silent_install_(false) { extensions_enabled_ = frontend_->extensions_enabled(); } @@ -221,7 +254,9 @@ void CrxInstaller::ConfirmInstall() { current_version_ = frontend_->extension_prefs()->GetVersionString(extension_->id()); - if (client_) { + if (client_ && + (!allow_silent_install_ || + !ClearWhitelistedInstallId(extension_->id()))) { AddRef(); // Balanced in Proceed() and Abort(). client_->ConfirmInstall(this, extension_.get()); } else { diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h index ce9b8e6..7613d93 100644 --- a/chrome/browser/extensions/crx_installer.h +++ b/chrome/browser/extensions/crx_installer.h @@ -44,6 +44,18 @@ class CrxInstaller : public SandboxedExtensionUnpackerClient, public ExtensionInstallUI::Delegate { public: + + // This is pretty lame, but given the difficulty of connecting a particular + // ExtensionFunction to a resulting download in the download manager, it's + // currently necessary. This is the |id| of an extension to be installed + // *by the web store only* which should not get the permissions install + // prompt. + // crbug.com/54916 + static void SetWhitelistedInstallId(const std::string& id); + + // Returns whether |id| was found and removed (was whitelisted). + static bool ClearWhitelistedInstallId(const std::string& id); + // Constructor. Extensions will be unpacked to |install_directory|. // Extension objects will be sent to |frontend|, and any UI will be shown // via |client|. For silent install, pass NULL for |client|. @@ -83,6 +95,9 @@ class CrxInstaller allow_privilege_increase_ = val; } + bool allow_silent_install() const { return allow_silent_install_; } + void set_allow_silent_install(bool val) { allow_silent_install_ = val; } + bool limit_web_extent_to_download_host() const { return limit_web_extent_to_download_host_; } @@ -203,6 +218,13 @@ class CrxInstaller // Used to trigger extra checks before installing. bool apps_require_extension_mime_type_; + // Allows for the possibility of a normal install (one in which a |client| + // is provided in the ctor) to procede without showing the permissions prompt + // dialog. Note that this will only take place if |allow_silent_install_| + // is true AND the unpacked id of the extension is whitelisted with + // SetWhitelistedInstallId(). + bool allow_silent_install_; + // The value of the content type header sent with the CRX. // Ignorred unless |require_extension_mime_type_| is true. std::string original_mime_type_; diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc index 005bff0..bddce0f 100644 --- a/chrome/browser/extensions/extension_apitest.cc +++ b/chrome/browser/extensions/extension_apitest.cc @@ -81,32 +81,53 @@ bool ExtensionApiTest::RunExtensionTest(const char* extension_name) { } bool ExtensionApiTest::RunExtensionSubtest(const char* extension_name, - const std::string& subtest_page) { - DCHECK(!subtest_page.empty()) << "Argument subtest_page is required."; - return RunExtensionTestImpl(extension_name, subtest_page); + const std::string& page_url) { + DCHECK(!page_url.empty()) << "Argument page_url is required."; + return RunExtensionTestImpl(extension_name, page_url); } -// Load an extension and wait for it to notify of PASSED or FAILED. +bool ExtensionApiTest::RunPageTest(const std::string& page_url) { + return RunExtensionSubtest("", page_url); +} + +// Load |extension_name| extension and/or |page_url| and wait for +// PASSED or FAILED notification. bool ExtensionApiTest::RunExtensionTestImpl(const char* extension_name, - const std::string& subtest_page) { + const std::string& page_url) { ResultCatcher catcher; - LOG(INFO) << "Running ExtensionApiTest with: " << extension_name; - - if (!LoadExtension(test_data_dir_.AppendASCII(extension_name))) { - message_ = "Failed to load extension."; - return false; + DCHECK(!std::string(extension_name).empty() || !page_url.empty()) << + "extension_name and page_url cannot both be empty"; + LOG(INFO) << "Running ExtensionApiTest"; + + if (!std::string(extension_name).empty()) { + LOG(INFO) << "Loading Extension: " << extension_name; + if (!LoadExtension(test_data_dir_.AppendASCII(extension_name))) { + message_ = "Failed to load extension."; + return false; + } } - // If there is a subtest to load, navigate to the subtest page. - if (!subtest_page.empty()) { - ExtensionsService* service = browser()->profile()->GetExtensionsService(); - Extension* extension = - service->GetExtensionById(last_loaded_extension_id_, false); - if (!extension) - return false; + // If there is a page_url to load, navigate it. + if (!page_url.empty()) { + GURL url = GURL(page_url); + + // Note: We use is_valid() here in the expectation that the provided url + // may lack a scheme & host and thus be a relative url within the loaded + // extension. + if (!url.is_valid()) { + DCHECK(!std::string(extension_name).empty()) << + "Relative page_url given with no extension_name"; + + ExtensionsService* service = browser()->profile()->GetExtensionsService(); + Extension* extension = + service->GetExtensionById(last_loaded_extension_id_, false); + if (!extension) + return false; + + url = extension->GetResourceURL(page_url); + } - GURL url = extension->GetResourceURL(subtest_page); - LOG(ERROR) << "Loading subtest page url: " << url.spec(); + LOG(ERROR) << "Loading page url: " << url.spec(); ui_test_utils::NavigateToURL(browser(), url); } diff --git a/chrome/browser/extensions/extension_apitest.h b/chrome/browser/extensions/extension_apitest.h index dd56a10..638131c 100644 --- a/chrome/browser/extensions/extension_apitest.h +++ b/chrome/browser/extensions/extension_apitest.h @@ -69,11 +69,17 @@ class ExtensionApiTest : public ExtensionBrowserTest { // |extension_name| is a directory in "test/data/extensions/api_test". bool RunExtensionTest(const char* extension_name); - // Load |extension_name|, load page at path |subtest_page| under the - // extension, and wait for pass / fail notification. |extension_name| - // is a directory in "test/data/extensions/api_test". + // If not empty, Load |extension_name|, load |page_url| and wait for pass / + // fail notification from the extension API on the page. Note that if + // |page_url| is not a valid url, it will be treated as a resource within + // the extension. |extension_name| is a directory in + // "test/data/extensions/api_test". bool RunExtensionSubtest(const char* extension_name, - const std::string& subtest_page); + const std::string& page_url); + + // Load |page_url| and wait for pass / fail notification from the extension + // API on the page. + bool RunPageTest(const std::string& page_url); // Test that exactly one extension loaded. If so, return a pointer to // the extension. If not, return NULL and set message_. diff --git a/chrome/browser/extensions/extension_gallery_install_apitest.cc b/chrome/browser/extensions/extension_gallery_install_apitest.cc new file mode 100644 index 0000000..8c704a9 --- /dev/null +++ b/chrome/browser/extensions/extension_gallery_install_apitest.cc @@ -0,0 +1,36 @@ +// Copyright (c) 2010 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/stringprintf.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_webstore_private_api.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/test/ui_test_utils.h" +#include "net/base/mock_host_resolver.h" + +class ExtensionGalleryInstallApiTest : public ExtensionApiTest { + public: + void SetUpCommandLine(CommandLine* command_line) { + ExtensionApiTest::SetUpCommandLine(command_line); + command_line->AppendSwitchASCII(switches::kAppsGalleryURL, + "http://www.example.com"); + } +}; + +IN_PROC_BROWSER_TEST_F(ExtensionGalleryInstallApiTest, InstallAndUninstall) { + host_resolver()->AddRule("www.example.com", "127.0.0.1"); + ASSERT_TRUE(test_server()->Start()); + + std::string base_url = StringPrintf( + "http://www.example.com:%u/files/extensions/", + test_server()->host_port_pair().port()); + + std::string testing_install_base_url = base_url; + testing_install_base_url += "good.crx"; + InstallFunction::SetTestingInstallBaseUrl(testing_install_base_url.c_str()); + + std::string page_url = base_url; + page_url += "api_test/extension_gallery_install/test.html"; + ASSERT_TRUE(RunPageTest(page_url.c_str())); +} diff --git a/chrome/browser/extensions/extension_management_api.cc b/chrome/browser/extensions/extension_management_api.cc index 27caab3..e51c747 100644 --- a/chrome/browser/extensions/extension_management_api.cc +++ b/chrome/browser/extensions/extension_management_api.cc @@ -10,11 +10,14 @@ #include "base/basictypes.h" #include "base/json/json_writer.h" #include "base/string_number_conversions.h" +#include "base/string_util.h" #include "chrome/browser/browser.h" #include "chrome/browser/extensions/extension_event_names.h" #include "chrome/browser/extensions/extension_message_service.h" +#include "chrome/browser/extensions/extension_updater.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/profile.h" +#include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_error_utils.h" #include "chrome/common/notification_service.h" #include "chrome/common/notification_type.h" @@ -22,6 +25,8 @@ using base::IntToString; namespace events = extension_event_names; +namespace { + const char kAppLaunchUrlKey[] = "appLaunchUrl"; const char kEnabledKey[] = "enabled"; const char kIconsKey[] = "icons"; @@ -34,6 +39,8 @@ const char kUrlKey[] = "url"; const char kNoExtensionError[] = "No extension with id *"; +} + ExtensionsService* ExtensionManagementFunction::service() { return profile()->GetExtensionsService(); } @@ -117,11 +124,6 @@ bool SetEnabledFunction::RunImpl() { return true; } -bool InstallFunction::RunImpl() { - NOTIMPLEMENTED(); - return false; -} - bool UninstallFunction::RunImpl() { std::string extension_id; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id)); @@ -136,7 +138,6 @@ bool UninstallFunction::RunImpl() { return true; } - // static ExtensionManagementEventRouter* ExtensionManagementEventRouter::GetInstance() { return Singleton<ExtensionManagementEventRouter>::get(); diff --git a/chrome/browser/extensions/extension_management_api.h b/chrome/browser/extensions/extension_management_api.h index 9d3108d..841baea 100644 --- a/chrome/browser/extensions/extension_management_api.h +++ b/chrome/browser/extensions/extension_management_api.h @@ -30,12 +30,6 @@ class SetEnabledFunction : public ExtensionManagementFunction { DECLARE_EXTENSION_FUNCTION_NAME("experimental.management.setEnabled"); }; -class InstallFunction : public ExtensionManagementFunction { - ~InstallFunction() {} - virtual bool RunImpl(); - DECLARE_EXTENSION_FUNCTION_NAME("experimental.management.install"); -}; - class UninstallFunction : public ExtensionManagementFunction { ~UninstallFunction() {} virtual bool RunImpl(); diff --git a/chrome/browser/extensions/extension_webstore_private_api.cc b/chrome/browser/extensions/extension_webstore_private_api.cc index 536008c..184c9e5 100644 --- a/chrome/browser/extensions/extension_webstore_private_api.cc +++ b/chrome/browser/extensions/extension_webstore_private_api.cc @@ -4,17 +4,72 @@ #include "chrome/browser/extensions/extension_webstore_private_api.h" +#include "base/string_util.h" #include "base/values.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/crx_installer.h" #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/extensions/extension_constants.h" +#include "net/base/escape.h" + +namespace { + +const char* install_base_url = extension_urls::kGalleryUpdateHttpsUrl; + +} static bool IsWebStoreURL(const GURL& url) { GURL store_url(Extension::ChromeStoreURL()); if (!url.is_valid() || !store_url.is_valid()) { return false; } - return store_url.GetOrigin() == url.GetOrigin(); + + // Ignore port. It may be set on |url| during tests, but cannot be set on + // ChromeStoreURL. + return store_url.scheme() == url.scheme() && + store_url.host() == url.host(); +} + +// static +void InstallFunction::SetTestingInstallBaseUrl( + const char* testing_install_base_url) { + install_base_url = testing_install_base_url; +} + +bool InstallFunction::RunImpl() { + if (!IsWebStoreURL(source_url())) + return false; + + std::string id; + EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &id)); + EXTENSION_FUNCTION_VALIDATE(Extension::IdIsValid(id)); + + std::vector<std::string> params; + params.push_back("id=" + id); + params.push_back("lang=" + g_browser_process->GetApplicationLocale()); + params.push_back("uc"); + std::string url_string = install_base_url; + + GURL url(url_string + "?response=redirect&x=" + + EscapeQueryParamValue(JoinString(params, '&'), true)); + DCHECK(url.is_valid()); + + // Cleared in ~CrxInstaller(). + CrxInstaller::SetWhitelistedInstallId(id); + + // The download url for the given |id| is now contained in |url|. We + // navigate the current (calling) tab to this url which will result in a + // download starting. Once completed it will go through the normal extension + // install flow. The above call to SetWhitelistedInstallId will bypass the + // normal permissions install dialog. + NavigationController& controller = + dispatcher()->delegate()->associated_tab_contents()->controller(); + controller.LoadURL(url, source_url(), PageTransition::LINK); + + return true; } bool GetSyncLoginFunction::RunImpl() { diff --git a/chrome/browser/extensions/extension_webstore_private_api.h b/chrome/browser/extensions/extension_webstore_private_api.h index 1f2abff..0ba27ac 100644 --- a/chrome/browser/extensions/extension_webstore_private_api.h +++ b/chrome/browser/extensions/extension_webstore_private_api.h @@ -8,6 +8,16 @@ #include "chrome/browser/extensions/extension_function.h" +class InstallFunction : public SyncExtensionFunction { + public: + static void SetTestingInstallBaseUrl(const char* testing_install_base_url); + + protected: + ~InstallFunction() {} + virtual bool RunImpl(); + DECLARE_EXTENSION_FUNCTION_NAME("webstorePrivate.install"); +}; + class GetSyncLoginFunction : public SyncExtensionFunction { virtual bool RunImpl(); DECLARE_EXTENSION_FUNCTION_NAME("webstorePrivate.getSyncLogin"); diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc index e876e9f..7011ed7 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -164,7 +164,7 @@ bool IsGalleryDownloadURL(const GURL& download_url) { switches::kAppsGalleryURL); if (!command_line_gallery_url.empty() && StartsWithASCII(download_url.spec(), - extension_urls::kGalleryDownloadPrefix, false)) + command_line_gallery_url, false)) return true; return false; diff --git a/chrome/browser/resources/webstore_app/manifest.json b/chrome/browser/resources/webstore_app/manifest.json index 6282d7c..b27e1e3 100644 --- a/chrome/browser/resources/webstore_app/manifest.json +++ b/chrome/browser/resources/webstore_app/manifest.json @@ -16,6 +16,7 @@ ] }, "permissions": [ - "webstorePrivate" + "webstorePrivate", + "experimental" ] } |