summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/component_updater/component_patcher.cc81
-rw-r--r--chrome/browser/component_updater/component_patcher.h86
-rw-r--r--chrome/browser/component_updater/component_patcher_operation.cc222
-rw-r--r--chrome/browser/component_updater/component_patcher_operation.h158
-rw-r--r--chrome/browser/component_updater/component_patcher_win.cc98
-rw-r--r--chrome/browser/component_updater/component_patcher_win.h24
-rw-r--r--chrome/browser/component_updater/component_unpacker.cc88
-rw-r--r--chrome/browser/component_updater/component_unpacker.h20
-rw-r--r--chrome/browser/component_updater/component_updater_configurator.cc55
-rw-r--r--chrome/browser/component_updater/component_updater_service.cc211
-rw-r--r--chrome/browser/component_updater/component_updater_service.h23
-rw-r--r--chrome/browser/component_updater/pepper_flash_component_installer.cc8
-rw-r--r--chrome/browser/component_updater/pnacl/pnacl_component_installer.cc5
-rw-r--r--chrome/browser/component_updater/pnacl/pnacl_component_installer.h6
-rw-r--r--chrome/browser/component_updater/pnacl/pnacl_profile_observer.h1
-rw-r--r--chrome/browser/component_updater/recovery_component_installer.cc8
-rw-r--r--chrome/browser/component_updater/swiftshader_component_installer.cc8
-rw-r--r--chrome/browser/component_updater/test/DEPS1
-rw-r--r--chrome/browser/component_updater/test/component_patcher_mock.h28
-rw-r--r--chrome/browser/component_updater/test/component_patcher_unittest.cc200
-rw-r--r--chrome/browser/component_updater/test/component_updater_service_unittest.cc685
-rw-r--r--chrome/browser/component_updater/test/test_installer.cc81
-rw-r--r--chrome/browser/component_updater/test/test_installer.h73
-rw-r--r--chrome/browser/component_updater/widevine_cdm_component_installer.cc8
-rw-r--r--chrome/browser/net/crl_set_fetcher.cc5
-rw-r--r--chrome/browser/net/crl_set_fetcher.h2
-rw-r--r--chrome/chrome_browser.gypi6
-rw-r--r--chrome/chrome_tests_unit.gypi4
-rw-r--r--chrome/common/chrome_switches.cc2
-rw-r--r--chrome/common/chrome_switches.h2
-rw-r--r--chrome/common/extensions/update_manifest.cc23
-rw-r--r--chrome/common/extensions/update_manifest.h31
-rw-r--r--chrome/common/omaha_query_params/omaha_query_params.cc35
-rw-r--r--chrome/common/omaha_query_params/omaha_query_params.h4
-rw-r--r--chrome/installer/setup/setup_main.cc24
-rw-r--r--chrome/installer/setup/setup_util.cc70
-rw-r--r--chrome/installer/setup/setup_util.h27
-rw-r--r--chrome/installer/util/util_constants.cc12
-rw-r--r--chrome/installer/util/util_constants.h20
-rw-r--r--chrome/test/data/components/updatecheck_diff_reply_1.xml6
-rw-r--r--chrome/test/data/components/updatecheck_diff_reply_2.xml6
-rw-r--r--chrome/test/data/components/updatecheck_diff_reply_3.xml6
-rw-r--r--chrome/test/data/components/updatecheck_reply_noupdate.xml6
-rw-r--r--courgette/disassembler_elf_32.h14
-rw-r--r--courgette/disassembler_elf_32_x86.h5
-rw-r--r--courgette/third_party/bsdiff.h10
-rw-r--r--courgette/third_party/bsdiff_apply.cc41
-rw-r--r--extensions/common/crx_file.cc17
-rw-r--r--extensions/common/crx_file.h4
49 files changed, 2211 insertions, 349 deletions
diff --git a/chrome/browser/component_updater/component_patcher.cc b/chrome/browser/component_updater/component_patcher.cc
new file mode 100644
index 0000000..54bc62d
--- /dev/null
+++ b/chrome/browser/component_updater/component_patcher.cc
@@ -0,0 +1,81 @@
+// Copyright 2013 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 "chrome/browser/component_updater/component_patcher.h"
+
+#include <string>
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/values.h"
+#include "chrome/browser/component_updater/component_patcher_operation.h"
+#include "chrome/browser/component_updater/component_updater_service.h"
+
+namespace {
+
+// Deserialize the commands file (present in delta update packages). The top
+// level must be a list.
+base::ListValue* ReadCommands(const base::FilePath& unpack_path) {
+ const base::FilePath commands =
+ unpack_path.Append(FILE_PATH_LITERAL("commands.json"));
+ if (!file_util::PathExists(commands))
+ return NULL;
+
+ JSONFileValueSerializer serializer(commands);
+ scoped_ptr<base::Value> root(serializer.Deserialize(NULL, NULL));
+
+ return (root.get() && root->IsType(base::Value::TYPE_LIST)) ?
+ static_cast<base::ListValue*>(root.release()) : NULL;
+}
+
+} // namespace
+
+
+// The patching support is not cross-platform at the moment.
+ComponentPatcherCrossPlatform::ComponentPatcherCrossPlatform() {}
+
+ComponentUnpacker::Error ComponentPatcherCrossPlatform::Patch(
+ PatchType patch_type,
+ const base::FilePath& input_file,
+ const base::FilePath& patch_file,
+ const base::FilePath& output_file,
+ int* error) {
+ return ComponentUnpacker::kDeltaUnsupportedCommand;
+}
+
+
+// Takes the contents of a differential component update in input_dir
+// and produces the contents of a full component update in unpack_dir
+// using input_abs_path_ files that the installer knows about.
+ComponentUnpacker::Error DifferentialUpdatePatch(
+ const base::FilePath& input_dir,
+ const base::FilePath& unpack_dir,
+ ComponentPatcher* patcher,
+ ComponentInstaller* installer,
+ int* error) {
+ *error = 0;
+ scoped_ptr<base::ListValue> commands(ReadCommands(input_dir));
+ if (!commands.get())
+ return ComponentUnpacker::kDeltaBadCommands;
+
+ for (base::ValueVector::const_iterator command = commands->begin(),
+ end = commands->end(); command != end; command++) {
+ if (!(*command)->IsType(base::Value::TYPE_DICTIONARY))
+ return ComponentUnpacker::kDeltaBadCommands;
+ base::DictionaryValue* command_args =
+ static_cast<base::DictionaryValue*>(*command);
+ scoped_ptr<DeltaUpdateOp> operation(CreateDeltaUpdateOp(command_args));
+ if (!operation)
+ return ComponentUnpacker::kDeltaUnsupportedCommand;
+
+ ComponentUnpacker::Error result = operation->Run(
+ command_args, input_dir, unpack_dir, patcher, installer, error);
+ if (result != ComponentUnpacker::kNone)
+ return result;
+ }
+
+ return ComponentUnpacker::kNone;
+}
+
diff --git a/chrome/browser/component_updater/component_patcher.h b/chrome/browser/component_updater/component_patcher.h
new file mode 100644
index 0000000..1990922
--- /dev/null
+++ b/chrome/browser/component_updater/component_patcher.h
@@ -0,0 +1,86 @@
+// Copyright 2013 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.
+
+// Component updates can be either differential updates or full updates.
+// Full updates come in CRX format; differential updates come in CRX-style
+// archives, but have a different magic number. They contain "commands.json", a
+// list of commands for the patcher to follow. The patcher uses these commands,
+// the other files in the archive, and the files from the existing installation
+// of the component to create the contents of a full update, which is then
+// installed normally.
+// Component updates are specified by the 'codebasediff' attribute of an
+// updatecheck response:
+// <updatecheck codebase="http://example.com/extension_1.2.3.4.crx"
+// hash="12345" size="9854" status="ok" version="1.2.3.4"
+// prodversionmin="2.0.143.0"
+// codebasediff="http://example.com/diff_1.2.3.4.crx"
+// hashdiff="123" sizediff="101"
+// fp="1.123" />
+// The component updater will attempt a differential update if it is available
+// and allowed to, and fall back to a full update if it fails.
+//
+// After installation (diff or full), the component updater records "fp", the
+// fingerprint of the installed files, to later identify the existing files to
+// the server so that a proper differential update can be provided next cycle.
+
+
+#ifndef CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_PATCHER_H_
+#define CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_PATCHER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "chrome/browser/component_updater/component_unpacker.h"
+
+namespace base {
+class FilePath;
+}
+
+class ComponentInstaller;
+
+// Applies a delta patch to a single file. Specifically, creates a file at
+// |output_file| using |input_file| patched according to the algorithm
+// specified by |patch_type| using |patch_file|. Sets the value of error to
+// the error code of the failing patch operation, if there is such a failure.
+class ComponentPatcher {
+ public:
+ // The type of a patch file.
+ enum PatchType {
+ kPatchTypeUnknown,
+ kPatchTypeCourgette,
+ kPatchTypeBsdiff,
+ };
+
+ virtual ComponentUnpacker::Error Patch(PatchType patch_type,
+ const base::FilePath& input_file,
+ const base::FilePath& patch_file,
+ const base::FilePath& output_file,
+ int* error) = 0;
+ virtual ~ComponentPatcher() {}
+};
+
+class ComponentPatcherCrossPlatform : public ComponentPatcher {
+ public:
+ ComponentPatcherCrossPlatform();
+ virtual ComponentUnpacker::Error Patch(PatchType patch_type,
+ const base::FilePath& input_file,
+ const base::FilePath& patch_file,
+ const base::FilePath& output_file,
+ int* error) OVERRIDE;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ComponentPatcherCrossPlatform);
+};
+
+// This function takes an unpacked differential CRX (|input_dir|) and a
+// component installer, and creates a new (non-differential) unpacked CRX, which
+// is then installed normally.
+// The non-differential files are written into the |unpack_dir| directory.
+// Sets |error| to the error code of the first failing patch operation.
+ComponentUnpacker::Error DifferentialUpdatePatch(
+ const base::FilePath& input_dir,
+ const base::FilePath& unpack_dir,
+ ComponentPatcher* component_patcher,
+ ComponentInstaller* installer,
+ int* error);
+
+#endif // CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_PATCHER_H_
diff --git a/chrome/browser/component_updater/component_patcher_operation.cc b/chrome/browser/component_updater/component_patcher_operation.cc
new file mode 100644
index 0000000..0c119919
--- /dev/null
+++ b/chrome/browser/component_updater/component_patcher_operation.cc
@@ -0,0 +1,222 @@
+// Copyright 2013 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 "chrome/browser/component_updater/component_patcher_operation.h"
+
+#include <string>
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/files/memory_mapped_file.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/memory/scoped_handle.h"
+#include "base/path_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/component_updater/component_patcher.h"
+#include "chrome/browser/component_updater/component_updater_service.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "crypto/secure_hash.h"
+#include "crypto/sha2.h"
+#include "crypto/signature_verifier.h"
+#include "extensions/common/crx_file.h"
+#include "third_party/zlib/google/zip.h"
+
+using crypto::SecureHash;
+
+namespace {
+
+const char kInput[] = "input";
+const char kOp[] = "op";
+const char kOutput[] = "output";
+const char kPatch[] = "patch";
+const char kSha256[] = "sha256";
+
+} // namespace
+
+DeltaUpdateOp* CreateDeltaUpdateOp(base::DictionaryValue* command) {
+ std::string operation;
+ if (!command->GetString(kOp, &operation))
+ return NULL;
+ if (operation == "copy")
+ return new DeltaUpdateOpCopy();
+ else if (operation == "create")
+ return new DeltaUpdateOpCreate();
+ else if (operation == "bsdiff")
+ return new DeltaUpdateOpPatchBsdiff();
+ else if (operation == "courgette")
+ return new DeltaUpdateOpPatchCourgette();
+ return NULL;
+}
+
+DeltaUpdateOp::DeltaUpdateOp() {}
+
+DeltaUpdateOp::~DeltaUpdateOp() {}
+
+ComponentUnpacker::Error DeltaUpdateOp::Run(base::DictionaryValue* command_args,
+ const base::FilePath& input_dir,
+ const base::FilePath& unpack_dir,
+ ComponentPatcher* patcher,
+ ComponentInstaller* installer,
+ int* error) {
+ std::string output_rel_path;
+ if (!command_args->GetString(kOutput, &output_rel_path) ||
+ !command_args->GetString(kSha256, &output_sha256_))
+ return ComponentUnpacker::kDeltaBadCommands;
+
+ output_abs_path_ = unpack_dir.Append(
+ base::FilePath::FromUTF8Unsafe(output_rel_path));
+ ComponentUnpacker::Error parse_result = DoParseArguments(
+ command_args, input_dir, installer);
+ if (parse_result != ComponentUnpacker::kNone)
+ return parse_result;
+
+ const base::FilePath parent = output_abs_path_.DirName();
+ if (!file_util::DirectoryExists(parent)) {
+ if (!file_util::CreateDirectory(parent))
+ return ComponentUnpacker::kIoError;
+ }
+
+ ComponentUnpacker::Error run_result = DoRun(patcher, error);
+ if (run_result != ComponentUnpacker::kNone)
+ return run_result;
+
+ return CheckHash();
+}
+
+// Uses the hash as a checksum to confirm that the file now residing in the
+// output directory probably has the contents it should.
+ComponentUnpacker::Error DeltaUpdateOp::CheckHash() {
+ std::vector<uint8> expected_hash;
+ if (!base::HexStringToBytes(output_sha256_, &expected_hash) ||
+ expected_hash.size() != crypto::kSHA256Length)
+ return ComponentUnpacker::kDeltaVerificationFailure;
+
+ base::MemoryMappedFile output_file_mmapped;
+ if (!output_file_mmapped.Initialize(output_abs_path_))
+ return ComponentUnpacker::kDeltaVerificationFailure;
+
+ uint8 actual_hash[crypto::kSHA256Length] = {0};
+ const scoped_ptr<SecureHash> hasher(SecureHash::Create(SecureHash::SHA256));
+ hasher->Update(output_file_mmapped.data(), output_file_mmapped.length());
+ hasher->Finish(actual_hash, sizeof(actual_hash));
+ if (memcmp(actual_hash, &expected_hash[0], sizeof(actual_hash)))
+ return ComponentUnpacker::kDeltaVerificationFailure;
+
+ return ComponentUnpacker::kNone;
+}
+
+DeltaUpdateOpCopy::DeltaUpdateOpCopy() {}
+
+ComponentUnpacker::Error DeltaUpdateOpCopy::DoParseArguments(
+ base::DictionaryValue* command_args,
+ const base::FilePath& input_dir,
+ ComponentInstaller* installer) {
+ std::string input_rel_path;
+ if (!command_args->GetString(kInput, &input_rel_path))
+ return ComponentUnpacker::kDeltaBadCommands;
+
+ if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_))
+ return ComponentUnpacker::kDeltaMissingExistingFile;
+
+ return ComponentUnpacker::kNone;
+}
+
+ComponentUnpacker::Error DeltaUpdateOpCopy::DoRun(ComponentPatcher*,
+ int* error) {
+ *error = 0;
+ if (!file_util::CopyFile(input_abs_path_, output_abs_path_))
+ return ComponentUnpacker::kDeltaOperationFailure;
+
+ return ComponentUnpacker::kNone;
+}
+
+DeltaUpdateOpCreate::DeltaUpdateOpCreate() {}
+
+ComponentUnpacker::Error DeltaUpdateOpCreate::DoParseArguments(
+ base::DictionaryValue* command_args,
+ const base::FilePath& input_dir,
+ ComponentInstaller* installer) {
+ std::string patch_rel_path;
+ if (!command_args->GetString(kPatch, &patch_rel_path))
+ return ComponentUnpacker::kDeltaBadCommands;
+
+ patch_abs_path_ = input_dir.Append(
+ base::FilePath::FromUTF8Unsafe(patch_rel_path));
+
+ return ComponentUnpacker::kNone;
+}
+
+ComponentUnpacker::Error DeltaUpdateOpCreate::DoRun(ComponentPatcher*,
+ int* error) {
+ *error = 0;
+ if (!file_util::Move(patch_abs_path_, output_abs_path_))
+ return ComponentUnpacker::kDeltaOperationFailure;
+
+ return ComponentUnpacker::kNone;
+}
+
+DeltaUpdateOpPatchBsdiff::DeltaUpdateOpPatchBsdiff() {}
+
+ComponentUnpacker::Error DeltaUpdateOpPatchBsdiff::DoParseArguments(
+ base::DictionaryValue* command_args,
+ const base::FilePath& input_dir,
+ ComponentInstaller* installer) {
+ std::string patch_rel_path;
+ std::string input_rel_path;
+ if (!command_args->GetString(kPatch, &patch_rel_path) ||
+ !command_args->GetString(kInput, &input_rel_path))
+ return ComponentUnpacker::kDeltaBadCommands;
+
+ if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_))
+ return ComponentUnpacker::kDeltaMissingExistingFile;
+
+ patch_abs_path_ = input_dir.Append(
+ base::FilePath::FromUTF8Unsafe(patch_rel_path));
+
+ return ComponentUnpacker::kNone;
+}
+
+ComponentUnpacker::Error DeltaUpdateOpPatchBsdiff::DoRun(
+ ComponentPatcher* patcher,
+ int* error) {
+ *error = 0;
+ return patcher->Patch(ComponentPatcher::kPatchTypeBsdiff,
+ input_abs_path_,
+ patch_abs_path_,
+ output_abs_path_,
+ error);
+}
+
+DeltaUpdateOpPatchCourgette::DeltaUpdateOpPatchCourgette() {}
+
+ComponentUnpacker::Error DeltaUpdateOpPatchCourgette::DoParseArguments(
+ base::DictionaryValue* command_args,
+ const base::FilePath& input_dir,
+ ComponentInstaller* installer) {
+ std::string patch_rel_path;
+ std::string input_rel_path;
+ if (!command_args->GetString(kPatch, &patch_rel_path) ||
+ !command_args->GetString(kInput, &input_rel_path))
+ return ComponentUnpacker::kDeltaBadCommands;
+
+ if (!installer->GetInstalledFile(input_rel_path, &input_abs_path_))
+ return ComponentUnpacker::kDeltaMissingExistingFile;
+
+ patch_abs_path_ = input_dir.Append(
+ base::FilePath::FromUTF8Unsafe(patch_rel_path));
+
+ return ComponentUnpacker::kNone;
+}
+
+ComponentUnpacker::Error DeltaUpdateOpPatchCourgette::DoRun(
+ ComponentPatcher* patcher,
+ int* error) {
+ *error = 0;
+ return patcher->Patch(ComponentPatcher::kPatchTypeCourgette,
+ input_abs_path_,
+ patch_abs_path_,
+ output_abs_path_,
+ error);
+}
+
diff --git a/chrome/browser/component_updater/component_patcher_operation.h b/chrome/browser/component_updater/component_patcher_operation.h
new file mode 100644
index 0000000..6e9fe5be
--- /dev/null
+++ b/chrome/browser/component_updater/component_patcher_operation.h
@@ -0,0 +1,158 @@
+// Copyright 2013 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_COMPONENT_UPDATER_COMPONENT_PATCHER_OPERATION_H_
+#define CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_PATCHER_OPERATION_H_
+
+#include <string>
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "chrome/browser/component_updater/component_unpacker.h"
+
+namespace base {
+
+class FilePath;
+class DictionaryValue;
+
+} // namespace base
+
+class ComponentInstaller;
+class ComponentPatcher;
+
+class DeltaUpdateOp {
+ public:
+
+ DeltaUpdateOp();
+ virtual ~DeltaUpdateOp();
+
+ // Parses, runs, and verifies the operation, returning an error code if an
+ // error is encountered, and DELTA_OK otherwise. In case of errors,
+ // extended error information can be returned in the |error| parameter.
+ ComponentUnpacker::Error Run(
+ base::DictionaryValue* command_args,
+ const base::FilePath& input_dir,
+ const base::FilePath& unpack_dir,
+ ComponentPatcher* patcher,
+ ComponentInstaller* installer,
+ int* error);
+
+ protected:
+ std::string output_sha256_;
+ base::FilePath output_abs_path_;
+
+ private:
+ ComponentUnpacker::Error CheckHash();
+
+ // Subclasses must override DoParseArguments to parse operation-specific
+ // arguments. DoParseArguments returns DELTA_OK on success; any other code
+ // represents failure.
+ virtual ComponentUnpacker::Error DoParseArguments(
+ base::DictionaryValue* command_args,
+ const base::FilePath& input_dir,
+ ComponentInstaller* installer) = 0;
+
+ // Subclasses must override DoRun to actually perform the patching operation.
+ // DoRun returns DELTA_OK on success; any other code represents failure.
+ // Additional error information can be returned in the |error| parameter.
+ virtual ComponentUnpacker::Error DoRun(ComponentPatcher* patcher,
+ int* error) = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(DeltaUpdateOp);
+};
+
+// A 'copy' operation takes a file currently residing on the disk and moves it
+// into the unpacking directory: this represents "no change" in the file being
+// installed.
+class DeltaUpdateOpCopy : public DeltaUpdateOp {
+ public:
+ DeltaUpdateOpCopy();
+
+ private:
+ // Overrides of DeltaUpdateOp.
+ virtual ComponentUnpacker::Error DoParseArguments(
+ base::DictionaryValue* command_args,
+ const base::FilePath& input_dir,
+ ComponentInstaller* installer) OVERRIDE;
+
+ virtual ComponentUnpacker::Error DoRun(ComponentPatcher* patcher,
+ int* error) OVERRIDE;
+
+ base::FilePath input_abs_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeltaUpdateOpCopy);
+};
+
+// A 'create' operation takes a full file that was sent in the delta update
+// archive and moves it into the unpacking directory: this represents the
+// addition of a new file, or a file so different that no bandwidth could be
+// saved by transmitting a differential update.
+class DeltaUpdateOpCreate : public DeltaUpdateOp {
+ public:
+ DeltaUpdateOpCreate();
+
+ private:
+ // Overrides of DeltaUpdateOp.
+ virtual ComponentUnpacker::Error DoParseArguments(
+ base::DictionaryValue* command_args,
+ const base::FilePath& input_dir,
+ ComponentInstaller* installer) OVERRIDE;
+
+ virtual ComponentUnpacker::Error DoRun(ComponentPatcher* patcher,
+ int* error) OVERRIDE;
+
+ base::FilePath patch_abs_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeltaUpdateOpCreate);
+};
+
+// A 'bsdiff' operation takes an existing file on disk, and a bsdiff-
+// format patch file provided in the delta update package, and runs bsdiff
+// to construct an output file in the unpacking directory.
+class DeltaUpdateOpPatchBsdiff : public DeltaUpdateOp {
+ public:
+ DeltaUpdateOpPatchBsdiff();
+
+ private:
+ // Overrides of DeltaUpdateOp.
+ virtual ComponentUnpacker::Error DoParseArguments(
+ base::DictionaryValue* command_args,
+ const base::FilePath& input_dir,
+ ComponentInstaller* installer) OVERRIDE;
+
+ virtual ComponentUnpacker::Error DoRun(ComponentPatcher* patcher,
+ int* error) OVERRIDE;
+
+ base::FilePath patch_abs_path_;
+ base::FilePath input_abs_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeltaUpdateOpPatchBsdiff);
+};
+
+// A 'courgette' operation takes an existing file on disk, and a Courgette-
+// format patch file provided in the delta update package, and runs Courgette
+// to construct an output file in the unpacking directory.
+class DeltaUpdateOpPatchCourgette : public DeltaUpdateOp {
+ public:
+ DeltaUpdateOpPatchCourgette();
+
+ private:
+ // Overrides of DeltaUpdateOp.
+ virtual ComponentUnpacker::Error DoParseArguments(
+ base::DictionaryValue* command_args,
+ const base::FilePath& input_dir,
+ ComponentInstaller* installer) OVERRIDE;
+
+ virtual ComponentUnpacker::Error DoRun(ComponentPatcher* patcher,
+ int* error) OVERRIDE;
+
+ base::FilePath patch_abs_path_;
+ base::FilePath input_abs_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(DeltaUpdateOpPatchCourgette);
+};
+
+// Factory function to create DeltaUpdateOp instances.
+DeltaUpdateOp* CreateDeltaUpdateOp(base::DictionaryValue* command);
+
+#endif // CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_PATCHER_OPERATION_H_
diff --git a/chrome/browser/component_updater/component_patcher_win.cc b/chrome/browser/component_updater/component_patcher_win.cc
new file mode 100644
index 0000000..97aaeee
--- /dev/null
+++ b/chrome/browser/component_updater/component_patcher_win.cc
@@ -0,0 +1,98 @@
+// Copyright 2013 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 "chrome/browser/component_updater/component_patcher_win.h"
+
+#include <string>
+
+#include "base/base_paths.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/string_util.h"
+#include "chrome/installer/util/util_constants.h"
+
+namespace {
+
+std::string PatchTypeToCommandLineSwitch(
+ ComponentPatcher::PatchType patch_type) {
+ if (patch_type == ComponentPatcher::kPatchTypeCourgette)
+ return std::string(installer::kCourgette);
+ else if (patch_type == ComponentPatcher::kPatchTypeBsdiff)
+ return std::string(installer::kBsdiff);
+ else
+ return std::string();
+}
+
+// Finds the path to the setup.exe. First, it looks for the program in the
+// "installer" directory. If the program is not found there, it tries to find it
+// in the directory where chrome.dll lives. Returns the path to the setup.exe,
+// if the path exists, otherwise it returns an an empty path.
+base::FilePath FindSetupProgram() {
+ base::FilePath exe_dir;
+ if (!PathService::Get(base::DIR_MODULE, &exe_dir))
+ return base::FilePath();
+
+ const std::string installer_dir(WideToASCII(installer::kInstallerDir));
+ const std::string setup_exe(WideToASCII(installer::kSetupExe));
+
+ base::FilePath setup_path = exe_dir;
+ setup_path = setup_path.AppendASCII(installer_dir);
+ setup_path = setup_path.AppendASCII(setup_exe);
+ if (file_util::PathExists(setup_path))
+ return setup_path;
+
+ setup_path = exe_dir;
+ setup_path = setup_path.AppendASCII(setup_exe);
+ if (file_util::PathExists(setup_path))
+ return setup_path;
+
+ return base::FilePath();
+}
+
+} // namespace
+
+// Applies the patch to the input file. Returns kNone if the patch was
+// successfully applied, kDeltaOperationFailure if the patch operation
+// encountered errors, and kDeltaPatchProcessFailure if there was an error
+// when running the patch code out of process. In the error case, detailed error
+// information could be returned in the error parameter.
+ComponentUnpacker::Error ComponentPatcherWin::Patch(
+ PatchType patch_type,
+ const base::FilePath& input_file,
+ const base::FilePath& patch_file,
+ const base::FilePath& output_file,
+ int* error) {
+ *error = 0;
+
+ const base::FilePath exe_path = FindSetupProgram();
+ if (exe_path.empty())
+ return ComponentUnpacker::kDeltaPatchProcessFailure;
+
+ const std::string patch_type_str(PatchTypeToCommandLineSwitch(patch_type));
+
+ CommandLine cl(CommandLine::NO_PROGRAM);
+ cl.AppendSwitchASCII(installer::switches::kPatch, patch_type_str.c_str());
+ cl.AppendSwitchPath(installer::switches::kInputFile, input_file);
+ cl.AppendSwitchPath(installer::switches::kPatchFile, patch_file);
+ cl.AppendSwitchPath(installer::switches::kOutputFile, output_file);
+
+ base::LaunchOptions launch_options;
+ launch_options.wait = true;
+ launch_options.start_hidden = true;
+ CommandLine setup_path(exe_path);
+ setup_path.AppendArguments(cl, false);
+
+ base::ProcessHandle ph;
+ int exit_code = 0;
+ if (!base::LaunchProcess(setup_path, launch_options, &ph) ||
+ !base::WaitForExitCode(ph, &exit_code))
+ return ComponentUnpacker::kDeltaPatchProcessFailure;
+
+ *error = exit_code;
+ return *error ? ComponentUnpacker::kDeltaOperationFailure :
+ ComponentUnpacker::kNone;
+}
+
diff --git a/chrome/browser/component_updater/component_patcher_win.h b/chrome/browser/component_updater/component_patcher_win.h
new file mode 100644
index 0000000..d87ba54
--- /dev/null
+++ b/chrome/browser/component_updater/component_patcher_win.h
@@ -0,0 +1,24 @@
+// Copyright 2013 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_COMPONENT_UPDATER_COMPONENT_PATCHER_WIN_H_
+#define CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_PATCHER_WIN_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "chrome/browser/component_updater/component_patcher.h"
+
+class ComponentPatcherWin : public ComponentPatcher {
+ public:
+ ComponentPatcherWin() {}
+ virtual ComponentUnpacker::Error Patch(PatchType patch_type,
+ const base::FilePath& input_file,
+ const base::FilePath& patch_file,
+ const base::FilePath& output_file,
+ int* error) OVERRIDE;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ComponentPatcherWin);
+};
+
+#endif // CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_PATCHER_WIN_H_
diff --git a/chrome/browser/component_updater/component_unpacker.cc b/chrome/browser/component_updater/component_unpacker.cc
index 9454637..341df22 100644
--- a/chrome/browser/component_updater/component_unpacker.cc
+++ b/chrome/browser/component_updater/component_unpacker.cc
@@ -12,6 +12,7 @@
#include "base/memory/scoped_handle.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
+#include "chrome/browser/component_updater/component_patcher.h"
#include "chrome/browser/component_updater/component_updater_service.h"
#include "chrome/common/extensions/extension_constants.h"
#include "crypto/secure_hash.h"
@@ -22,11 +23,12 @@
using crypto::SecureHash;
namespace {
+
// This class makes sure that the CRX digital signature is valid
// and well formed.
class CRXValidator {
public:
- explicit CRXValidator(FILE* crx_file) : valid_(false) {
+ explicit CRXValidator(FILE* crx_file) : valid_(false), delta_(false) {
extensions::CrxFile::Header header;
size_t len = fread(&header, 1, sizeof(header), crx_file);
if (len < sizeof(header))
@@ -37,6 +39,7 @@ class CRXValidator {
extensions::CrxFile::Parse(header, &error));
if (!crx.get())
return;
+ delta_ = extensions::CrxFile::HeaderIsDelta(header);
std::vector<uint8> key(header.key_size);
len = fread(&key[0], sizeof(uint8), header.key_size, crx_file);
@@ -72,10 +75,13 @@ class CRXValidator {
bool valid() const { return valid_; }
+ bool delta() const { return delta_; }
+
const std::vector<uint8>& public_key() const { return public_key_; }
private:
bool valid_;
+ bool delta_;
std::vector<uint8> public_key_;
};
@@ -98,12 +104,29 @@ base::DictionaryValue* ReadManifest(const base::FilePath& unpack_path) {
return static_cast<base::DictionaryValue*>(root.release());
}
+// Deletes a path if it exists, and then creates a directory there.
+// Returns true if and only if these operations were successful.
+// This method doesn't take any special steps to prevent files from
+// being inserted into the target directory by another process or thread.
+bool MakeEmptyDirectory(const base::FilePath& path) {
+ if (file_util::PathExists(path)) {
+ if (!file_util::Delete(path, true))
+ return false;
+ }
+ if (!file_util::CreateDirectory(path))
+ return false;
+ return true;
+}
+
} // namespace.
ComponentUnpacker::ComponentUnpacker(const std::vector<uint8>& pk_hash,
const base::FilePath& path,
+ const std::string& fingerprint,
+ ComponentPatcher* patcher,
ComponentInstaller* installer)
- : error_(kNone) {
+ : error_(kNone),
+ extended_error_(0) {
if (pk_hash.empty() || path.empty()) {
error_ = kInvalidParams;
return;
@@ -135,31 +158,61 @@ ComponentUnpacker::ComponentUnpacker(const std::vector<uint8>& pk_hash,
return;
}
// We want the temporary directory to be unique and yet predictable, so
- // we can easily find the package in a end user machine.
- std::string dir(
+ // we can easily find the package in an end user machine.
+ const std::string dir(
base::StringPrintf("CRX_%s", base::HexEncode(hash, 6).c_str()));
unpack_path_ = path.DirName().AppendASCII(dir.c_str());
- if (file_util::DirectoryExists(unpack_path_)) {
- if (!file_util::Delete(unpack_path_, true)) {
- unpack_path_.clear();
- error_ = kUzipPathError;
- return;
- }
- }
- if (!file_util::CreateDirectory(unpack_path_)) {
+ if (!MakeEmptyDirectory(unpack_path_)) {
unpack_path_.clear();
- error_ = kUzipPathError;
+ error_ = kUnzipPathError;
return;
}
- if (!zip::Unzip(path, unpack_path_)) {
- error_ = kUnzipFailed;
- return;
+ if (validator.delta()) { // Package is a diff package.
+ // We want a different temp directory for the delta files; we'll put the
+ // patch output into unpack_path_.
+ std::string dir(
+ base::StringPrintf("CRX_%s_diff", base::HexEncode(hash, 6).c_str()));
+ base::FilePath unpack_diff_path = path.DirName().AppendASCII(dir.c_str());
+ if (!MakeEmptyDirectory(unpack_diff_path)) {
+ error_ = kUnzipPathError;
+ return;
+ }
+ if (!zip::Unzip(path, unpack_diff_path)) {
+ error_ = kUnzipFailed;
+ return;
+ }
+ ComponentUnpacker::Error result = DifferentialUpdatePatch(unpack_diff_path,
+ unpack_path_,
+ patcher,
+ installer,
+ &extended_error_);
+ file_util::Delete(unpack_diff_path, true);
+ unpack_diff_path.clear();
+ error_ = result;
+ if (error_ != kNone) {
+ return;
+ }
+ } else {
+ // Package is a normal update/install; unzip it into unpack_path_ directly.
+ if (!zip::Unzip(path, unpack_path_)) {
+ error_ = kUnzipFailed;
+ return;
+ }
}
scoped_ptr<base::DictionaryValue> manifest(ReadManifest(unpack_path_));
if (!manifest.get()) {
error_ = kBadManifest;
return;
}
+ // Write the fingerprint to disk.
+ if (static_cast<int>(fingerprint.size()) !=
+ file_util::WriteFile(
+ unpack_path_.Append(FILE_PATH_LITERAL("manifest.fingerprint")),
+ fingerprint.c_str(),
+ fingerprint.size())) {
+ error_ = kFingerprintWriteFailed;
+ return;
+ }
if (!installer->Install(*manifest, unpack_path_)) {
error_ = kInstallerError;
return;
@@ -169,7 +222,6 @@ ComponentUnpacker::ComponentUnpacker(const std::vector<uint8>& pk_hash,
}
ComponentUnpacker::~ComponentUnpacker() {
- if (!unpack_path_.empty()) {
+ if (!unpack_path_.empty())
file_util::Delete(unpack_path_, true);
- }
}
diff --git a/chrome/browser/component_updater/component_unpacker.h b/chrome/browser/component_updater/component_unpacker.h
index e51555c..6ae7277 100644
--- a/chrome/browser/component_updater/component_unpacker.h
+++ b/chrome/browser/component_updater/component_unpacker.h
@@ -5,11 +5,13 @@
#ifndef CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_UNPACKER_H_
#define CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_UNPACKER_H_
+#include <string>
#include <vector>
-
+#include "base/basictypes.h"
#include "base/files/file_path.h"
class ComponentInstaller;
+class ComponentPatcher;
// In charge of unpacking the component CRX package and verifying that it is
// well formed and the cryptographic signature is correct. If there is no
@@ -26,22 +28,33 @@ class ComponentInstaller;
class ComponentUnpacker {
public:
// Possible error conditions.
+ // Add only to the bottom of this enum; the order must be kept stable.
enum Error {
kNone,
kInvalidParams,
kInvalidFile,
- kUzipPathError,
+ kUnzipPathError,
kUnzipFailed,
kNoManifest,
kBadManifest,
kBadExtension,
kInvalidId,
kInstallerError,
+ kIoError,
+ kDeltaVerificationFailure,
+ kDeltaBadCommands,
+ kDeltaUnsupportedCommand,
+ kDeltaOperationFailure,
+ kDeltaPatchProcessFailure,
+ kDeltaMissingExistingFile,
+ kFingerprintWriteFailed,
};
// Unpacks, verifies and calls the installer. |pk_hash| is the expected
// public key SHA256 hash. |path| is the current location of the CRX.
ComponentUnpacker(const std::vector<uint8>& pk_hash,
const base::FilePath& path,
+ const std::string& fingerprint,
+ ComponentPatcher* patcher,
ComponentInstaller* installer);
// If something went wrong during unpacking or installer invocation, the
@@ -50,9 +63,12 @@ class ComponentUnpacker {
Error error() const { return error_; }
+ int extended_error() const { return extended_error_; }
+
private:
base::FilePath unpack_path_;
Error error_;
+ int extended_error_; // Provides additional error information.
};
#endif // CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_UNPACKER_H_
diff --git a/chrome/browser/component_updater/component_updater_configurator.cc b/chrome/browser/component_updater/component_updater_configurator.cc
index 16fa3e9..43e9072 100644
--- a/chrome/browser/component_updater/component_updater_configurator.cc
+++ b/chrome/browser/component_updater/component_updater_configurator.cc
@@ -14,31 +14,39 @@
#include "base/strings/string_util.h"
#include "base/win/windows_version.h"
#include "build/build_config.h"
+#include "chrome/browser/component_updater/component_patcher.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/omaha_query_params/omaha_query_params.h"
#include "net/url_request/url_request_context_getter.h"
+#if defined(OS_WIN)
+#include "chrome/browser/component_updater/component_patcher_win.h"
+#endif
+
namespace {
+
// Default time constants.
const int kDelayOneMinute = 60;
const int kDelayOneHour = kDelayOneMinute * 60;
// Debug values you can pass to --component-updater-debug=value1,value2.
// Speed up component checking.
-const char kDebugFastUpdate[] = "fast-update";
+const char kSwitchFastUpdate[] = "fast-update";
// Force out-of-process-xml parsing.
-const char kDebugOutOfProcess[] = "out-of-process";
+const char kSwitchOutOfProcess[] = "out-of-process";
// Add "testrequest=1" parameter to the update check query.
-const char kDebugRequestParam[] = "test-request";
+const char kSwitchRequestParam[] = "test-request";
+// Disables differential updates.
+const char kSwitchDisableDeltaUpdates[] = "disable-delta-updates";
// The urls from which an update manifest can be fetched.
const char* kUrlSources[] = {
"http://clients2.google.com/service/update2/crx", // BANDAID
"http://omaha.google.com/service/update2/crx", // CWS_PUBLIC
- "http://omaha.sandbox.google.com/service/update2/crx" // CWS_SANDBOX
+ "http://omaha.sandbox.google.com/service/update2/crx", // CWS_SANDBOX
};
-bool HasDebugValue(const std::vector<std::string>& vec, const char* test) {
+bool HasSwitchValue(const std::vector<std::string>& vec, const char* test) {
if (vec.empty())
return 0;
return (std::find(vec.begin(), vec.end(), test) != vec.end());
@@ -64,25 +72,36 @@ class ChromeConfigurator : public ComponentUpdateService::Configurator {
virtual net::URLRequestContextGetter* RequestContext() OVERRIDE;
virtual bool InProcess() OVERRIDE;
virtual void OnEvent(Events event, int val) OVERRIDE;
+ virtual ComponentPatcher* CreateComponentPatcher() OVERRIDE;
+ virtual bool DeltasEnabled() const OVERRIDE;
private:
net::URLRequestContextGetter* url_request_getter_;
std::string extra_info_;
bool fast_update_;
bool out_of_process_;
+ bool deltas_enabled_;
};
ChromeConfigurator::ChromeConfigurator(const CommandLine* cmdline,
net::URLRequestContextGetter* url_request_getter)
: url_request_getter_(url_request_getter),
extra_info_(chrome::OmahaQueryParams::Get(
- chrome::OmahaQueryParams::CHROME)) {
+ chrome::OmahaQueryParams::CHROME)),
+ fast_update_(false),
+ out_of_process_(false),
+ deltas_enabled_(false) {
// Parse comma-delimited debug flags.
- std::vector<std::string> debug_values;
- Tokenize(cmdline->GetSwitchValueASCII(switches::kComponentUpdaterDebug),
- ",", &debug_values);
- fast_update_ = HasDebugValue(debug_values, kDebugFastUpdate);
- out_of_process_ = HasDebugValue(debug_values, kDebugOutOfProcess);
+ std::vector<std::string> switch_values;
+ Tokenize(cmdline->GetSwitchValueASCII(switches::kComponentUpdater),
+ ",", &switch_values);
+ fast_update_ = HasSwitchValue(switch_values, kSwitchFastUpdate);
+ out_of_process_ = HasSwitchValue(switch_values, kSwitchOutOfProcess);
+#if defined(OS_WIN)
+ deltas_enabled_ = !HasSwitchValue(switch_values, kSwitchDisableDeltaUpdates);
+#else
+ deltas_enabled_ = false;
+#endif
// Make the extra request params, they are necessary so omaha does
// not deliver components that are going to be rejected at install time.
@@ -91,7 +110,7 @@ ChromeConfigurator::ChromeConfigurator(const CommandLine* cmdline,
base::win::OSInfo::WOW64_ENABLED)
extra_info_ += "&wow64=1";
#endif
- if (HasDebugValue(debug_values, kDebugRequestParam))
+ if (HasSwitchValue(switch_values, kSwitchRequestParam))
extra_info_ += "&testrequest=1";
}
@@ -161,6 +180,18 @@ void ChromeConfigurator::OnEvent(Events event, int val) {
}
}
+ComponentPatcher* ChromeConfigurator::CreateComponentPatcher() {
+#if defined(OS_WIN)
+ return new ComponentPatcherWin();
+#else
+ return new ComponentPatcherCrossPlatform();
+#endif
+}
+
+bool ChromeConfigurator::DeltasEnabled() const {
+ return deltas_enabled_;
+}
+
ComponentUpdateService::Configurator* MakeChromeComponentUpdaterConfigurator(
const CommandLine* cmdline, net::URLRequestContextGetter* context_getter) {
return new ChromeConfigurator(cmdline, context_getter);
diff --git a/chrome/browser/component_updater/component_updater_service.cc b/chrome/browser/component_updater/component_updater_service.cc
index f3eee77..367ff6e 100644
--- a/chrome/browser/component_updater/component_updater_service.cc
+++ b/chrome/browser/component_updater/component_updater_service.cc
@@ -10,6 +10,7 @@
#include "base/at_exit.h"
#include "base/bind.h"
+#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/logging.h"
@@ -21,11 +22,13 @@
#include "base/strings/stringprintf.h"
#include "base/timer.h"
#include "chrome/browser/browser_process.h"
+#include "chrome/browser/component_updater/component_patcher.h"
#include "chrome/browser/component_updater/component_unpacker.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_utility_messages.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/extensions/extension.h"
+#include "chrome/common/omaha_query_params/omaha_query_params.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/utility_process_host.h"
@@ -47,21 +50,24 @@ using extensions::Extension;
// base::Bind() calls are not refcounted.
namespace {
+
// Manifest sources, from most important to least important.
const CrxComponent::UrlSource kManifestSources[] = {
CrxComponent::BANDAID,
CrxComponent::CWS_PUBLIC,
- CrxComponent::CWS_SANDBOX
+ CrxComponent::CWS_SANDBOX,
};
// Extends an omaha compatible update check url |query| string. Does
// not mutate the string if it would be longer than |limit| chars.
bool AddQueryString(const std::string& id,
const std::string& version,
+ const std::string& fingerprint,
size_t limit,
std::string* query) {
std::string additional =
- base::StringPrintf("id=%s&v=%s&uc", id.c_str(), version.c_str());
+ base::StringPrintf("id=%s&v=%s&fp=%s&uc",
+ id.c_str(), version.c_str(), fingerprint.c_str());
additional = "x=" + net::EscapeQueryParamValue(additional, true);
if ((additional.size() + query->size() + 1) > limit)
return false;
@@ -163,7 +169,7 @@ void StartFetch(net::URLFetcher* fetcher,
fetcher->Start();
}
-// Returs true if the url request of |fetcher| was succesful.
+// Returns true if the url request of |fetcher| was succesful.
bool FetchSuccess(const net::URLFetcher& fetcher) {
return (fetcher.GetStatus().status() == net::URLRequestStatus::SUCCESS) &&
(fetcher.GetResponseCode() == 200);
@@ -174,23 +180,44 @@ bool FetchSuccess(const net::URLFetcher& fetcher) {
// which is supplied by the the component updater client and |status| which
// is modified as the item is processed by the update pipeline. The expected
// transition graph is:
-// error error error
-// +--kNoUpdate<------<-------+------<------+------<------+
-// | | | |
-// V yes | | |
-// kNew --->kChecking-->[update?]----->kCanUpdate-->kDownloading-->kUpdating
-// ^ | |
-// | |no |
-// |--kUpToDate<---+ |
-// | success |
-// +--kUpdated<-------------------------------------------+
+//
+// kNew
+// |
+// V
+// +----------------------> kChecking -<---------+-----<-------+
+// | | | |
+// | error V no | |
+// kNoUpdate <---------------- [update?] ->---- kUpToDate kUpdated
+// ^ | ^
+// | yes | |
+// | diff=false V |
+// | +-----------> kCanUpdate |
+// | | | |
+// | | V no |
+// | | [differential update?]->----+ |
+// | | | | |
+// | | yes | | |
+// | | error V | |
+// | +---------<- kDownloadingDiff | |
+// | | | | |
+// | | | | |
+// | | error V | |
+// | +---------<- kUpdatingDiff ->--------|-----------+ success
+// | | |
+// | error V |
+// +----------------------------------------- kDownloading |
+// | | |
+// | error V |
+// +------------------------------------------ kUpdating ->----+ success
//
struct CrxUpdateItem {
enum Status {
kNew,
kChecking,
kCanUpdate,
+ kDownloadingDiff,
kDownloading,
+ kUpdatingDiff,
kUpdating,
kUpdated,
kUpToDate,
@@ -199,13 +226,33 @@ struct CrxUpdateItem {
};
Status status;
- GURL crx_url;
std::string id;
- base::Time last_check;
CrxComponent component;
+
+ base::Time last_check;
+
+ // These members are initialized with their corresponding values from the
+ // update server response.
+ GURL crx_url;
+ GURL diff_crx_url;
+ int size;
+ int diff_size;
+
+ // The from/to version and fingerprint values.
+ Version previous_version;
Version next_version;
+ std::string previous_fp;
+ std::string next_fp;
- CrxUpdateItem() : status(kNew) {}
+ // True if the differential update failed for any reason.
+ bool diff_update_failed;
+
+ CrxUpdateItem()
+ : status(kNew),
+ size(0),
+ diff_size(0),
+ diff_update_failed(false) {
+ }
// Function object used to find a specific component.
class FindById {
@@ -220,9 +267,21 @@ struct CrxUpdateItem {
};
};
-} // namespace.
+// Returns true if a differential update is available for the update item.
+bool IsDiffUpdateAvailable(const CrxUpdateItem* update_item) {
+ return update_item->diff_crx_url.is_valid();
+}
-typedef ComponentUpdateService::Configurator Config;
+// Returns true if a differential update is available, it has not failed yet,
+// and the configuration allows it.
+bool CanTryDiffUpdate(const CrxUpdateItem* update_item,
+ const ComponentUpdateService::Configurator& config) {
+ return IsDiffUpdateAvailable(update_item) &&
+ !update_item->diff_update_failed &&
+ config.DeltasEnabled();
+}
+
+} // namespace.
CrxComponent::CrxComponent()
: installer(NULL),
@@ -240,7 +299,7 @@ CrxComponent::~CrxComponent() {
// rest of the browser, so even if we have many components registered and
// eligible for update, we only do one thing at a time with pauses in between
// the tasks. Also when we do network requests there is only one |url_fetcher_|
-// in flight at at a time.
+// in flight at a time.
// There are no locks in this code, the main structure |work_items_| is mutated
// only from the UI thread. The unpack and installation is done in the file
// thread and the network requests are done in the IO thread and in the file
@@ -304,6 +363,7 @@ class CrxUpdateService : public ComponentUpdateService {
ComponentInstaller* installer;
std::vector<uint8> pk_hash;
std::string id;
+ std::string fingerprint;
CRXContext() : installer(NULL) {}
};
@@ -319,8 +379,7 @@ class CrxUpdateService : public ComponentUpdateService {
const UpdateManifest::Results& results);
// See ManifestParserBridge.
- void OnParseUpdateManifestFailed(
- const std::string& error_message);
+ void OnParseUpdateManifestFailed(const std::string& error_message);
bool AddItemToUpdateCheck(CrxUpdateItem* item, std::string* query);
@@ -333,19 +392,22 @@ class CrxUpdateService : public ComponentUpdateService {
void Install(const CRXContext* context, const base::FilePath& crx_path);
void DoneInstalling(const std::string& component_id,
- ComponentUnpacker::Error error);
+ ComponentUnpacker::Error error,
+ int extended_error);
size_t ChangeItemStatus(CrxUpdateItem::Status from,
CrxUpdateItem::Status to);
CrxUpdateItem* FindUpdateItemById(const std::string& id);
- scoped_ptr<Config> config_;
+ scoped_ptr<ComponentUpdateService::Configurator> config_;
+
+ scoped_ptr<ComponentPatcher> component_patcher_;
scoped_ptr<net::URLFetcher> url_fetcher_;
- typedef std::vector<CrxUpdateItem*> UpdateItems;
// A collection of every work item.
+ typedef std::vector<CrxUpdateItem*> UpdateItems;
UpdateItems work_items_;
// A particular set of items from work_items_, which should be checked ASAP.
@@ -353,7 +415,8 @@ class CrxUpdateService : public ComponentUpdateService {
base::OneShotTimer<CrxUpdateService> timer_;
- Version chrome_version_;
+ const Version chrome_version_;
+ const std::string prod_id_;
bool running_;
@@ -362,10 +425,12 @@ class CrxUpdateService : public ComponentUpdateService {
//////////////////////////////////////////////////////////////////////////////
-CrxUpdateService::CrxUpdateService(
- ComponentUpdateService::Configurator* config)
+CrxUpdateService::CrxUpdateService(ComponentUpdateService::Configurator* config)
: config_(config),
+ component_patcher_(config->CreateComponentPatcher()),
chrome_version_(chrome::VersionInfo().Version()),
+ prod_id_(chrome::OmahaQueryParams::GetProdIdString(
+ chrome::OmahaQueryParams::CHROME)),
running_(false) {
}
@@ -450,7 +515,7 @@ CrxUpdateItem* CrxUpdateService::FindUpdateItemById(const std::string& id) {
}
// Changes all the components in |work_items_| that have |from| status to
-// |to| statatus and returns how many have been changed.
+// |to| status and returns how many have been changed.
size_t CrxUpdateService::ChangeItemStatus(CrxUpdateItem::Status from,
CrxUpdateItem::Status to) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -491,6 +556,7 @@ ComponentUpdateService::Status CrxUpdateService::RegisterComponent(
uit = new CrxUpdateItem;
uit->id.swap(id);
uit->component = component;
+
work_items_.push_back(uit);
// If this is the first component registered we call Start to
// schedule the first timer.
@@ -501,17 +567,24 @@ ComponentUpdateService::Status CrxUpdateService::RegisterComponent(
}
// Sets a component to be checked for updates.
-// The componet to add is |crxit| and the |query| string is modified with the
+// The component to add is |crxit| and the |query| string is modified with the
// required omaha compatible query. Returns false when the query strings
// is longer than specified by UrlSizeLimit().
bool CrxUpdateService::AddItemToUpdateCheck(CrxUpdateItem* item,
std::string* query) {
if (!AddQueryString(item->id,
item->component.version.GetString(),
+ item->component.fingerprint,
config_->UrlSizeLimit(), query))
return false;
+
item->status = CrxUpdateItem::kChecking;
item->last_check = base::Time::Now();
+ item->previous_version = item->component.version;
+ item->next_version = Version();
+ item->previous_fp = item->component.fingerprint;
+ item->next_fp.clear();
+ item->diff_update_failed = false;
return true;
}
@@ -535,16 +608,17 @@ ComponentUpdateService::Status CrxUpdateService::CheckForUpdateSoon(
// Check if the request is too soon.
base::TimeDelta delta = base::Time::Now() - uit->last_check;
- if (delta < base::TimeDelta::FromSeconds(config_->OnDemandDelay())) {
+ if (delta < base::TimeDelta::FromSeconds(config_->OnDemandDelay()))
return kError;
- }
switch (uit->status) {
// If the item is already in the process of being updated, there is
// no point in this call, so return kInProgress.
case CrxUpdateItem::kChecking:
case CrxUpdateItem::kCanUpdate:
+ case CrxUpdateItem::kDownloadingDiff:
case CrxUpdateItem::kDownloading:
+ case CrxUpdateItem::kUpdatingDiff:
case CrxUpdateItem::kUpdating:
return kInProgress;
// Otherwise the item was already checked a while back (or it is new),
@@ -583,13 +657,21 @@ void CrxUpdateService::ProcessPendingItems() {
if (item->status != CrxUpdateItem::kCanUpdate)
continue;
// Found component to update, start the process.
- item->status = CrxUpdateItem::kDownloading;
CRXContext* context = new CRXContext;
context->pk_hash = item->component.pk_hash;
context->id = item->id;
context->installer = item->component.installer;
+ context->fingerprint = item->next_fp;
+ GURL package_url;
+ if (CanTryDiffUpdate(item, *config_)) {
+ package_url = item->diff_crx_url;
+ item->status = CrxUpdateItem::kDownloadingDiff;
+ } else {
+ package_url = item->crx_url;
+ item->status = CrxUpdateItem::kDownloading;
+ }
url_fetcher_.reset(net::URLFetcher::Create(
- 0, item->crx_url, net::URLFetcher::GET,
+ 0, package_url, net::URLFetcher::GET,
MakeContextDelegate(this, context)));
StartFetch(url_fetcher_.get(), config_->RequestContext(), true);
return;
@@ -700,11 +782,10 @@ void CrxUpdateService::ParseManifest(const std::string& xml) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (config_->InProcess()) {
UpdateManifest manifest;
- if (!manifest.Parse(xml)) {
+ if (!manifest.Parse(xml))
CrxUpdateService::OnParseUpdateManifestFailed(manifest.errors());
- } else {
+ else
CrxUpdateService::OnParseUpdateManifestSucceeded(manifest.results());
- }
} else {
UtilityProcessHost* host =
UtilityProcessHost::Create(new ManifestParserBridge(this),
@@ -753,8 +834,12 @@ void CrxUpdateService::OnParseUpdateManifestSucceeded(
// All test passed. Queue an upgrade for this component and fire the
// notifications.
crx->crx_url = it->crx_url;
+ crx->size = it->size;
+ crx->diff_crx_url = it->diff_crx_url;
+ crx->diff_size = it->diff_size;
crx->status = CrxUpdateItem::kCanUpdate;
crx->next_version = Version(it->version);
+ crx->next_fp = it->package_fingerprint;
++update_pending;
content::NotificationService::current()->Notify(
@@ -789,19 +874,39 @@ void CrxUpdateService::OnURLFetchComplete(const net::URLFetcher* source,
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
int error_code = net::OK;
+ CrxUpdateItem* crx = FindUpdateItemById(context->id);
+ DCHECK(crx->status == CrxUpdateItem::kDownloadingDiff ||
+ crx->status == CrxUpdateItem::kDownloading);
+
if (source->FileErrorOccurred(&error_code) || !FetchSuccess(*source)) {
+ if (crx->status == CrxUpdateItem::kDownloadingDiff) {
+ size_t count = ChangeItemStatus(CrxUpdateItem::kDownloadingDiff,
+ CrxUpdateItem::kCanUpdate);
+ DCHECK_EQ(count, 1ul);
+ ScheduleNextRun(true);
+ return;
+ }
size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
CrxUpdateItem::kNoUpdate);
DCHECK_EQ(count, 1ul);
config_->OnEvent(Configurator::kNetworkError, CrxIdtoUMAId(context->id));
url_fetcher_.reset();
+
ScheduleNextRun(false);
} else {
base::FilePath temp_crx_path;
CHECK(source->GetResponseAsFilePath(true, &temp_crx_path));
- size_t count = ChangeItemStatus(CrxUpdateItem::kDownloading,
- CrxUpdateItem::kUpdating);
+
+ size_t count = 0;
+ if (crx->status == CrxUpdateItem::kDownloadingDiff) {
+ count = ChangeItemStatus(CrxUpdateItem::kDownloadingDiff,
+ CrxUpdateItem::kUpdatingDiff);
+ } else {
+ count = ChangeItemStatus(CrxUpdateItem::kDownloading,
+ CrxUpdateItem::kUpdating);
+ }
DCHECK_EQ(count, 1ul);
+
url_fetcher_.reset();
content::NotificationService::current()->Notify(
@@ -829,17 +934,19 @@ void CrxUpdateService::Install(const CRXContext* context,
const base::FilePath& crx_path) {
// This function owns the |crx_path| and the |context| object.
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- ComponentUnpacker
- unpacker(context->pk_hash, crx_path, context->installer);
- if (!file_util::Delete(crx_path, false)) {
+ ComponentUnpacker unpacker(context->pk_hash,
+ crx_path,
+ context->fingerprint,
+ component_patcher_.get(),
+ context->installer);
+ if (!file_util::Delete(crx_path, false))
NOTREACHED() << crx_path.value();
- }
// Why unretained? See comment at top of file.
BrowserThread::PostDelayedTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&CrxUpdateService::DoneInstalling, base::Unretained(this),
- context->id, unpacker.error()),
+ context->id, unpacker.error(), unpacker.extended_error()),
base::TimeDelta::FromMilliseconds(config_->StepDelay()));
delete context;
}
@@ -847,14 +954,28 @@ void CrxUpdateService::Install(const CRXContext* context,
// Installation has been completed. Adjust the component status and
// schedule the next check.
void CrxUpdateService::DoneInstalling(const std::string& component_id,
- ComponentUnpacker::Error error) {
+ ComponentUnpacker::Error error,
+ int extra_code) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
CrxUpdateItem* item = FindUpdateItemById(component_id);
+ if (item->status == CrxUpdateItem::kUpdatingDiff) {
+ if (error != ComponentUnpacker::kNone) {
+ item->diff_update_failed = true;
+ size_t count = ChangeItemStatus(CrxUpdateItem::kUpdatingDiff,
+ CrxUpdateItem::kCanUpdate);
+ DCHECK_EQ(count, 1ul);
+ ScheduleNextRun(true);
+ return;
+ }
+ }
+
item->status = (error == ComponentUnpacker::kNone) ? CrxUpdateItem::kUpdated :
CrxUpdateItem::kNoUpdate;
- if (item->status == CrxUpdateItem::kUpdated)
+ if (item->status == CrxUpdateItem::kUpdated) {
item->component.version = item->next_version;
+ item->component.fingerprint = item->next_fp;
+ }
Configurator::Events event;
switch (error) {
diff --git a/chrome/browser/component_updater/component_updater_service.h b/chrome/browser/component_updater/component_updater_service.h
index 3ac645d..bf011eb4 100644
--- a/chrome/browser/component_updater/component_updater_service.h
+++ b/chrome/browser/component_updater/component_updater_service.h
@@ -20,6 +20,8 @@ class DictionaryValue;
class FilePath;
}
+class ComponentPatcher;
+
// Component specific installers must derive from this class and implement
// OnUpdateError() and Install(). A valid instance of this class must be
// given to ComponentUpdateService::RegisterComponent().
@@ -37,15 +39,22 @@ class ComponentInstaller {
virtual bool Install(const base::DictionaryValue& manifest,
const base::FilePath& unpack_path) = 0;
+ // Set |installed_file| to the full path to the installed |file|. |file| is
+ // the filename of the file in this component's CRX. Returns false if this is
+ // not possible (the file has been removed or modified, or its current
+ // location is unknown). Otherwise, returns true.
+ virtual bool GetInstalledFile(const std::string& file,
+ base::FilePath* installed_file) = 0;
+
protected:
virtual ~ComponentInstaller() {}
};
// Describes a particular component that can be installed or updated. This
// structure is required to register a component with the component updater.
-// Only |name| is optional. |pk_hash| is the SHA256 hash of the component's
-// public key. If the component is to be installed then version should be
-// "0" or "0.0", else it should be the current version.
+// |pk_hash| is the SHA256 hash of the component's public key. If the component
+// is to be installed then version should be "0" or "0.0", else it should be
+// the current version. |fingerprint| and |name| are optional.
// |source| is by default pointing to BANDAID but if needed it can be made
// to point to the webstore (CWS_PUBLIC) or to the webstore sandbox. It is
// important to note that the BANDAID source if active throught the day
@@ -55,12 +64,13 @@ struct CrxComponent {
enum UrlSource {
BANDAID,
CWS_PUBLIC,
- CWS_SANDBOX
+ CWS_SANDBOX,
};
std::vector<uint8> pk_hash;
ComponentInstaller* installer;
Version version;
+ std::string fingerprint;
std::string name;
UrlSource source;
CrxComponent();
@@ -128,6 +138,11 @@ class ComponentUpdateService {
// happens. It should be used mostly as a place to add application specific
// logging or telemetry. |extra| is |event| dependent.
virtual void OnEvent(Events event, int extra) = 0;
+ // Creates a new ComponentPatcher in a platform-specific way. This is useful
+ // for dependency injection.
+ virtual ComponentPatcher* CreateComponentPatcher() = 0;
+ // True means that this client can handle delta updates.
+ virtual bool DeltasEnabled() const = 0;
};
// Start doing update checks and installing new versions of registered
diff --git a/chrome/browser/component_updater/pepper_flash_component_installer.cc b/chrome/browser/component_updater/pepper_flash_component_installer.cc
index 696c6d9..009afa0 100644
--- a/chrome/browser/component_updater/pepper_flash_component_installer.cc
+++ b/chrome/browser/component_updater/pepper_flash_component_installer.cc
@@ -250,6 +250,9 @@ class PepperFlashComponentInstaller : public ComponentInstaller {
virtual bool Install(const base::DictionaryValue& manifest,
const base::FilePath& unpack_path) OVERRIDE;
+ virtual bool GetInstalledFile(const std::string& file,
+ base::FilePath* installed_file) OVERRIDE;
+
private:
Version current_version_;
};
@@ -292,6 +295,11 @@ bool PepperFlashComponentInstaller::Install(
return true;
}
+bool PepperFlashComponentInstaller::GetInstalledFile(
+ const std::string& file, base::FilePath* installed_file) {
+ return false;
+}
+
bool CheckPepperFlashManifest(const base::DictionaryValue& manifest,
Version* version_out) {
std::string name;
diff --git a/chrome/browser/component_updater/pnacl/pnacl_component_installer.cc b/chrome/browser/component_updater/pnacl/pnacl_component_installer.cc
index cc8504d..a0d3250 100644
--- a/chrome/browser/component_updater/pnacl/pnacl_component_installer.cc
+++ b/chrome/browser/component_updater/pnacl/pnacl_component_installer.cc
@@ -281,6 +281,11 @@ bool PnaclComponentInstaller::Install(const base::DictionaryValue& manifest,
return true;
}
+bool PnaclComponentInstaller::GetInstalledFile(
+ const std::string& file, base::FilePath* installed_file) {
+ return false;
+}
+
namespace {
void DoCheckForUpdate(ComponentUpdateService* cus,
diff --git a/chrome/browser/component_updater/pnacl/pnacl_component_installer.h b/chrome/browser/component_updater/pnacl/pnacl_component_installer.h
index 9ba2997..3ecf2a9 100644
--- a/chrome/browser/component_updater/pnacl/pnacl_component_installer.h
+++ b/chrome/browser/component_updater/pnacl/pnacl_component_installer.h
@@ -5,6 +5,9 @@
#ifndef CHROME_BROWSER_COMPONENT_UPDATER_PNACL_PNACL_COMPONENT_INSTALLER_H_
#define CHROME_BROWSER_COMPONENT_UPDATER_PNACL_PNACL_COMPONENT_INSTALLER_H_
+#include <string>
+
+#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/version.h"
@@ -31,6 +34,9 @@ class PnaclComponentInstaller : public ComponentInstaller {
virtual bool Install(const base::DictionaryValue& manifest,
const base::FilePath& unpack_path) OVERRIDE;
+ virtual bool GetInstalledFile(const std::string& file,
+ base::FilePath* installed_file) OVERRIDE;
+
// Register a PNaCl component for the first time.
void RegisterPnaclComponent(ComponentUpdateService* cus,
const CommandLine& command_line);
diff --git a/chrome/browser/component_updater/pnacl/pnacl_profile_observer.h b/chrome/browser/component_updater/pnacl/pnacl_profile_observer.h
index 52ab984..18ef4e8 100644
--- a/chrome/browser/component_updater/pnacl/pnacl_profile_observer.h
+++ b/chrome/browser/component_updater/pnacl/pnacl_profile_observer.h
@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_COMPONENT_UPDATER_PNACL_PNACL_PROFILE_OBSERVER_H_
#define CHROME_BROWSER_COMPONENT_UPDATER_PNACL_PNACL_PROFILE_OBSERVER_H_
+#include "base/compiler_specific.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
diff --git a/chrome/browser/component_updater/recovery_component_installer.cc b/chrome/browser/component_updater/recovery_component_installer.cc
index c2a7d5a..4891aab1 100644
--- a/chrome/browser/component_updater/recovery_component_installer.cc
+++ b/chrome/browser/component_updater/recovery_component_installer.cc
@@ -56,6 +56,9 @@ class RecoveryComponentInstaller : public ComponentInstaller {
virtual bool Install(const base::DictionaryValue& manifest,
const base::FilePath& unpack_path) OVERRIDE;
+ virtual bool GetInstalledFile(const std::string& file,
+ base::FilePath* installed_file) OVERRIDE;
+
private:
Version current_version_;
PrefService* prefs_;
@@ -130,6 +133,11 @@ bool RecoveryComponentInstaller::Install(const base::DictionaryValue& manifest,
return base::LaunchProcess(cmdline, base::LaunchOptions(), NULL);
}
+bool RecoveryComponentInstaller::GetInstalledFile(
+ const std::string& file, base::FilePath* installed_file) {
+ return false;
+}
+
void RegisterRecoveryComponent(ComponentUpdateService* cus,
PrefService* prefs) {
#if !defined(OS_CHROMEOS)
diff --git a/chrome/browser/component_updater/swiftshader_component_installer.cc b/chrome/browser/component_updater/swiftshader_component_installer.cc
index e09e524..5c2eca8 100644
--- a/chrome/browser/component_updater/swiftshader_component_installer.cc
+++ b/chrome/browser/component_updater/swiftshader_component_installer.cc
@@ -105,6 +105,9 @@ class SwiftShaderComponentInstaller : public ComponentInstaller {
virtual bool Install(const base::DictionaryValue& manifest,
const base::FilePath& unpack_path) OVERRIDE;
+ virtual bool GetInstalledFile(const std::string& file,
+ base::FilePath* installed_file) OVERRIDE;
+
private:
Version current_version_;
};
@@ -149,6 +152,11 @@ bool SwiftShaderComponentInstaller::Install(
return true;
}
+bool SwiftShaderComponentInstaller::GetInstalledFile(
+ const std::string& file, base::FilePath* installed_file) {
+ return false;
+}
+
void FinishSwiftShaderUpdateRegistration(ComponentUpdateService* cus,
const Version& version) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
diff --git a/chrome/browser/component_updater/test/DEPS b/chrome/browser/component_updater/test/DEPS
index e63018d..397b12e 100644
--- a/chrome/browser/component_updater/test/DEPS
+++ b/chrome/browser/component_updater/test/DEPS
@@ -1,4 +1,5 @@
include_rules = [
# For access to the ppapi test globals.
"+ppapi/shared_impl",
+ "+courgette",
]
diff --git a/chrome/browser/component_updater/test/component_patcher_mock.h b/chrome/browser/component_updater/test/component_patcher_mock.h
new file mode 100644
index 0000000..1843b29
--- /dev/null
+++ b/chrome/browser/component_updater/test/component_patcher_mock.h
@@ -0,0 +1,28 @@
+// Copyright 2013 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_COMPONENT_UPDATER_TEST_COMPONENT_PATCHER_MOCK_H_
+#define CHROME_BROWSER_COMPONENT_UPDATER_TEST_COMPONENT_PATCHER_MOCK_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "chrome/browser/component_updater/component_patcher.h"
+
+namespace base {
+class FilePath;
+}
+
+class MockComponentPatcher : public ComponentPatcher {
+ public:
+ MockComponentPatcher() {}
+ virtual ComponentUnpacker::Error Patch(PatchType patch_type,
+ const base::FilePath& input_file,
+ const base::FilePath& patch_file,
+ const base::FilePath& output_file,
+ int* error) OVERRIDE;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockComponentPatcher);
+};
+
+#endif // CHROME_BROWSER_COMPONENT_UPDATER_TEST_COMPONENT_PATCHER_MOCK_H_
diff --git a/chrome/browser/component_updater/test/component_patcher_unittest.cc b/chrome/browser/component_updater/test/component_patcher_unittest.cc
new file mode 100644
index 0000000..a675f28
--- /dev/null
+++ b/chrome/browser/component_updater/test/component_patcher_unittest.cc
@@ -0,0 +1,200 @@
+// Copyright 2013 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/compiler_specific.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/path_service.h"
+#include "base/values.h"
+#include "chrome/browser/component_updater/component_patcher.h"
+#include "chrome/browser/component_updater/component_patcher_operation.h"
+#include "chrome/browser/component_updater/component_updater_service.h"
+#include "chrome/browser/component_updater/test/component_patcher_mock.h"
+#include "chrome/browser/component_updater/test/test_installer.h"
+#include "chrome/common/chrome_paths.h"
+#include "courgette/courgette.h"
+#include "courgette/third_party/bsdiff.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+const char binary_output_hash[] =
+ "599aba6d15a7da390621ef1bacb66601ed6aed04dadc1f9b445dcfe31296142a";
+
+// These constants are duplicated from chrome/installer/util/util_constants.h,
+// to avoid introducing a dependency from the unit tests to the installer.
+const int kCourgetteErrorOffset = 300;
+const int kBsdiffErrorOffset = 600;
+
+base::FilePath test_file(const char* file);
+
+class ComponentPatcherOperationTest : public testing::Test {
+ public:
+ ComponentPatcherOperationTest();
+
+ protected:
+ base::ScopedTempDir input_dir_;
+ base::ScopedTempDir installed_dir_;
+ base::ScopedTempDir unpack_dir_;
+ scoped_ptr<MockComponentPatcher> patcher_;
+ scoped_ptr<ReadOnlyTestInstaller> installer_;
+};
+
+base::FilePath test_file(const char* file) {
+ base::FilePath path;
+ PathService::Get(chrome::DIR_TEST_DATA, &path);
+ return path.AppendASCII("components").AppendASCII(file);
+}
+
+ComponentPatcherOperationTest::ComponentPatcherOperationTest() {
+ EXPECT_TRUE(unpack_dir_.CreateUniqueTempDir());
+ EXPECT_TRUE(input_dir_.CreateUniqueTempDir());
+ EXPECT_TRUE(installed_dir_.CreateUniqueTempDir());
+ patcher_.reset(new MockComponentPatcher());
+ installer_.reset(new ReadOnlyTestInstaller(installed_dir_.path()));
+}
+
+ComponentUnpacker::Error MockComponentPatcher::Patch(
+ PatchType patch_type,
+ const base::FilePath& input_file,
+ const base::FilePath& patch_file,
+ const base::FilePath& output_file,
+ int* error) {
+ *error = 0;
+ int exit_code;
+ if (patch_type == kPatchTypeCourgette) {
+ exit_code = courgette::ApplyEnsemblePatch(input_file.value().c_str(),
+ patch_file.value().c_str(),
+ output_file.value().c_str());
+ if (exit_code == courgette::C_OK)
+ return ComponentUnpacker::kNone;
+ *error = exit_code + kCourgetteErrorOffset;
+ } else if (patch_type == kPatchTypeBsdiff) {
+ exit_code = courgette::ApplyBinaryPatch(input_file,
+ patch_file,
+ output_file);
+ if (exit_code == courgette::OK)
+ return ComponentUnpacker::kNone;
+ *error = exit_code + kBsdiffErrorOffset;
+ }
+ return ComponentUnpacker::kDeltaOperationFailure;
+}
+
+// Verify that a 'create' delta update operation works correctly.
+TEST_F(ComponentPatcherOperationTest, CheckCreateOperation) {
+ EXPECT_TRUE(file_util::CopyFile(
+ test_file("binary_output.bin"),
+ input_dir_.path().Append(FILE_PATH_LITERAL("binary_output.bin"))));
+
+ scoped_ptr<base::DictionaryValue> command_args(new base::DictionaryValue());
+ command_args->SetString("output", "output.bin");
+ command_args->SetString("sha256", binary_output_hash);
+ command_args->SetString("op", "create");
+ command_args->SetString("patch", "binary_output.bin");
+
+ int error = 0;
+ scoped_ptr<DeltaUpdateOp> op(new DeltaUpdateOpCreate());
+ ComponentUnpacker::Error result = op->Run(command_args.get(),
+ input_dir_.path(),
+ unpack_dir_.path(),
+ patcher_.get(),
+ NULL,
+ &error);
+
+ EXPECT_EQ(ComponentUnpacker::kNone, result);
+ EXPECT_EQ(0, error);
+ EXPECT_TRUE(file_util::ContentsEqual(
+ unpack_dir_.path().Append(FILE_PATH_LITERAL("output.bin")),
+ test_file("binary_output.bin")));
+}
+
+// Verify that a 'copy' delta update operation works correctly.
+TEST_F(ComponentPatcherOperationTest, CheckCopyOperation) {
+ EXPECT_TRUE(file_util::CopyFile(
+ test_file("binary_output.bin"),
+ installed_dir_.path().Append(FILE_PATH_LITERAL("binary_output.bin"))));
+
+ scoped_ptr<base::DictionaryValue> command_args(new base::DictionaryValue());
+ command_args->SetString("output", "output.bin");
+ command_args->SetString("sha256", binary_output_hash);
+ command_args->SetString("op", "copy");
+ command_args->SetString("input", "binary_output.bin");
+
+ int error = 0;
+ scoped_ptr<DeltaUpdateOp> op(new DeltaUpdateOpCopy());
+ ComponentUnpacker::Error result = op->Run(command_args.get(),
+ input_dir_.path(),
+ unpack_dir_.path(),
+ patcher_.get(),
+ installer_.get(),
+ &error);
+ EXPECT_EQ(ComponentUnpacker::kNone, result);
+ EXPECT_EQ(0, error);
+ EXPECT_TRUE(file_util::ContentsEqual(
+ unpack_dir_.path().Append(FILE_PATH_LITERAL("output.bin")),
+ test_file("binary_output.bin")));
+}
+
+// Verify that a 'courgette' delta update operation works correctly.
+TEST_F(ComponentPatcherOperationTest, CheckCourgetteOperation) {
+ EXPECT_TRUE(file_util::CopyFile(
+ test_file("binary_input.bin"),
+ installed_dir_.path().Append(FILE_PATH_LITERAL("binary_input.bin"))));
+ EXPECT_TRUE(file_util::CopyFile(
+ test_file("binary_courgette_patch.bin"),
+ input_dir_.path().Append(
+ FILE_PATH_LITERAL("binary_courgette_patch.bin"))));
+
+ scoped_ptr<base::DictionaryValue> command_args(new base::DictionaryValue());
+ command_args->SetString("output", "output.bin");
+ command_args->SetString("sha256", binary_output_hash);
+ command_args->SetString("op", "courgette");
+ command_args->SetString("input", "binary_input.bin");
+ command_args->SetString("patch", "binary_courgette_patch.bin");
+
+ int error = 0;
+ scoped_ptr<DeltaUpdateOp> op(new DeltaUpdateOpPatchCourgette());
+ ComponentUnpacker::Error result = op->Run(command_args.get(),
+ input_dir_.path(),
+ unpack_dir_.path(),
+ patcher_.get(),
+ installer_.get(),
+ &error);
+ EXPECT_EQ(ComponentUnpacker::kNone, result);
+ EXPECT_EQ(0, error);
+ EXPECT_TRUE(file_util::ContentsEqual(
+ unpack_dir_.path().Append(FILE_PATH_LITERAL("output.bin")),
+ test_file("binary_output.bin")));
+}
+
+// Verify that a 'bsdiff' delta update operation works correctly.
+TEST_F(ComponentPatcherOperationTest, CheckBsdiffOperation) {
+ EXPECT_TRUE(file_util::CopyFile(
+ test_file("binary_input.bin"),
+ installed_dir_.path().Append(FILE_PATH_LITERAL("binary_input.bin"))));
+ EXPECT_TRUE(file_util::CopyFile(
+ test_file("binary_bsdiff_patch.bin"),
+ input_dir_.path().Append(FILE_PATH_LITERAL("binary_bsdiff_patch.bin"))));
+
+ scoped_ptr<base::DictionaryValue> command_args(new base::DictionaryValue());
+ command_args->SetString("output", "output.bin");
+ command_args->SetString("sha256", binary_output_hash);
+ command_args->SetString("op", "courgette");
+ command_args->SetString("input", "binary_input.bin");
+ command_args->SetString("patch", "binary_bsdiff_patch.bin");
+
+ int error = 0;
+ scoped_ptr<DeltaUpdateOp> op(new DeltaUpdateOpPatchBsdiff());
+ ComponentUnpacker::Error result = op->Run(command_args.get(),
+ input_dir_.path(),
+ unpack_dir_.path(),
+ patcher_.get(),
+ installer_.get(),
+ &error);
+ EXPECT_EQ(ComponentUnpacker::kNone, result);
+ EXPECT_EQ(0, error);
+ EXPECT_TRUE(file_util::ContentsEqual(
+ unpack_dir_.path().Append(FILE_PATH_LITERAL("output.bin")),
+ test_file("binary_output.bin")));
+}
+
diff --git a/chrome/browser/component_updater/test/component_updater_service_unittest.cc b/chrome/browser/component_updater/test/component_updater_service_unittest.cc
index 080b1d2..b389e80 100644
--- a/chrome/browser/component_updater/test/component_updater_service_unittest.cc
+++ b/chrome/browser/component_updater/test/component_updater_service_unittest.cc
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/component_updater/component_updater_service.h"
-
#include <list>
#include <utility>
@@ -13,7 +11,12 @@
#include "base/memory/scoped_vector.h"
#include "base/message_loop.h"
#include "base/path_service.h"
+#include "base/stringprintf.h"
+#include "base/strings/string_number_conversions.h"
#include "base/values.h"
+#include "chrome/browser/component_updater/component_updater_service.h"
+#include "chrome/browser/component_updater/test/component_patcher_mock.h"
+#include "chrome/browser/component_updater/test/test_installer.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_paths.h"
#include "content/public/browser/notification_observer.h"
@@ -23,7 +26,11 @@
#include "content/test/net/url_request_prepackaged_interceptor.h"
#include "googleurl/src/gurl.h"
#include "libxml/globals.h"
+#include "net/base/upload_bytes_element_reader.h"
#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_filter.h"
+#include "net/url_request/url_request_simple_job.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -31,98 +38,68 @@ using content::BrowserThread;
using content::TestNotificationTracker;
namespace {
-// Overrides some of the component updater behaviors so it is easier to test
-// and loops faster. In actual usage it takes hours do to a full cycle.
+
+// component 1 has extension id "jebgalgnebhfojomionfpkfelancnnkf", and
+// the RSA public key the following hash:
+const uint8 jebg_hash[] = {0x94, 0x16, 0x0b, 0x6d, 0x41, 0x75, 0xe9, 0xec,
+ 0x8e, 0xd5, 0xfa, 0x54, 0xb0, 0xd2, 0xdd, 0xa5,
+ 0x6e, 0x05, 0x6b, 0xe8, 0x73, 0x47, 0xf6, 0xc4,
+ 0x11, 0x9f, 0xbc, 0xb3, 0x09, 0xb3, 0x5b, 0x40};
+// component 2 has extension id "abagagagagagagagagagagagagagagag", and
+// the RSA public key the following hash:
+const uint8 abag_hash[] = {0x01, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x01};
+// component 3 has extension id "ihfokbkgjpifnbbojhneepfflplebdkc", and
+// the RSA public key the following hash:
+const uint8 ihfo_hash[] = {0x87, 0x5e, 0xa1, 0xa6, 0x9f, 0x85, 0xd1, 0x1e,
+ 0x97, 0xd4, 0x4f, 0x55, 0xbf, 0xb4, 0x13, 0xa2,
+ 0xe7, 0xc5, 0xc8, 0xf5, 0x60, 0x19, 0x78, 0x1b,
+ 0x6d, 0xe9, 0x4c, 0xeb, 0x96, 0x05, 0x42, 0x17};
+
class TestConfigurator : public ComponentUpdateService::Configurator {
public:
- TestConfigurator()
- : times_(1), recheck_time_(0), ondemand_time_(0), cus_(NULL) {
- }
+ TestConfigurator();
- virtual int InitialDelay() OVERRIDE { return 0; }
+ virtual int InitialDelay() OVERRIDE;
typedef std::pair<CrxComponent*, int> CheckAtLoopCount;
- virtual int NextCheckDelay() OVERRIDE {
- // This is called when a new full cycle of checking for updates is going
- // to happen. In test we normally only test one cycle so it is a good
- // time to break from the test messageloop Run() method so the test can
- // finish.
- if (--times_ <= 0) {
- base::MessageLoop::current()->Quit();
- return 0;
+ virtual int NextCheckDelay() OVERRIDE;
- }
+ virtual int StepDelay() OVERRIDE;
- // Look for checks to issue in the middle of the loop.
- for (std::list<CheckAtLoopCount>::iterator
- i = components_to_check_.begin();
- i != components_to_check_.end(); ) {
- if (i->second == times_) {
- cus_->CheckForUpdateSoon(*i->first);
- i = components_to_check_.erase(i);
- } else {
- ++i;
- }
- }
- return 1;
- }
+ virtual int MinimumReCheckWait() OVERRIDE;
- virtual int StepDelay() OVERRIDE {
- return 0;
- }
+ virtual int OnDemandDelay() OVERRIDE;
- virtual int MinimumReCheckWait() OVERRIDE {
- return recheck_time_;
- }
+ virtual GURL UpdateUrl(CrxComponent::UrlSource source) OVERRIDE;
- virtual int OnDemandDelay() OVERRIDE {
- return ondemand_time_;
- }
+ virtual const char* ExtraRequestParams() OVERRIDE;
- virtual GURL UpdateUrl(CrxComponent::UrlSource source) OVERRIDE {
- switch (source) {
- case CrxComponent::BANDAID:
- return GURL("http://localhost/upd");
- case CrxComponent::CWS_PUBLIC:
- return GURL("http://localhost/cws");
- default:
- return GURL("http://wronghost/bad");
- };
- }
+ virtual size_t UrlSizeLimit() OVERRIDE;
- virtual const char* ExtraRequestParams() OVERRIDE { return "extra=foo"; }
+ virtual net::URLRequestContextGetter* RequestContext() OVERRIDE;
- virtual size_t UrlSizeLimit() OVERRIDE { return 256; }
+ // Don't use the utility process to decode files.
+ virtual bool InProcess() OVERRIDE;
- virtual net::URLRequestContextGetter* RequestContext() OVERRIDE {
- return new net::TestURLRequestContextGetter(
- BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO));
- }
+ virtual void OnEvent(Events event, int extra) OVERRIDE;
- // Don't use the utility process to decode files.
- virtual bool InProcess() OVERRIDE { return true; }
+ virtual ComponentPatcher* CreateComponentPatcher() OVERRIDE;
- virtual void OnEvent(Events event, int extra) OVERRIDE { }
+ virtual bool DeltasEnabled() const OVERRIDE;
- // Set how many update checks are called, the default value is just once.
- void SetLoopCount(int times) { times_ = times; }
+ void SetLoopCount(int times);
- void SetRecheckTime(int seconds) {
- recheck_time_ = seconds;
- }
+ void SetRecheckTime(int seconds);
- void SetOnDemandTime(int seconds) {
- ondemand_time_ = seconds;
- }
+ void SetOnDemandTime(int seconds);
- void AddComponentToCheck(CrxComponent* com, int at_loop_iter) {
- components_to_check_.push_back(std::make_pair(com, at_loop_iter));
- }
+ void AddComponentToCheck(CrxComponent* com, int at_loop_iter);
- void SetComponentUpdateService(ComponentUpdateService* cus) {
- cus_ = cus;
- }
+ void SetComponentUpdateService(ComponentUpdateService* cus);
private:
int times_;
@@ -133,132 +110,209 @@ class TestConfigurator : public ComponentUpdateService::Configurator {
ComponentUpdateService* cus_;
};
-class TestInstaller : public ComponentInstaller {
- public :
- explicit TestInstaller()
- : error_(0), install_count_(0) {
- }
+class ComponentUpdaterTest : public testing::Test {
+ public:
+ enum TestComponents {
+ kTestComponent_abag,
+ kTestComponent_jebg,
+ kTestComponent_ihfo,
+ };
- virtual void OnUpdateError(int error) OVERRIDE {
- EXPECT_NE(0, error);
- error_ = error;
- }
+ ComponentUpdaterTest();
- virtual bool Install(const base::DictionaryValue& manifest,
- const base::FilePath& unpack_path) OVERRIDE {
- ++install_count_;
- return file_util::Delete(unpack_path, true);
- }
+ virtual ~ComponentUpdaterTest();
- int error() const { return error_; }
+ virtual void TearDown();
- int install_count() const { return install_count_; }
+ ComponentUpdateService* component_updater();
+
+ // Makes the full path to a component updater test file.
+ const base::FilePath test_file(const char* file);
+
+ TestNotificationTracker& notification_tracker();
+
+ TestConfigurator* test_configurator();
+
+ ComponentUpdateService::Status RegisterComponent(CrxComponent* com,
+ TestComponents component,
+ const Version& version,
+ TestInstaller* installer);
private:
- int error_;
- int install_count_;
+ scoped_ptr<ComponentUpdateService> component_updater_;
+ base::FilePath test_data_dir_;
+ TestNotificationTracker notification_tracker_;
+ TestConfigurator* test_config_;
};
-// component 1 has extension id "jebgalgnebhfojomionfpkfelancnnkf", and
-// the RSA public key the following hash:
-const uint8 jebg_hash[] = {0x94,0x16,0x0b,0x6d,0x41,0x75,0xe9,0xec,0x8e,0xd5,
- 0xfa,0x54,0xb0,0xd2,0xdd,0xa5,0x6e,0x05,0x6b,0xe8,
- 0x73,0x47,0xf6,0xc4,0x11,0x9f,0xbc,0xb3,0x09,0xb3,
- 0x5b,0x40};
-// component 2 has extension id "abagagagagagagagagagagagagagagag", and
-// the RSA public key the following hash:
-const uint8 abag_hash[] = {0x01,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,
- 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,
- 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,
- 0x06,0x01};
-
const char expected_crx_url[] =
"http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx";
} // namespace
-// Common fixture for all the component updater tests.
-class ComponentUpdaterTest : public testing::Test {
- public:
- enum TestComponents {
- kTestComponent_abag,
- kTestComponent_jebg
- };
+TestConfigurator::TestConfigurator()
+ : times_(1), recheck_time_(0), ondemand_time_(0), cus_(NULL) {
+}
- ComponentUpdaterTest() : test_config_(NULL) {
- // The component updater instance under test.
- test_config_ = new TestConfigurator;
- component_updater_.reset(ComponentUpdateServiceFactory(test_config_));
- test_config_->SetComponentUpdateService(component_updater_.get());
- // The test directory is chrome/test/data/components.
- PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
- test_data_dir_ = test_data_dir_.AppendASCII("components");
-
- // Subscribe to all component updater notifications.
- const int notifications[] = {
- chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED,
- chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING,
- chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND,
- chrome::NOTIFICATION_COMPONENT_UPDATE_READY
- };
-
- for (int ix = 0; ix != arraysize(notifications); ++ix) {
- notification_tracker_.ListenFor(
- notifications[ix], content::NotificationService::AllSources());
- }
- net::URLFetcher::SetEnableInterceptionForTests(true);
- }
+int TestConfigurator::InitialDelay() { return 0; }
- virtual ~ComponentUpdaterTest() {
- net::URLFetcher::SetEnableInterceptionForTests(false);
+int TestConfigurator::NextCheckDelay() {
+ // This is called when a new full cycle of checking for updates is going
+ // to happen. In test we normally only test one cycle so it is a good
+ // time to break from the test messageloop Run() method so the test can
+ // finish.
+ if (--times_ <= 0) {
+ base::MessageLoop::current()->Quit();
+ return 0;
}
- virtual void TearDown() {
- xmlCleanupGlobals();
+ // Look for checks to issue in the middle of the loop.
+ for (std::list<CheckAtLoopCount>::iterator
+ i = components_to_check_.begin();
+ i != components_to_check_.end(); ) {
+ if (i->second == times_) {
+ cus_->CheckForUpdateSoon(*i->first);
+ i = components_to_check_.erase(i);
+ } else {
+ ++i;
+ }
}
+ return 1;
+}
+
+int TestConfigurator::StepDelay() {
+ return 0;
+}
- ComponentUpdateService* component_updater() {
- return component_updater_.get();
+int TestConfigurator::MinimumReCheckWait() {
+ return recheck_time_;
+}
+
+int TestConfigurator::OnDemandDelay() {
+ return ondemand_time_;
+}
+
+GURL TestConfigurator::UpdateUrl(CrxComponent::UrlSource source) {
+ switch (source) {
+ case CrxComponent::BANDAID:
+ return GURL("http://localhost/upd");
+ case CrxComponent::CWS_PUBLIC:
+ return GURL("http://localhost/cws");
+ default:
+ return GURL("http://wronghost/bad");
+ };
+}
+
+const char* TestConfigurator::ExtraRequestParams() { return "extra=foo"; }
+
+size_t TestConfigurator::UrlSizeLimit() { return 256; }
+
+net::URLRequestContextGetter* TestConfigurator::RequestContext() {
+ return new net::TestURLRequestContextGetter(
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO));
+}
+
+// Don't use the utility process to decode files.
+bool TestConfigurator::InProcess() { return true; }
+
+void TestConfigurator::OnEvent(Events event, int extra) { }
+
+ComponentPatcher* TestConfigurator::CreateComponentPatcher() {
+ return new MockComponentPatcher();
+}
+
+bool TestConfigurator::DeltasEnabled() const {
+ return true;
+}
+
+// Set how many update checks are called, the default value is just once.
+void TestConfigurator::SetLoopCount(int times) { times_ = times; }
+
+void TestConfigurator::SetRecheckTime(int seconds) {
+ recheck_time_ = seconds;
+}
+
+void TestConfigurator::SetOnDemandTime(int seconds) {
+ ondemand_time_ = seconds;
+}
+
+void TestConfigurator::AddComponentToCheck(CrxComponent* com,
+ int at_loop_iter) {
+ components_to_check_.push_back(std::make_pair(com, at_loop_iter));
+}
+
+void TestConfigurator::SetComponentUpdateService(ComponentUpdateService* cus) {
+ cus_ = cus;
+}
+
+ComponentUpdaterTest::ComponentUpdaterTest() : test_config_(NULL) {
+ // The component updater instance under test.
+ test_config_ = new TestConfigurator;
+ component_updater_.reset(ComponentUpdateServiceFactory(test_config_));
+ test_config_->SetComponentUpdateService(component_updater_.get());
+ // The test directory is chrome/test/data/components.
+ PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
+ test_data_dir_ = test_data_dir_.AppendASCII("components");
+
+ // Subscribe to all component updater notifications.
+ const int notifications[] = {
+ chrome::NOTIFICATION_COMPONENT_UPDATER_STARTED,
+ chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING,
+ chrome::NOTIFICATION_COMPONENT_UPDATE_FOUND,
+ chrome::NOTIFICATION_COMPONENT_UPDATE_READY
+ };
+
+ for (int ix = 0; ix != arraysize(notifications); ++ix) {
+ notification_tracker_.ListenFor(
+ notifications[ix], content::NotificationService::AllSources());
}
+ net::URLFetcher::SetEnableInterceptionForTests(true);
+}
+
+ComponentUpdaterTest::~ComponentUpdaterTest() {
+ net::URLFetcher::SetEnableInterceptionForTests(false);
+}
+
+void ComponentUpdaterTest::TearDown() {
+ xmlCleanupGlobals();
+}
+
+ComponentUpdateService* ComponentUpdaterTest::component_updater() {
+ return component_updater_.get();
+}
// Makes the full path to a component updater test file.
- const base::FilePath test_file(const char* file) {
- return test_data_dir_.AppendASCII(file);
- }
+const base::FilePath ComponentUpdaterTest::test_file(const char* file) {
+ return test_data_dir_.AppendASCII(file);
+}
- TestNotificationTracker& notification_tracker() {
- return notification_tracker_;
- }
+TestNotificationTracker& ComponentUpdaterTest::notification_tracker() {
+ return notification_tracker_;
+}
- TestConfigurator* test_configurator() {
- return test_config_;
- }
+TestConfigurator* ComponentUpdaterTest::test_configurator() {
+ return test_config_;
+}
- ComponentUpdateService::Status RegisterComponent(CrxComponent* com,
- TestComponents component,
- const Version& version) {
- if (component == kTestComponent_abag) {
- com->name = "test_abag";
- com->pk_hash.assign(abag_hash, abag_hash + arraysize(abag_hash));
- } else {
- com->name = "test_jebg";
- com->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
- }
- com->version = version;
- TestInstaller* installer = new TestInstaller;
- com->installer = installer;
- test_installers_.push_back(installer);
- return component_updater_->RegisterComponent(*com);
+ComponentUpdateService::Status ComponentUpdaterTest::RegisterComponent(
+ CrxComponent* com,
+ TestComponents component,
+ const Version& version,
+ TestInstaller* installer) {
+ if (component == kTestComponent_abag) {
+ com->name = "test_abag";
+ com->pk_hash.assign(abag_hash, abag_hash + arraysize(abag_hash));
+ } else if (component == kTestComponent_jebg) {
+ com->name = "test_jebg";
+ com->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
+ } else {
+ com->name = "test_ihfo";
+ com->pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash));
}
-
- private:
- scoped_ptr<ComponentUpdateService> component_updater_;
- base::FilePath test_data_dir_;
- TestNotificationTracker notification_tracker_;
- TestConfigurator* test_config_;
- // ComponentInstaller objects to delete after each test.
- ScopedVector<TestInstaller> test_installers_;
-};
+ com->version = version;
+ com->installer = installer;
+ return component_updater_->RegisterComponent(*com);
+}
// Verify that our test fixture work and the component updater can
// be created and destroyed with no side effects.
@@ -295,13 +349,17 @@ TEST_F(ComponentUpdaterTest, CheckCrxSleep) {
content::URLLocalHostRequestPrepackagedInterceptor interceptor;
+ TestInstaller installer;
CrxComponent com;
EXPECT_EQ(ComponentUpdateService::kOk,
- RegisterComponent(&com, kTestComponent_abag, Version("1.1")));
+ RegisterComponent(&com,
+ kTestComponent_abag,
+ Version("1.1"),
+ &installer));
const GURL expected_update_url(
- "http://localhost/upd?extra=foo&x=id%3D"
- "abagagagagagagagagagagagagagagag%26v%3D1.1%26uc");
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D1.1%26fp%3D%26uc");
interceptor.SetResponse(expected_update_url,
test_file("updatecheck_reply_1.xml"));
@@ -374,20 +432,22 @@ TEST_F(ComponentUpdaterTest, InstallCrx) {
content::URLLocalHostRequestPrepackagedInterceptor interceptor;
+ TestInstaller installer1;
CrxComponent com1;
- RegisterComponent(&com1, kTestComponent_jebg, Version("0.9"));
+ RegisterComponent(&com1, kTestComponent_jebg, Version("0.9"), &installer1);
+ TestInstaller installer2;
CrxComponent com2;
- RegisterComponent(&com2, kTestComponent_abag, Version("2.2"));
+ RegisterComponent(&com2, kTestComponent_abag, Version("2.2"), &installer2);
const GURL expected_update_url_1(
- "http://localhost/upd?extra=foo&x=id%3D"
- "jebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26uc&x=id%3D"
- "abagagagagagagagagagagagagagagag%26v%3D2.2%26uc");
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc"
+ "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc");
const GURL expected_update_url_2(
- "http://localhost/upd?extra=foo&x=id%3D"
- "abagagagagagagagagagagagagagagag%26v%3D2.2%26uc&x=id%3D"
- "jebgalgnebhfojomionfpkfelancnnkf%26v%3D1.0%26uc");
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc"
+ "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D1.0%26fp%3D%26uc");
interceptor.SetResponse(expected_update_url_1,
test_file("updatecheck_reply_1.xml"));
@@ -439,19 +499,21 @@ TEST_F(ComponentUpdaterTest, InstallCrxTwoSources) {
content::URLLocalHostRequestPrepackagedInterceptor interceptor;
+ TestInstaller installer1;
CrxComponent com1;
- RegisterComponent(&com1, kTestComponent_abag, Version("2.2"));
+ RegisterComponent(&com1, kTestComponent_abag, Version("2.2"), &installer1);
+ TestInstaller installer2;
CrxComponent com2;
com2.source = CrxComponent::CWS_PUBLIC;
- RegisterComponent(&com2, kTestComponent_jebg, Version("0.9"));
+ RegisterComponent(&com2, kTestComponent_jebg, Version("0.9"), &installer2);
const GURL expected_update_url_1(
"http://localhost/upd?extra=foo&x=id%3D"
- "abagagagagagagagagagagagagagagag%26v%3D2.2%26uc");
+ "abagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc");
const GURL expected_update_url_2(
"http://localhost/cws?extra=foo&x=id%3D"
- "jebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26uc");
+ "jebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc");
interceptor.SetResponse(expected_update_url_1,
test_file("updatecheck_reply_3.xml"));
@@ -511,12 +573,13 @@ TEST_F(ComponentUpdaterTest, ProdVersionCheck) {
content::URLLocalHostRequestPrepackagedInterceptor interceptor;
+ TestInstaller installer;
CrxComponent com;
- RegisterComponent(&com, kTestComponent_jebg, Version("0.9"));
+ RegisterComponent(&com, kTestComponent_jebg, Version("0.9"), &installer);
const GURL expected_update_url(
"http://localhost/upd?extra=foo&x=id%3D"
- "jebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26uc");
+ "jebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc");
interceptor.SetResponse(expected_update_url,
test_file("updatecheck_reply_2.xml"));
@@ -551,20 +614,22 @@ TEST_F(ComponentUpdaterTest, CheckForUpdateSoon) {
content::URLLocalHostRequestPrepackagedInterceptor interceptor;
+ TestInstaller installer1;
CrxComponent com1;
- RegisterComponent(&com1, kTestComponent_abag, Version("2.2"));
+ RegisterComponent(&com1, kTestComponent_abag, Version("2.2"), &installer1);
+ TestInstaller installer2;
CrxComponent com2;
- RegisterComponent(&com2, kTestComponent_jebg, Version("0.9"));
+ RegisterComponent(&com2, kTestComponent_jebg, Version("0.9"), &installer2);
const GURL expected_update_url_1(
- "http://localhost/upd?extra=foo&x=id%3D"
- "abagagagagagagagagagagagagagagag%26v%3D2.2%26uc&x=id%3D"
- "jebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26uc");
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc"
+ "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc");
const GURL expected_update_url_2(
- "http://localhost/upd?extra=foo&x=id%3D"
- "jebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26uc&x=id%3D"
- "abagagagagagagagagagagagagagagag%26v%3D2.2%26uc");
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc"
+ "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc");
interceptor.SetResponse(expected_update_url_1,
test_file("updatecheck_reply_empty"));
@@ -613,9 +678,9 @@ TEST_F(ComponentUpdaterTest, CheckForUpdateSoon) {
// Test a few error cases. NOTE: We don't have callbacks for
// when the updates failed yet.
const GURL expected_update_url_3(
- "http://localhost/upd?extra=foo&x=id%3D"
- "jebgalgnebhfojomionfpkfelancnnkf%26v%3D1.0%26uc&x=id%3D"
- "abagagagagagagagagagagagagagagag%26v%3D2.2%26uc");
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D1.0%26fp%3D%26uc"
+ "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc");
// No update: error from no server response
interceptor.SetResponse(expected_update_url_3,
@@ -667,21 +732,23 @@ TEST_F(ComponentUpdaterTest, CheckReRegistration) {
content::URLLocalHostRequestPrepackagedInterceptor interceptor;
+ TestInstaller installer1;
CrxComponent com1;
- RegisterComponent(&com1, kTestComponent_jebg, Version("0.9"));
+ RegisterComponent(&com1, kTestComponent_jebg, Version("0.9"), &installer1);
+ TestInstaller installer2;
CrxComponent com2;
- RegisterComponent(&com2, kTestComponent_abag, Version("2.2"));
+ RegisterComponent(&com2, kTestComponent_abag, Version("2.2"), &installer2);
// Start with 0.9, and update to 1.0
const GURL expected_update_url_1(
- "http://localhost/upd?extra=foo&x=id%3D"
- "jebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26uc&x=id%3D"
- "abagagagagagagagagagagagagagagag%26v%3D2.2%26uc");
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D0.9%26fp%3D%26uc"
+ "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc");
const GURL expected_update_url_2(
- "http://localhost/upd?extra=foo&x=id%3D"
- "abagagagagagagagagagagagagagagag%26v%3D2.2%26uc&x=id%3D"
- "jebgalgnebhfojomionfpkfelancnnkf%26v%3D1.0%26uc");
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc"
+ "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D1.0%26fp%3D%26uc");
interceptor.SetResponse(expected_update_url_1,
test_file("updatecheck_reply_1.xml"));
@@ -722,16 +789,20 @@ TEST_F(ComponentUpdaterTest, CheckReRegistration) {
EXPECT_EQ(chrome::NOTIFICATION_COMPONENT_UPDATER_SLEEPING, ev4.type);
// Now re-register, pretending to be an even newer version (2.2)
+ TestInstaller installer3;
component_updater()->Stop();
EXPECT_EQ(ComponentUpdateService::kReplaced,
- RegisterComponent(&com1, kTestComponent_jebg, Version("2.2")));
+ RegisterComponent(&com1,
+ kTestComponent_jebg,
+ Version("2.2"),
+ &installer3));
// Check that we send out 2.2 as our version.
// Interceptor's hit count should go up by 1.
const GURL expected_update_url_3(
- "http://localhost/upd?extra=foo&x=id%3D"
- "jebgalgnebhfojomionfpkfelancnnkf%26v%3D2.2%26uc&x=id%3D"
- "abagagagagagagagagagagagagagagag%26v%3D2.2%26uc");
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Djebgalgnebhfojomionfpkfelancnnkf%26v%3D2.2%26fp%3D%26uc"
+ "&x=id%3Dabagagagagagagagagagagagagagagag%26v%3D2.2%26fp%3D%26uc");
interceptor.SetResponse(expected_update_url_3,
test_file("updatecheck_reply_1.xml"));
@@ -753,8 +824,7 @@ TEST_F(ComponentUpdaterTest, CheckReRegistration) {
EXPECT_EQ(4, interceptor.GetHitCount());
- // The test harness's Register() function creates a new installer,
- // so the counts go back to 0.
+ // We created a new installer, so the counts go back to 0.
EXPECT_EQ(0, static_cast<TestInstaller*>(com1.installer)->error());
EXPECT_EQ(0, static_cast<TestInstaller*>(com1.installer)->install_count());
EXPECT_EQ(0, static_cast<TestInstaller*>(com2.installer)->error());
@@ -762,3 +832,190 @@ TEST_F(ComponentUpdaterTest, CheckReRegistration) {
component_updater()->Stop();
}
+
+// Verify that we can download and install a component and a differential
+// update to that component. We do three loops; the final loop should do
+// nothing.
+// We also check that exactly 5 network requests are issued:
+// 1- update check (response: v1 available)
+// 2- download crx (v1)
+// 3- update check (response: v2 available)
+// 4- download differential crx (v1 to v2)
+// 5- update check (response: no further update available)
+TEST_F(ComponentUpdaterTest, DifferentialUpdate) {
+ base::MessageLoop message_loop;
+ content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
+ content::TestBrowserThread file_thread(BrowserThread::FILE);
+ content::TestBrowserThread io_thread(BrowserThread::IO);
+
+ io_thread.StartIOThread();
+ file_thread.Start();
+
+ content::URLLocalHostRequestPrepackagedInterceptor interceptor;
+
+ VersionedTestInstaller installer;
+ CrxComponent com;
+ RegisterComponent(&com, kTestComponent_ihfo, Version("0.0"), &installer);
+
+ const GURL expected_update_url_0(
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D0.0%26fp%3D%26uc");
+ const GURL expected_update_url_1(
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D1.0%26fp%3D1%26uc");
+ const GURL expected_update_url_2(
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D2.0%26fp%3Df22%26uc");
+ const GURL expected_crx_url_1(
+ "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx");
+ const GURL expected_crx_url_1_diff_2(
+ "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx");
+
+ interceptor.SetResponse(expected_update_url_0,
+ test_file("updatecheck_diff_reply_1.xml"));
+ interceptor.SetResponse(expected_update_url_1,
+ test_file("updatecheck_diff_reply_2.xml"));
+ interceptor.SetResponse(expected_update_url_2,
+ test_file("updatecheck_diff_reply_3.xml"));
+ interceptor.SetResponse(expected_crx_url_1,
+ test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"));
+ interceptor.SetResponse(
+ expected_crx_url_1_diff_2,
+ test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"));
+
+ test_configurator()->SetLoopCount(3);
+
+ component_updater()->Start();
+ message_loop.Run();
+
+ EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
+ EXPECT_EQ(2, static_cast<TestInstaller*>(com.installer)->install_count());
+
+ EXPECT_EQ(5, interceptor.GetHitCount());
+
+ component_updater()->Stop();
+}
+
+// Verify that component installation falls back to downloading and installing
+// a full update if the differential update fails (in this case, because the
+// installer does not know about the existing files). We do two loops; the final
+// loop should do nothing.
+// We also check that exactly 4 network requests are issued:
+// 1- update check (loop 1)
+// 2- download differential crx
+// 3- download full crx
+// 4- update check (loop 2 - no update available)
+TEST_F(ComponentUpdaterTest, DifferentialUpdateFails) {
+ base::MessageLoop message_loop;
+ content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
+ content::TestBrowserThread file_thread(BrowserThread::FILE);
+ content::TestBrowserThread io_thread(BrowserThread::IO);
+
+ io_thread.StartIOThread();
+ file_thread.Start();
+
+ content::URLLocalHostRequestPrepackagedInterceptor interceptor;
+
+ TestInstaller installer;
+ CrxComponent com;
+ RegisterComponent(&com, kTestComponent_ihfo, Version("1.0"), &installer);
+
+ const GURL expected_update_url_1(
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D1.0%26fp%3D%26uc");
+ const GURL expected_update_url_2(
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D2.0%26fp%3Df22%26uc");
+ const GURL expected_crx_url_1(
+ "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx");
+ const GURL expected_crx_url_1_diff_2(
+ "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx");
+ const GURL expected_crx_url_2(
+ "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx");
+
+ interceptor.SetResponse(expected_update_url_1,
+ test_file("updatecheck_diff_reply_2.xml"));
+ interceptor.SetResponse(expected_update_url_2,
+ test_file("updatecheck_diff_reply_3.xml"));
+ interceptor.SetResponse(expected_crx_url_1,
+ test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"));
+ interceptor.SetResponse(
+ expected_crx_url_1_diff_2,
+ test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"));
+ interceptor.SetResponse(expected_crx_url_2,
+ test_file("ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"));
+
+ test_configurator()->SetLoopCount(2);
+
+ component_updater()->Start();
+ message_loop.Run();
+
+ // A failed differential update does not count as a failed install.
+ EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
+ EXPECT_EQ(1, static_cast<TestInstaller*>(com.installer)->install_count());
+
+ EXPECT_EQ(4, interceptor.GetHitCount());
+
+ component_updater()->Stop();
+}
+
+// Verify that we successfully propagate a patcher error.
+// ihfokbkgjpifnbbojhneepfflplebdkc_1to2_bad.crx contains an incorrect
+// patching instruction that should fail.
+TEST_F(ComponentUpdaterTest, DifferentialUpdateFailErrorcode) {
+ base::MessageLoop message_loop;
+ content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop);
+ content::TestBrowserThread file_thread(BrowserThread::FILE);
+ content::TestBrowserThread io_thread(BrowserThread::IO);
+
+ io_thread.StartIOThread();
+ file_thread.Start();
+
+ content::URLLocalHostRequestPrepackagedInterceptor interceptor;
+
+ VersionedTestInstaller installer;
+ CrxComponent com;
+ RegisterComponent(&com, kTestComponent_ihfo, Version("0.0"), &installer);
+
+ const GURL expected_update_url_0(
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D0.0%26fp%3D%26uc");
+ const GURL expected_update_url_1(
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D1.0%26fp%3D1%26uc");
+ const GURL expected_update_url_2(
+ "http://localhost/upd?extra=foo"
+ "&x=id%3Dihfokbkgjpifnbbojhneepfflplebdkc%26v%3D2.0%26fp%3Df22%26uc");
+ const GURL expected_crx_url_1(
+ "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx");
+ const GURL expected_crx_url_1_diff_2(
+ "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx");
+ const GURL expected_crx_url_2(
+ "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx");
+
+ interceptor.SetResponse(expected_update_url_0,
+ test_file("updatecheck_diff_reply_1.xml"));
+ interceptor.SetResponse(expected_update_url_1,
+ test_file("updatecheck_diff_reply_2.xml"));
+ interceptor.SetResponse(expected_update_url_2,
+ test_file("updatecheck_diff_reply_3.xml"));
+ interceptor.SetResponse(expected_crx_url_1,
+ test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"));
+ interceptor.SetResponse(
+ expected_crx_url_1_diff_2,
+ test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1to2_bad.crx"));
+ interceptor.SetResponse(expected_crx_url_2,
+ test_file("ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"));
+
+ test_configurator()->SetLoopCount(3);
+
+ component_updater()->Start();
+ message_loop.Run();
+
+ EXPECT_EQ(0, static_cast<TestInstaller*>(com.installer)->error());
+ EXPECT_EQ(2, static_cast<TestInstaller*>(com.installer)->install_count());
+
+ EXPECT_EQ(6, interceptor.GetHitCount());
+
+ component_updater()->Stop();
+}
diff --git a/chrome/browser/component_updater/test/test_installer.cc b/chrome/browser/component_updater/test/test_installer.cc
new file mode 100644
index 0000000..9a4098c
--- /dev/null
+++ b/chrome/browser/component_updater/test/test_installer.cc
@@ -0,0 +1,81 @@
+// Copyright 2013 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 "chrome/browser/component_updater/test/test_installer.h"
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/values.h"
+
+TestInstaller::TestInstaller()
+ : error_(0), install_count_(0) {
+}
+
+void TestInstaller::OnUpdateError(int error) {
+ error_ = error;
+}
+
+bool TestInstaller::Install(const base::DictionaryValue& manifest,
+ const base::FilePath& unpack_path) {
+ ++install_count_;
+ return file_util::Delete(unpack_path, true);
+}
+
+bool TestInstaller::GetInstalledFile(const std::string& file,
+ base::FilePath* installed_file) {
+ return false;
+}
+
+int TestInstaller::error() const { return error_; }
+
+int TestInstaller::install_count() const { return install_count_; }
+
+
+ReadOnlyTestInstaller::ReadOnlyTestInstaller(const base::FilePath& install_dir)
+ : install_directory_(install_dir) {
+}
+
+ReadOnlyTestInstaller::~ReadOnlyTestInstaller() {
+}
+
+bool ReadOnlyTestInstaller::GetInstalledFile(const std::string& file,
+ base::FilePath* installed_file) {
+ *installed_file = install_directory_.AppendASCII(file);
+ return true;
+}
+
+
+VersionedTestInstaller::VersionedTestInstaller() {
+ file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("TEST_"),
+ &install_directory_);
+}
+
+VersionedTestInstaller::~VersionedTestInstaller() {
+ file_util::Delete(install_directory_, true);
+}
+
+
+bool VersionedTestInstaller::Install(const base::DictionaryValue& manifest,
+ const base::FilePath& unpack_path) {
+ std::string version_string;
+ manifest.GetStringASCII("version", &version_string);
+ Version version(version_string.c_str());
+
+ base::FilePath path;
+ path = install_directory_.AppendASCII(version.GetString());
+ file_util::CreateDirectory(path.DirName());
+ if (!file_util::Move(unpack_path, path))
+ return false;
+ current_version_ = version;
+ ++install_count_;
+ return true;
+}
+
+bool VersionedTestInstaller::GetInstalledFile(const std::string& file,
+ base::FilePath* installed_file) {
+ base::FilePath path;
+ path = install_directory_.AppendASCII(current_version_.GetString());
+ *installed_file = path.Append(base::FilePath::FromUTF8Unsafe(file));
+ return true;
+}
diff --git a/chrome/browser/component_updater/test/test_installer.h b/chrome/browser/component_updater/test/test_installer.h
new file mode 100644
index 0000000..2233d88
--- /dev/null
+++ b/chrome/browser/component_updater/test/test_installer.h
@@ -0,0 +1,73 @@
+// Copyright 2013 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_COMPONENT_UPDATER_TEST_TEST_INSTALLER_H_
+#define CHROME_BROWSER_COMPONENT_UPDATER_TEST_TEST_INSTALLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "chrome/browser/component_updater/component_updater_service.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+// A TestInstaller is an installer that does nothing for installation except
+// increment a counter.
+class TestInstaller : public ComponentInstaller {
+ public:
+ explicit TestInstaller();
+
+ virtual void OnUpdateError(int error) OVERRIDE;
+
+ virtual bool Install(const base::DictionaryValue& manifest,
+ const base::FilePath& unpack_path) OVERRIDE;
+
+ virtual bool GetInstalledFile(const std::string& file,
+ base::FilePath* installed_file) OVERRIDE;
+
+ int error() const;
+
+ int install_count() const;
+
+ protected:
+ int error_;
+ int install_count_;
+};
+
+// A ReadOnlyTestInstaller is an installer that knows about files in an existing
+// directory. It will not write to the directory.
+class ReadOnlyTestInstaller : public TestInstaller {
+ public:
+ explicit ReadOnlyTestInstaller(const base::FilePath& installed_path);
+
+ virtual ~ReadOnlyTestInstaller();
+
+ virtual bool GetInstalledFile(const std::string& file,
+ base::FilePath* installed_file) OVERRIDE;
+
+ private:
+ base::FilePath install_directory_;
+};
+
+// A VersionedTestInstaller is an installer that installs files into versioned
+// directories (e.g. somedir/25.23.89.141/<files>).
+class VersionedTestInstaller : public TestInstaller {
+ public :
+ explicit VersionedTestInstaller();
+
+ virtual ~VersionedTestInstaller();
+
+ virtual bool Install(const base::DictionaryValue& manifest,
+ const base::FilePath& unpack_path) OVERRIDE;
+
+ virtual bool GetInstalledFile(const std::string& file,
+ base::FilePath* installed_file) OVERRIDE;
+
+ private:
+ base::FilePath install_directory_;
+ Version current_version_;
+};
+
+#endif // CHROME_BROWSER_COMPONENT_UPDATER_TEST_TEST_INSTALLER_H_
diff --git a/chrome/browser/component_updater/widevine_cdm_component_installer.cc b/chrome/browser/component_updater/widevine_cdm_component_installer.cc
index 822aff2..8bc3fef 100644
--- a/chrome/browser/component_updater/widevine_cdm_component_installer.cc
+++ b/chrome/browser/component_updater/widevine_cdm_component_installer.cc
@@ -195,6 +195,9 @@ class WidevineCdmComponentInstaller : public ComponentInstaller {
virtual bool Install(const base::DictionaryValue& manifest,
const base::FilePath& unpack_path) OVERRIDE;
+ virtual bool GetInstalledFile(const std::string& file,
+ base::FilePath* installed_file) OVERRIDE;
+
private:
base::Version current_version_;
};
@@ -246,6 +249,11 @@ bool WidevineCdmComponentInstaller::Install(
return true;
}
+bool WidevineCdmComponentInstaller::GetInstalledFile(
+ const std::string& file, base::FilePath* installed_file) {
+ return false;
+}
+
void FinishWidevineCdmUpdateRegistration(ComponentUpdateService* cus,
const base::Version& version) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
diff --git a/chrome/browser/net/crl_set_fetcher.cc b/chrome/browser/net/crl_set_fetcher.cc
index eba5d7c..796b2f3 100644
--- a/chrome/browser/net/crl_set_fetcher.cc
+++ b/chrome/browser/net/crl_set_fetcher.cc
@@ -205,4 +205,9 @@ bool CRLSetFetcher::Install(const base::DictionaryValue& manifest,
return true;
}
+bool CRLSetFetcher::GetInstalledFile(
+ const std::string& file, base::FilePath* installed_file) {
+ return false;
+}
+
CRLSetFetcher::~CRLSetFetcher() {}
diff --git a/chrome/browser/net/crl_set_fetcher.h b/chrome/browser/net/crl_set_fetcher.h
index 476e1b9..5f7bdf3 100644
--- a/chrome/browser/net/crl_set_fetcher.h
+++ b/chrome/browser/net/crl_set_fetcher.h
@@ -33,6 +33,8 @@ class CRLSetFetcher : public ComponentInstaller,
virtual void OnUpdateError(int error) OVERRIDE;
virtual bool Install(const base::DictionaryValue& manifest,
const base::FilePath& unpack_path) OVERRIDE;
+ virtual bool GetInstalledFile(const std::string& file,
+ base::FilePath* installed_file) OVERRIDE;
private:
friend class base::RefCountedThreadSafe<CRLSetFetcher>;
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 9ecf4ae..c61dcc5 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -382,6 +382,12 @@
'browser/command_updater_delegate.h',
'browser/common/cancelable_request.cc',
'browser/common/cancelable_request.h',
+ 'browser/component_updater/component_patcher.cc',
+ 'browser/component_updater/component_patcher.h',
+ 'browser/component_updater/component_patcher_operation.cc',
+ 'browser/component_updater/component_patcher_operation.h',
+ 'browser/component_updater/component_patcher_win.cc',
+ 'browser/component_updater/component_patcher_win.h',
'browser/component_updater/component_updater_configurator.cc',
'browser/component_updater/component_updater_configurator.h',
'browser/component_updater/component_unpacker.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 501cc0c..d263df0 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -437,6 +437,7 @@
'test_support_common',
'test_support_unit',
# 3) anything tests directly depend on
+ '../courgette/courgette.gyp:courgette_lib',
'../google_apis/google_apis.gyp:google_apis',
'../skia/skia.gyp:skia',
'../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation',
@@ -686,7 +687,10 @@
'browser/chromeos/web_socket_proxy_helper_unittest.cc',
'browser/command_updater_unittest.cc',
'browser/component_updater/test/component_installers_unittest.cc',
+ 'browser/component_updater/test/component_patcher_mock.h',
+ 'browser/component_updater/test/component_patcher_unittest.cc',
'browser/component_updater/test/component_updater_service_unittest.cc',
+ 'browser/component_updater/test/test_installer.cc',
'browser/content_settings/content_settings_default_provider_unittest.cc',
'browser/content_settings/content_settings_mock_observer.cc',
'browser/content_settings/content_settings_mock_observer.h',
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 18d1cb1..2317369 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -208,7 +208,7 @@ const char kCloudPrintServiceURL[] = "cloud-print-service";
// Comma-separated options to troubleshoot the component updater. Only valid
// for the browser process.
-const char kComponentUpdaterDebug[] = "component-updater-debug";
+const char kComponentUpdater[] = "component-updater";
// Causes the browser process to inspect loaded and registered DLLs for known
// conflicts and warn the user.
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 7e404c1..f2199c9 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -71,7 +71,7 @@ extern const char kCloudPrintFileType[];
extern const char kCloudPrintPrintTicket[];
extern const char kCloudPrintSetupProxy[];
extern const char kCloudPrintServiceURL[];
-extern const char kComponentUpdaterDebug[];
+extern const char kComponentUpdater[];
extern const char kConflictingModulesCheck[];
extern const char kContentSettings2[];
extern const char kCountry[];
diff --git a/chrome/common/extensions/update_manifest.cc b/chrome/common/extensions/update_manifest.cc
index feb6f70..0ed653f 100644
--- a/chrome/common/extensions/update_manifest.cc
+++ b/chrome/common/extensions/update_manifest.cc
@@ -19,7 +19,9 @@ static const char* kExpectedGupdateProtocol = "2.0";
static const char* kExpectedGupdateXmlns =
"http://www.google.com/update2/response";
-UpdateManifest::Result::Result() {}
+UpdateManifest::Result::Result()
+ : size(0),
+ diff_size(0) {}
UpdateManifest::Result::~Result() {}
@@ -154,7 +156,7 @@ static bool ParseSingleAppTag(xmlNode* app_node, xmlNs* xml_namespace,
result->crx_url = GURL(GetAttribute(updatecheck, "codebase"));
if (!result->crx_url.is_valid()) {
*error_detail = "Invalid codebase url: '";
- *error_detail += GetAttribute(updatecheck, "codebase");
+ *error_detail += result->crx_url.possibly_invalid_spec();
*error_detail += "'.";
return false;
}
@@ -189,6 +191,23 @@ static bool ParseSingleAppTag(xmlNode* app_node, xmlNs* xml_namespace,
// sha256 hash of the package in hex format.
result->package_hash = GetAttribute(updatecheck, "hash");
+ int size = 0;
+ if (base::StringToInt(GetAttribute(updatecheck, "size"), &size)) {
+ result->size = size;
+ }
+
+ // package_fingerprint is optional. It identifies the package, preferably
+ // with a modified sha256 hash of the package in hex format.
+ result->package_fingerprint = GetAttribute(updatecheck, "fp");
+
+ // Differential update information is optional.
+ result->diff_crx_url = GURL(GetAttribute(updatecheck, "codebasediff"));
+ result->diff_package_hash = GetAttribute(updatecheck, "hashdiff");
+ int sizediff = 0;
+ if (base::StringToInt(GetAttribute(updatecheck, "sizediff"), &sizediff)) {
+ result->diff_size = sizediff;
+ }
+
return true;
}
diff --git a/chrome/common/extensions/update_manifest.h b/chrome/common/extensions/update_manifest.h
index d847743..bff70b4 100644
--- a/chrome/common/extensions/update_manifest.h
+++ b/chrome/common/extensions/update_manifest.h
@@ -15,13 +15,16 @@ class UpdateManifest {
// An update manifest looks like this:
//
- // <?xml version='1.0' encoding='UTF-8'?>
- // <gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
- // <daystart elapsed_seconds='300' />
- // <app appid='12345'>
- // <updatecheck codebase='http://example.com/extension_1.2.3.4.crx'
- // version='1.2.3.4' prodversionmin='2.0.143.0'
- // hash="12345"/>
+ // <?xml version="1.0" encoding="UTF-8"?>
+ // <gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
+ // <daystart elapsed_seconds="300" />
+ // <app appid="12345" status="ok">
+ // <updatecheck codebase="http://example.com/extension_1.2.3.4.crx"
+ // hash="12345" size="9854" status="ok" version="1.2.3.4"
+ // prodversionmin="2.0.143.0"
+ // codebasediff="http://example.com/diff_1.2.3.4.crx"
+ // hashdiff="123" sizediff="101"
+ // fp="1.123" />
// </app>
// </gupdate>
//
@@ -33,6 +36,9 @@ class UpdateManifest {
// fetch the updated crx file, and the "prodversionmin" attribute refers to
// the minimum version of the chrome browser that the update applies to.
+ // The diff data members correspond to the differential update package, if
+ // a differential update is specified in the response.
+
// The result of parsing one <app> tag in an xml update check manifest.
struct Result {
Result();
@@ -41,8 +47,17 @@ class UpdateManifest {
std::string extension_id;
std::string version;
std::string browser_min_version;
- std::string package_hash;
+
+ // Attributes for the full update.
GURL crx_url;
+ std::string package_hash;
+ int size;
+ std::string package_fingerprint;
+
+ // Attributes for the differential update.
+ GURL diff_crx_url;
+ std::string diff_package_hash;
+ int diff_size;
};
static const int kNoDaystart = -1;
diff --git a/chrome/common/omaha_query_params/omaha_query_params.cc b/chrome/common/omaha_query_params/omaha_query_params.cc
index 6db55d9..04fb7ab 100644
--- a/chrome/common/omaha_query_params/omaha_query_params.cc
+++ b/chrome/common/omaha_query_params/omaha_query_params.cc
@@ -49,22 +49,6 @@ const char kChrome[] = "chrome";
const char kChromeCrx[] = "chromecrx";
const char kChromiumCrx[] = "chromiumcrx";
-const char* GetProdIdString(chrome::OmahaQueryParams::ProdId prod) {
- switch (prod) {
- case chrome::OmahaQueryParams::CHROME:
- return kChrome;
- break;
- case chrome::OmahaQueryParams::CRX:
-#if defined(GOOGLE_CHROME_BUILD)
- return kChromeCrx;
-#else
- return kChromiumCrx;
-#endif
- break;
- }
- return kUnknown;
-}
-
const char kStable[] = "stable";
const char kBeta[] = "beta";
const char kDev[] = "dev";
@@ -95,6 +79,7 @@ const char* GetChannelString() {
namespace chrome {
+// static
std::string OmahaQueryParams::Get(ProdId prod) {
return base::StringPrintf(
"os=%s&arch=%s&nacl_arch=%s&prod=%s&prodchannel=%s&prodversion=%s",
@@ -107,6 +92,24 @@ std::string OmahaQueryParams::Get(ProdId prod) {
}
// static
+const char* OmahaQueryParams::GetProdIdString(
+ chrome::OmahaQueryParams::ProdId prod) {
+ switch (prod) {
+ case chrome::OmahaQueryParams::CHROME:
+ return kChrome;
+ break;
+ case chrome::OmahaQueryParams::CRX:
+#if defined(GOOGLE_CHROME_BUILD)
+ return kChromeCrx;
+#else
+ return kChromiumCrx;
+#endif
+ break;
+ }
+ return kUnknown;
+}
+
+// static
const char* OmahaQueryParams::getOS() {
return kOs;
}
diff --git a/chrome/common/omaha_query_params/omaha_query_params.h b/chrome/common/omaha_query_params/omaha_query_params.h
index 7f74be1..91f6f18 100644
--- a/chrome/common/omaha_query_params/omaha_query_params.h
+++ b/chrome/common/omaha_query_params/omaha_query_params.h
@@ -23,6 +23,10 @@ class OmahaQueryParams {
// prod, prodchannel, prodversion.
static std::string Get(ProdId prod);
+ // Returns the value we use for the "prod=" parameter. Possible return values
+ // include "chrome", "chromecrx", "chromiumcrx", and "unknown".
+ static const char* GetProdIdString(chrome::OmahaQueryParams::ProdId prod);
+
// Returns the value we use for the "os=" parameter. Possible return values
// include: "mac", "win", "android", "cros", "linux", and "openbsd".
static const char* getOS();
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc
index 735be3f..2c60a20 100644
--- a/chrome/installer/setup/setup_main.cc
+++ b/chrome/installer/setup/setup_main.cc
@@ -14,6 +14,7 @@
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/file_version_info.h"
+#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/process_util.h"
@@ -491,7 +492,6 @@ bool CheckAppHostPreconditions(const InstallationState& original_state,
InstallerState* installer_state,
installer::InstallStatus* status) {
if (installer_state->FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
-
if (!installer_state->is_multi_install()) {
LOG(DFATAL) << "App Launcher requires multi install";
*status = installer::APP_HOST_REQUIRES_MULTI_INSTALL;
@@ -507,7 +507,6 @@ bool CheckAppHostPreconditions(const InstallationState& original_state,
installer_state->WriteInstallerResult(*status, 0, NULL);
return false;
}
-
}
return true;
@@ -1477,6 +1476,27 @@ bool HandleNonInstallCmdLineOptions(const InstallationState& original_state,
} else if (cmd_line.HasSwitch(installer::switches::kChromeFrameQuickEnable)) {
*exit_code = installer::ChromeFrameQuickEnable(original_state,
installer_state);
+ } else if (cmd_line.HasSwitch(installer::switches::kPatch)) {
+ const std::string patch_type_str(
+ cmd_line.GetSwitchValueASCII(installer::switches::kPatch));
+ const base::FilePath input_file(
+ cmd_line.GetSwitchValuePath(installer::switches::kInputFile));
+ const base::FilePath patch_file(
+ cmd_line.GetSwitchValuePath(installer::switches::kPatchFile));
+ const base::FilePath output_file(
+ cmd_line.GetSwitchValuePath(installer::switches::kOutputFile));
+
+ if (patch_type_str == installer::kCourgette) {
+ *exit_code = installer::CourgettePatchFiles(input_file,
+ patch_file,
+ output_file);
+ } else if (patch_type_str == installer::kBsdiff) {
+ *exit_code = installer::BsdiffPatchFiles(input_file,
+ patch_file,
+ output_file);
+ } else {
+ *exit_code = installer::PATCH_INVALID_ARGUMENTS;
+ }
} else {
handled = false;
}
diff --git a/chrome/installer/setup/setup_util.cc b/chrome/installer/setup/setup_util.cc
index ef0f8360..d679274 100644
--- a/chrome/installer/setup/setup_util.cc
+++ b/chrome/installer/setup/setup_util.cc
@@ -24,6 +24,7 @@
#include "chrome/installer/util/util_constants.h"
#include "chrome/installer/util/work_item.h"
#include "courgette/courgette.h"
+#include "courgette/third_party/bsdiff.h"
#include "third_party/bspatch/mbspatch.h"
namespace installer {
@@ -86,12 +87,59 @@ bool SupportsSingleInstall(BrowserDistribution::Type type) {
} // namespace
+int CourgettePatchFiles(const base::FilePath& src,
+ const base::FilePath& patch,
+ const base::FilePath& dest) {
+ VLOG(1) << "Applying Courgette patch " << patch.value()
+ << " to file " << src.value()
+ << " and generating file " << dest.value();
+
+ if (src.empty() || patch.empty() || dest.empty())
+ return installer::PATCH_INVALID_ARGUMENTS;
+
+ const courgette::Status patch_status =
+ courgette::ApplyEnsemblePatch(src.value().c_str(),
+ patch.value().c_str(),
+ dest.value().c_str());
+ const int exit_code = (patch_status != courgette::C_OK) ?
+ static_cast<int>(patch_status) + kCourgetteErrorOffset : 0;
+
+ LOG_IF(ERROR, exit_code)
+ << "Failed to apply Courgette patch " << patch.value()
+ << " to file " << src.value() << " and generating file " << dest.value()
+ << ". err=" << exit_code;
+
+ return exit_code;
+}
+
+int BsdiffPatchFiles(const base::FilePath& src,
+ const base::FilePath& patch,
+ const base::FilePath& dest) {
+ VLOG(1) << "Applying bsdiff patch " << patch.value()
+ << " to file " << src.value()
+ << " and generating file " << dest.value();
+
+ if (src.empty() || patch.empty() || dest.empty())
+ return installer::PATCH_INVALID_ARGUMENTS;
+
+ const int patch_status = courgette::ApplyBinaryPatch(src, patch, dest);
+ const int exit_code = patch_status != OK ?
+ patch_status + kBsdiffErrorOffset : 0;
+
+ LOG_IF(ERROR, exit_code)
+ << "Failed to apply bsdiff patch " << patch.value()
+ << " to file " << src.value() << " and generating file " << dest.value()
+ << ". err=" << exit_code;
+
+ return exit_code;
+}
+
int ApplyDiffPatch(const base::FilePath& src,
const base::FilePath& patch,
const base::FilePath& dest,
const InstallerState* installer_state) {
- VLOG(1) << "Applying patch " << patch.value() << " to file " << src.value()
- << " and generating file " << dest.value();
+ VLOG(1) << "Applying patch " << patch.value() << " to file "
+ << src.value() << " and generating file " << dest.value();
if (installer_state != NULL)
installer_state->UpdateStage(installer::ENSEMBLE_PATCHING);
@@ -105,8 +153,10 @@ int ApplyDiffPatch(const base::FilePath& src,
if (patch_status == courgette::C_OK)
return 0;
- VLOG(1) << "Failed to apply patch " << patch.value()
- << " using courgette. err=" << patch_status;
+ LOG(ERROR)
+ << "Failed to apply patch " << patch.value()
+ << " to file " << src.value() << " and generating file " << dest.value()
+ << " using courgette. err=" << patch_status;
// If we ran out of memory or disk space, then these are likely the errors
// we will see. If we run into them, return an error and stay on the
@@ -119,8 +169,16 @@ int ApplyDiffPatch(const base::FilePath& src,
if (installer_state != NULL)
installer_state->UpdateStage(installer::BINARY_PATCHING);
- return ApplyBinaryPatch(src.value().c_str(), patch.value().c_str(),
- dest.value().c_str());
+ int binary_patch_status = ApplyBinaryPatch(src.value().c_str(),
+ patch.value().c_str(),
+ dest.value().c_str());
+
+ LOG_IF(ERROR, binary_patch_status != OK)
+ << "Failed to apply patch " << patch.value()
+ << " to file " << src.value() << " and generating file " << dest.value()
+ << " using bsdiff. err=" << binary_patch_status;
+
+ return binary_patch_status;
}
Version* GetMaxVersionFromArchiveDir(const base::FilePath& chrome_path) {
diff --git a/chrome/installer/setup/setup_util.h b/chrome/installer/setup/setup_util.h
index 2a7530a..97bf4d4 100644
--- a/chrome/installer/setup/setup_util.h
+++ b/chrome/installer/setup/setup_util.h
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
-// This file declares util functions for setup project.
+// This file declares util functions for setup project. It also declares a
+// few functions that the Chrome component updater uses for patching binary
+// deltas.
#ifndef CHROME_INSTALLER_SETUP_SETUP_UTIL_H_
#define CHROME_INSTALLER_SETUP_SETUP_UTIL_H_
@@ -28,15 +30,32 @@ class InstallationState;
class InstallerState;
class ProductState;
-// Apply a diff patch to source file. First tries to apply it using courgette
-// since it checks for courgette header and fails quickly. If that fails
-// tries to apply the patch using regular bsdiff. Returns status code.
+// Apply a diff patch to source file. First tries to apply it using Courgette
+// since it checks for Courgette header and fails quickly. If that fails
+// tries to apply the patch using regular bsdiff. Returns status code as
+// defined by the bsdiff code (see third_party/bspatch/mbspatch.h for the
+// definitions of the codes).
// The installer stage is updated if |installer_state| is non-NULL.
int ApplyDiffPatch(const base::FilePath& src,
const base::FilePath& patch,
const base::FilePath& dest,
const InstallerState* installer_state);
+// Applies a patch file to source file using Courgette. Returns 0 in case of
+// success. In case of errors, it returns kCourgetteErrorOffset + a Courgette
+// status code, as defined in courgette/courgette.h
+int CourgettePatchFiles(const base::FilePath& src,
+ const base::FilePath& patch,
+ const base::FilePath& dest);
+
+// Applies a patch file to source file using bsdiff. This function uses
+// Courgette's flavor of bsdiff. Returns 0 in case of success, or
+// kBsdiffErrorOffset + a bsdiff status code in case of errors.
+// See courgette/third_party/bsdiff.h for details.
+int BsdiffPatchFiles(const base::FilePath& src,
+ const base::FilePath& patch,
+ const base::FilePath& dest);
+
// Find the version of Chrome from an install source directory.
// Chrome_path should contain at least one version folder.
// Returns the maximum version found or NULL if no version is found.
diff --git a/chrome/installer/util/util_constants.cc b/chrome/installer/util/util_constants.cc
index c380a5f..e293e76 100644
--- a/chrome/installer/util/util_constants.cc
+++ b/chrome/installer/util/util_constants.cc
@@ -196,6 +196,15 @@ const char kExperimentGroup[] = "experiment-group";
// to. See DuplicateGoogleUpdateSystemClientKey for details.
const char kToastResultsKey[] = "toast-results-key";
+// Applies a binary patch to a file. The input, patch, and the output file are
+// specified as command line arguments following the --patch switch.
+// Ex: --patch=courgette --input_file='input' --patch_file='patch'
+// --output_file='output'
+const char kPatch[] = "patch";
+const char kInputFile[] = "input-file";
+const char kPatchFile[] = "patch-file";
+const char kOutputFile[] = "output-file";
+
} // namespace switches
// The Active Setup executable will be an identical copy of setup.exe; this is
@@ -261,4 +270,7 @@ const wchar_t kChromeChannelStable[] = L"";
const size_t kMaxAppModelIdLength = 64U;
+const char kCourgette[] = "courgette";
+const char kBsdiff[] = "bsdiff";
+
} // namespace installer
diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h
index bb7b693..3f9a496 100644
--- a/chrome/installer/util/util_constants.h
+++ b/chrome/installer/util/util_constants.h
@@ -80,12 +80,14 @@ enum InstallStatus {
INVALID_STATE_FOR_OPTION, // 47. A non-install option was called with an
// invalid installer state.
WAIT_FOR_EXISTING_FAILED, // 48. OS error waiting for existing setup.exe.
+ PATCH_INVALID_ARGUMENTS, // 49. The arguments of --patch were missing or
+ // they were invalid for any reason.
// Friendly reminder: note the COMPILE_ASSERT below.
};
// Existing InstallStatus values must not change. Always add to the end.
-COMPILE_ASSERT(installer::WAIT_FOR_EXISTING_FAILED == 48,
+COMPILE_ASSERT(installer::PATCH_INVALID_ARGUMENTS == 49,
dont_change_enum);
// The type of an update archive.
@@ -128,6 +130,7 @@ COMPILE_ASSERT(DEFERRING_TO_HIGHER_VERSION == 18,
never_ever_ever_change_InstallerStage_values_bang);
namespace switches {
+
extern const char kAutoLaunchChrome[];
extern const char kChrome[];
extern const char kChromeAppHostDeprecated[]; // TODO(huangs): Remove by M27.
@@ -177,6 +180,11 @@ extern const char kInactiveUserToast[];
extern const char kSystemLevelToast[];
extern const char kExperimentGroup[];
extern const char kToastResultsKey[];
+extern const char kPatch[];
+extern const char kInputFile[];
+extern const char kPatchFile[];
+extern const char kOutputFile[];
+
} // namespace switches
extern const wchar_t kActiveSetupExe[];
@@ -237,6 +245,16 @@ extern const wchar_t kChromeChannelStable[];
extern const size_t kMaxAppModelIdLength;
+// The range of error values for the installer, Courgette, and bsdiff is
+// overlapping. These offset values disambiguate between different sets
+// of errors by shifting the values up with the specified offset.
+const int kCourgetteErrorOffset = 300;
+const int kBsdiffErrorOffset = 600;
+
+// Arguments to --patch switch
+extern const char kCourgette[];
+extern const char kBsdiff[];
+
} // namespace installer
#endif // CHROME_INSTALLER_UTIL_UTIL_CONSTANTS_H_
diff --git a/chrome/test/data/components/updatecheck_diff_reply_1.xml b/chrome/test/data/components/updatecheck_diff_reply_1.xml
new file mode 100644
index 0000000..894c966
--- /dev/null
+++ b/chrome/test/data/components/updatecheck_diff_reply_1.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
+ <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'>
+ <updatecheck codebase='http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx' version='1.0' prodversionmin='11.0.1.0' fp='1'/>
+ </app>
+</gupdate>
diff --git a/chrome/test/data/components/updatecheck_diff_reply_2.xml b/chrome/test/data/components/updatecheck_diff_reply_2.xml
new file mode 100644
index 0000000..5afa6d3
--- /dev/null
+++ b/chrome/test/data/components/updatecheck_diff_reply_2.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
+ <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'>
+ <updatecheck codebase='http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx' version='2.0' prodversionmin='11.0.1.0' fp='f22' codebasediff='http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx'/>
+ </app>
+</gupdate>
diff --git a/chrome/test/data/components/updatecheck_diff_reply_3.xml b/chrome/test/data/components/updatecheck_diff_reply_3.xml
new file mode 100644
index 0000000..83bd41f
--- /dev/null
+++ b/chrome/test/data/components/updatecheck_diff_reply_3.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
+ <app appid='ihfokbkgjpifnbbojhneepfflplebdkc'>
+ <updatecheck status='noupdate'/>
+ </app>
+</gupdate>
diff --git a/chrome/test/data/components/updatecheck_reply_noupdate.xml b/chrome/test/data/components/updatecheck_reply_noupdate.xml
new file mode 100644
index 0000000..254b167
--- /dev/null
+++ b/chrome/test/data/components/updatecheck_reply_noupdate.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
+ <app appid='jebgalgnebhfojomionfpkfelancnnkf'>
+ <updatecheck status='noupdate' />
+ </app>
+</gupdate>
diff --git a/courgette/disassembler_elf_32.h b/courgette/disassembler_elf_32.h
index 5c3f8a2..b1832dc 100644
--- a/courgette/disassembler_elf_32.h
+++ b/courgette/disassembler_elf_32.h
@@ -47,7 +47,7 @@ class DisassemblerElf32 : public Disassembler {
// Misc Section Helpers
- const Elf32_Half SectionHeaderCount() const {
+ Elf32_Half SectionHeaderCount() const {
return section_header_table_size_;
}
@@ -60,13 +60,13 @@ class DisassemblerElf32 : public Disassembler {
return OffsetToPointer(SectionHeader(id)->sh_offset);
}
- const Elf32_Word SectionBodySize(int id) const {
+ Elf32_Word SectionBodySize(int id) const {
return SectionHeader(id)->sh_size;
}
// Misc Segment Helpers
- const Elf32_Half ProgramSegmentHeaderCount() const {
+ Elf32_Half ProgramSegmentHeaderCount() const {
return program_header_table_size_;
}
@@ -76,22 +76,22 @@ class DisassemblerElf32 : public Disassembler {
}
// The virtual memory address at which this program segment will be loaded
- const Elf32_Addr ProgramSegmentMemoryBegin(int id) const {
+ Elf32_Addr ProgramSegmentMemoryBegin(int id) const {
return ProgramSegmentHeader(id)->p_vaddr;
}
// The number of virtual memory bytes for this program segment
- const Elf32_Word ProgramSegmentMemorySize(int id) const {
+ Elf32_Word ProgramSegmentMemorySize(int id) const {
return ProgramSegmentHeader(id)->p_memsz;
}
// Pointer into the source file for this program segment
- const Elf32_Addr ProgramSegmentFileOffset(int id) const {
+ Elf32_Addr ProgramSegmentFileOffset(int id) const {
return ProgramSegmentHeader(id)->p_offset;
}
// Number of file bytes for this program segment. Is <= ProgramMemorySize.
- const Elf32_Word ProgramSegmentFileSize(int id) const {
+ Elf32_Word ProgramSegmentFileSize(int id) const {
return ProgramSegmentHeader(id)->p_filesz;
}
diff --git a/courgette/disassembler_elf_32_x86.h b/courgette/disassembler_elf_32_x86.h
index 6096781..28de7cf 100644
--- a/courgette/disassembler_elf_32_x86.h
+++ b/courgette/disassembler_elf_32_x86.h
@@ -23,13 +23,12 @@ class DisassemblerElf32X86 : public DisassemblerElf32 {
virtual e_machine_values ElfEM() { return EM_386; }
protected:
-
virtual CheckBool RelToRVA(Elf32_Rel rel, RVA* result)
- const WARN_UNUSED_RESULT;
+ const WARN_UNUSED_RESULT;
virtual CheckBool ParseRelocationSection(
const Elf32_Shdr *section_header,
- AssemblyProgram* program) WARN_UNUSED_RESULT;
+ AssemblyProgram* program) WARN_UNUSED_RESULT;
virtual CheckBool ParseRel32RelocsFromSection(
const Elf32_Shdr* section) WARN_UNUSED_RESULT;
diff --git a/courgette/third_party/bsdiff.h b/courgette/third_party/bsdiff.h
index 5e5683a..77897a8 100644
--- a/courgette/third_party/bsdiff.h
+++ b/courgette/third_party/bsdiff.h
@@ -30,12 +30,15 @@
* 2009-03-31 - Change to use Streams. Move CRC code to crc.{h,cc}
* Changed status to an enum, removed unused status codes.
* --Stephen Adams <sra@chromium.org>
+ * 2013-04-10 - Added wrapper to apply a patch directly to files.
+ * --Joshua Pawlicki <waffles@chromium.org>
*/
#ifndef COURGETTE_BSDIFF_H_
#define COURGETTE_BSDIFF_H_
#include "base/basictypes.h"
+#include "base/file_util.h"
namespace courgette {
@@ -44,7 +47,8 @@ enum BSDiffStatus {
MEM_ERROR = 1,
CRC_ERROR = 2,
READ_ERROR = 3,
- UNEXPECTED_ERROR = 4
+ UNEXPECTED_ERROR = 4,
+ WRITE_ERROR = 5
};
class SourceStream;
@@ -64,6 +68,10 @@ BSDiffStatus ApplyBinaryPatch(SourceStream* old_stream,
SourceStream* patch_stream,
SinkStream* new_stream);
+// As above, but simply takes the file paths.
+BSDiffStatus ApplyBinaryPatch(const base::FilePath& old_stream,
+ const base::FilePath& patch_stream,
+ const base::FilePath& new_stream);
// The following declarations are common to the patch-creation and
// patch-application code.
diff --git a/courgette/third_party/bsdiff_apply.cc b/courgette/third_party/bsdiff_apply.cc
index 762c12c..3ed346e 100644
--- a/courgette/third_party/bsdiff_apply.cc
+++ b/courgette/third_party/bsdiff_apply.cc
@@ -26,6 +26,8 @@
* Changelog:
* 2009-03-31 - Change to use Streams. Move CRC code to crc.{h,cc}
* --Stephen Adams <sra@chromium.org>
+ * 2013-04-10 - Add wrapper method to apply a patch to files directly.
+ * --Joshua Pawlicki <waffles@chromium.org>
*/
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
@@ -34,6 +36,7 @@
#include "courgette/third_party/bsdiff.h"
+#include "base/files/memory_mapped_file.h"
#include "courgette/crc.h"
#include "courgette/streams.h"
@@ -168,4 +171,42 @@ BSDiffStatus ApplyBinaryPatch(SourceStream* old_stream,
return OK;
}
+BSDiffStatus ApplyBinaryPatch(const base::FilePath& old_file_path,
+ const base::FilePath& patch_file_path,
+ const base::FilePath& new_file_path) {
+ // Set up the old stream.
+ base::MemoryMappedFile old_file;
+ if (!old_file.Initialize(old_file_path)) {
+ return READ_ERROR;
+ }
+ SourceStream old_file_stream;
+ old_file_stream.Init(old_file.data(), old_file.length());
+
+ // Set up the patch stream.
+ base::MemoryMappedFile patch_file;
+ if (!patch_file.Initialize(patch_file_path)) {
+ return READ_ERROR;
+ }
+ SourceStream patch_file_stream;
+ patch_file_stream.Init(patch_file.data(), patch_file.length());
+
+ // Set up the new stream and apply the patch.
+ SinkStream new_sink_stream;
+ BSDiffStatus status = ApplyBinaryPatch(&old_file_stream,
+ &patch_file_stream,
+ &new_sink_stream);
+ if (status != OK) {
+ return status;
+ }
+
+ // Write the stream to disk.
+ int written = file_util::WriteFile(
+ new_file_path,
+ reinterpret_cast<const char*>(new_sink_stream.Buffer()),
+ static_cast<int>(new_sink_stream.Length()));
+ if (written != static_cast<int>(new_sink_stream.Length()))
+ return WRITE_ERROR;
+ return OK;
+}
+
} // namespace
diff --git a/extensions/common/crx_file.cc b/extensions/common/crx_file.cc
index 4f50962..73e7f7b 100644
--- a/extensions/common/crx_file.cc
+++ b/extensions/common/crx_file.cc
@@ -11,6 +11,9 @@ namespace {
// The current version of the crx format.
static const uint32 kCurrentVersion = 2;
+// The current version of the crx diff format.
+static const uint32 kCurrentDiffVersion = 0;
+
// The maximum size the crx parser will tolerate for a public key.
static const uint32 kMaxPublicKeySize = 1 << 16;
@@ -21,6 +24,7 @@ static const uint32 kMaxSignatureSize = 1 << 16;
// The magic string embedded in the header.
const char kCrxFileHeaderMagic[] = "Cr24";
+const char kCrxDiffFileHeaderMagic[] = "CrOD";
scoped_ptr<CrxFile> CrxFile::Parse(const CrxFile::Header& header,
CrxFile::Error* error) {
@@ -45,12 +49,21 @@ scoped_ptr<CrxFile> CrxFile::Create(const uint32 key_size,
CrxFile::CrxFile(const Header& header) : header_(header) {
}
+bool CrxFile::HeaderIsDelta(const CrxFile::Header& header) {
+ return !strncmp(kCrxDiffFileHeaderMagic, header.magic, sizeof(header.magic));
+}
+
bool CrxFile::HeaderIsValid(const CrxFile::Header& header,
CrxFile::Error* error) {
bool valid = false;
- if (strncmp(kCrxFileHeaderMagic, header.magic, sizeof(header.magic)))
+ bool diffCrx = false;
+ if (!strncmp(kCrxDiffFileHeaderMagic, header.magic, sizeof(header.magic)))
+ diffCrx = true;
+ if (strncmp(kCrxFileHeaderMagic, header.magic, sizeof(header.magic)) &&
+ !diffCrx)
*error = kWrongMagic;
- else if (header.version != kCurrentVersion)
+ else if (header.version != kCurrentVersion
+ && !(diffCrx && header.version == kCurrentDiffVersion))
*error = kInvalidVersion;
else if (header.key_size > kMaxPublicKeySize)
*error = kInvalidKeyTooLarge;
diff --git a/extensions/common/crx_file.h b/extensions/common/crx_file.h
index cb450af..3ac8189 100644
--- a/extensions/common/crx_file.h
+++ b/extensions/common/crx_file.h
@@ -60,6 +60,10 @@ class CrxFile {
// Returns the header structure for writing out to a CRX file.
const Header& header() const { return header_; }
+ // Checks a valid |header| to determine whether or not the CRX represents a
+ // differential CRX.
+ static bool HeaderIsDelta(const Header& header);
+
private:
Header header_;