diff options
author | asargent <asargent@chromium.org> | 2015-10-15 14:51:48 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-10-15 21:52:40 +0000 |
commit | 631a99a61cbe3e7e03e372b3d37c6a37fa700392 (patch) | |
tree | 95c4fc66df8e20ba8744159f0ae63913e1750a4a | |
parent | a33cc7d19a2c878e2a7d9b4a540d06f3e0c3fc16 (diff) | |
download | chromium_src-631a99a61cbe3e7e03e372b3d37c6a37fa700392.zip chromium_src-631a99a61cbe3e7e03e372b3d37c6a37fa700392.tar.gz chromium_src-631a99a61cbe3e7e03e372b3d37c6a37fa700392.tar.bz2 |
Add extensions code to use common updater in components/update_client/
This is another piece of work towards implementing differential
extensions update and share autoupdate code with the chrome components
system. It adds code to the extensions/browser directory that makes use
of the common autoupdate code via the update_client::UpdateClient class
to send update checks to the server, download a full .crx or
differential update, and hand back an unpacked directory with the
contents of the new extension version.
This CL does not yet add any code in chrome/ that actually uses these
new capabilities; that will be in a forthcoming CL.
BUG=490418
Review URL: https://codereview.chromium.org/1362043005
Cr-Commit-Position: refs/heads/master@{#354370}
38 files changed, 944 insertions, 334 deletions
diff --git a/chrome/browser/extensions/chrome_extensions_browser_client.cc b/chrome/browser/extensions/chrome_extensions_browser_client.cc index db81dee..abae1f8 100644 --- a/chrome/browser/extensions/chrome_extensions_browser_client.cc +++ b/chrome/browser/extensions/chrome_extensions_browser_client.cc @@ -26,6 +26,7 @@ #include "chrome/browser/extensions/extension_system_factory.h" #include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/menu_manager.h" +#include "chrome/browser/extensions/updater/chrome_update_client_config.h" #include "chrome/browser/external_protocol/external_protocol_handler.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" @@ -36,6 +37,7 @@ #include "chrome/common/extensions/features/feature_channel.h" #include "chrome/common/pref_names.h" #include "components/net_log/chrome_net_log.h" +#include "components/update_client/update_client.h" #include "components/version_info/version_info.h" #include "content/public/browser/render_process_host.h" #include "content/public/common/content_switches.h" @@ -380,4 +382,11 @@ void ChromeExtensionsBrowserClient::AttachExtensionTaskManagerTag( } } +scoped_refptr<update_client::UpdateClient> +ChromeExtensionsBrowserClient::CreateUpdateClient( + content::BrowserContext* context) { + return update_client::UpdateClientFactory( + make_scoped_refptr(new ChromeUpdateClientConfig(context))); +} + } // namespace extensions diff --git a/chrome/browser/extensions/chrome_extensions_browser_client.h b/chrome/browser/extensions/chrome_extensions_browser_client.h index c9d41a3..4633436 100644 --- a/chrome/browser/extensions/chrome_extensions_browser_client.h +++ b/chrome/browser/extensions/chrome_extensions_browser_client.h @@ -5,11 +5,13 @@ #ifndef CHROME_BROWSER_EXTENSIONS_CHROME_EXTENSIONS_BROWSER_CLIENT_H_ #define CHROME_BROWSER_EXTENSIONS_CHROME_EXTENSIONS_BROWSER_CLIENT_H_ -#include <map> +#include <string> +#include <vector> #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/lazy_instance.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "chrome/browser/extensions/chrome_notification_observer.h" #include "extensions/browser/extensions_browser_client.h" @@ -111,6 +113,8 @@ class ChromeExtensionsBrowserClient : public ExtensionsBrowserClient { int view_instance_id) override; void AttachExtensionTaskManagerTag(content::WebContents* web_contents, ViewType view_type) override; + scoped_refptr<update_client::UpdateClient> CreateUpdateClient( + content::BrowserContext* context) override; private: friend struct base::DefaultLazyInstanceTraits<ChromeExtensionsBrowserClient>; diff --git a/chrome/browser/extensions/extension_system_impl.cc b/chrome/browser/extensions/extension_system_impl.cc index bbf1452..836c829 100644 --- a/chrome/browser/extensions/extension_system_impl.cc +++ b/chrome/browser/extensions/extension_system_impl.cc @@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/memory/weak_ptr.h" #include "base/strings/string_tokenizer.h" #include "base/trace_event/trace_event.h" @@ -374,6 +375,12 @@ scoped_ptr<ExtensionSet> ExtensionSystemImpl::GetDependentExtensions( extension); } +void ExtensionSystemImpl::InstallUpdate(const std::string& extension_id, + const base::FilePath& temp_dir) { + NOTREACHED() << "Not yet implemented"; + base::DeleteFile(temp_dir, true /* recursive */); +} + void ExtensionSystemImpl::RegisterExtensionWithRequestContexts( const Extension* extension, const base::Closure& callback) { diff --git a/chrome/browser/extensions/extension_system_impl.h b/chrome/browser/extensions/extension_system_impl.h index 49369ff..0ad9357 100644 --- a/chrome/browser/extensions/extension_system_impl.h +++ b/chrome/browser/extensions/extension_system_impl.h @@ -54,6 +54,8 @@ class ExtensionSystemImpl : public ExtensionSystem { ContentVerifier* content_verifier() override; // shared scoped_ptr<ExtensionSet> GetDependentExtensions( const Extension* extension) override; + void InstallUpdate(const std::string& extension_id, + const base::FilePath& temp_dir) override; private: friend class ExtensionSystemSharedFactory; diff --git a/chrome/browser/extensions/test_extension_system.cc b/chrome/browser/extensions/test_extension_system.cc index ad06d0c..5801ae7 100644 --- a/chrome/browser/extensions/test_extension_system.cc +++ b/chrome/browser/extensions/test_extension_system.cc @@ -127,6 +127,11 @@ scoped_ptr<ExtensionSet> TestExtensionSystem::GetDependentExtensions( extension); } +void TestExtensionSystem::InstallUpdate(const std::string& extension_id, + const base::FilePath& temp_dir) { + NOTREACHED(); +} + // static scoped_ptr<KeyedService> TestExtensionSystem::Build( content::BrowserContext* profile) { diff --git a/chrome/browser/extensions/test_extension_system.h b/chrome/browser/extensions/test_extension_system.h index d0c11e4..9e85ec4 100644 --- a/chrome/browser/extensions/test_extension_system.h +++ b/chrome/browser/extensions/test_extension_system.h @@ -58,6 +58,8 @@ class TestExtensionSystem : public ExtensionSystem { ContentVerifier* content_verifier() override; scoped_ptr<ExtensionSet> GetDependentExtensions( const Extension* extension) override; + void InstallUpdate(const std::string& extension_id, + const base::FilePath& temp_dir) override; // Note that you probably want to use base::RunLoop().RunUntilIdle() right // after this to run all the accumulated tasks. diff --git a/chrome/browser/extensions/updater/chrome_update_client_config.cc b/chrome/browser/extensions/updater/chrome_update_client_config.cc new file mode 100644 index 0000000..1c095f7 --- /dev/null +++ b/chrome/browser/extensions/updater/chrome_update_client_config.cc @@ -0,0 +1,89 @@ +// Copyright 2015 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 "base/version.h" +#include "chrome/browser/component_updater/component_patcher_operation_out_of_process.h" +#include "chrome/browser/extensions/updater/chrome_update_client_config.h" +#include "chrome/browser/update_client/chrome_update_query_params_delegate.h" +#include "chrome/common/channel_info.h" +#include "content/public/browser/browser_context.h" + +namespace extensions { + +ChromeUpdateClientConfig::ChromeUpdateClientConfig( + content::BrowserContext* context) + : impl_(base::CommandLine::ForCurrentProcess(), + context->GetRequestContext()) { + impl_.set_enable_alt_source_url(false); +} + +int ChromeUpdateClientConfig::InitialDelay() const { + return impl_.InitialDelay(); +} + +int ChromeUpdateClientConfig::NextCheckDelay() const { + return impl_.NextCheckDelay(); +} + +int ChromeUpdateClientConfig::StepDelay() const { + return impl_.StepDelay(); +} + +int ChromeUpdateClientConfig::OnDemandDelay() const { + return impl_.OnDemandDelay(); +} + +int ChromeUpdateClientConfig::UpdateDelay() const { + return 0; +} + +std::vector<GURL> ChromeUpdateClientConfig::UpdateUrl() const { + return impl_.UpdateUrl(); +} + +std::vector<GURL> ChromeUpdateClientConfig::PingUrl() const { + return impl_.PingUrl(); +} + +base::Version ChromeUpdateClientConfig::GetBrowserVersion() const { + return impl_.GetBrowserVersion(); +} + +std::string ChromeUpdateClientConfig::GetChannel() const { + return chrome::GetChannelString(); +} + +std::string ChromeUpdateClientConfig::GetLang() const { + return ChromeUpdateQueryParamsDelegate::GetLang(); +} + +std::string ChromeUpdateClientConfig::GetOSLongName() const { + return impl_.GetOSLongName(); +} + +std::string ChromeUpdateClientConfig::ExtraRequestParams() const { + return impl_.ExtraRequestParams(); +} + +net::URLRequestContextGetter* ChromeUpdateClientConfig::RequestContext() const { + return impl_.RequestContext(); +} + +scoped_refptr<update_client::OutOfProcessPatcher> +ChromeUpdateClientConfig::CreateOutOfProcessPatcher() const { + return make_scoped_refptr(new component_updater::ChromeOutOfProcessPatcher); +} + +bool ChromeUpdateClientConfig::DeltasEnabled() const { + return impl_.DeltasEnabled(); +} + +bool ChromeUpdateClientConfig::UseBackgroundDownloader() const { + return impl_.UseBackgroundDownloader(); +} + +ChromeUpdateClientConfig::~ChromeUpdateClientConfig() {} + +} // namespace extensions diff --git a/chrome/browser/extensions/updater/chrome_update_client_config.h b/chrome/browser/extensions/updater/chrome_update_client_config.h new file mode 100644 index 0000000..1e6cab6 --- /dev/null +++ b/chrome/browser/extensions/updater/chrome_update_client_config.h @@ -0,0 +1,55 @@ +// Copyright 2015 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_UPDATER_CHROME_UPDATE_CLIENT_CONFIG_H_ +#define CHROME_BROWSER_EXTENSIONS_UPDATER_CHROME_UPDATE_CLIENT_CONFIG_H_ + +#include <string> +#include <vector> + +#include "base/memory/ref_counted.h" +#include "components/component_updater/configurator_impl.h" +#include "extensions/browser/updater/update_client_config.h" + +namespace content { +class BrowserContext; +} + +namespace extensions { + +class ChromeUpdateClientConfig : public UpdateClientConfig { + public: + explicit ChromeUpdateClientConfig(content::BrowserContext* context); + + int InitialDelay() const override; + int NextCheckDelay() const override; + int StepDelay() const override; + int OnDemandDelay() const override; + int UpdateDelay() const override; + std::vector<GURL> UpdateUrl() const override; + std::vector<GURL> PingUrl() const override; + base::Version GetBrowserVersion() const override; + std::string GetChannel() const override; + std::string GetLang() const override; + std::string GetOSLongName() const override; + std::string ExtraRequestParams() const override; + net::URLRequestContextGetter* RequestContext() const override; + scoped_refptr<update_client::OutOfProcessPatcher> CreateOutOfProcessPatcher() + const override; + bool DeltasEnabled() const override; + bool UseBackgroundDownloader() const override; + + protected: + friend class base::RefCountedThreadSafe<ChromeUpdateClientConfig>; + ~ChromeUpdateClientConfig() override; + + private: + component_updater::ConfiguratorImpl impl_; + + DISALLOW_COPY_AND_ASSIGN(ChromeUpdateClientConfig); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_UPDATER_CHROME_UPDATE_CLIENT_CONFIG_H_ diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index 098eae3..65fdc7a 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -851,6 +851,8 @@ 'browser/extensions/unpacked_installer.h', 'browser/extensions/updater/chrome_extension_downloader_factory.cc', 'browser/extensions/updater/chrome_extension_downloader_factory.h', + 'browser/extensions/updater/chrome_update_client_config.cc', + 'browser/extensions/updater/chrome_update_client_config.h', 'browser/extensions/updater/extension_updater.cc', 'browser/extensions/updater/extension_updater.h', 'browser/extensions/user_script_listener.cc', diff --git a/components/component_updater/configurator_impl.cc b/components/component_updater/configurator_impl.cc index cc6d7f0..fb8f1db 100644 --- a/components/component_updater/configurator_impl.cc +++ b/components/component_updater/configurator_impl.cc @@ -192,4 +192,8 @@ bool ConfiguratorImpl::UseBackgroundDownloader() const { return background_downloads_enabled_; } +void ConfiguratorImpl::set_enable_alt_source_url(bool enable_alt_source_url) { + fallback_to_alt_source_url_enabled_ = enable_alt_source_url; +} + } // namespace component_updater diff --git a/components/component_updater/configurator_impl.h b/components/component_updater/configurator_impl.h index df58473..b2e75a9 100644 --- a/components/component_updater/configurator_impl.h +++ b/components/component_updater/configurator_impl.h @@ -77,6 +77,11 @@ class ConfiguratorImpl { // non on-demand components. bool UseBackgroundDownloader() const; + // Setting this to false means that we'll only use secure transport (eg https) + // for update/ping urls. This is already false by default everywhere but older + // versions of Windows XP. + void set_enable_alt_source_url(bool enable_alt_source_url); + private: net::URLRequestContextGetter* url_request_getter_; std::string extra_info_; diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn index 9c9424e..3fe0d25 100644 --- a/extensions/browser/BUILD.gn +++ b/extensions/browser/BUILD.gn @@ -18,6 +18,8 @@ source_set("browser") { "//components/pref_registry", "//components/sessions", "//components/ui/zoom", + "//components/update_client", + "//components/version_info", "//components/web_cache/browser", "//components/web_modal", "//content/public/browser", diff --git a/extensions/browser/extension_system.h b/extensions/browser/extension_system.h index c6d2e09..7758051d 100644 --- a/extensions/browser/extension_system.h +++ b/extensions/browser/extension_system.h @@ -122,6 +122,13 @@ class ExtensionSystem : public KeyedService { // so it can be retrieved from ExtensionSystem directly. virtual scoped_ptr<ExtensionSet> GetDependentExtensions( const Extension* extension) = 0; + + // Install an updated version of |extension_id| with the version given in + // temp_dir. Ownership of |temp_dir| in the filesystem is transferred and + // implementors of this function are responsible for cleaning it up on + // errors, etc. + virtual void InstallUpdate(const std::string& extension_id, + const base::FilePath& temp_dir) = 0; }; } // namespace extensions diff --git a/extensions/browser/extensions_browser_client.cc b/extensions/browser/extensions_browser_client.cc index 495ac50..d113f11 100644 --- a/extensions/browser/extensions_browser_client.cc +++ b/extensions/browser/extensions_browser_client.cc @@ -6,7 +6,9 @@ #include "base/basictypes.h" #include "base/logging.h" +#include "components/update_client/update_client.h" #include "extensions/browser/extension_error.h" +#include "extensions/browser/updater/update_client_config.h" namespace extensions { @@ -16,6 +18,11 @@ ExtensionsBrowserClient* g_client = NULL; } // namespace +scoped_refptr<update_client::UpdateClient> +ExtensionsBrowserClient::CreateUpdateClient(content::BrowserContext* context) { + return scoped_refptr<update_client::UpdateClient>(nullptr); +} + void ExtensionsBrowserClient::ReportError(content::BrowserContext* context, scoped_ptr<ExtensionError> error) { LOG(ERROR) << error->GetDebugString(); diff --git a/extensions/browser/extensions_browser_client.h b/extensions/browser/extensions_browser_client.h index 89763d9..a5ddd86 100644 --- a/extensions/browser/extensions_browser_client.h +++ b/extensions/browser/extensions_browser_client.h @@ -8,6 +8,7 @@ #include <string> #include <vector> +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "extensions/browser/extension_event_histogram_value.h" #include "extensions/browser/extension_prefs_observer.h" @@ -35,6 +36,10 @@ class URLRequest; class URLRequestJob; } +namespace update_client { +class UpdateClient; +} + namespace extensions { class ApiActivityMonitor; @@ -236,6 +241,10 @@ class ExtensionsBrowserClient { virtual void AttachExtensionTaskManagerTag(content::WebContents* web_contents, ViewType view_type) {} + // Returns a new UpdateClient. + virtual scoped_refptr<update_client::UpdateClient> CreateUpdateClient( + content::BrowserContext* context); + // Returns the single instance of |this|. static ExtensionsBrowserClient* Get(); diff --git a/extensions/browser/mock_extension_system.cc b/extensions/browser/mock_extension_system.cc index 00e3fc2..efcc546 100644 --- a/extensions/browser/mock_extension_system.cc +++ b/extensions/browser/mock_extension_system.cc @@ -71,4 +71,9 @@ scoped_ptr<ExtensionSet> MockExtensionSystem::GetDependentExtensions( return scoped_ptr<ExtensionSet>(); } +void MockExtensionSystem::InstallUpdate(const std::string& extension_id, + const base::FilePath& temp_dir) { + NOTREACHED(); +} + } // namespace extensions diff --git a/extensions/browser/mock_extension_system.h b/extensions/browser/mock_extension_system.h index af847f5..b055b42 100644 --- a/extensions/browser/mock_extension_system.h +++ b/extensions/browser/mock_extension_system.h @@ -41,6 +41,8 @@ class MockExtensionSystem : public ExtensionSystem { ContentVerifier* content_verifier() override; scoped_ptr<ExtensionSet> GetDependentExtensions( const Extension* extension) override; + void InstallUpdate(const std::string& extension_id, + const base::FilePath& temp_dir) override; private: content::BrowserContext* browser_context_; diff --git a/extensions/browser/test_extensions_browser_client.cc b/extensions/browser/test_extensions_browser_client.cc index d98f7af..da5ef27 100644 --- a/extensions/browser/test_extensions_browser_client.cc +++ b/extensions/browser/test_extensions_browser_client.cc @@ -27,6 +27,11 @@ TestExtensionsBrowserClient::TestExtensionsBrowserClient( TestExtensionsBrowserClient::~TestExtensionsBrowserClient() {} +void TestExtensionsBrowserClient::SetUpdateClientFactory( + const base::Callback<update_client::UpdateClient*(void)>& factory) { + update_client_factory_ = factory; +} + void TestExtensionsBrowserClient::SetIncognitoContext(BrowserContext* context) { // If a context is provided it must be off-the-record. DCHECK(!context || context->IsOffTheRecord()); @@ -204,4 +209,12 @@ TestExtensionsBrowserClient::GetExtensionWebContentsObserver( return nullptr; } +scoped_refptr<update_client::UpdateClient> +TestExtensionsBrowserClient::CreateUpdateClient( + content::BrowserContext* context) { + return update_client_factory_.is_null() + ? nullptr + : make_scoped_refptr(update_client_factory_.Run()); +} + } // namespace extensions diff --git a/extensions/browser/test_extensions_browser_client.h b/extensions/browser/test_extensions_browser_client.h index 274a8a9..be660e7 100644 --- a/extensions/browser/test_extensions_browser_client.h +++ b/extensions/browser/test_extensions_browser_client.h @@ -5,7 +5,13 @@ #ifndef EXTENSIONS_BROWSER_TEST_EXTENSIONS_BROWSER_CLIENT_H_ #define EXTENSIONS_BROWSER_TEST_EXTENSIONS_BROWSER_CLIENT_H_ +#include <string> +#include <vector> + +#include "base/callback.h" #include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "components/update_client/update_client.h" #include "extensions/browser/extensions_browser_client.h" #include "extensions/browser/updater/extension_cache.h" @@ -30,6 +36,10 @@ class TestExtensionsBrowserClient : public ExtensionsBrowserClient { extension_cache_ = extension_cache.Pass(); } + // Sets a factory to respond to calls of the CreateUpdateClient method. + void SetUpdateClientFactory( + const base::Callback<update_client::UpdateClient*(void)>& factory); + // Associates an incognito context with |main_context_|. void SetIncognitoContext(content::BrowserContext* incognito_context); @@ -97,6 +107,8 @@ class TestExtensionsBrowserClient : public ExtensionsBrowserClient { bool IsMinBrowserVersionSupported(const std::string& min_version) override; ExtensionWebContentsObserver* GetExtensionWebContentsObserver( content::WebContents* web_contents) override; + scoped_refptr<update_client::UpdateClient> CreateUpdateClient( + content::BrowserContext* context) override; private: content::BrowserContext* main_context_; // Not owned. @@ -110,6 +122,8 @@ class TestExtensionsBrowserClient : public ExtensionsBrowserClient { scoped_ptr<ExtensionCache> extension_cache_; + base::Callback<update_client::UpdateClient*(void)> update_client_factory_; + DISALLOW_COPY_AND_ASSIGN(TestExtensionsBrowserClient); }; diff --git a/extensions/browser/updater/update_client_config.cc b/extensions/browser/updater/update_client_config.cc new file mode 100644 index 0000000..9f1d0d1 --- /dev/null +++ b/extensions/browser/updater/update_client_config.cc @@ -0,0 +1,29 @@ +// Copyright 2015 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 "extensions/browser/updater/update_client_config.h" + +#include "content/public/browser/browser_thread.h" + +namespace extensions { + +UpdateClientConfig::UpdateClientConfig() {} + +scoped_refptr<base::SequencedTaskRunner> +UpdateClientConfig::GetSequencedTaskRunner() const { + return content::BrowserThread::GetBlockingPool() + ->GetSequencedTaskRunnerWithShutdownBehavior( + content::BrowserThread::GetBlockingPool()->GetSequenceToken(), + base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); +} + +scoped_refptr<base::SingleThreadTaskRunner> +UpdateClientConfig::GetSingleThreadTaskRunner() const { + return content::BrowserThread::GetMessageLoopProxyForThread( + content::BrowserThread::FILE); +} + +UpdateClientConfig::~UpdateClientConfig() {} + +} // namespace extensions diff --git a/extensions/browser/updater/update_client_config.h b/extensions/browser/updater/update_client_config.h new file mode 100644 index 0000000..32e7811 --- /dev/null +++ b/extensions/browser/updater/update_client_config.h @@ -0,0 +1,40 @@ +// Copyright 2015 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. + +#ifndef EXTENSIONS_BROWSER_UPDATER_UPDATE_CLIENT_CONFIG_H_ +#define EXTENSIONS_BROWSER_UPDATER_UPDATE_CLIENT_CONFIG_H_ + +#include <string> + +#include "base/memory/ref_counted.h" +#include "components/update_client/configurator.h" + +namespace base { +class SequencedTaskRunner; +class SingleThreadTaskRunner; +} + +namespace extensions { + +// Used to provide configuration settings to the UpdateClient. +class UpdateClientConfig : public update_client::Configurator { + public: + UpdateClientConfig(); + + scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() + const override; + scoped_refptr<base::SingleThreadTaskRunner> GetSingleThreadTaskRunner() + const override; + + protected: + friend class base::RefCountedThreadSafe<UpdateClientConfig>; + ~UpdateClientConfig() override; + + private: + DISALLOW_COPY_AND_ASSIGN(UpdateClientConfig); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_UPDATER_UPDATE_CLIENT_CONFIG_H_ diff --git a/extensions/browser/updater/update_data_provider.cc b/extensions/browser/updater/update_data_provider.cc new file mode 100644 index 0000000..e0ad403 --- /dev/null +++ b/extensions/browser/updater/update_data_provider.cc @@ -0,0 +1,72 @@ +// Copyright 2015 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 "extensions/browser/updater/update_data_provider.h" + +#include "base/base64.h" +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "components/update_client/update_client.h" +#include "content/public/browser/browser_thread.h" +#include "crypto/sha2.h" +#include "extensions/browser/content_verifier.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_system.h" +#include "extensions/browser/updater/update_install_shim.h" +#include "extensions/common/extension.h" + +namespace extensions { + +UpdateDataProvider::UpdateDataProvider(content::BrowserContext* context, + const InstallCallback& callback) + : context_(context), callback_(callback) {} + +UpdateDataProvider::~UpdateDataProvider() {} + +void UpdateDataProvider::Shutdown() { + context_ = nullptr; +} + +void UpdateDataProvider::GetData( + const std::vector<std::string>& ids, + std::vector<update_client::CrxComponent>* data) { + if (!context_) + return; + const ExtensionRegistry* registry = ExtensionRegistry::Get(context_); + for (const auto& id : ids) { + const Extension* extension = registry->GetInstalledExtension(id); + if (!extension) + continue; + data->push_back(update_client::CrxComponent()); + update_client::CrxComponent* info = &data->back(); + std::string pubkey_bytes; + base::Base64Decode(extension->public_key(), &pubkey_bytes); + info->pk_hash.resize(crypto::kSHA256Length, 0); + crypto::SHA256HashString(pubkey_bytes, vector_as_array(&info->pk_hash), + info->pk_hash.size()); + info->version = *extension->version(); + info->allow_background_download = false; + + info->installer = new UpdateInstallShim( + id, extension->path(), + base::Bind(&UpdateDataProvider::RunInstallCallback, this)); + } +} + +void UpdateDataProvider::RunInstallCallback(const std::string& extension_id, + const base::FilePath& temp_dir) { + if (!context_) { + content::BrowserThread::PostBlockingPoolTask( + FROM_HERE, + base::Bind(base::IgnoreResult(&base::DeleteFile), temp_dir, false)); + return; + } else { + callback_.Run(context_, extension_id, temp_dir); + } +} + +} // namespace extensions diff --git a/extensions/browser/updater/update_data_provider.h b/extensions/browser/updater/update_data_provider.h new file mode 100644 index 0000000..95cea59 --- /dev/null +++ b/extensions/browser/updater/update_data_provider.h @@ -0,0 +1,69 @@ +// Copyright 2015 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. + +#ifndef EXTENSIONS_BROWSER_UPDATER_UPDATE_DATA_PROVIDER_H_ +#define EXTENSIONS_BROWSER_UPDATER_UPDATE_DATA_PROVIDER_H_ + +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" + +namespace base { +class FilePath; +} + +namespace content { +class BrowserContext; +} + +namespace update_client { +struct CrxComponent; +} + +namespace extensions { + +// This class exists to let an UpdateClient retrieve information about a set of +// extensions it is doing an update check for. +class UpdateDataProvider : public base::RefCounted<UpdateDataProvider> { + public: + typedef base::Callback<void(content::BrowserContext* context, + const std::string& /* extension_id */, + const base::FilePath& /* temp_dir */)> + InstallCallback; + + // We need a browser context to use when retrieving data for a set of + // extension ids, as well as a callback for proceeding with installation + // steps once the UpdateClient has downloaded and unpacked an update for an + // extension. + UpdateDataProvider(content::BrowserContext* context, + const InstallCallback& callback); + + // Notify this object that the associated browser context is being shut down + // the pointer to the context should be dropped and no more work should be + // done. + void Shutdown(); + + // Matches update_client::UpdateClient::CrxDataCallback + void GetData(const std::vector<std::string>& ids, + std::vector<update_client::CrxComponent>* data); + + private: + friend class base::RefCounted<UpdateDataProvider>; + ~UpdateDataProvider(); + + void RunInstallCallback(const std::string& extension_id, + const base::FilePath& temp_dir); + + content::BrowserContext* context_; + InstallCallback callback_; + + DISALLOW_COPY_AND_ASSIGN(UpdateDataProvider); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_UPDATER_UPDATE_DATA_PROVIDER_H_ diff --git a/extensions/browser/updater/update_install_shim.cc b/extensions/browser/updater/update_install_shim.cc new file mode 100644 index 0000000..4b6f582 --- /dev/null +++ b/extensions/browser/updater/update_install_shim.cc @@ -0,0 +1,83 @@ +// Copyright 2015 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 "extensions/browser/updater/update_install_shim.h" + +#include "base/files/file_enumerator.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/values.h" +#include "content/public/browser/browser_thread.h" + +namespace extensions { + +UpdateInstallShim::UpdateInstallShim(std::string extension_id, + const base::FilePath& extension_root, + const UpdateInstallShimCallback& callback) + : extension_id_(extension_id), + extension_root_(extension_root), + callback_(callback) {} + +void UpdateInstallShim::OnUpdateError(int error) { + VLOG(1) << "OnUpdateError (" << extension_id_ << ") " << error; +} + +bool UpdateInstallShim::Install(const base::DictionaryValue& manifest, + const base::FilePath& unpack_path) { + base::ScopedTempDir temp_dir; + if (!temp_dir.CreateUniqueTempDir()) + return false; + + // The UpdateClient code will delete unpack_path if it still exists after + // this method is done, so we rename it on top of our temp dir. + if (!base::DeleteFile(temp_dir.path(), true) || + !base::Move(unpack_path, temp_dir.path())) { + LOG(ERROR) << "Trying to install update for " << extension_id_ + << "and failed to move " << unpack_path.value() << " to " + << temp_dir.path().value(); + return false; + } + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(&UpdateInstallShim::RunCallbackOnUIThread, this, + temp_dir.Take())); + return true; +} + +bool UpdateInstallShim::GetInstalledFile(const std::string& file, + base::FilePath* installed_file) { + base::FilePath relative_path = base::FilePath::FromUTF8Unsafe(file); + if (relative_path.IsAbsolute() || relative_path.ReferencesParent()) + return false; + *installed_file = extension_root_.Append(relative_path); + if (!extension_root_.IsParent(*installed_file) || + !base::PathExists(*installed_file)) { + VLOG(1) << "GetInstalledFile failed to find " << installed_file->value(); + installed_file->clear(); + return false; + } + return true; +} + +bool UpdateInstallShim::Uninstall() { + NOTREACHED(); + return false; +} + +UpdateInstallShim::~UpdateInstallShim() {} + +void UpdateInstallShim::RunCallbackOnUIThread(const base::FilePath& temp_dir) { + if (callback_.is_null()) { + content::BrowserThread::PostBlockingPoolTask( + FROM_HERE, base::Bind(base::IgnoreResult(&base::DeleteFile), temp_dir, + true /*recursive */)); + return; + } + callback_.Run(extension_id_, temp_dir); +} + +} // namespace extensions diff --git a/extensions/browser/updater/update_install_shim.h b/extensions/browser/updater/update_install_shim.h new file mode 100644 index 0000000..586e94e --- /dev/null +++ b/extensions/browser/updater/update_install_shim.h @@ -0,0 +1,76 @@ +// Copyright 2015 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. + +#ifndef EXTENSIONS_BROWSER_UPDATER_UPDATE_INSTALL_SHIM_H_ +#define EXTENSIONS_BROWSER_UPDATER_UPDATE_INSTALL_SHIM_H_ + +#include <string> + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "components/update_client/update_client.h" + +namespace base { +class DictionaryValue; +} + +namespace extensions { + +// A callback to implement the install of a new version of the extension. +// Takes ownership of the directory at |temp_dir|. +using UpdateInstallShimCallback = + base::Callback<void(const std::string& extension_id, + const base::FilePath& temp_dir)>; + +// This class is used as a shim between the components::update_client and +// extensions code, to help the generic update_client code prepare and then +// install an updated version of an extension. Because the update_client code +// doesn't have the notion of extension ids, we use instances of this class to +// map an install request back to the original update check for a given +// extension. +class UpdateInstallShim : public update_client::CrxInstaller { + public: + // This method takes the id and root directory for an extension we're doing + // an update check for, as well as a callback to call if we get a new version + // of it to install. + UpdateInstallShim(std::string extension_id, + const base::FilePath& extension_root, + const UpdateInstallShimCallback& callback); + + // Called when an update attempt failed. + void OnUpdateError(int error) override; + + // This is called when a new version of an extension is unpacked at + // |unpack_path| and is ready for install. + bool Install(const base::DictionaryValue& manifest, + const base::FilePath& unpack_path) override; + + // This is called by the generic differential update code in the + // update_client to provide the path to an existing file in the current + // version of the extension, so that it can be copied (or serve as the input + // to diff-patching) with output going to the directory with the new version + // being staged on disk for install. + bool GetInstalledFile(const std::string& file, + base::FilePath* installed_file) override; + + // This method is not relevant to extension updating. + bool Uninstall() override; + + private: + friend class base::RefCountedThreadSafe<UpdateInstallShim>; + ~UpdateInstallShim() override; + + // Takes ownership of the directory at path |temp_dir|. + void RunCallbackOnUIThread(const base::FilePath& temp_dir); + + std::string extension_id_; + base::FilePath extension_root_; + UpdateInstallShimCallback callback_; + + DISALLOW_COPY_AND_ASSIGN(UpdateInstallShim); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_UPDATER_UPDATE_INSTALL_SHIM_H_ diff --git a/extensions/browser/updater/update_service.cc b/extensions/browser/updater/update_service.cc index 0cb055c..ad40abd 100644 --- a/extensions/browser/updater/update_service.cc +++ b/extensions/browser/updater/update_service.cc @@ -4,79 +4,61 @@ #include "extensions/browser/updater/update_service.h" -#include <set> - -#include "base/message_loop/message_loop.h" -#include "components/update_client/update_query_params.h" +#include "base/bind.h" +#include "base/files/file_util.h" +#include "components/update_client/update_client.h" #include "content/public/browser/browser_context.h" -#include "extensions/browser/updater/extension_downloader.h" +#include "extensions/browser/extension_system.h" +#include "extensions/browser/extensions_browser_client.h" +#include "extensions/browser/updater/update_data_provider.h" #include "extensions/browser/updater/update_service_factory.h" -#include "extensions/common/extension_urls.h" -using update_client::UpdateQueryParams; +namespace { -namespace extensions { +void UpdateCheckCompleteCallback(int error) {} -// static -UpdateService* UpdateService::Get(content::BrowserContext* context) { - return UpdateServiceFactory::GetForBrowserContext(context); +void InstallUpdateCallback(content::BrowserContext* context, + const std::string& extension_id, + const base::FilePath& temp_dir) { + extensions::ExtensionSystem::Get(context) + ->InstallUpdate(extension_id, temp_dir); } -void UpdateService::DownloadAndInstall( - const std::string& id, - const base::Callback<void(bool)>& callback) { - DCHECK(download_callback_.is_null()); - download_callback_ = callback; - downloader_->AddPendingExtension(id, extension_urls::GetWebstoreUpdateUrl(), - 0); - downloader_->StartAllPending(nullptr); -} +} // namespace -UpdateService::UpdateService(content::BrowserContext* context) - : browser_context_(context), - downloader_(new ExtensionDownloader(this, context->GetRequestContext())) { - downloader_->set_manifest_query_params( - UpdateQueryParams::Get(UpdateQueryParams::CRX)); -} +namespace extensions { -UpdateService::~UpdateService() { +// static +UpdateService* UpdateService::Get(content::BrowserContext* context) { + return UpdateServiceFactory::GetForBrowserContext(context); } -void UpdateService::OnExtensionDownloadFailed( - const std::string& id, - Error error, - const PingResult& ping, - const std::set<int>& request_ids) { - auto callback = download_callback_; - download_callback_.Reset(); - callback.Run(false); +void UpdateService::Shutdown() { + if (update_data_provider_) { + update_data_provider_->Shutdown(); + update_data_provider_ = nullptr; + } + update_client_ = nullptr; + context_ = nullptr; } -void UpdateService::OnExtensionDownloadFinished( - const CRXFileInfo& file, - bool file_ownership_passed, - const GURL& download_url, - const std::string& version, - const PingResult& ping, - const std::set<int>& request_id, - const InstallCallback& install_callback) { - // TODO(rockot): Actually unpack and install the CRX. - auto callback = download_callback_; - download_callback_.Reset(); - callback.Run(true); - if (!install_callback.is_null()) - install_callback.Run(true); +void UpdateService::StartUpdateCheck(std::vector<std::string> extension_ids) { + if (!update_client_) + return; + update_client_->Update(extension_ids, base::Bind(&UpdateDataProvider::GetData, + update_data_provider_), + base::Bind(&UpdateCheckCompleteCallback)); } -bool UpdateService::IsExtensionPending(const std::string& id) { - // TODO(rockot): Implement this. For now all IDs are "pending". - return true; +UpdateService::UpdateService( + content::BrowserContext* context, + scoped_refptr<update_client::UpdateClient> update_client) + : context_(context), update_client_(update_client) { + CHECK(update_client_); + update_data_provider_ = + new UpdateDataProvider(context_, base::Bind(&InstallUpdateCallback)); } -bool UpdateService::GetExtensionExistingVersion(const std::string& id, - std::string* version) { - // TODO(rockot): Implement this. - return false; -} +UpdateService::~UpdateService() {} } // namespace extensions diff --git a/extensions/browser/updater/update_service.h b/extensions/browser/updater/update_service.h index 4d19204..0ee321c 100644 --- a/extensions/browser/updater/update_service.h +++ b/extensions/browser/updater/update_service.h @@ -6,62 +6,53 @@ #define EXTENSIONS_BROWSER_UPDATER_UPDATE_SERVICE_H_ #include <string> +#include <vector> #include "base/callback.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "components/keyed_service/core/keyed_service.h" -#include "extensions/browser/updater/extension_downloader_delegate.h" namespace content { class BrowserContext; } +namespace update_client { +class UpdateClient; +} + namespace extensions { -class ExtensionDownloader; +class UpdateDataProvider; +class UpdateService; class UpdateServiceFactory; -class UpdateServiceTest; -// This service manages the download, update, and installation of extensions. -// It is currently only used by app_shell, but should eventually replace -// ExtensionUpdater in Chrome. +// This service manages the autoupdate of extensions. It should eventually +// replace ExtensionUpdater in Chrome. // TODO(rockot): Replace ExtensionUpdater with this service. -class UpdateService : public KeyedService, public ExtensionDownloaderDelegate { +class UpdateService : public KeyedService { public: static UpdateService* Get(content::BrowserContext* context); - // TODO(rockot): Remove this. It's a placeholder for a real service interface. - // Downloads and (TODO) installs a CRX within the current browser context. - void DownloadAndInstall(const std::string& id, - const base::Callback<void(bool)>& callback); + void Shutdown() override; + + // Starts an update check for each of |extension_ids|. If there are any + // updates available, they will be downloaded, checked for integrity, + // unpacked, and then passed off to the ExtensionSystem::InstallUpdate method + // for install completion. + void StartUpdateCheck(std::vector<std::string> extension_ids); private: friend class UpdateServiceFactory; - friend class UpdateServiceTest; - explicit UpdateService(content::BrowserContext* context); + UpdateService(content::BrowserContext* context, + scoped_refptr<update_client::UpdateClient> update_client); ~UpdateService() override; - // ExtensionDownloaderDelegate: - void OnExtensionDownloadFailed(const std::string& id, - Error error, - const PingResult& ping, - const std::set<int>& request_ids) override; - void OnExtensionDownloadFinished(const CRXFileInfo& file, - bool file_ownership_passed, - const GURL& download_url, - const std::string& version, - const PingResult& ping, - const std::set<int>& request_id, - const InstallCallback& callback) override; - bool IsExtensionPending(const std::string& id) override; - bool GetExtensionExistingVersion(const std::string& id, - std::string* version) override; - - content::BrowserContext* browser_context_; - scoped_ptr<ExtensionDownloader> downloader_; - base::Callback<void(bool)> download_callback_; + content::BrowserContext* context_; + + scoped_refptr<update_client::UpdateClient> update_client_; + scoped_refptr<UpdateDataProvider> update_data_provider_; DISALLOW_COPY_AND_ASSIGN(UpdateService); }; diff --git a/extensions/browser/updater/update_service_browsertest.cc b/extensions/browser/updater/update_service_browsertest.cc deleted file mode 100644 index 097de35..0000000 --- a/extensions/browser/updater/update_service_browsertest.cc +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2014 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 <map> -#include <string> -#include <utility> - -#include "base/bind.h" -#include "base/files/scoped_temp_dir.h" -#include "base/run_loop.h" -#include "base/strings/stringprintf.h" -#include "content/public/test/test_browser_thread_bundle.h" -#include "content/public/test/test_utils.h" -#include "extensions/browser/api/extensions_api_client.h" -#include "extensions/browser/extensions_browser_client.h" -#include "extensions/browser/updater/update_service.h" -#include "extensions/common/extension_urls.h" -#include "extensions/shell/test/shell_test.h" -#include "net/base/escape.h" -#include "net/http/http_status_code.h" -#include "net/url_request/test_url_fetcher_factory.h" - -namespace extensions { - -namespace { - -using FakeResponse = std::pair<std::string, net::HttpStatusCode>; - -// TODO(rockot): In general there's enough mock-Omaha-noise that this might be -// better placed into some test library code in //components/update_client. -FakeResponse CreateFakeUpdateResponse(const std::string& id, - size_t crx_length) { - std::string response_text = base::StringPrintf( - "<gupdate xmlns=\"http://www.google.com/update2/response\" " - " protocol=\"2.0\" server=\"prod\">\n" - " <daystart elapsed_days=\"2860\" elapsed_seconds=\"42042\"/>\n" - " <app appid=\"%s\" status=\"ok\">\n" - " <updatecheck codebase=\"%s\" fp=\"0\" hash=\"\" hash_sha256=\"\" " - " size=\"%d\" status=\"ok\" version=\"1\"/>\n" - " </app>\n" - "</gupdate>\n", - id.c_str(), - base::StringPrintf("https://fake-omaha-hostname/%s.crx", - id.c_str()).c_str(), - static_cast<int>(crx_length)); - return std::make_pair(response_text, net::HTTP_OK); -} - -FakeResponse CreateFakeUpdateNotFoundResponse() { - return std::make_pair( - std::string( - "<gupdate xmlns=\"http://www.google.com/update2/response\" " - " protocol=\"2.0\" server=\"prod\">\n" - " <daystart elapsed_days=\"4242\" elapsed_seconds=\"42042\"/>\n" - " <app appid=\"\" status=\"error-invalidAppId\">\n" - "</gupdate>"), - net::HTTP_OK); -} - -bool ExtractKeyValueFromComponent(const std::string& component_str, - const std::string& target_key, - std::string* extracted_value) { - url::Component component(0, static_cast<int>(component_str.length())); - url::Component key, value; - while (url::ExtractQueryKeyValue(component_str.c_str(), &component, &key, - &value)) { - if (target_key == component_str.substr(key.begin, key.len)) { - *extracted_value = component_str.substr(value.begin, value.len); - return true; - } - } - return false; -} - -// This extracts an extension ID from an Omaha update query. Queries have the -// form https://foo/bar/update?x=id%3Dabcdefghijklmnopqrstuvwxyzaaaaaa%26... -// This function extracts the 'x' query parameter (e.g. "id%3Dabcdef...."), -// unescapes its value (to become e.g., "id=abcdef...", and then extracts the -// 'id' value from the result (e.g. "abcdef..."). -bool ExtractIdFromUpdateQuery(const std::string& query_str, std::string* id) { - std::string data_string; - if (!ExtractKeyValueFromComponent(query_str, "x", &data_string)) - return false; - data_string = net::UnescapeURLComponent(data_string, - net::UnescapeRule::URL_SPECIAL_CHARS); - if (!ExtractKeyValueFromComponent(data_string, "id", id)) - return false; - EXPECT_EQ(32u, id->size()); - return true; -} - -void ExpectDownloadSuccess(const base::Closure& continuation, bool success) { - EXPECT_TRUE(success) << "Download failed."; - continuation.Run(); -} - -class FakeUpdateURLFetcherFactory : public net::URLFetcherFactory { - public: - FakeUpdateURLFetcherFactory() { EXPECT_TRUE(dir_.CreateUniqueTempDir()); } - - ~FakeUpdateURLFetcherFactory() override {} - - void RegisterFakeExtension(const std::string& id, - const std::string& contents) { - CHECK_EQ(32u, id.size()); - fake_extensions_.insert(std::make_pair(id, contents)); - } - - // net::URLFetcherFactory: - scoped_ptr<net::URLFetcher> CreateURLFetcher( - int id, - const GURL& url, - net::URLFetcher::RequestType request_type, - net::URLFetcherDelegate* delegate) override { - if (url.spec().find(extension_urls::GetWebstoreUpdateUrl().spec()) == 0) { - // Handle fake Omaha requests. - return CreateUpdateManifestFetcher(url, delegate); - } else if (url.spec().find("https://fake-omaha-hostname") == 0) { - // Handle a fake CRX request. - return CreateCrxFetcher(url, delegate); - } - NOTREACHED(); - return nullptr; - } - - private: - scoped_ptr<net::URLFetcher> CreateUpdateManifestFetcher( - const GURL& url, - net::URLFetcherDelegate* delegate) { - // If we have a fake CRX for the ID, return a fake update blob for it. - // Otherwise return an invalid-ID response. - FakeResponse response; - std::string extension_id; - if (!ExtractIdFromUpdateQuery(url.query(), &extension_id)) { - response = CreateFakeUpdateNotFoundResponse(); - } else { - const auto& iter = fake_extensions_.find(extension_id); - if (iter == fake_extensions_.end()) - response = CreateFakeUpdateNotFoundResponse(); - else - response = CreateFakeUpdateResponse(extension_id, iter->second.size()); - } - return scoped_ptr<net::URLFetcher>( - new net::FakeURLFetcher(url, delegate, response.first, response.second, - net::URLRequestStatus::SUCCESS)); - } - - scoped_ptr<net::URLFetcher> CreateCrxFetcher( - const GURL& url, - net::URLFetcherDelegate* delegate) { - FakeResponse response; - std::string extension_id = url.path().substr(1, 32); - const auto& iter = fake_extensions_.find(extension_id); - if (iter == fake_extensions_.end()) - response = std::make_pair(std::string(), net::HTTP_NOT_FOUND); - else - response = std::make_pair(iter->second, net::HTTP_OK); - net::TestURLFetcher* fetcher = - new net::FakeURLFetcher(url, delegate, response.first, response.second, - net::URLRequestStatus::SUCCESS); - base::FilePath path = dir_.path().Append( - base::FilePath::FromUTF8Unsafe(url.path().substr(1))); - fetcher->SetResponseFilePath(path); - return scoped_ptr<net::URLFetcher>(fetcher); - } - - base::ScopedTempDir dir_; - - std::map<std::string, std::string> fake_extensions_; - - DISALLOW_COPY_AND_ASSIGN(FakeUpdateURLFetcherFactory); -}; - -} // namespace - -class UpdateServiceTest : public AppShellTest { - public: - UpdateServiceTest() {} - ~UpdateServiceTest() override {} - - void SetUpOnMainThread() override { - AppShellTest::SetUpOnMainThread(); - - update_service_ = UpdateService::Get(browser_context()); - - default_url_fetcher_factory_.reset(new FakeUpdateURLFetcherFactory()); - url_fetcher_factory_.reset( - new net::FakeURLFetcherFactory(default_url_fetcher_factory_.get())); - } - - protected: - void RegisterFakeExtension(const std::string& id, - const std::string& crx_contents) { - default_url_fetcher_factory_->RegisterFakeExtension(id, crx_contents); - } - - UpdateService* update_service() const { return update_service_; } - - private: - scoped_ptr<FakeUpdateURLFetcherFactory> default_url_fetcher_factory_; - scoped_ptr<net::FakeURLFetcherFactory> url_fetcher_factory_; - - UpdateService* update_service_; -}; - -IN_PROC_BROWSER_TEST_F(UpdateServiceTest, DownloadAndInstall) { - const char kCrxId[] = "abcdefghijklmnopqrstuvwxyzaaaaaa"; - const char kCrxContents[] = "Hello, world!"; - RegisterFakeExtension(kCrxId, kCrxContents); - - base::RunLoop run_loop; - update_service()->DownloadAndInstall( - kCrxId, base::Bind(ExpectDownloadSuccess, run_loop.QuitClosure())); - run_loop.Run(); -} - -} // namespace extensions diff --git a/extensions/browser/updater/update_service_factory.cc b/extensions/browser/updater/update_service_factory.cc index a87c88b..e441dc3 100644 --- a/extensions/browser/updater/update_service_factory.cc +++ b/extensions/browser/updater/update_service_factory.cc @@ -5,7 +5,9 @@ #include "extensions/browser/updater/update_service_factory.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "components/update_client/update_client.h" #include "extensions/browser/extension_registry_factory.h" +#include "extensions/browser/extensions_browser_client.h" #include "extensions/browser/process_manager_factory.h" #include "extensions/browser/updater/update_service.h" @@ -27,8 +29,6 @@ UpdateServiceFactory::UpdateServiceFactory() : BrowserContextKeyedServiceFactory( "UpdateService", BrowserContextDependencyManager::GetInstance()) { - DependsOn(extensions::ExtensionRegistryFactory::GetInstance()); - DependsOn(extensions::ProcessManagerFactory::GetInstance()); } UpdateServiceFactory::~UpdateServiceFactory() { @@ -36,7 +36,8 @@ UpdateServiceFactory::~UpdateServiceFactory() { KeyedService* UpdateServiceFactory::BuildServiceInstanceFor( content::BrowserContext* context) const { - return new UpdateService(context); + return new UpdateService( + context, ExtensionsBrowserClient::Get()->CreateUpdateClient(context)); } } // namespace extensions diff --git a/extensions/browser/updater/update_service_unittest.cc b/extensions/browser/updater/update_service_unittest.cc new file mode 100644 index 0000000..f570303 --- /dev/null +++ b/extensions/browser/updater/update_service_unittest.cc @@ -0,0 +1,248 @@ +// Copyright 2014 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 <vector> + +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/run_loop.h" +#include "base/values.h" +#include "components/crx_file/id_util.h" +#include "components/update_client/update_client.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "content/public/test/test_utils.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/extensions_test.h" +#include "extensions/browser/mock_extension_system.h" +#include "extensions/browser/test_extensions_browser_client.h" +#include "extensions/browser/updater/update_service.h" +#include "extensions/common/extension_builder.h" +#include "extensions/common/value_builder.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class FakeUpdateClient : public update_client::UpdateClient { + public: + FakeUpdateClient(); + + // Returns the data we've gotten from the CrxDataCallback for ids passed to + // the Update function. + std::vector<update_client::CrxComponent>* data() { return &data_; } + + // update_client::UpdateClient + void AddObserver(Observer* observer) override {} + void RemoveObserver(Observer* observer) override {} + void Install(const std::string& id, + const CrxDataCallback& crx_data_callback, + const CompletionCallback& completion_callback) override {} + void Update(const std::vector<std::string>& ids, + const CrxDataCallback& crx_data_callback, + const CompletionCallback& completion_callback) override; + bool GetCrxUpdateState( + const std::string& id, + update_client::CrxUpdateItem* update_item) const override { + return false; + } + bool IsUpdating(const std::string& id) const override { return false; } + + protected: + friend class base::RefCounted<FakeUpdateClient>; + ~FakeUpdateClient() override {} + + std::vector<update_client::CrxComponent> data_; +}; + +FakeUpdateClient::FakeUpdateClient() {} + +void FakeUpdateClient::Update(const std::vector<std::string>& ids, + const CrxDataCallback& crx_data_callback, + const CompletionCallback& completion_callback) { + crx_data_callback.Run(ids, &data_); +} + +} // namespace + +namespace extensions { + +namespace { + +// A fake ExtensionSystem that lets us intercept calls to install new +// versions of an extension. +class FakeExtensionSystem : public MockExtensionSystem { + public: + explicit FakeExtensionSystem(content::BrowserContext* context) + : MockExtensionSystem(context) {} + ~FakeExtensionSystem() override {} + + struct InstallUpdateRequest { + std::string extension_id; + base::FilePath temp_dir; + }; + + std::vector<InstallUpdateRequest>* install_requests() { + return &install_requests_; + } + + void set_install_callback(const base::Closure& callback) { + next_install_callback_ = callback; + } + + // ExtensionSystem override + void InstallUpdate(const std::string& extension_id, + const base::FilePath& temp_dir) override { + base::DeleteFile(temp_dir, true /*recursive*/); + InstallUpdateRequest request; + request.extension_id = extension_id; + request.temp_dir = temp_dir; + install_requests_.push_back(request); + if (!next_install_callback_.is_null()) { + base::Closure tmp = next_install_callback_; + next_install_callback_.Reset(); + tmp.Run(); + } + } + + private: + std::vector<InstallUpdateRequest> install_requests_; + base::Closure next_install_callback_; +}; + +class UpdateServiceTest : public ExtensionsTest { + public: + UpdateServiceTest() { + extensions_browser_client()->set_extension_system_factory( + &fake_extension_system_factory_); + } + ~UpdateServiceTest() override {} + + void SetUp() override { + ExtensionsTest::SetUp(); + browser_threads_.reset(new content::TestBrowserThreadBundle( + content::TestBrowserThreadBundle::DEFAULT)); + + extensions_browser_client()->SetUpdateClientFactory(base::Bind( + &UpdateServiceTest::CreateUpdateClient, base::Unretained(this))); + + update_service_ = UpdateService::Get(browser_context()); + } + + protected: + UpdateService* update_service() const { return update_service_; } + FakeUpdateClient* update_client() const { return update_client_.get(); } + + update_client::UpdateClient* CreateUpdateClient() { + // We only expect that this will get called once, so consider it an error + // if our update_client_ is already non-null. + EXPECT_EQ(nullptr, update_client_.get()); + update_client_ = new FakeUpdateClient(); + return update_client_.get(); + } + + // Helper function that creates a file at |relative_path| within |directory| + // and fills it with |content|. + bool AddFileToDirectory(const base::FilePath& directory, + const base::FilePath& relative_path, + const std::string& content) { + base::FilePath full_path = directory.Append(relative_path); + if (!CreateDirectory(full_path.DirName())) + return false; + int result = base::WriteFile(full_path, content.data(), content.size()); + return (static_cast<size_t>(result) == content.size()); + } + + FakeExtensionSystem* extension_system() { + return static_cast<FakeExtensionSystem*>( + fake_extension_system_factory_.GetForBrowserContext(browser_context())); + } + + private: + UpdateService* update_service_; + scoped_refptr<FakeUpdateClient> update_client_; + scoped_ptr<content::TestBrowserThreadBundle> browser_threads_; + MockExtensionSystemFactory<FakeExtensionSystem> + fake_extension_system_factory_; +}; + +TEST_F(UpdateServiceTest, BasicUpdateOperations) { + // Create a temporary directory that a fake extension will live in and fill + // it with some test files. + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + base::FilePath foo_js(FILE_PATH_LITERAL("foo.js")); + base::FilePath bar_html(FILE_PATH_LITERAL("bar/bar.html")); + ASSERT_TRUE(AddFileToDirectory(temp_dir.path(), foo_js, "hello")) + << "Failed to write " << temp_dir.path().value() << "/" << foo_js.value(); + ASSERT_TRUE(AddFileToDirectory(temp_dir.path(), bar_html, "world")); + + ExtensionBuilder builder; + builder.SetManifest(DictionaryBuilder() + .Set("name", "Foo") + .Set("version", "1.0") + .Set("manifest_version", 2)); + builder.SetID(crx_file::id_util::GenerateId("whatever")); + builder.SetPath(temp_dir.path()); + + scoped_refptr<Extension> extension1(builder.Build()); + + ExtensionRegistry::Get(browser_context())->AddEnabled(extension1); + std::vector<std::string> ids; + ids.push_back(extension1->id()); + + // Start an update check and verify that the UpdateClient was sent the right + // data. + update_service()->StartUpdateCheck(ids); + std::vector<update_client::CrxComponent>* data = update_client()->data(); + ASSERT_NE(nullptr, data); + ASSERT_EQ(1u, data->size()); + + ASSERT_TRUE(data->at(0).version.Equals(*extension1->version())); + update_client::CrxInstaller* installer = data->at(0).installer.get(); + ASSERT_NE(installer, nullptr); + + // The GetInstalledFile method is used when processing differential updates + // to get a path to an existing file in an extension. We want to test a + // number of scenarios to be user we handle invalid relative paths, don't + // accidentally return paths outside the extension's dir, etc. + base::FilePath tmp; + EXPECT_TRUE(installer->GetInstalledFile(foo_js.MaybeAsASCII(), &tmp)); + EXPECT_EQ(temp_dir.path().Append(foo_js), tmp) << tmp.value(); + + EXPECT_TRUE(installer->GetInstalledFile(bar_html.MaybeAsASCII(), &tmp)); + EXPECT_EQ(temp_dir.path().Append(bar_html), tmp) << tmp.value(); + + EXPECT_FALSE(installer->GetInstalledFile("does_not_exist", &tmp)); + EXPECT_FALSE(installer->GetInstalledFile("does/not/exist", &tmp)); + EXPECT_FALSE(installer->GetInstalledFile("/does/not/exist", &tmp)); + EXPECT_FALSE(installer->GetInstalledFile("C:\\tmp", &tmp)); + + base::FilePath system_temp_dir; + ASSERT_TRUE(base::GetTempDir(&system_temp_dir)); + EXPECT_FALSE( + installer->GetInstalledFile(system_temp_dir.MaybeAsASCII(), &tmp)); + + // Test the install callback. + base::ScopedTempDir new_version_dir; + ASSERT_TRUE(new_version_dir.CreateUniqueTempDir()); + scoped_ptr<base::DictionaryValue> new_manifest( + extension1->manifest()->value()->DeepCopy()); + new_manifest->SetString("version", "2.0"); + + installer->Install(*new_manifest, new_version_dir.path()); + + scoped_refptr<content::MessageLoopRunner> loop_runner = + new content::MessageLoopRunner(); + extension_system()->set_install_callback(loop_runner->QuitClosure()); + loop_runner->Run(); + + std::vector<FakeExtensionSystem::InstallUpdateRequest>* requests = + extension_system()->install_requests(); + ASSERT_EQ(1u, requests->size()); + EXPECT_EQ(requests->at(0).extension_id, extension1->id()); + EXPECT_NE(requests->at(0).temp_dir.value(), new_version_dir.path().value()); +} + +} // namespace + +} // namespace extensions diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp index c7976da..3c7657e 100644 --- a/extensions/extensions.gyp +++ b/extensions/extensions.gyp @@ -111,7 +111,9 @@ '../components/components.gyp:sessions_content', '../components/components.gyp:storage_monitor', '../components/components.gyp:ui_zoom', + '../components/components.gyp:update_client', '../components/components.gyp:variations', + '../components/components.gyp:version_info', '../components/components.gyp:web_cache_browser', '../components/components.gyp:web_modal', '../content/content.gyp:content_browser', diff --git a/extensions/extensions.gypi b/extensions/extensions.gypi index 450a943..d730681 100644 --- a/extensions/extensions.gypi +++ b/extensions/extensions.gypi @@ -752,6 +752,12 @@ 'browser/updater/request_queue_impl.h', 'browser/updater/safe_manifest_parser.cc', 'browser/updater/safe_manifest_parser.h', + 'browser/updater/update_client_config.cc', + 'browser/updater/update_client_config.h', + 'browser/updater/update_data_provider.cc', + 'browser/updater/update_data_provider.h', + 'browser/updater/update_install_shim.cc', + 'browser/updater/update_install_shim.h', 'browser/updater/update_service.cc', 'browser/updater/update_service.h', 'browser/updater/update_service_factory.cc', diff --git a/extensions/extensions_tests.gypi b/extensions/extensions_tests.gypi index 2019e37..aab6570 100644 --- a/extensions/extensions_tests.gypi +++ b/extensions/extensions_tests.gypi @@ -27,7 +27,6 @@ 'browser/guest_view/web_view/web_view_apitest.cc', 'browser/guest_view/web_view/web_view_apitest.h', 'browser/guest_view/web_view/web_view_media_access_apitest.cc', - 'browser/updater/update_service_browsertest.cc', 'shell/browser/geolocation/geolocation_apitest.cc', 'shell/browser/shell_browsertest.cc', 'shell/test/shell_apitest.cc', @@ -98,6 +97,7 @@ 'browser/extension_throttle_test_support.cc', 'browser/extension_throttle_test_support.h', 'browser/extension_throttle_unittest.cc', + 'browser/updater/update_service_unittest.cc', 'browser/value_store/leveldb_value_store_unittest.cc', 'browser/value_store/testing_value_store_unittest.cc', 'browser/value_store/value_store_change_unittest.cc', diff --git a/extensions/shell/browser/shell_browser_main_parts.cc b/extensions/shell/browser/shell_browser_main_parts.cc index 073dacb..0153679 100644 --- a/extensions/shell/browser/shell_browser_main_parts.cc +++ b/extensions/shell/browser/shell_browser_main_parts.cc @@ -73,13 +73,6 @@ using content::BrowserThread; namespace extensions { -namespace { - -void CrxInstallComplete(bool success) { - VLOG(1) << "CRX download complete. Success: " << success; -} -} - ShellBrowserMainParts::ShellBrowserMainParts( const content::MainFunctionParams& parameters, ShellBrowserMainDelegate* browser_main_delegate) @@ -211,17 +204,6 @@ void ShellBrowserMainParts::PreMainMessageLoopRun() { base::Bind(nacl::NaClProcessHost::EarlyStartup)); #endif - // TODO(rockot): Remove this temporary hack test. - std::string install_crx_id = - cmd->GetSwitchValueASCII(switches::kAppShellInstallCrx); - if (install_crx_id.size() != 0) { - CHECK(install_crx_id.size() == 32) - << "Extension ID must be exactly 32 characters long."; - UpdateService* update_service = UpdateService::Get(browser_context_.get()); - update_service->DownloadAndInstall(install_crx_id, - base::Bind(CrxInstallComplete)); - } - devtools_http_handler_.reset( content::ShellDevToolsManagerDelegate::CreateHttpHandler( browser_context_.get())); diff --git a/extensions/shell/browser/shell_extension_system.cc b/extensions/shell/browser/shell_extension_system.cc index 0457491..b6cff6a 100644 --- a/extensions/shell/browser/shell_extension_system.cc +++ b/extensions/shell/browser/shell_extension_system.cc @@ -176,6 +176,12 @@ scoped_ptr<ExtensionSet> ShellExtensionSystem::GetDependentExtensions( return make_scoped_ptr(new ExtensionSet()); } +void ShellExtensionSystem::InstallUpdate(const std::string& extension_id, + const base::FilePath& temp_dir) { + NOTREACHED(); + base::DeleteFile(temp_dir, true /* recursive */); +} + void ShellExtensionSystem::OnExtensionRegisteredWithRequestContexts( scoped_refptr<Extension> extension) { ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_); diff --git a/extensions/shell/browser/shell_extension_system.h b/extensions/shell/browser/shell_extension_system.h index f042f2d..ca2cb5e 100644 --- a/extensions/shell/browser/shell_extension_system.h +++ b/extensions/shell/browser/shell_extension_system.h @@ -64,6 +64,8 @@ class ShellExtensionSystem : public ExtensionSystem { ContentVerifier* content_verifier() override; scoped_ptr<ExtensionSet> GetDependentExtensions( const Extension* extension) override; + void InstallUpdate(const std::string& extension_id, + const base::FilePath& temp_dir) override; private: void OnExtensionRegisteredWithRequestContexts( diff --git a/extensions/shell/common/switches.cc b/extensions/shell/common/switches.cc index fbb5783..66bd7e5 100644 --- a/extensions/shell/common/switches.cc +++ b/extensions/shell/common/switches.cc @@ -13,9 +13,6 @@ const char kAppShellAllowRoaming[] = "app-shell-allow-roaming"; // Size for the host window to create (i.e. "800x600"). const char kAppShellHostWindowSize[] = "app-shell-host-window-size"; -// ID of an extension CRX to be downloaded from the web store. -const char kAppShellInstallCrx[] = "app-shell-install-crx"; - // SSID of the preferred WiFi network. const char kAppShellPreferredNetwork[] = "app-shell-preferred-network"; diff --git a/extensions/shell/common/switches.h b/extensions/shell/common/switches.h index b6b3932..7cfe1d6 100644 --- a/extensions/shell/common/switches.h +++ b/extensions/shell/common/switches.h @@ -12,7 +12,6 @@ namespace switches { // alongside the definition of their values in the .cc file. extern const char kAppShellAllowRoaming[]; extern const char kAppShellHostWindowSize[]; -extern const char kAppShellInstallCrx[]; extern const char kAppShellPreferredNetwork[]; extern const char kAppShellRefreshToken[]; extern const char kAppShellUser[]; |