summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
authorasargent <asargent@chromium.org>2015-10-15 14:51:48 -0700
committerCommit bot <commit-bot@chromium.org>2015-10-15 21:52:40 +0000
commit631a99a61cbe3e7e03e372b3d37c6a37fa700392 (patch)
tree95c4fc66df8e20ba8744159f0ae63913e1750a4a /extensions
parenta33cc7d19a2c878e2a7d9b4a540d06f3e0c3fc16 (diff)
downloadchromium_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}
Diffstat (limited to 'extensions')
-rw-r--r--extensions/browser/BUILD.gn2
-rw-r--r--extensions/browser/extension_system.h7
-rw-r--r--extensions/browser/extensions_browser_client.cc7
-rw-r--r--extensions/browser/extensions_browser_client.h9
-rw-r--r--extensions/browser/mock_extension_system.cc5
-rw-r--r--extensions/browser/mock_extension_system.h2
-rw-r--r--extensions/browser/test_extensions_browser_client.cc13
-rw-r--r--extensions/browser/test_extensions_browser_client.h14
-rw-r--r--extensions/browser/updater/update_client_config.cc29
-rw-r--r--extensions/browser/updater/update_client_config.h40
-rw-r--r--extensions/browser/updater/update_data_provider.cc72
-rw-r--r--extensions/browser/updater/update_data_provider.h69
-rw-r--r--extensions/browser/updater/update_install_shim.cc83
-rw-r--r--extensions/browser/updater/update_install_shim.h76
-rw-r--r--extensions/browser/updater/update_service.cc96
-rw-r--r--extensions/browser/updater/update_service.h55
-rw-r--r--extensions/browser/updater/update_service_browsertest.cc218
-rw-r--r--extensions/browser/updater/update_service_factory.cc7
-rw-r--r--extensions/browser/updater/update_service_unittest.cc248
-rw-r--r--extensions/extensions.gyp2
-rw-r--r--extensions/extensions.gypi6
-rw-r--r--extensions/extensions_tests.gypi2
-rw-r--r--extensions/shell/browser/shell_browser_main_parts.cc18
-rw-r--r--extensions/shell/browser/shell_extension_system.cc6
-rw-r--r--extensions/shell/browser/shell_extension_system.h2
-rw-r--r--extensions/shell/common/switches.cc3
-rw-r--r--extensions/shell/common/switches.h1
27 files changed, 759 insertions, 333 deletions
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[];