summaryrefslogtreecommitdiffstats
path: root/chrome/installer/util
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/installer/util
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/installer/util')
-rw-r--r--chrome/installer/util/SConscript106
-rw-r--r--chrome/installer/util/copy_tree_work_item.cc169
-rw-r--r--chrome/installer/util/copy_tree_work_item.h105
-rw-r--r--chrome/installer/util/copy_tree_work_item_unittest.cc620
-rw-r--r--chrome/installer/util/create_dir_work_item.cc94
-rw-r--r--chrome/installer/util/create_dir_work_item.h70
-rw-r--r--chrome/installer/util/create_dir_work_item_unittest.cc169
-rw-r--r--chrome/installer/util/create_reg_key_work_item.cc131
-rw-r--r--chrome/installer/util/create_reg_key_work_item.h74
-rw-r--r--chrome/installer/util/create_reg_key_work_item_unittest.cc212
-rw-r--r--chrome/installer/util/delete_tree_work_item.cc97
-rw-r--r--chrome/installer/util/delete_tree_work_item.h74
-rw-r--r--chrome/installer/util/delete_tree_work_item_unittest.cc243
-rw-r--r--chrome/installer/util/google_update_constants.cc43
-rw-r--r--chrome/installer/util/google_update_constants.h52
-rw-r--r--chrome/installer/util/helper.cc147
-rw-r--r--chrome/installer/util/helper.h72
-rw-r--r--chrome/installer/util/helper_unittest.cc231
-rw-r--r--chrome/installer/util/install_util.cc120
-rw-r--r--chrome/installer/util/install_util.h89
-rw-r--r--chrome/installer/util/install_util_unittest.cc97
-rw-r--r--chrome/installer/util/installer_unittests.vcproj194
-rw-r--r--chrome/installer/util/l10n_string_util.cc148
-rw-r--r--chrome/installer/util/l10n_string_util.h52
-rw-r--r--chrome/installer/util/logging_installer.cc92
-rw-r--r--chrome/installer/util/logging_installer.h52
-rw-r--r--chrome/installer/util/lzma_util.cc210
-rw-r--r--chrome/installer/util/lzma_util.h62
-rw-r--r--chrome/installer/util/prebuild/create_string_rc.bat10
-rw-r--r--chrome/installer/util/prebuild/create_string_rc.py180
-rw-r--r--chrome/installer/util/prebuild/util_prebuild.vcproj150
-rw-r--r--chrome/installer/util/prebuild/util_prebuild.vsprops11
-rw-r--r--chrome/installer/util/run_all_unittests.cc34
-rw-r--r--chrome/installer/util/set_reg_value_work_item.cc176
-rw-r--r--chrome/installer/util/set_reg_value_work_item.h101
-rw-r--r--chrome/installer/util/set_reg_value_work_item_unittest.cc263
-rw-r--r--chrome/installer/util/shell_util.cc376
-rw-r--r--chrome/installer/util/shell_util.h145
-rw-r--r--chrome/installer/util/using_util.vsprops11
-rw-r--r--chrome/installer/util/util.vcproj180
-rw-r--r--chrome/installer/util/util_constants.cc82
-rw-r--r--chrome/installer/util/util_constants.h88
-rw-r--r--chrome/installer/util/version.cc71
-rw-r--r--chrome/installer/util/version.h71
-rw-r--r--chrome/installer/util/work_item.cc85
-rw-r--r--chrome/installer/util/work_item.h124
-rw-r--r--chrome/installer/util/work_item_list.cc142
-rw-r--r--chrome/installer/util/work_item_list.h116
-rw-r--r--chrome/installer/util/work_item_list_unittest.cc192
49 files changed, 6433 insertions, 0 deletions
diff --git a/chrome/installer/util/SConscript b/chrome/installer/util/SConscript
new file mode 100644
index 0000000..7a517cf
--- /dev/null
+++ b/chrome/installer/util/SConscript
@@ -0,0 +1,106 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Import('env')
+
+env = env.Clone()
+
+
+env.Prepend(
+ CPPPATH = [
+ Dir("#/../third_party/lzma_sdk"),
+ Dir("../chrome/third_party/wtl/include"),
+ Dir("#/../third_party/npapi"),
+ Dir("#/../third_party/libxml/include"),
+ #/I "C:/src/trunk/chrome/Debug/obj/generated_resources"
+ #/I "C:/src/trunk/chrome/Debug/obj/localized_strings"
+ Dir("#/../skia/include"),
+ Dir("#/../skia/include/corecg"),
+ Dir("#/../skia/platform"),
+ Dir("#/../third_party/libpng"),
+ Dir("#/../third_party/zlib"),
+ Dir("#/../breakpad/src"),
+ Dir("#/../third_party/libjpeg"),
+ Dir("#/../third_party/icu38/public/common"),
+ Dir("#/../third_party/icu38/public/i18n"),
+ Dir("#/.."),
+ Dir("."),
+ ],
+ CPPDEFINES = [
+ "_LZMA_IN_CB",
+ "LIBXML_STATIC",
+ "PNG_USER_CONFIG",
+ "CHROME_PNG_WRITE_SUPPORT"
+ "U_STATIC_IMPLEMENTATION",
+ "CERT_CHAIN_PARA_HAS_EXTRA_FIELDS",
+ "WIN32_LEAN_AND_MEAN",
+ ],
+ CCFLAGS = [
+ '/TP',
+
+ '/Wp64',
+
+ '/wd4503',
+ '/wd4819',
+ ],
+)
+
+input_files = [
+ "../../app/google_update_settings$OBJSUFFIX",
+ "copy_tree_work_item.cc",
+ "create_dir_work_item.cc",
+ "create_reg_key_work_item.cc",
+ "delete_tree_work_item.cc",
+ "google_update_constants.cc",
+ "helper.cc",
+ "install_util.cc",
+ "l10n_string_util.cc",
+ "logging_installer.cc",
+ "lzma_util.cc",
+ "set_reg_value_work_item.cc",
+ "shell_util.cc",
+ "util_constants.cc",
+ "version.cc",
+ "work_item.cc",
+ "work_item_list.cc",
+]
+
+x = env.StaticLibrary('util', input_files)
+
+
+# create_string_rc.py imports FP.py from the tools/grit/grit/extern
+# directory, so add that to PYTHONPATH for this command execution.
+env_x = env.Clone()
+env_x.AppendENVPath('PYTHONPATH', [Dir('#/../tools/grit/grit/extern').abspath])
+env_x.Command(['$CHROME_DIR/installer/util/setup_strings.rc',
+ '$CHROME_DIR/installer/util/setup_strings.h'],
+ ['$CHROME_DIR/installer/util/prebuild/create_string_rc.py',
+ '$CHROME_DIR/app/generated_resources.grd'] +
+ env.Glob('$CHROME_DIR/app/resources/*.xtb'),
+ "$PYTHON ${SOURCES[0]} ${TARGET.dir}")
diff --git a/chrome/installer/util/copy_tree_work_item.cc b/chrome/installer/util/copy_tree_work_item.cc
new file mode 100644
index 0000000..c6eaacd
--- /dev/null
+++ b/chrome/installer/util/copy_tree_work_item.cc
@@ -0,0 +1,169 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/installer/util/copy_tree_work_item.h"
+
+#include <shlwapi.h>
+#include "base/file_util.h"
+#include "chrome/installer/util/logging_installer.h"
+
+CopyTreeWorkItem::~CopyTreeWorkItem() {
+ if (file_util::PathExists(backup_path_)) {
+ file_util::Delete(backup_path_, true);
+ }
+}
+
+CopyTreeWorkItem::CopyTreeWorkItem(std::wstring source_path,
+ std::wstring dest_path,
+ std::wstring temp_dir,
+ CopyOverWriteOption overwrite_option,
+ std::wstring alternative_path)
+ : source_path_(source_path),
+ dest_path_(dest_path),
+ temp_dir_(temp_dir),
+ overwrite_option_(overwrite_option),
+ alternative_path_(alternative_path),
+ copied_to_dest_path_(false),
+ moved_to_backup_(false),
+ copied_to_alternate_path_(false) {
+}
+
+bool CopyTreeWorkItem::Do() {
+ if (!file_util::PathExists(source_path_)) {
+ LOG(ERROR) << source_path_ << " does not exist";
+ return false;
+ }
+
+ bool dest_exist = file_util::PathExists(dest_path_);
+ // handle overwrite_option_ = IF_DIFFERENT case
+ if ((dest_exist) &&
+ (overwrite_option_ == WorkItem::IF_DIFFERENT) && // only for single file
+ (!PathIsDirectory(source_path_.c_str())) &&
+ (!PathIsDirectory(dest_path_.c_str())) &&
+ (file_util::ContentsEqual(source_path_, dest_path_))) {
+ LOG(INFO) << "Source file " << source_path_
+ << " and destination file " << dest_path_
+ << " are exactly same. Returning true.";
+ return true;
+ }
+
+ // handle overwrite_option_ = RENAME_IF_IN_USE case
+ if ((dest_exist) &&
+ (overwrite_option_ == WorkItem::RENAME_IF_IN_USE) && // only for a file
+ (!PathIsDirectory(source_path_.c_str())) &&
+ (!PathIsDirectory(dest_path_.c_str())) &&
+ (IsFileInUse(dest_path_))) {
+ if (alternative_path_.empty() ||
+ file_util::PathExists(alternative_path_) ||
+ !file_util::CopyFile(source_path_, alternative_path_)) {
+ LOG(ERROR) << "failed to copy " << source_path_ <<
+ " to " << alternative_path_;
+ return false;
+ } else {
+ copied_to_alternate_path_ = true;
+ LOG(INFO) << "Copied source file " << source_path_
+ << " to alternative path " << alternative_path_;
+ return true;
+ }
+ }
+
+ // All other cases where we move dest if it exists, and copy the files
+ if (dest_exist) {
+ if (!GetBackupPath())
+ return false;
+
+ if (file_util::Move(dest_path_, backup_path_)) {
+ moved_to_backup_ = true;
+ LOG(INFO) << "Moved destination " << dest_path_
+ << " to backup path " << backup_path_;
+ } else {
+ LOG(ERROR) << "failed moving " << dest_path_ << " to " << backup_path_;
+ return false;
+ }
+ }
+
+ if (file_util::CopyDirectory(source_path_, dest_path_, true)) {
+ copied_to_dest_path_ = true;
+ LOG(INFO) << "Copied source " << source_path_
+ << " to destination " << dest_path_;
+ } else {
+ LOG(ERROR) << "failed copy " << source_path_ << " to " << dest_path_;
+ return false;
+ }
+
+ return true;
+}
+
+void CopyTreeWorkItem::Rollback() {
+ // Normally the delete operations below should not fail unless some
+ // programs like anti-virus are inpecting the files we just copied.
+ // If this does happen sometimes, we may consider using Move instead of
+ // Delete here. For now we just log the error and continue with the
+ // rest of rollback operation.
+ if (copied_to_dest_path_ && !file_util::Delete(dest_path_, true)) {
+ LOG(ERROR) << "Can not delete " << dest_path_;
+ }
+ if (moved_to_backup_ && !file_util::Move(backup_path_, dest_path_)) {
+ LOG(ERROR) << "failed move " << backup_path_ << " to " << dest_path_;
+ }
+ if (copied_to_alternate_path_ &&
+ !file_util::Delete(alternative_path_, true)) {
+ LOG(ERROR) << "Can not delete " << alternative_path_;
+ }
+}
+
+bool CopyTreeWorkItem::IsFileInUse(std::wstring path) {
+ if (!file_util::PathExists(path))
+ return false;
+
+ HANDLE handle = ::CreateFile(path.c_str(), FILE_ALL_ACCESS,
+ NULL, NULL, OPEN_EXISTING, NULL, NULL);
+ if (handle == INVALID_HANDLE_VALUE)
+ return true;
+
+ CloseHandle(handle);
+ return false;
+}
+
+bool CopyTreeWorkItem::GetBackupPath() {
+ std::wstring file_name = file_util::GetFilenameFromPath(dest_path_);
+ backup_path_.assign(temp_dir_);
+ file_util::AppendToPath(&backup_path_, file_name);
+
+ if (file_util::PathExists(backup_path_)) {
+ // Ideally we should not fail immediately. Instead we could try some
+ // random paths under temp_dir_ until we reach certain limit.
+ // For now our caller always provides a good temporary directory so
+ // we don't bother.
+ LOG(ERROR) << "backup path " << backup_path_ << " already exists";
+ return false;
+ }
+
+ return true;
+}
diff --git a/chrome/installer/util/copy_tree_work_item.h b/chrome/installer/util/copy_tree_work_item.h
new file mode 100644
index 0000000..c942fe9
--- /dev/null
+++ b/chrome/installer/util/copy_tree_work_item.h
@@ -0,0 +1,105 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_INSTALLER_UTIL_COPY_TREE_WORK_ITEM_H__
+#define CHROME_INSTALLER_UTIL_COPY_TREE_WORK_ITEM_H__
+
+#include <string>
+#include <windows.h>
+#include "chrome/installer/util/work_item.h"
+
+// A WorkItem subclass that recursively copies a file system hierarchy from
+// source path to destination path. It also creates all necessary intermediate
+// paths of the destination path if they do not exist. The file system
+// hierarchy could be a single file, or a directory.
+// Under the cover CopyTreeWorkItem moves the destination path, if existing,
+// to the temporary directory passed in, and then copies the source hierarchy
+// to the destination location. During rollback the original destination
+// hierarchy is moved back.
+class CopyTreeWorkItem : public WorkItem {
+ public:
+ virtual ~CopyTreeWorkItem();
+
+ virtual bool Do();
+
+ virtual void Rollback();
+
+ private:
+ friend class WorkItem;
+
+ // See comments on corresponding member varibles for the semantics of
+ // arguments.
+ // Notes on temp_path: to facilitate rollback, the caller needs to supply
+ // a temporary directory to save the original files if they exist under
+ // dest_path.
+ CopyTreeWorkItem(std::wstring source_path, std::wstring dest_path,
+ std::wstring temp_dir, CopyOverWriteOption overwrite_option,
+ std::wstring alternative_path);
+
+ // Checks if the path specified is in use (and hence can not be deleted)
+ bool IsFileInUse(std::wstring path);
+
+ // Get a backup path that can keep the original files under dest_path_,
+ // and set backup_path_ with the result.
+ bool GetBackupPath();
+
+ // Source path to copy files from.
+ std::wstring source_path_;
+
+ // Destination path to copy files to.
+ std::wstring dest_path_;
+
+ // Temporary directory that can be used.
+ std::wstring temp_dir_;
+
+ // Controls the behavior for overwriting.
+ CopyOverWriteOption overwrite_option_;
+
+ // If overwrite_option_ = RENAME_IF_IN_USE, this variables stores the path
+ // to be used if the file is in use and hence we want to copy it to a
+ // different path.
+ std::wstring alternative_path_;
+
+ // Whether the source was copied to dest_path_
+ bool copied_to_dest_path_;
+
+ // Whether the original files have been moved to backup path under
+ // temporary directory. If true, moving back is needed during rollback.
+ bool moved_to_backup_;
+
+ // Whether the source was copied to alternative_path_ because dest_path_
+ // existed and was in use. Needed during rollback.
+ bool copied_to_alternate_path_;
+
+ // The full path in temporary directory that the original dest_path_ has
+ // been moved to.
+ std::wstring backup_path_;
+};
+
+#endif // CHROME_INSTALLER_UTIL_COPY_TREE_WORK_ITEM_H__
diff --git a/chrome/installer/util/copy_tree_work_item_unittest.cc b/chrome/installer/util/copy_tree_work_item_unittest.cc
new file mode 100644
index 0000000..40e4a95
--- /dev/null
+++ b/chrome/installer/util/copy_tree_work_item_unittest.cc
@@ -0,0 +1,620 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include <fstream>
+#include <iostream>
+
+#include "base/base_paths.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "chrome/installer/util/work_item.h"
+#include "chrome/installer/util/copy_tree_work_item.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+ class CopyTreeWorkItemTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Name a subdirectory of the user temp directory.
+ ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_));
+ file_util::AppendToPath(&test_dir_, L"CopyTreeWorkItemTest");
+
+ // Create a fresh, empty copy of this test directory.
+ file_util::Delete(test_dir_, true);
+ CreateDirectory(test_dir_.c_str(), NULL);
+
+ // Create a tempory directory under the test directory.
+ temp_dir_.assign(test_dir_);
+ file_util::AppendToPath(&temp_dir_, L"temp");
+ CreateDirectory(temp_dir_.c_str(), NULL);
+
+ // Create a log file
+ std::wstring log_file;
+ ASSERT_TRUE(file_util::CreateTemporaryFileName(&log_file));
+ ASSERT_TRUE(file_util::PathExists(log_file));
+
+ logging::InitLogging(log_file.c_str(),
+ logging::LOG_ONLY_TO_FILE,
+ logging::LOCK_LOG_FILE,
+ logging::DELETE_OLD_LOG_FILE);
+ logging::SetMinLogLevel(logging::LOG_INFO);
+
+ ASSERT_TRUE(file_util::PathExists(test_dir_));
+ ASSERT_TRUE(file_util::PathExists(temp_dir_));
+ }
+
+ virtual void TearDown() {
+ logging::CloseLogFile();
+ // Clean up test directory
+ ASSERT_TRUE(file_util::Delete(test_dir_, false));
+ ASSERT_FALSE(file_util::PathExists(test_dir_));
+ }
+
+ // the path to temporary directory used to contain the test operations
+ std::wstring test_dir_;
+ std::wstring temp_dir_;
+ };
+
+ // Simple function to dump some text into a new file.
+ void CreateTextFile(const std::wstring& filename,
+ const std::wstring& contents) {
+ std::ofstream file;
+ file.open(filename.c_str());
+ ASSERT_TRUE(file.is_open());
+ file << contents;
+ file.close();
+ }
+
+ // Simple function to read text from a file.
+ std::wstring ReadTextFile(const std::wstring& filename) {
+ WCHAR contents[64];
+ std::wifstream file;
+ file.open(filename.c_str());
+ EXPECT_TRUE(file.is_open());
+ file.getline(contents, 64);
+ file.close();
+ return std::wstring(contents);
+ }
+
+ wchar_t text_content_1[] = L"Gooooooooooooooooooooogle";
+ wchar_t text_content_2[] = L"Overwrite Me";
+};
+
+// Copy one file from source to destination.
+TEST_F(CopyTreeWorkItemTest, CopyFile) {
+ // Create source file
+ std::wstring file_name_from(test_dir_);
+ file_util::AppendToPath(&file_name_from, L"File_From.txt");
+ CreateTextFile(file_name_from, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Create destination path
+ std::wstring dir_name_to(test_dir_);
+ file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir");
+ CreateDirectory(dir_name_to.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_to));
+
+ std::wstring file_name_to(dir_name_to);
+ file_util::AppendToPath(&file_name_to, L"File_To.txt");
+
+ // test Do()
+ scoped_ptr<CopyTreeWorkItem> work_item(
+ WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to,
+ temp_dir_, WorkItem::ALWAYS));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_TRUE(file_util::ContentsEqual(file_name_from, file_name_to));
+
+ // test rollback()
+ work_item->Rollback();
+
+ EXPECT_FALSE(file_util::PathExists(file_name_to));
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+}
+
+// Copy one file, overwriting the existing one in destination.
+// Test with always_overwrite being true or false. The file is overwritten
+// regardless since the content at destination file is different from source.
+TEST_F(CopyTreeWorkItemTest, CopyFileOverwrite) {
+ // Create source file
+ std::wstring file_name_from(test_dir_);
+ file_util::AppendToPath(&file_name_from, L"File_From.txt");
+ CreateTextFile(file_name_from, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Create destination file
+ std::wstring dir_name_to(test_dir_);
+ file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir");
+ CreateDirectory(dir_name_to.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_to));
+
+ std::wstring file_name_to(dir_name_to);
+ file_util::AppendToPath(&file_name_to, L"File_To.txt");
+ CreateTextFile(file_name_to, text_content_2);
+ ASSERT_TRUE(file_util::PathExists(file_name_to));
+
+ // test Do() with always_overwrite being true.
+ scoped_ptr<CopyTreeWorkItem> work_item(
+ WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to,
+ temp_dir_, WorkItem::ALWAYS));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1));
+ EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1));
+
+ // test rollback()
+ work_item->Rollback();
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1));
+ EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_2));
+
+ // test Do() with always_overwrite being false.
+ // the file is still overwritten since the content is different.
+ work_item.reset(
+ WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to,
+ temp_dir_, WorkItem::IF_DIFFERENT));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1));
+ EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1));
+
+ // test rollback()
+ work_item->Rollback();
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1));
+ EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_2));
+}
+
+// Copy one file, with the existing one in destination having the same
+// content.
+// If always_overwrite being true, the file is overwritten.
+// If always_overwrite being false, the file is unchanged.
+TEST_F(CopyTreeWorkItemTest, CopyFileSameContent) {
+ // Create source file
+ std::wstring file_name_from(test_dir_);
+ file_util::AppendToPath(&file_name_from, L"File_From.txt");
+ CreateTextFile(file_name_from, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Create destination file
+ std::wstring dir_name_to(test_dir_);
+ file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir");
+ CreateDirectory(dir_name_to.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_to));
+
+ std::wstring file_name_to(dir_name_to);
+ file_util::AppendToPath(&file_name_to, L"File_To.txt");
+ CreateTextFile(file_name_to, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(file_name_to));
+
+ // Get the path of backup file
+ std::wstring backup_file(temp_dir_);
+ file_util::AppendToPath(&backup_file, L"File_To.txt");
+
+ // test Do() with always_overwrite being true.
+ scoped_ptr<CopyTreeWorkItem> work_item(
+ WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to,
+ temp_dir_, WorkItem::ALWAYS));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1));
+ EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1));
+ // we verify the file is overwritten by checking the existence of backup
+ // file.
+ EXPECT_TRUE(file_util::PathExists(backup_file));
+ EXPECT_EQ(0, ReadTextFile(backup_file).compare(text_content_1));
+
+ // test rollback()
+ work_item->Rollback();
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1));
+ EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1));
+ // the backup file should be gone after rollback
+ EXPECT_FALSE(file_util::PathExists(backup_file));
+
+ // test Do() with always_overwrite being false. nothing should change.
+ work_item.reset(
+ WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to,
+ temp_dir_, WorkItem::IF_DIFFERENT));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1));
+ EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1));
+ // we verify the file is not overwritten by checking that the backup
+ // file does not exist.
+ EXPECT_FALSE(file_util::PathExists(backup_file));
+
+ // test rollback(). nothing should happen here.
+ work_item->Rollback();
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1));
+ EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1));
+ EXPECT_FALSE(file_util::PathExists(backup_file));
+}
+
+// Copy one file and without rollback. Verify all temporary files are deleted.
+TEST_F(CopyTreeWorkItemTest, CopyFileAndCleanup) {
+ // Create source file
+ std::wstring file_name_from(test_dir_);
+ file_util::AppendToPath(&file_name_from, L"File_From.txt");
+ CreateTextFile(file_name_from, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Create destination file
+ std::wstring dir_name_to(test_dir_);
+ file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir");
+ CreateDirectory(dir_name_to.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_to));
+
+ std::wstring file_name_to(dir_name_to);
+ file_util::AppendToPath(&file_name_to, L"File_To.txt");
+ CreateTextFile(file_name_to, text_content_2);
+ ASSERT_TRUE(file_util::PathExists(file_name_to));
+
+ // Get the path of backup file
+ std::wstring backup_file(temp_dir_);
+ file_util::AppendToPath(&backup_file, L"File_To.txt");
+
+ {
+ // test Do().
+ scoped_ptr<CopyTreeWorkItem> work_item(
+ WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to,
+ temp_dir_, WorkItem::IF_DIFFERENT));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1));
+ EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1));
+ // verify the file is moved to backup place.
+ EXPECT_TRUE(file_util::PathExists(backup_file));
+ EXPECT_EQ(0, ReadTextFile(backup_file).compare(text_content_2));
+ }
+
+ // verify the backup file is cleaned up as well.
+ EXPECT_FALSE(file_util::PathExists(backup_file));
+}
+
+// Copy one file, with the existing one in destination being used with
+// overwrite option as IF_DIFFERENT. This destination-file-in-use should
+// be moved to backup location after Do() and moved back after Rollback().
+TEST_F(CopyTreeWorkItemTest, CopyFileInUse) {
+ // Create source file
+ std::wstring file_name_from(test_dir_);
+ file_util::AppendToPath(&file_name_from, L"File_From");
+ CreateTextFile(file_name_from, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Create an executable in destination path by copying ourself to it.
+ wchar_t exe_full_path_str[MAX_PATH];
+ ::GetModuleFileNameW(NULL, exe_full_path_str, MAX_PATH);
+ std::wstring exe_full_path(exe_full_path_str);
+
+ std::wstring dir_name_to(test_dir_);
+ file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir");
+ CreateDirectory(dir_name_to.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_to));
+
+ std::wstring file_name_to(dir_name_to);
+ file_util::AppendToPath(&file_name_to, L"File_To");
+ file_util::CopyFile(exe_full_path, file_name_to);
+ ASSERT_TRUE(file_util::PathExists(file_name_to));
+
+ LOG(INFO) << "copy ourself from " << exe_full_path << " to " << file_name_to;
+
+ // Run the executable in destination path
+ STARTUPINFOW si = {sizeof(si)};
+ PROCESS_INFORMATION pi = {0};
+ ASSERT_TRUE(
+ ::CreateProcessW(NULL, const_cast<wchar_t*>(file_name_to.c_str()),
+ NULL, NULL, FALSE, CREATE_NO_WINDOW | CREATE_SUSPENDED,
+ NULL, NULL, &si, &pi));
+
+ // Get the path of backup file
+ std::wstring backup_file(temp_dir_);
+ file_util::AppendToPath(&backup_file, L"File_To");
+
+ // test Do().
+ scoped_ptr<CopyTreeWorkItem> work_item(
+ WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to,
+ temp_dir_, WorkItem::IF_DIFFERENT));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1));
+ EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1));
+ // verify the file in used is moved to backup place.
+ EXPECT_TRUE(file_util::PathExists(backup_file));
+ EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, backup_file));
+
+ // test rollback()
+ work_item->Rollback();
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1));
+ EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, file_name_to));
+ // the backup file should be gone after rollback
+ EXPECT_FALSE(file_util::PathExists(backup_file));
+
+ TerminateProcess(pi.hProcess, 0);
+ // make sure the handle is closed.
+ WaitForSingleObject(pi.hProcess, INFINITE);
+}
+
+// Test overwrite option RENAME_IF_IN_USE:
+// 1. If destination file is in use, the source should be copied with the
+// new name after Do() and this new name file should be deleted
+// after rollback.
+// 2. If destination file is not in use, the source should be copied in the
+// destination folder after Do() and should be rolled back after Rollback().
+TEST_F(CopyTreeWorkItemTest, RenameAndCopyTest) {
+ // Create source file
+ std::wstring file_name_from(test_dir_);
+ file_util::AppendToPath(&file_name_from, L"File_From");
+ CreateTextFile(file_name_from, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Create an executable in destination path by copying ourself to it.
+ wchar_t exe_full_path_str[MAX_PATH];
+ ::GetModuleFileNameW(NULL, exe_full_path_str, MAX_PATH);
+ std::wstring exe_full_path(exe_full_path_str);
+
+ std::wstring dir_name_to(test_dir_);
+ file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir");
+ CreateDirectory(dir_name_to.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_to));
+
+ std::wstring file_name_to(dir_name_to), alternate_to(dir_name_to);
+ file_util::AppendToPath(&file_name_to, L"File_To");
+ file_util::AppendToPath(&alternate_to, L"Alternate_To");
+ file_util::CopyFile(exe_full_path, file_name_to);
+ ASSERT_TRUE(file_util::PathExists(file_name_to));
+
+ LOG(INFO) << "copy ourself from " << exe_full_path << " to " << file_name_to;
+
+ // Run the executable in destination path
+ STARTUPINFOW si = {sizeof(si)};
+ PROCESS_INFORMATION pi = {0};
+ ASSERT_TRUE(
+ ::CreateProcessW(NULL, const_cast<wchar_t*>(file_name_to.c_str()),
+ NULL, NULL, FALSE, CREATE_NO_WINDOW | CREATE_SUSPENDED,
+ NULL, NULL, &si, &pi));
+
+ // Get the path of backup file
+ std::wstring backup_file(temp_dir_);
+ file_util::AppendToPath(&backup_file, L"File_To");
+
+ // test Do().
+ scoped_ptr<CopyTreeWorkItem> work_item(
+ WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to,
+ temp_dir_, WorkItem::RENAME_IF_IN_USE,
+ alternate_to));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1));
+ EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, file_name_to));
+ // verify that the backup path does not exist
+ EXPECT_FALSE(file_util::PathExists(backup_file));
+ EXPECT_TRUE(file_util::ContentsEqual(file_name_from, alternate_to));
+
+ // test rollback()
+ work_item->Rollback();
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1));
+ EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, file_name_to));
+ EXPECT_FALSE(file_util::PathExists(backup_file));
+ // the alternate file should be gone after rollback
+ EXPECT_FALSE(file_util::PathExists(alternate_to));
+
+ TerminateProcess(pi.hProcess, 0);
+ // make sure the handle is closed.
+ WaitForSingleObject(pi.hProcess, INFINITE);
+
+ // Now the process has terminated, lets try overwriting the file again
+ work_item.reset(WorkItem::CreateCopyTreeWorkItem(
+ file_name_from, file_name_to, temp_dir_, WorkItem::RENAME_IF_IN_USE,
+ alternate_to));
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1));
+ EXPECT_TRUE(file_util::ContentsEqual(file_name_from, file_name_to));
+ // verify that the backup path does exist
+ EXPECT_TRUE(file_util::PathExists(backup_file));
+ EXPECT_FALSE(file_util::PathExists(alternate_to));
+
+ // test rollback()
+ work_item->Rollback();
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1));
+ EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, file_name_to));
+ // the backup file should be gone after rollback
+ EXPECT_FALSE(file_util::PathExists(backup_file));
+ EXPECT_FALSE(file_util::PathExists(alternate_to));
+}
+
+// Copy one file without rollback. The existing one in destination is in use.
+// Verify it is moved to backup location and stays there.
+TEST_F(CopyTreeWorkItemTest, CopyFileInUseAndCleanup) {
+ // Create source file
+ std::wstring file_name_from(test_dir_);
+ file_util::AppendToPath(&file_name_from, L"File_From");
+ CreateTextFile(file_name_from, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Create an executable in destination path by copying ourself to it.
+ wchar_t exe_full_path_str[MAX_PATH];
+ ::GetModuleFileNameW(NULL, exe_full_path_str, MAX_PATH);
+ std::wstring exe_full_path(exe_full_path_str);
+
+ std::wstring dir_name_to(test_dir_);
+ file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir");
+ CreateDirectory(dir_name_to.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_to));
+
+ std::wstring file_name_to(dir_name_to);
+ file_util::AppendToPath(&file_name_to, L"File_To");
+ file_util::CopyFile(exe_full_path, file_name_to);
+ ASSERT_TRUE(file_util::PathExists(file_name_to));
+
+ LOG(INFO) << "copy ourself from " << exe_full_path << " to " << file_name_to;
+
+ // Run the executable in destination path
+ STARTUPINFOW si = {sizeof(si)};
+ PROCESS_INFORMATION pi = {0};
+ ASSERT_TRUE(
+ ::CreateProcessW(NULL, const_cast<wchar_t*>(file_name_to.c_str()),
+ NULL, NULL, FALSE, CREATE_NO_WINDOW | CREATE_SUSPENDED,
+ NULL, NULL, &si, &pi));
+
+ // Get the path of backup file
+ std::wstring backup_file(temp_dir_);
+ file_util::AppendToPath(&backup_file, L"File_To");
+
+ // test Do().
+ {
+ scoped_ptr<CopyTreeWorkItem> work_item(
+ WorkItem::CreateCopyTreeWorkItem(file_name_from, file_name_to,
+ temp_dir_, WorkItem::IF_DIFFERENT));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_EQ(0, ReadTextFile(file_name_from).compare(text_content_1));
+ EXPECT_EQ(0, ReadTextFile(file_name_to).compare(text_content_1));
+ // verify the file in used is moved to backup place.
+ EXPECT_TRUE(file_util::PathExists(backup_file));
+ EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, backup_file));
+ }
+
+ // verify the file in used should be still at the backup place.
+ EXPECT_TRUE(file_util::PathExists(backup_file));
+ EXPECT_TRUE(file_util::ContentsEqual(exe_full_path, backup_file));
+
+ TerminateProcess(pi.hProcess, 0);
+ // make sure the handle is closed.
+ WaitForSingleObject(pi.hProcess, INFINITE);
+}
+
+// Copy a tree from source to destination.
+TEST_F(CopyTreeWorkItemTest, CopyTree) {
+ // Create source tree
+ std::wstring dir_name_from(test_dir_);
+ file_util::AppendToPath(&dir_name_from, L"from");
+ CreateDirectory(dir_name_from.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+ std::wstring dir_name_from_1(dir_name_from);
+ file_util::AppendToPath(&dir_name_from_1, L"1");
+ CreateDirectory(dir_name_from_1.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_from_1));
+
+ std::wstring dir_name_from_2(dir_name_from);
+ file_util::AppendToPath(&dir_name_from_2, L"2");
+ CreateDirectory(dir_name_from_2.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_from_2));
+
+ std::wstring file_name_from_1(dir_name_from_1);
+ file_util::AppendToPath(&file_name_from_1, L"File_1.txt");
+ CreateTextFile(file_name_from_1, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(file_name_from_1));
+
+ std::wstring file_name_from_2(dir_name_from_2);
+ file_util::AppendToPath(&file_name_from_2, L"File_2.txt");
+ CreateTextFile(file_name_from_2, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(file_name_from_2));
+
+ std::wstring dir_name_to(test_dir_);
+ file_util::AppendToPath(&dir_name_to, L"to");
+
+ // test Do()
+ {
+ scoped_ptr<CopyTreeWorkItem> work_item(
+ WorkItem::CreateCopyTreeWorkItem(dir_name_from, dir_name_to,
+ temp_dir_, WorkItem::ALWAYS));
+
+ EXPECT_TRUE(work_item->Do());
+ }
+
+ std::wstring file_name_to_1(dir_name_to);
+ file_util::AppendToPath(&file_name_to_1, L"1");
+ file_util::AppendToPath(&file_name_to_1, L"File_1.txt");
+ EXPECT_TRUE(file_util::PathExists(file_name_to_1));
+ LOG(INFO) << "compare " << file_name_from_1 << " and " << file_name_to_1;
+ EXPECT_TRUE(file_util::ContentsEqual(file_name_from_1, file_name_to_1));
+
+ std::wstring file_name_to_2(dir_name_to);
+ file_util::AppendToPath(&file_name_to_2, L"2");
+ file_util::AppendToPath(&file_name_to_2, L"File_2.txt");
+ EXPECT_TRUE(file_util::PathExists(file_name_to_2));
+ LOG(INFO) << "compare " << file_name_from_2 << " and " << file_name_to_2;
+ EXPECT_TRUE(file_util::ContentsEqual(file_name_from_2, file_name_to_2));
+}
diff --git a/chrome/installer/util/create_dir_work_item.cc b/chrome/installer/util/create_dir_work_item.cc
new file mode 100644
index 0000000..ba0f0a5
--- /dev/null
+++ b/chrome/installer/util/create_dir_work_item.cc
@@ -0,0 +1,94 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/file_util.h"
+#include "chrome/installer/util/create_dir_work_item.h"
+#include "chrome/installer/util/logging_installer.h"
+
+CreateDirWorkItem::~CreateDirWorkItem() {
+}
+
+CreateDirWorkItem::CreateDirWorkItem(const std::wstring& path)
+ : path_(path),
+ rollback_needed_(false) {
+}
+
+void CreateDirWorkItem::GetTopDirToCreate() {
+ if (file_util::PathExists(path_)) {
+ top_path_.clear();
+ return;
+ }
+
+ std::wstring parent_dir(path_);
+ do {
+ top_path_.assign(parent_dir);
+ file_util::UpOneDirectoryOrEmpty(&parent_dir);
+ } while (!parent_dir.empty() && !file_util::PathExists(parent_dir));
+ return;
+}
+
+bool CreateDirWorkItem::Do() {
+ LOG(INFO) << "creating directory " << path_;
+ GetTopDirToCreate();
+ if (top_path_.empty())
+ return true;
+
+ LOG(INFO) << "top directory that needs to be created: " \
+ << top_path_;
+ bool result = file_util::CreateDirectory(path_);
+ LOG(INFO) << "directory creation result: " << result;
+
+ rollback_needed_ = true;
+
+ return result;
+}
+
+void CreateDirWorkItem::Rollback() {
+ if (!rollback_needed_)
+ return;
+
+ // Delete all the directories we created to rollback.
+ // Note we can not recusively delete top_path_ since we don't want to
+ // delete non-empty directory. (We may have created a shared directory).
+ // Instead we walk through path_ to top_path_ and delete directories
+ // along the way.
+ std::wstring path_to_delete(path_);
+
+ while(1) {
+ if (file_util::PathExists(path_to_delete)) {
+ if (!RemoveDirectory(path_to_delete.c_str()))
+ break;
+ }
+ if (!path_to_delete.compare(top_path_))
+ break;
+ file_util::UpOneDirectoryOrEmpty(&path_to_delete);
+ }
+
+ return;
+}
diff --git a/chrome/installer/util/create_dir_work_item.h b/chrome/installer/util/create_dir_work_item.h
new file mode 100644
index 0000000..9d3971d
--- /dev/null
+++ b/chrome/installer/util/create_dir_work_item.h
@@ -0,0 +1,70 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_INSTALLER_UTIL_CREATE_DIR_WORK_ITEM_H__
+#define CHROME_INSTALLER_UTIL_CREATE_DIR_WORK_ITEM_H__
+
+#include <string>
+#include <windows.h>
+#include "chrome/installer/util/work_item.h"
+
+// A WorkItem subclass that creates a directory with the specified path.
+// It also creates all necessary intermediate paths if they do not exist.
+class CreateDirWorkItem : public WorkItem {
+ public:
+ virtual ~CreateDirWorkItem();
+
+ virtual bool Do();
+
+ // Rollback tries to remove all directories created along the path.
+ // If the leaf directory or one of the intermediate directories are not
+ // empty, the non-empty directory and its parent directories will not be
+ // removed.
+ virtual void Rollback();
+
+ private:
+ friend class WorkItem;
+
+ CreateDirWorkItem(const std::wstring& path);
+
+ // Get the top most directory that needs to be created in order to create
+ // "path_", and set "top_path_" accordingly. if "path_" already exists,
+ // "top_path_" is set to empty string.
+ void GetTopDirToCreate();
+
+ // Path of the directory to be created.
+ std::wstring path_;
+
+ // The top most directory that needs to be created.
+ std::wstring top_path_;
+
+ bool rollback_needed_;
+};
+
+#endif // CHROME_INSTALLER_UTIL_CREATE_DIR_WORK_ITEM_H__
diff --git a/chrome/installer/util/create_dir_work_item_unittest.cc b/chrome/installer/util/create_dir_work_item_unittest.cc
new file mode 100644
index 0000000..d4ba9b2
--- /dev/null
+++ b/chrome/installer/util/create_dir_work_item_unittest.cc
@@ -0,0 +1,169 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/base_paths.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "chrome/installer/util/work_item.h"
+#include "chrome/installer/util/create_dir_work_item.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+ class CreateDirWorkItemTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Name a subdirectory of the temp directory.
+ ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_));
+ file_util::AppendToPath(&test_dir_, L"CreateDirWorkItemTest");
+
+ // Create a fresh, empty copy of this directory.
+ file_util::Delete(test_dir_, true);
+ CreateDirectory(test_dir_.c_str(), NULL);
+ }
+ virtual void TearDown() {
+ // Clean up test directory
+ ASSERT_TRUE(file_util::Delete(test_dir_, false));
+ ASSERT_FALSE(file_util::PathExists(test_dir_));
+ }
+
+ // the path to temporary directory used to contain the test operations
+ std::wstring test_dir_;
+ };
+};
+
+TEST_F(CreateDirWorkItemTest, CreatePath) {
+ std::wstring parent_dir(test_dir_);
+ file_util::AppendToPath(&parent_dir, L"a");
+ CreateDirectory(parent_dir.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(parent_dir));
+
+ std::wstring top_dir_to_create(parent_dir);
+ file_util::AppendToPath(&top_dir_to_create, L"b");
+
+ std::wstring dir_to_create(top_dir_to_create);
+ file_util::AppendToPath(&dir_to_create, L"c");
+ file_util::AppendToPath(&dir_to_create, L"d");
+
+ scoped_ptr<CreateDirWorkItem> work_item(
+ WorkItem::CreateCreateDirWorkItem(dir_to_create));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(dir_to_create));
+
+ work_item->Rollback();
+
+ // Rollback should delete all the paths up to top_dir_to_create.
+ EXPECT_FALSE(file_util::PathExists(top_dir_to_create));
+ EXPECT_TRUE(file_util::PathExists(parent_dir));
+}
+
+TEST_F(CreateDirWorkItemTest, CreateExistingPath) {
+ std::wstring dir_to_create(test_dir_);
+ file_util::AppendToPath(&dir_to_create, L"aa");
+ CreateDirectory(dir_to_create.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_to_create));
+
+ scoped_ptr<CreateDirWorkItem> work_item(
+ WorkItem::CreateCreateDirWorkItem(dir_to_create));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(dir_to_create));
+
+ work_item->Rollback();
+
+ // Rollback should not remove the path since it exists before
+ // the CreateDirWorkItem is called.
+ EXPECT_TRUE(file_util::PathExists(dir_to_create));
+}
+
+TEST_F(CreateDirWorkItemTest, CreateSharedPath) {
+ std::wstring dir_to_create_1(test_dir_);
+ file_util::AppendToPath(&dir_to_create_1, L"aaa");
+
+ std::wstring dir_to_create_2(dir_to_create_1);
+ file_util::AppendToPath(&dir_to_create_2, L"bbb");
+
+ std::wstring dir_to_create_3(dir_to_create_2);
+ file_util::AppendToPath(&dir_to_create_3, L"ccc");
+
+ scoped_ptr<CreateDirWorkItem> work_item(
+ WorkItem::CreateCreateDirWorkItem(dir_to_create_3));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(dir_to_create_3));
+
+ // Create another directory under dir_to_create_2
+ std::wstring dir_to_create_4(dir_to_create_2);
+ file_util::AppendToPath(&dir_to_create_4, L"ddd");
+ CreateDirectory(dir_to_create_4.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_to_create_4));
+
+ work_item->Rollback();
+
+ // Rollback should delete dir_to_create_3.
+ EXPECT_FALSE(file_util::PathExists(dir_to_create_3));
+
+ // Rollback should not delete dir_to_create_2 as it is shared.
+ EXPECT_TRUE(file_util::PathExists(dir_to_create_2));
+ EXPECT_TRUE(file_util::PathExists(dir_to_create_4));
+}
+
+TEST_F(CreateDirWorkItemTest, RollbackWithMissingDir) {
+ std::wstring dir_to_create_1(test_dir_);
+ file_util::AppendToPath(&dir_to_create_1, L"aaaa");
+
+ std::wstring dir_to_create_2(dir_to_create_1);
+ file_util::AppendToPath(&dir_to_create_2, L"bbbb");
+
+ std::wstring dir_to_create_3(dir_to_create_2);
+ file_util::AppendToPath(&dir_to_create_3, L"cccc");
+
+ scoped_ptr<CreateDirWorkItem> work_item(
+ WorkItem::CreateCreateDirWorkItem(dir_to_create_3));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(file_util::PathExists(dir_to_create_3));
+
+ RemoveDirectory(dir_to_create_3.c_str());
+ ASSERT_FALSE(file_util::PathExists(dir_to_create_3));
+
+ work_item->Rollback();
+
+ // dir_to_create_3 has already been deleted, Rollback should delete
+ // the rest.
+ EXPECT_FALSE(file_util::PathExists(dir_to_create_1));
+}
diff --git a/chrome/installer/util/create_reg_key_work_item.cc b/chrome/installer/util/create_reg_key_work_item.cc
new file mode 100644
index 0000000..719a4207a
--- /dev/null
+++ b/chrome/installer/util/create_reg_key_work_item.cc
@@ -0,0 +1,131 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "shlwapi.h"
+
+#include "base/file_util.h"
+#include "base/registry.h"
+#include "chrome/installer/util/create_reg_key_work_item.h"
+#include "chrome/installer/util/install_util.h"
+#include "chrome/installer/util/logging_installer.h"
+
+CreateRegKeyWorkItem::~CreateRegKeyWorkItem() {
+}
+
+CreateRegKeyWorkItem::CreateRegKeyWorkItem(HKEY predefined_root,
+ std::wstring path)
+ : predefined_root_(predefined_root),
+ path_(path),
+ key_created_(false) {
+}
+
+bool CreateRegKeyWorkItem::Do() {
+ if(!InitKeyList()) {
+ // Nothing needs to be done here.
+ LOG(INFO) << "no key to create";
+ return true;
+ }
+
+ RegKey key;
+ std::wstring key_path;
+
+ // To create keys, we iterate from back to front.
+ for (size_t i = key_list_.size(); i > 0; i--) {
+ DWORD disposition;
+ key_path.assign(key_list_[i - 1]);
+
+ if(key.CreateWithDisposition(predefined_root_, key_path.c_str(),
+ &disposition)) {
+ if (disposition == REG_OPENED_EXISTING_KEY) {
+ if (key_created_) {
+ // This should not happen. Someone created a subkey under the key
+ // we just created?
+ LOG(ERROR) << key_path << " exists, this is not expected.";
+ return false;
+ }
+ LOG(INFO) << key_path << " exists";
+ // Remove the key path from list if it is already present.
+ key_list_.pop_back();
+ } else if (disposition == REG_CREATED_NEW_KEY){
+ LOG(INFO) << "created " << key_path;
+ key_created_ = true;
+ } else {
+ LOG(ERROR) << "unkown disposition";
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "fail to create " << key_path << " and the error is: "
+ << InstallUtil::FormatLastWin32Error();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void CreateRegKeyWorkItem::Rollback() {
+ if (!key_created_)
+ return;
+
+ std::wstring key_path;
+ // To delete keys, we iterate from front to back.
+ std::vector<std::wstring>::iterator itr;
+ for (itr = key_list_.begin(); itr != key_list_.end(); ++itr) {
+ key_path.assign(*itr);
+ if (SHDeleteEmptyKey(predefined_root_, key_path.c_str()) ==
+ ERROR_SUCCESS) {
+ LOG(INFO) << "rollback: delete " << key_path;
+ } else {
+ LOG(INFO) << "rollback: can not delete " << key_path;
+ // The key might have been deleted, but we don't reliably know what
+ // error code(s) are returned in this case. So we just keep tring delete
+ // the rest.
+ }
+ }
+
+ key_created_ = false;
+ key_list_.clear();
+ return;
+}
+
+bool CreateRegKeyWorkItem::InitKeyList() {
+ if (path_.empty())
+ return false;
+
+ std::wstring key_path(path_);
+
+ do {
+ key_list_.push_back(key_path);
+ // This is pure string operation so it does not matter whether the
+ // path is file path or registry path.
+ file_util::UpOneDirectoryOrEmpty(&key_path);
+ } while(!key_path.empty());
+
+ return true;
+}
diff --git a/chrome/installer/util/create_reg_key_work_item.h b/chrome/installer/util/create_reg_key_work_item.h
new file mode 100644
index 0000000..ce2b272
--- /dev/null
+++ b/chrome/installer/util/create_reg_key_work_item.h
@@ -0,0 +1,74 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_INSTALLER_UTIL_CREATE_REG_KEY_WORK_ITEM_H__
+#define CHROME_INSTALLER_UTIL_CREATE_REG_KEY_WORK_ITEM_H__
+
+#include <string>
+#include <vector>
+
+#include <windows.h>
+
+#include "chrome/installer/util/work_item.h"
+
+// A WorkItem subclass that creates a registry key at the given path.
+// It also creates all necessary intermediate keys if they do not exist.
+class CreateRegKeyWorkItem : public WorkItem {
+ public:
+ virtual ~CreateRegKeyWorkItem();
+
+ virtual bool Do();
+
+ virtual void Rollback();
+
+ private:
+ friend class WorkItem;
+
+ CreateRegKeyWorkItem(HKEY predefined_root, std::wstring path);
+
+ // Initialize key_list_ by adding all paths of keys from predefined_root_
+ // to path_. Returns true if key_list_ is non empty.
+ bool InitKeyList();
+
+ // Root key under which we create the new key. The root key can only be
+ // one of the predefined keys on Windows.
+ HKEY predefined_root_;
+
+ // Path of the key to be created.
+ std::wstring path_;
+
+ // List of paths to all keys that need to be created from predefined_root_
+ // to path_.
+ std::vector<std::wstring> key_list_;
+
+ // Whether any key has been created.
+ bool key_created_;
+};
+
+#endif // CHROME_INSTALLER_UTIL_CREATE_REG_KEY_WORK_ITEM_H__
diff --git a/chrome/installer/util/create_reg_key_work_item_unittest.cc b/chrome/installer/util/create_reg_key_work_item_unittest.cc
new file mode 100644
index 0000000..e694491
--- /dev/null
+++ b/chrome/installer/util/create_reg_key_work_item_unittest.cc
@@ -0,0 +1,212 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/registry.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "chrome/installer/util/create_reg_key_work_item.h"
+#include "chrome/installer/util/work_item.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+ wchar_t test_root[] = L"TmpTmp";
+ class CreateRegKeyWorkItemTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Create a temporary key for testing
+ RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS);
+ key.DeleteKey(test_root);
+ ASSERT_FALSE(key.Open(HKEY_CURRENT_USER, test_root, KEY_READ));
+ ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, test_root, KEY_READ));
+
+ // Create a log file
+ std::wstring log_file;
+ ASSERT_TRUE(file_util::CreateTemporaryFileName(&log_file));
+ ASSERT_TRUE(file_util::PathExists(log_file));
+
+ logging::InitLogging(log_file.c_str(),
+ logging::LOG_ONLY_TO_FILE,
+ logging::LOCK_LOG_FILE,
+ logging::DELETE_OLD_LOG_FILE);
+ logging::SetMinLogLevel(logging::LOG_INFO);
+ }
+ virtual void TearDown() {
+ logging::CloseLogFile();
+ // Clean up the temporary key
+ RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS);
+ ASSERT_TRUE(key.DeleteKey(test_root));
+ }
+ };
+};
+
+TEST_F(CreateRegKeyWorkItemTest, CreateKey) {
+ RegKey key;
+
+ std::wstring parent_key(test_root);
+ file_util::AppendToPath(&parent_key, L"a");
+ ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, parent_key.c_str(), KEY_READ));
+
+ std::wstring top_key_to_create(parent_key);
+ file_util::AppendToPath(&top_key_to_create, L"b");
+
+ std::wstring key_to_create(top_key_to_create);
+ file_util::AppendToPath(&key_to_create, L"c");
+ file_util::AppendToPath(&key_to_create, L"d");
+
+ scoped_ptr<CreateRegKeyWorkItem> work_item(
+ WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, key_to_create));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ));
+
+ work_item->Rollback();
+
+ // Rollback should delete all the keys up to top_key_to_create.
+ EXPECT_FALSE(key.Open(HKEY_CURRENT_USER, top_key_to_create.c_str(),
+ KEY_READ));
+ EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, parent_key.c_str(), KEY_READ));
+}
+
+TEST_F(CreateRegKeyWorkItemTest, CreateExistingKey) {
+ RegKey key;
+
+ std::wstring key_to_create(test_root);
+ file_util::AppendToPath(&key_to_create, L"aa");
+ ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ));
+
+ scoped_ptr<CreateRegKeyWorkItem> work_item(
+ WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, key_to_create));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ));
+
+ work_item->Rollback();
+
+ // Rollback should not remove the key since it exists before
+ // the CreateRegKeyWorkItem is called.
+ EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ));
+}
+
+TEST_F(CreateRegKeyWorkItemTest, CreateSharedKey) {
+ RegKey key;
+ std::wstring key_to_create_1(test_root);
+ file_util::AppendToPath(&key_to_create_1, L"aaa");
+
+ std::wstring key_to_create_2(key_to_create_1);
+ file_util::AppendToPath(&key_to_create_2, L"bbb");
+
+ std::wstring key_to_create_3(key_to_create_2);
+ file_util::AppendToPath(&key_to_create_3, L"ccc");
+
+ scoped_ptr<CreateRegKeyWorkItem> work_item(
+ WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER,
+ key_to_create_3));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create_3.c_str(), KEY_READ));
+
+ // Create another key under key_to_create_2
+ std::wstring key_to_create_4(key_to_create_2);
+ file_util::AppendToPath(&key_to_create_4, L"ddd");
+ ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, key_to_create_4.c_str(),
+ KEY_READ));
+
+ work_item->Rollback();
+
+ // Rollback should delete key_to_create_3.
+ EXPECT_FALSE(key.Open(HKEY_CURRENT_USER, key_to_create_3.c_str(), KEY_READ));
+
+ // Rollback should not delete key_to_create_2 as it is shared.
+ EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create_2.c_str(), KEY_READ));
+ EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create_4.c_str(), KEY_READ));
+}
+
+TEST_F(CreateRegKeyWorkItemTest, RollbackWithMissingKey) {
+ RegKey key;
+ std::wstring key_to_create_1(test_root);
+ file_util::AppendToPath(&key_to_create_1, L"aaaa");
+
+ std::wstring key_to_create_2(key_to_create_1);
+ file_util::AppendToPath(&key_to_create_2, L"bbbb");
+
+ std::wstring key_to_create_3(key_to_create_2);
+ file_util::AppendToPath(&key_to_create_3, L"cccc");
+
+ scoped_ptr<CreateRegKeyWorkItem> work_item(
+ WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER,
+ key_to_create_3));
+
+ EXPECT_TRUE(work_item->Do());
+
+ EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create_3.c_str(), KEY_READ));
+ key.Close();
+
+ // now delete key_to_create_3
+ ASSERT_TRUE(RegDeleteKey(HKEY_CURRENT_USER, key_to_create_3.c_str()) ==
+ ERROR_SUCCESS);
+ ASSERT_FALSE(key.Open(HKEY_CURRENT_USER, key_to_create_3.c_str(),
+ KEY_READ));
+
+ work_item->Rollback();
+
+ // key_to_create_3 has already been deleted, Rollback should delete
+ // the rest.
+ ASSERT_FALSE(key.Open(HKEY_CURRENT_USER, key_to_create_1.c_str(),
+ KEY_READ));
+}
+
+TEST_F(CreateRegKeyWorkItemTest, RollbackWithSetValue) {
+ RegKey key;
+
+ std::wstring key_to_create(test_root);
+ file_util::AppendToPath(&key_to_create, L"aaaaa");
+
+ scoped_ptr<CreateRegKeyWorkItem> work_item(
+ WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, key_to_create));
+
+ EXPECT_TRUE(work_item->Do());
+
+ // Write a value under the key we just created.
+ EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(),
+ KEY_READ | KEY_SET_VALUE));
+ EXPECT_TRUE(key.WriteValue(L"name", L"value"));
+ key.Close();
+
+ work_item->Rollback();
+
+ // Rollback should not remove the key.
+ EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ));
+}
diff --git a/chrome/installer/util/delete_tree_work_item.cc b/chrome/installer/util/delete_tree_work_item.cc
new file mode 100644
index 0000000..8a96915
--- /dev/null
+++ b/chrome/installer/util/delete_tree_work_item.cc
@@ -0,0 +1,97 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "chrome/installer/util/delete_tree_work_item.h"
+
+DeleteTreeWorkItem::~DeleteTreeWorkItem() {
+ std::wstring tmp_dir = file_util::GetDirectoryFromPath(backup_path_);
+ if (file_util::PathExists(tmp_dir)) {
+ file_util::Delete(tmp_dir, true);
+ }
+ tmp_dir = file_util::GetDirectoryFromPath(key_backup_path_);
+ if (file_util::PathExists(tmp_dir)) {
+ file_util::Delete(tmp_dir, true);
+ }
+}
+
+DeleteTreeWorkItem::DeleteTreeWorkItem(std::wstring root_path,
+ std::wstring key_path)
+ : root_path_(root_path),
+ key_path_(key_path) {
+}
+
+// We first try to move key_path_ to backup_path. If it succeeds, we go ahead
+// and move the rest.
+bool DeleteTreeWorkItem::Do() {
+ if (!key_path_.empty() && file_util::PathExists(key_path_)) {
+ if (!GetBackupPath(key_path_, &key_backup_path_) ||
+ !file_util::CopyDirectory(key_path_, key_backup_path_, true) ||
+ !file_util::Delete(key_path_, true)) {
+ LOG(ERROR) << "can not delete " << key_path_
+ << " OR copy it to backup path " << key_backup_path_;
+ return false;
+ }
+ }
+
+ if (!root_path_.empty() && file_util::PathExists(root_path_)) {
+ if (!GetBackupPath(root_path_, &backup_path_) ||
+ !file_util::CopyDirectory(root_path_, backup_path_, true) ||
+ !file_util::Delete(root_path_, true)) {
+ LOG(ERROR) << "can not delete " << root_path_
+ << " OR copy it to backup path " << backup_path_;
+ return false;
+ }
+ }
+ return true;
+}
+
+// If there are files in backup paths move them back.
+void DeleteTreeWorkItem::Rollback() {
+ if (!backup_path_.empty() && file_util::PathExists(backup_path_)) {
+ file_util::Move(backup_path_, root_path_);
+ }
+ if (!key_backup_path_.empty() && file_util::PathExists(key_backup_path_)) {
+ file_util::Move(key_backup_path_, key_path_);
+ }
+}
+
+bool DeleteTreeWorkItem::GetBackupPath(std::wstring for_path,
+ std::wstring* backup_path) {
+ if (!file_util::CreateNewTempDirectory(L"", backup_path)) {
+ // We assume that CreateNewTempDirectory() is doing its job well.
+ LOG(ERROR) << "Couldn't get backup path for delete.";
+ return false;
+ }
+ std::wstring file_name = file_util::GetFilenameFromPath(for_path);
+ file_util::AppendToPath(backup_path, file_name);
+
+ return true;
+}
diff --git a/chrome/installer/util/delete_tree_work_item.h b/chrome/installer/util/delete_tree_work_item.h
new file mode 100644
index 0000000..e480eec
--- /dev/null
+++ b/chrome/installer/util/delete_tree_work_item.h
@@ -0,0 +1,74 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_INSTALLER_UTIL_DELETE_TREE_WORK_ITEM_H__
+#define CHROME_INSTALLER_UTIL_DELETE_TREE_WORK_ITEM_H__
+
+#include <string>
+#include <windows.h>
+#include "chrome/installer/util/work_item.h"
+
+// A WorkItem subclass that recursively deletes a file system hierarchy at the
+// given root path. The file system hierarchy could be a single file, or a
+// directory.
+// The file system hierarchy to be deleted can have a key file. If the key file
+// is specified, deletion will be performed only if the key file is not in use.
+class DeleteTreeWorkItem : public WorkItem {
+ public:
+ virtual ~DeleteTreeWorkItem();
+
+ virtual bool Do();
+
+ virtual void Rollback();
+
+ private:
+ friend class WorkItem;
+
+ // Get a backup path that can keep root_path_ or key_path_
+ bool GetBackupPath(std::wstring for_path, std::wstring* backup_path);
+
+ DeleteTreeWorkItem(std::wstring root_path, std::wstring key_path);
+
+ // Root path to delete.
+ std::wstring root_path_;
+
+ // Path to the key file. If the key file is specified, deletion will be
+ // performed only if the key file is not in use.
+ std::wstring key_path_;
+
+ // The full path in temporary directory that the original root_path_ has
+ // been moved to.
+ std::wstring backup_path_;
+
+ // The full path in temporary directory that the original key_path_ has
+ // been moved to.
+ std::wstring key_backup_path_;
+};
+
+#endif // CHROME_INSTALLER_UTIL_DELETE_TREE_WORK_ITEM_H__
diff --git a/chrome/installer/util/delete_tree_work_item_unittest.cc b/chrome/installer/util/delete_tree_work_item_unittest.cc
new file mode 100644
index 0000000..a055846
--- /dev/null
+++ b/chrome/installer/util/delete_tree_work_item_unittest.cc
@@ -0,0 +1,243 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include <fstream>
+#include <iostream>
+
+#include "base/base_paths.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "chrome/installer/util/delete_tree_work_item.h"
+#include "chrome/installer/util/work_item.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+ class DeleteTreeWorkItemTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Name a subdirectory of the user temp directory.
+ ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_));
+ file_util::AppendToPath(&test_dir_, L"DeleteTreeWorkItemTest");
+
+ // Create a fresh, empty copy of this test directory.
+ file_util::Delete(test_dir_, true);
+ CreateDirectory(test_dir_.c_str(), NULL);
+
+ ASSERT_TRUE(file_util::PathExists(test_dir_));
+ }
+
+ virtual void TearDown() {
+ // Clean up test directory
+ ASSERT_TRUE(file_util::Delete(test_dir_, false));
+ ASSERT_FALSE(file_util::PathExists(test_dir_));
+ }
+
+ // the path to temporary directory used to contain the test operations
+ std::wstring test_dir_;
+ };
+
+ // Simple function to dump some text into a new file.
+ void CreateTextFile(const std::wstring& filename,
+ const std::wstring& contents) {
+ std::ofstream file;
+ file.open(filename.c_str());
+ ASSERT_TRUE(file.is_open());
+ file << contents;
+ file.close();
+ }
+
+ wchar_t text_content_1[] = L"delete me";
+ wchar_t text_content_2[] = L"delete me as well";
+};
+
+// Delete a tree without key path. Everything should be deleted.
+TEST_F(DeleteTreeWorkItemTest, DeleteTreeNoKeyPath) {
+ // Create tree to be deleted
+ std::wstring dir_name_delete(test_dir_);
+ file_util::AppendToPath(&dir_name_delete, L"to_be_delete");
+ CreateDirectory(dir_name_delete.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_delete));
+
+ std::wstring dir_name_delete_1(dir_name_delete);
+ file_util::AppendToPath(&dir_name_delete_1, L"1");
+ CreateDirectory(dir_name_delete_1.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_delete_1));
+
+ std::wstring dir_name_delete_2(dir_name_delete);
+ file_util::AppendToPath(&dir_name_delete_2, L"2");
+ CreateDirectory(dir_name_delete_2.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_delete_2));
+
+ std::wstring file_name_delete_1(dir_name_delete_1);
+ file_util::AppendToPath(&file_name_delete_1, L"File_1.txt");
+ CreateTextFile(file_name_delete_1, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(file_name_delete_1));
+
+ std::wstring file_name_delete_2(dir_name_delete_2);
+ file_util::AppendToPath(&file_name_delete_2, L"File_2.txt");
+ CreateTextFile(file_name_delete_2, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(file_name_delete_2));
+
+ // test Do()
+ scoped_ptr<DeleteTreeWorkItem> work_item(
+ WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, std::wstring()));
+ EXPECT_TRUE(work_item->Do());
+
+ // everything should be gone
+ EXPECT_FALSE(file_util::PathExists(file_name_delete_1));
+ EXPECT_FALSE(file_util::PathExists(file_name_delete_2));
+ EXPECT_FALSE(file_util::PathExists(dir_name_delete));
+
+ work_item->Rollback();
+ // everything should come back
+ EXPECT_TRUE(file_util::PathExists(file_name_delete_1));
+ EXPECT_TRUE(file_util::PathExists(file_name_delete_2));
+ EXPECT_TRUE(file_util::PathExists(dir_name_delete));
+}
+
+
+// Delete a tree with keypath but not in use. Everything should be gone.
+// Rollback should bring back everything
+TEST_F(DeleteTreeWorkItemTest, DeleteTree) {
+ // Create tree to be deleted
+ std::wstring dir_name_delete(test_dir_);
+ file_util::AppendToPath(&dir_name_delete, L"to_be_delete");
+ CreateDirectory(dir_name_delete.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_delete));
+
+ std::wstring dir_name_delete_1(dir_name_delete);
+ file_util::AppendToPath(&dir_name_delete_1, L"1");
+ CreateDirectory(dir_name_delete_1.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_delete_1));
+
+ std::wstring dir_name_delete_2(dir_name_delete);
+ file_util::AppendToPath(&dir_name_delete_2, L"2");
+ CreateDirectory(dir_name_delete_2.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_delete_2));
+
+ std::wstring file_name_delete_1(dir_name_delete_1);
+ file_util::AppendToPath(&file_name_delete_1, L"File_1.txt");
+ CreateTextFile(file_name_delete_1, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(file_name_delete_1));
+
+ std::wstring file_name_delete_2(dir_name_delete_2);
+ file_util::AppendToPath(&file_name_delete_2, L"File_2.txt");
+ CreateTextFile(file_name_delete_2, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(file_name_delete_2));
+
+ // test Do()
+ scoped_ptr<DeleteTreeWorkItem> work_item(
+ WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, file_name_delete_1));
+ EXPECT_TRUE(work_item->Do());
+
+ // everything should be gone
+ EXPECT_FALSE(file_util::PathExists(file_name_delete_1));
+ EXPECT_FALSE(file_util::PathExists(file_name_delete_2));
+ EXPECT_FALSE(file_util::PathExists(dir_name_delete));
+
+ work_item->Rollback();
+ // everything should come back
+ EXPECT_TRUE(file_util::PathExists(file_name_delete_1));
+ EXPECT_TRUE(file_util::PathExists(file_name_delete_2));
+ EXPECT_TRUE(file_util::PathExists(dir_name_delete));
+}
+
+// Delete a tree with key_path in use. Everything should still be there.
+TEST_F(DeleteTreeWorkItemTest, DeleteTreeInUse) {
+ // Create tree to be deleted
+ std::wstring dir_name_delete(test_dir_);
+ file_util::AppendToPath(&dir_name_delete, L"to_be_delete");
+ CreateDirectory(dir_name_delete.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_delete));
+
+ std::wstring dir_name_delete_1(dir_name_delete);
+ file_util::AppendToPath(&dir_name_delete_1, L"1");
+ CreateDirectory(dir_name_delete_1.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_delete_1));
+
+ std::wstring dir_name_delete_2(dir_name_delete);
+ file_util::AppendToPath(&dir_name_delete_2, L"2");
+ CreateDirectory(dir_name_delete_2.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_delete_2));
+
+ std::wstring file_name_delete_1(dir_name_delete_1);
+ file_util::AppendToPath(&file_name_delete_1, L"File_1.txt");
+ CreateTextFile(file_name_delete_1, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(file_name_delete_1));
+
+ std::wstring file_name_delete_2(dir_name_delete_2);
+ file_util::AppendToPath(&file_name_delete_2, L"File_2.txt");
+ CreateTextFile(file_name_delete_2, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(file_name_delete_2));
+
+ // Create a key path file.
+ std::wstring key_path(dir_name_delete);
+ file_util::AppendToPath(&key_path, L"key_file.exe");
+
+ wchar_t exe_full_path_str[MAX_PATH];
+ ::GetModuleFileNameW(NULL, exe_full_path_str, MAX_PATH);
+ std::wstring exe_full_path(exe_full_path_str);
+
+ file_util::CopyFile(exe_full_path, key_path);
+ ASSERT_TRUE(file_util::PathExists(key_path));
+
+ LOG(INFO) << "copy ourself from " << exe_full_path << " to " << key_path;
+
+ // Run the key path file to keep it in use.
+ STARTUPINFOW si = {sizeof(si)};
+ PROCESS_INFORMATION pi = {0};
+ ASSERT_TRUE(
+ ::CreateProcessW(NULL, const_cast<wchar_t*>(key_path.c_str()),
+ NULL, NULL, FALSE, CREATE_NO_WINDOW | CREATE_SUSPENDED,
+ NULL, NULL, &si, &pi));
+
+ // test Do().
+ {
+ scoped_ptr<DeleteTreeWorkItem> work_item(
+ WorkItem::CreateDeleteTreeWorkItem(dir_name_delete, key_path));
+
+ // delete should fail as file in use.
+ EXPECT_FALSE(work_item->Do());
+ }
+
+ // verify everything is still there.
+ EXPECT_TRUE(file_util::PathExists(key_path));
+ EXPECT_TRUE(file_util::PathExists(file_name_delete_1));
+ EXPECT_TRUE(file_util::PathExists(file_name_delete_2));
+
+ TerminateProcess(pi.hProcess, 0);
+ // make sure the handle is closed.
+ WaitForSingleObject(pi.hProcess, INFINITE);
+}
diff --git a/chrome/installer/util/google_update_constants.cc b/chrome/installer/util/google_update_constants.cc
new file mode 100644
index 0000000..8d89aa7
--- /dev/null
+++ b/chrome/installer/util/google_update_constants.cc
@@ -0,0 +1,43 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/installer/util/google_update_constants.h"
+
+namespace google_update {
+
+const wchar_t kChromeGuid[] = L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
+const wchar_t kGearsUpgradeCode[] = L"{D92DBAED-3E3E-4530-B30D-072D16C7DDD0}";
+const wchar_t kRegPathClients[] = L"Software\\Google\\Update\\Clients";
+const wchar_t kRegPathClientState[] = L"Software\\Google\\Update\\ClientState";
+const wchar_t kRegApFieldName[] = L"ap";
+const wchar_t kRegNameField[] = L"name";
+const wchar_t kRegVersionField[] = L"pv";
+const wchar_t kRegLastCheckedField[] = L"LastChecked";
+
+} // namespace installer
diff --git a/chrome/installer/util/google_update_constants.h b/chrome/installer/util/google_update_constants.h
new file mode 100644
index 0000000..984749f
--- /dev/null
+++ b/chrome/installer/util/google_update_constants.h
@@ -0,0 +1,52 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Defines all the command-line switches used with Google Update.
+
+#ifndef CHROME_INSTALLER_UTIL_GOOGLE_UPDATE_CONSTANTS_H__
+#define CHROME_INSTALLER_UTIL_GOOGLE_UPDATE_CONSTANTS_H__
+
+namespace google_update {
+
+extern const wchar_t kChromeGuid[];
+
+// Strictly speaking Google Update doesn't care about this GUID but it is still
+// related to install as it is used by MSI to identify Gears.
+extern const wchar_t kGearsUpgradeCode[];
+
+extern const wchar_t kRegPathClients[];
+extern const wchar_t kRegPathClientState[];
+extern const wchar_t kRegApFieldName[];
+extern const wchar_t kRegNameField[];
+extern const wchar_t kRegVersionField[];
+extern const wchar_t kRegLastCheckedField[];
+
+} // namespace google_update
+
+#endif // CHROME_INSTALLER_UTIL_GOOGLE_UPDATE_CONSTANTS_H__
diff --git a/chrome/installer/util/helper.cc b/chrome/installer/util/helper.cc
new file mode 100644
index 0000000..271fdb2
--- /dev/null
+++ b/chrome/installer/util/helper.cc
@@ -0,0 +1,147 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "chrome/installer/util/delete_tree_work_item.h"
+#include "chrome/installer/util/helper.h"
+#include "chrome/installer/util/logging_installer.h"
+#include "chrome/installer/util/util_constants.h"
+#include "chrome/installer/util/version.h"
+#include "chrome/installer/util/work_item_list.h"
+
+std::wstring installer::GetChromeInstallPath(bool system_install) {
+ std::wstring install_path;
+ if (system_install) {
+ PathService::Get(base::DIR_PROGRAM_FILES, &install_path);
+ } else {
+ PathService::Get(base::DIR_LOCAL_APP_DATA, &install_path);
+ }
+
+ if (!install_path.empty()) {
+ file_util::AppendToPath(&install_path,
+ std::wstring(installer_util::kInstallGoogleDir));
+ file_util::AppendToPath(&install_path,
+ std::wstring(installer_util::kChrome));
+ file_util::AppendToPath(&install_path,
+ std::wstring(installer_util::kInstallBinaryDir));
+ }
+
+ return install_path;
+}
+
+bool installer::LaunchChrome(bool system_install) {
+ std::wstring chrome_exe(L"\"");
+ chrome_exe.append(installer::GetChromeInstallPath(system_install));
+ file_util::AppendToPath(&chrome_exe, installer_util::kChromeExe);
+ chrome_exe.append(L"\"");
+ return process_util::LaunchApp(chrome_exe, false, false, NULL);
+}
+
+bool installer::LaunchChromeAndWaitForResult(bool system_install,
+ const std::wstring& options,
+ int32 timeout_ms,
+ int32* exit_code,
+ bool* is_timeout) {
+ std::wstring chrome_exe(installer::GetChromeInstallPath(system_install));
+ if (chrome_exe.empty())
+ return false;
+ file_util::AppendToPath(&chrome_exe, installer_util::kChromeExe);
+
+ std::wstring command_line(chrome_exe);
+ command_line.append(options);
+ STARTUPINFOW si = {sizeof(si)};
+ PROCESS_INFORMATION pi = {0};
+ if (!::CreateProcessW(chrome_exe.c_str(),
+ const_cast<wchar_t*>(command_line.c_str()),
+ NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL,
+ &si, &pi)) {
+ return false;
+ }
+
+ DWORD wr = ::WaitForSingleObject(pi.hProcess, timeout_ms);
+ if (WAIT_TIMEOUT == wr) {
+ if (is_timeout)
+ *is_timeout = true;
+ } else { // WAIT_OBJECT_0
+ if (is_timeout)
+ *is_timeout = false;
+ if (exit_code) {
+ ::GetExitCodeProcess(pi.hProcess, reinterpret_cast<DWORD*>(exit_code));
+ }
+ }
+
+ ::CloseHandle(pi.hProcess);
+ ::CloseHandle(pi.hThread);
+ return true;
+}
+
+void installer::RemoveOldVersionDirs(const std::wstring& chrome_path,
+ const std::wstring& latest_version_str) {
+ std::wstring search_path(chrome_path);
+ file_util::AppendToPath(&search_path, L"*");
+
+ WIN32_FIND_DATA find_file_data;
+ HANDLE file_handle = FindFirstFile(search_path.c_str(), &find_file_data);
+ if (file_handle == INVALID_HANDLE_VALUE)
+ return;
+
+ BOOL ret = TRUE;
+ scoped_ptr<installer::Version> version;
+ scoped_ptr<installer::Version> latest_version(
+ installer::Version::GetVersionFromString(latest_version_str));
+
+ // We try to delete all directories whose versions are lower than
+ // latest_version.
+ while (ret) {
+ if (find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ LOG(INFO) << "directory found: " << find_file_data.cFileName;
+ version.reset(
+ installer::Version::GetVersionFromString(find_file_data.cFileName));
+ if (version.get() && latest_version->IsHigherThan(version.get())) {
+ std::wstring remove_dir(chrome_path);
+ file_util::AppendToPath(&remove_dir, find_file_data.cFileName);
+ std::wstring chrome_dll_path(remove_dir);
+ file_util::AppendToPath(&chrome_dll_path, installer_util::kChromeDll);
+ LOG(INFO) << "deleting directory " << remove_dir;
+ scoped_ptr<DeleteTreeWorkItem> item;
+ item.reset(WorkItem::CreateDeleteTreeWorkItem(remove_dir,
+ chrome_dll_path));
+ item->Do();
+ }
+ }
+ ret = FindNextFile(file_handle, &find_file_data);
+ }
+
+ FindClose(file_handle);
+}
diff --git a/chrome/installer/util/helper.h b/chrome/installer/util/helper.h
new file mode 100644
index 0000000..68e5115
--- /dev/null
+++ b/chrome/installer/util/helper.h
@@ -0,0 +1,72 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file contains helper functions used by setup.
+
+#ifndef CHROME_INSTALLER_UTIL_HELPER_H__
+#define CHROME_INSTALLER_UTIL_HELPER_H__
+
+#include <string>
+
+namespace installer {
+
+// This function returns the install path for Chrome depending on whether its
+// system wide install or user specific install.
+// system_install: if true, the function returns system wide location
+// (ProgramFiles\Google). Otherwise it returns user specific
+// location (Document And Settings\<user>\Local Settings...)
+std::wstring GetChromeInstallPath(bool system_install);
+
+// Launches Chrome without waiting for its exit.
+bool LaunchChrome(bool system_install);
+
+// Launches Chrome with given command line, waits for Chrome to terminate
+// within timeout_ms, and gets the process exit code if available.
+// The function returns true as long as Chrome is successfully launched.
+// The status of Chrome at the return of the function is given by exit_code
+// and is_timeout.
+bool LaunchChromeAndWaitForResult(bool system_install,
+ const std::wstring& options,
+ int32 timeout_ms,
+ int32* exit_code,
+ bool* is_timeout);
+
+// This function tries to remove all previous version directories after a new
+// Chrome update. If an instance of Chrome with older version is still running
+// on the system, its corresponding version directory will be left intact.
+// (The version directory is subject for removal again during next update.)
+//
+// chrome_path: the root path of Chrome installation.
+// latest_version_str: the latest version of Chrome installed.
+void RemoveOldVersionDirs(const std::wstring& chrome_path,
+ const std::wstring& latest_version_str);
+
+} // namespace installer
+
+#endif
diff --git a/chrome/installer/util/helper_unittest.cc b/chrome/installer/util/helper_unittest.cc
new file mode 100644
index 0000000..be6b183
--- /dev/null
+++ b/chrome/installer/util/helper_unittest.cc
@@ -0,0 +1,231 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include <fstream>
+#include <iostream>
+
+#include "base/base_paths.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "chrome/installer/util/helper.h"
+#include "chrome/installer/util/work_item.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+ class SetupHelperTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Name a subdirectory of the user temp directory.
+ ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_));
+ file_util::AppendToPath(&test_dir_, L"SetupHelperTest");
+
+ // Create a fresh, empty copy of this test directory.
+ file_util::Delete(test_dir_, true);
+ CreateDirectory(test_dir_.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(test_dir_));
+
+ // Create a log file
+ std::wstring log_file;
+ ASSERT_TRUE(file_util::CreateTemporaryFileName(&log_file));
+ ASSERT_TRUE(file_util::PathExists(log_file));
+
+ logging::InitLogging(log_file.c_str(),
+ logging::LOG_ONLY_TO_FILE,
+ logging::LOCK_LOG_FILE,
+ logging::DELETE_OLD_LOG_FILE);
+ logging::SetMinLogLevel(logging::LOG_INFO);
+ }
+
+ virtual void TearDown() {
+ logging::CloseLogFile();
+ // Clean up test directory
+ ASSERT_TRUE(file_util::Delete(test_dir_, false));
+ ASSERT_FALSE(file_util::PathExists(test_dir_));
+ }
+
+ // the path to temporary directory used to contain the test operations
+ std::wstring test_dir_;
+ };
+
+ // Simple function to dump some text into a new file.
+ void CreateTextFile(const std::wstring& filename,
+ const std::wstring& contents) {
+ std::ofstream file;
+ file.open(filename.c_str());
+ ASSERT_TRUE(file.is_open());
+ file << contents;
+ file.close();
+ }
+
+ wchar_t text_content_1[] = L"delete me";
+ wchar_t text_content_2[] = L"delete me as well";
+};
+
+// Delete version directories. Everything lower than the given version
+// should be deleted.
+TEST_F(SetupHelperTest, Delete) {
+ // Create a Chrome dir
+ std::wstring chrome_dir(test_dir_);
+ file_util::AppendToPath(&chrome_dir, L"chrome");
+ CreateDirectory(chrome_dir.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(chrome_dir));
+
+ std::wstring chrome_dir_1(chrome_dir);
+ file_util::AppendToPath(&chrome_dir_1, L"1.0.1.0");
+ CreateDirectory(chrome_dir_1.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(chrome_dir_1));
+
+ std::wstring chrome_dir_2(chrome_dir);
+ file_util::AppendToPath(&chrome_dir_2, L"1.0.2.0");
+ CreateDirectory(chrome_dir_2.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(chrome_dir_2));
+
+ std::wstring chrome_dir_3(chrome_dir);
+ file_util::AppendToPath(&chrome_dir_3, L"1.0.3.0");
+ CreateDirectory(chrome_dir_3.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(chrome_dir_3));
+
+ std::wstring chrome_dir_4(chrome_dir);
+ file_util::AppendToPath(&chrome_dir_4, L"1.0.4.0");
+ CreateDirectory(chrome_dir_4.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(chrome_dir_4));
+
+ std::wstring chrome_dll_1(chrome_dir_1);
+ file_util::AppendToPath(&chrome_dll_1, L"chrome.dll");
+ CreateTextFile(chrome_dll_1, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(chrome_dll_1));
+
+ std::wstring chrome_dll_2(chrome_dir_2);
+ file_util::AppendToPath(&chrome_dll_2, L"chrome.dll");
+ CreateTextFile(chrome_dll_2, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(chrome_dll_2));
+
+ std::wstring chrome_dll_3(chrome_dir_3);
+ file_util::AppendToPath(&chrome_dll_3, L"chrome.dll");
+ CreateTextFile(chrome_dll_3, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(chrome_dll_3));
+
+ std::wstring chrome_dll_4(chrome_dir_4);
+ file_util::AppendToPath(&chrome_dll_4, L"chrome.dll");
+ CreateTextFile(chrome_dll_4, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(chrome_dll_4));
+
+ std::wstring latest_version(L"1.0.4.0");
+ installer::RemoveOldVersionDirs(chrome_dir, latest_version);
+
+ // old versions should be gone
+ EXPECT_FALSE(file_util::PathExists(chrome_dir_1));
+ EXPECT_FALSE(file_util::PathExists(chrome_dir_2));
+ EXPECT_FALSE(file_util::PathExists(chrome_dir_3));
+ // the latest version should stay
+ EXPECT_TRUE(file_util::PathExists(chrome_dll_4));
+}
+
+// Delete older version directories, keeping the one in used intact.
+TEST_F(SetupHelperTest, DeleteInUsed) {
+ // Create a Chrome dir
+ std::wstring chrome_dir(test_dir_);
+ file_util::AppendToPath(&chrome_dir, L"chrome");
+ CreateDirectory(chrome_dir.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(chrome_dir));
+
+ std::wstring chrome_dir_1(chrome_dir);
+ file_util::AppendToPath(&chrome_dir_1, L"1.0.1.0");
+ CreateDirectory(chrome_dir_1.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(chrome_dir_1));
+
+ std::wstring chrome_dir_2(chrome_dir);
+ file_util::AppendToPath(&chrome_dir_2, L"1.0.2.0");
+ CreateDirectory(chrome_dir_2.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(chrome_dir_2));
+
+ std::wstring chrome_dir_3(chrome_dir);
+ file_util::AppendToPath(&chrome_dir_3, L"1.0.3.0");
+ CreateDirectory(chrome_dir_3.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(chrome_dir_3));
+
+ std::wstring chrome_dir_4(chrome_dir);
+ file_util::AppendToPath(&chrome_dir_4, L"1.0.4.0");
+ CreateDirectory(chrome_dir_4.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(chrome_dir_4));
+
+ std::wstring chrome_dll_1(chrome_dir_1);
+ file_util::AppendToPath(&chrome_dll_1, L"chrome.dll");
+ CreateTextFile(chrome_dll_1, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(chrome_dll_1));
+
+ std::wstring chrome_dll_2(chrome_dir_2);
+ file_util::AppendToPath(&chrome_dll_2, L"chrome.dll");
+ CreateTextFile(chrome_dll_2, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(chrome_dll_2));
+
+ // Open the file to make it in use.
+ std::ofstream file;
+ file.open(chrome_dll_2.c_str());
+
+ std::wstring chrome_othera_2(chrome_dir_2);
+ file_util::AppendToPath(&chrome_othera_2, L"othera.dll");
+ CreateTextFile(chrome_othera_2, text_content_2);
+ ASSERT_TRUE(file_util::PathExists(chrome_othera_2));
+
+ std::wstring chrome_otherb_2(chrome_dir_2);
+ file_util::AppendToPath(&chrome_otherb_2, L"otherb.dll");
+ CreateTextFile(chrome_otherb_2, text_content_2);
+ ASSERT_TRUE(file_util::PathExists(chrome_otherb_2));
+
+ std::wstring chrome_dll_3(chrome_dir_3);
+ file_util::AppendToPath(&chrome_dll_3, L"chrome.dll");
+ CreateTextFile(chrome_dll_3, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(chrome_dll_3));
+
+ std::wstring chrome_dll_4(chrome_dir_4);
+ file_util::AppendToPath(&chrome_dll_4, L"chrome.dll");
+ CreateTextFile(chrome_dll_4, text_content_1);
+ ASSERT_TRUE(file_util::PathExists(chrome_dll_4));
+
+ std::wstring latest_version(L"1.0.4.0");
+ installer::RemoveOldVersionDirs(chrome_dir, latest_version);
+
+ // old versions not in used should be gone
+ EXPECT_FALSE(file_util::PathExists(chrome_dir_1));
+ EXPECT_FALSE(file_util::PathExists(chrome_dir_3));
+ // every thing under in used version should stay
+ EXPECT_TRUE(file_util::PathExists(chrome_dir_2));
+ EXPECT_TRUE(file_util::PathExists(chrome_dll_2));
+ EXPECT_TRUE(file_util::PathExists(chrome_othera_2));
+ EXPECT_TRUE(file_util::PathExists(chrome_otherb_2));
+ // the latest version should stay
+ EXPECT_TRUE(file_util::PathExists(chrome_dll_4));
+}
diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc
new file mode 100644
index 0000000..6424bd6
--- /dev/null
+++ b/chrome/installer/util/install_util.cc
@@ -0,0 +1,120 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// See the corresponding header file for description of the functions in this
+// file.
+
+#include "install_util.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+#include "base/registry.h"
+#include "base/string_util.h"
+#include "chrome/installer/util/google_update_constants.h"
+
+
+std::wstring InstallUtil::FormatLastWin32Error() {
+ unsigned messageid = GetLastError();
+ wchar_t* string_buffer = NULL;
+ unsigned string_length = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, messageid, 0, reinterpret_cast<wchar_t *>(&string_buffer), 0, NULL);
+
+ std::wstring formatted_string;
+ if (string_buffer) {
+ formatted_string = string_buffer;
+ LocalFree(reinterpret_cast<HLOCAL>(string_buffer));
+ } else {
+ // The formating failed. simply convert the message value into a string.
+ SStringPrintf(&formatted_string, L"message number %d", messageid);
+ }
+ return formatted_string;
+}
+
+
+std::wstring InstallUtil::GetChromeGoogleUpdateKey() {
+ std::wstring chrome_google_update_key(google_update::kRegPathClients);
+ chrome_google_update_key.append(L"\\");
+ chrome_google_update_key.append(google_update::kChromeGuid);
+ return chrome_google_update_key;
+}
+
+installer::Version* InstallUtil::GetChromeVersion(bool system_install) {
+ RegKey key;
+ std::wstring version_str;
+
+ HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+ if (!key.Open(reg_root, GetChromeGoogleUpdateKey().c_str(), KEY_READ) ||
+ !key.ReadValue(google_update::kRegVersionField, &version_str)) {
+ LOG(INFO) << "No existing Chrome install found.";
+ key.Close();
+ return NULL;
+ }
+ key.Close();
+ LOG(INFO) << "Existing Chrome version found " << version_str;
+ return installer::Version::GetVersionFromString(version_str);
+}
+
+std::wstring InstallUtil::GetNewGoogleUpdateApKey(bool diff_install,
+ installer_util::InstallStatus status, const std::wstring& value) {
+ // Magic suffix that we need to add or remove to "ap" key value.
+ const std::wstring kMagicSuffix = L"-full";
+
+ bool has_magic_string = false;
+ if ((value.length() >= kMagicSuffix.length()) &&
+ (value.rfind(kMagicSuffix) == (value.length() - kMagicSuffix.length()))) {
+ LOG(INFO) << "Incremental installer failure key already set.";
+ has_magic_string = true;
+ }
+
+ std::wstring new_value(value);
+ if ((!diff_install || InstallSuccessful(status)) && has_magic_string) {
+ LOG(INFO) << "Removing failure key from value " << value;
+ new_value = value.substr(0, value.length() - kMagicSuffix.length());
+ } else if ((diff_install && !InstallSuccessful(status)) &&
+ !has_magic_string) {
+ LOG(INFO) << "Incremental installer failed, setting failure key.";
+ new_value.append(kMagicSuffix);
+ }
+
+ return new_value;
+}
+
+bool InstallUtil::InstallSuccessful(installer_util::InstallStatus status) {
+ switch (status) {
+ case installer_util::FIRST_INSTALL_SUCCESS:
+ case installer_util::INSTALL_REPAIRED:
+ case installer_util::NEW_VERSION_UPDATED:
+ case installer_util::HIGHER_VERSION_EXISTS:
+ return true;
+ default:
+ return false;
+ }
+}
diff --git a/chrome/installer/util/install_util.h b/chrome/installer/util/install_util.h
new file mode 100644
index 0000000..8ef79bf
--- /dev/null
+++ b/chrome/installer/util/install_util.h
@@ -0,0 +1,89 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file declares utility functions for the installer. The original reason
+// for putting these functions in installer\util library is so that we can
+// separate out the critical logic and write unit tests for it.
+
+#ifndef CHROME_INSTALLER_UTIL_INSTALL_UTIL_H__
+#define CHROME_INSTALLER_UTIL_INSTALL_UTIL_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "chrome/installer/util/util_constants.h"
+#include "chrome/installer/util/version.h"
+
+// This is a utility class that provides common installation related
+// utility methods that can be used by installer and also unit tested
+// independently.
+class InstallUtil {
+ public:
+ // Gets the last Win32 error and generates a human readable message string.
+ // Uses the Win32 API GetLastError() to get the last error and API
+ // FormatMessage() to generate a string. This function has been copied
+ // from chrome\common\win_util.{h.cc} to avoid making setup.exe dependent
+ // on all the other libs (base_gfx, libjpeg, libpng and others) that we
+ // need to pull in and the size of setup.exe goes up by ~140KB.
+ static std::wstring FormatLastWin32Error();
+
+ // This method gets the Google Update registry key path for Chrome.
+ // i.e. - Software\Google\Update\Clients\<chrome-guid>";
+ static std::wstring GetChromeGoogleUpdateKey();
+
+ // Find the version of Chrome installed on the system by checking the
+ // Google Update registry key. Returns the version or NULL if no version is
+ // found.
+ // system_install: if true, looks for version number under the HKLM root,
+ // otherwise looks under the HKCU.
+ static installer::Version * GetChromeVersion(bool system_install);
+
+ // This method generates the new value for Oamaha "ap" key for Chrome
+ // based on whether we are doing incremental install (or not) and whether
+ // the install succeeded.
+ // - If install worked, remove the magic string (if present).
+ // - If incremental installer failed, append a magic string (if
+ // not present already).
+ // - If full installer failed, still remove this magic
+ // string (if it is present already).
+ //
+ // diff_install: tells whether this is incremental install or not.
+ // install_status: if 0, means installation was successful.
+ // value: current value of Google Update "ap" key.
+ static std::wstring GetNewGoogleUpdateApKey(bool diff_install,
+ installer_util::InstallStatus status, const std::wstring& value);
+
+ // Given an InstallStatus it tells whether the install was sucessful or not.
+ static bool InstallSuccessful(installer_util::InstallStatus status);
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(InstallUtil);
+};
+
+
+#endif // CHROME_INSTALLER_UTIL_INSTALL_UTIL_H__
diff --git a/chrome/installer/util/install_util_unittest.cc b/chrome/installer/util/install_util_unittest.cc
new file mode 100644
index 0000000..b0a816a
--- /dev/null
+++ b/chrome/installer/util/install_util_unittest.cc
@@ -0,0 +1,97 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Unit tests for InstallUtil class.
+
+#include <windows.h>
+
+#include "chrome/installer/util/install_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+class InstallUtilTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Currently no setup required.
+ }
+
+ virtual void TearDown() {
+ // Currently no tear down required.
+ }
+};
+} // namespace
+
+TEST_F(InstallUtilTest, GetNewGoogleUpdateApKeyTest) {
+ installer_util::InstallStatus s = installer_util::FIRST_INSTALL_SUCCESS;
+ installer_util::InstallStatus f = installer_util::INSTALL_FAILED;
+
+ // Incremental Installer that worked.
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, s, L""), L"");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, s, L"1.1"), L"1.1");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, s, L"1.1-dev"),
+ L"1.1-dev");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, s, L"-full"), L"");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, s, L"1.1-full"),
+ L"1.1");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, s, L"1.1-dev-full"),
+ L"1.1-dev");
+
+ // Incremental Installer that failed.
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, f, L""), L"-full");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, f, L"1.1"), L"1.1-full");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, f, L"1.1-dev"),
+ L"1.1-dev-full");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, f, L"-full"), L"-full");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, f, L"1.1-full"),
+ L"1.1-full");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(true, f, L"1.1-dev-full"),
+ L"1.1-dev-full");
+
+ // Full Installer that worked.
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, s, L""), L"");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, s, L"1.1"), L"1.1");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, s, L"1.1-dev"),
+ L"1.1-dev");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, s, L"-full"), L"");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, s, L"1.1-full"),
+ L"1.1");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, s, L"1.1-dev-full"),
+ L"1.1-dev");
+
+ // Full Installer that failed.
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, f, L""), L"");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, f, L"1.1"), L"1.1");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, f, L"1.1-dev"),
+ L"1.1-dev");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, f, L"-full"), L"");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, f, L"1.1-full"),
+ L"1.1");
+ EXPECT_EQ(InstallUtil::GetNewGoogleUpdateApKey(false, f, L"1.1-dev-full"),
+ L"1.1-dev");
+}
diff --git a/chrome/installer/util/installer_unittests.vcproj b/chrome/installer/util/installer_unittests.vcproj
new file mode 100644
index 0000000..b7e3eef
--- /dev/null
+++ b/chrome/installer/util/installer_unittests.vcproj
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="installer_unittests"
+ ProjectGUID="{903F8C1E-537A-4C9E-97BE-075147CBE769}"
+ RootNamespace="installer_unittests"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)\tools\build\win\unit_test.vsprops;$(SolutionDir)\installer\util\using_util.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ AdditionalManifestFiles="$(SolutionDir)installer\mini_installer\mini_installer.exe.manifest"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;$(SolutionDir)\tools\build\win\unit_test.vsprops;$(SolutionDir)\installer\util\using_util.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ AdditionalManifestFiles="$(SolutionDir)installer\mini_installer\mini_installer.exe.manifest"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="support"
+ >
+ <File
+ RelativePath="run_all_unittests.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="tests"
+ >
+ <File
+ RelativePath="copy_tree_work_item_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="create_dir_work_item_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="create_reg_key_work_item_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="delete_tree_work_item_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="helper_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="install_util_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="set_reg_value_work_item_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\setup\setup_constants.cc"
+ >
+ </File>
+ <File
+ RelativePath="work_item_list_unittest.cc"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/chrome/installer/util/l10n_string_util.cc b/chrome/installer/util/l10n_string_util.cc
new file mode 100644
index 0000000..b10e1a4
--- /dev/null
+++ b/chrome/installer/util/l10n_string_util.cc
@@ -0,0 +1,148 @@
+#include <atlbase.h>
+
+#include <map>
+
+#include "base/logging.h"
+#include "base/string_util.h"
+
+#include "setup_strings.h"
+
+namespace {
+
+// Gets the language from the OS. If we're unable to get the system language,
+// defaults to en-us.
+std::wstring GetSystemLanguage() {
+ static std::wstring language;
+ if (!language.empty())
+ return language;
+ // We don't have ICU at this point, so we use win32 apis.
+ LCID id = GetThreadLocale();
+ int length = GetLocaleInfo(id, LOCALE_SISO639LANGNAME, 0, 0);
+ if (0 == length)
+ return false;
+ length = GetLocaleInfo(id, LOCALE_SISO639LANGNAME,
+ WriteInto(&language, length), length);
+ DCHECK(length == language.length() + 1);
+ StringToLowerASCII(&language);
+
+ // Add the country if we need it.
+ std::wstring country;
+ length = GetLocaleInfo(id, LOCALE_SISO3166CTRYNAME, 0, 0);
+ if (0 != length) {
+ length = GetLocaleInfo(id, LOCALE_SISO3166CTRYNAME,
+ WriteInto(&country, length), length);
+ DCHECK(length == country.length() + 1);
+ StringToLowerASCII(&country);
+ if (L"en" == language) {
+ if (L"gb" == country) {
+ language.append(L"-gb");
+ } else {
+ language.append(L"-us");
+ }
+ } else if (L"es" == language && L"es" != country) {
+ language.append(L"-419");
+ } else if (L"pt" == language) {
+ if (L"br" == country) {
+ language.append(L"-br");
+ } else {
+ language.append(L"-pt");
+ }
+ } else if (L"zh" == language) {
+ if (L"tw" == country) {
+ language.append(L"-tw");
+ } else {
+ language.append(L"-cn");
+ }
+ }
+ }
+
+ if (language.empty())
+ language = L"en-us";
+
+ return language;
+}
+
+// This method returns the appropriate language offset given the language as a
+// string. Note: This method is not thread safe because of how we create
+// |offset_map|.
+int GetLanguageOffset(const std::wstring& language) {
+ static std::map<std::wstring, int> offset_map;
+ if (offset_map.empty()) {
+ offset_map[L"ar"] = IDS_L10N_OFFSET_AR;
+ offset_map[L"bg"] = IDS_L10N_OFFSET_BG;
+ offset_map[L"ca"] = IDS_L10N_OFFSET_CA;
+ offset_map[L"cs"] = IDS_L10N_OFFSET_CS;
+ offset_map[L"da"] = IDS_L10N_OFFSET_DA;
+ offset_map[L"de"] = IDS_L10N_OFFSET_DE;
+ offset_map[L"el"] = IDS_L10N_OFFSET_EL;
+ offset_map[L"en-gb"] = IDS_L10N_OFFSET_EN_GB;
+ offset_map[L"en-us"] = IDS_L10N_OFFSET_EN_US;
+ offset_map[L"es"] = IDS_L10N_OFFSET_ES;
+ offset_map[L"es-419"] = IDS_L10N_OFFSET_ES_419;
+ offset_map[L"et"] = IDS_L10N_OFFSET_ET;
+ offset_map[L"fi"] = IDS_L10N_OFFSET_FI;
+ offset_map[L"fil"] = IDS_L10N_OFFSET_FIL;
+ offset_map[L"fr"] = IDS_L10N_OFFSET_FR;
+ offset_map[L"he"] = IDS_L10N_OFFSET_HE;
+ offset_map[L"hi"] = IDS_L10N_OFFSET_HI;
+ offset_map[L"hr"] = IDS_L10N_OFFSET_HR;
+ offset_map[L"hu"] = IDS_L10N_OFFSET_HU;
+ offset_map[L"id"] = IDS_L10N_OFFSET_ID;
+ offset_map[L"it"] = IDS_L10N_OFFSET_IT;
+ offset_map[L"ja"] = IDS_L10N_OFFSET_JA;
+ offset_map[L"ko"] = IDS_L10N_OFFSET_KO;
+ offset_map[L"lt"] = IDS_L10N_OFFSET_LT;
+ offset_map[L"lv"] = IDS_L10N_OFFSET_LV;
+ // Google web properties use no for nb. Handle both just to be safe.
+ offset_map[L"nb"] = IDS_L10N_OFFSET_NO;
+ offset_map[L"nl"] = IDS_L10N_OFFSET_NL;
+ offset_map[L"no"] = IDS_L10N_OFFSET_NO;
+ offset_map[L"pl"] = IDS_L10N_OFFSET_PL;
+ offset_map[L"pt-br"] = IDS_L10N_OFFSET_PT_BR;
+ offset_map[L"pt-pt"] = IDS_L10N_OFFSET_PT_PT;
+ offset_map[L"ro"] = IDS_L10N_OFFSET_RO;
+ offset_map[L"ru"] = IDS_L10N_OFFSET_RU;
+ offset_map[L"sk"] = IDS_L10N_OFFSET_SK;
+ offset_map[L"sl"] = IDS_L10N_OFFSET_SL;
+ offset_map[L"sr"] = IDS_L10N_OFFSET_SR;
+ offset_map[L"sv"] = IDS_L10N_OFFSET_SV;
+ offset_map[L"th"] = IDS_L10N_OFFSET_TH;
+ offset_map[L"tr"] = IDS_L10N_OFFSET_TR;
+ offset_map[L"uk"] = IDS_L10N_OFFSET_UK;
+ offset_map[L"vi"] = IDS_L10N_OFFSET_VI;
+ offset_map[L"zh-cn"] = IDS_L10N_OFFSET_ZH_CN;
+ offset_map[L"zh-tw"] = IDS_L10N_OFFSET_ZH_TW;
+ }
+
+ std::map<std::wstring, int>::iterator it = offset_map.find(
+ StringToLowerASCII(language));
+ if (it != offset_map.end())
+ return it->second;
+
+ NOTREACHED() << "unknown system language-country";
+
+ // Fallback on the en-US offset just in case.
+ return IDS_L10N_OFFSET_EN_US;
+}
+
+} // namespace
+
+namespace installer_util {
+
+std::wstring GetLocalizedString(int base_message_id) {
+ std::wstring language = GetSystemLanguage();
+ std::wstring localized_string;
+
+ int message_id = base_message_id + GetLanguageOffset(language);
+ const ATLSTRINGRESOURCEIMAGE* image = AtlGetStringResourceImage(
+ _AtlBaseModule.GetModuleInstance(), message_id);
+ if (image) {
+ localized_string = std::wstring(image->achString, image->nLength);
+ } else {
+ NOTREACHED() << "Unable to find resource id " << message_id;
+ }
+
+ return localized_string;
+}
+
+} // namespace installer_util
diff --git a/chrome/installer/util/l10n_string_util.h b/chrome/installer/util/l10n_string_util.h
new file mode 100644
index 0000000..91989ce
--- /dev/null
+++ b/chrome/installer/util/l10n_string_util.h
@@ -0,0 +1,52 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file contains helper functions for getting strings that are included in
+// our DLL for all languages (i.e., does not come from our language DLL).
+//
+// These resource strings are organized such that we can get a localized string
+// by taking the base resource ID and adding a language offset. For example,
+// to get the resource id for the localized product name in en-US, we take
+// IDS_PRODUCT_NAME_BASE + IDS_L10N_OFFSET_EN_US.
+
+#ifndef CHROME_INSTALLER_UTIL_L10N_STRING_UTIL_H_
+#define CHROME_INSTALLER_UTIL_L10N_STRING_UTIL_H_
+
+#include <string>
+
+namespace installer_util {
+
+// Given a string base id, return the localized version of the string based on
+// the system language. This is used for shortcuts placed on the user's
+// desktop.
+std::wstring GetLocalizedString(int base_message_id);
+
+}
+
+#endif // CHROME_INSTALLER_UTIL_L10N_STRING_UTIL_H_
diff --git a/chrome/installer/util/logging_installer.cc b/chrome/installer/util/logging_installer.cc
new file mode 100644
index 0000000..ab0786d
--- /dev/null
+++ b/chrome/installer/util/logging_installer.cc
@@ -0,0 +1,92 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "chrome/installer/util/logging_installer.h"
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "chrome/installer/util/util_constants.h"
+
+namespace installer {
+
+// This should be true for the period between the end of
+// InitInstallerLogging() and the beginning of EndInstallerLogging().
+bool installer_logging_ = false;
+
+void InitInstallerLogging(const CommandLine& command_line) {
+
+ if (installer_logging_)
+ return;
+
+ if (command_line.HasSwitch(installer_util::switches::kDisableLogging)) {
+ installer_logging_ = true;
+ return;
+ }
+
+ logging::InitLogging(GetLogFilePath(command_line).c_str(),
+ logging::LOG_ONLY_TO_FILE,
+ logging::LOCK_LOG_FILE,
+ logging::DELETE_OLD_LOG_FILE);
+
+ if (command_line.HasSwitch(installer_util::switches::kVerboseLogging)) {
+ logging::SetMinLogLevel(logging::LOG_INFO);
+ } else {
+ logging::SetMinLogLevel(logging::LOG_ERROR);
+ }
+
+ installer_logging_ = true;
+}
+
+void EndInstallerLogging() {
+ logging::CloseLogFile();
+
+ installer_logging_ = false;
+}
+
+std::wstring GetLogFilePath(const CommandLine& command_line) {
+ if (command_line.HasSwitch(installer_util::switches::kLogFile)) {
+ return command_line.GetSwitchValue(installer_util::switches::kLogFile);
+ }
+
+ const std::wstring log_filename(L"chrome_installer.log");
+ std::wstring log_path;
+
+ if (PathService::Get(base::DIR_TEMP, &log_path)) {
+ file_util::AppendToPath(&log_path, log_filename);
+ return log_path;
+ } else {
+ return log_filename;
+ }
+}
+
+} // namespace installer
diff --git a/chrome/installer/util/logging_installer.h b/chrome/installer/util/logging_installer.h
new file mode 100644
index 0000000..769dec3
--- /dev/null
+++ b/chrome/installer/util/logging_installer.h
@@ -0,0 +1,52 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_INSTALLER_LOGGING_INSTALLER_H__
+#define CHROME_INSTALLER_LOGGING_INSTALLER_H__
+
+#include <string>
+
+#include "base/logging.h"
+
+class CommandLine;
+
+namespace installer {
+
+// Call to initialize logging for Chrome installer.
+void InitInstallerLogging(const CommandLine& command_line);
+
+// Call when done using logging for Chrome installer.
+void EndInstallerLogging();
+
+// Returns the full path of the log file.
+std::wstring GetLogFilePath(const CommandLine& command_line);
+
+} // namespace installer
+
+#endif
diff --git a/chrome/installer/util/lzma_util.cc b/chrome/installer/util/lzma_util.cc
new file mode 100644
index 0000000..45c30c4
--- /dev/null
+++ b/chrome/installer/util/lzma_util.cc
@@ -0,0 +1,210 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "lzma_util.h"
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+
+extern "C" {
+#include "Archive/7z/7zExtract.h"
+#include "Archive/7z/7zIn.h"
+#include "7zCrc.h"
+}
+
+
+namespace installer {
+
+typedef struct _CFileInStream {
+ ISzInStream InStream;
+ HANDLE File;
+} CFileInStream;
+
+
+size_t LzmaReadFile(HANDLE file, void *data, size_t size) {
+ if (size == 0)
+ return 0;
+
+ size_t processedSize = 0;
+ do {
+ DWORD processedLoc = 0;
+ BOOL res = ReadFile(file, data, (DWORD) size, &processedLoc, NULL);
+ data = (void *)((unsigned char *) data + processedLoc);
+ size -= processedLoc;
+ processedSize += processedLoc;
+ if (!res || processedLoc == 0)
+ break;
+ } while (size > 0);
+
+ return processedSize;
+}
+
+SZ_RESULT SzFileSeekImp(void *object, CFileSize pos) {
+ CFileInStream *s = (CFileInStream *) object;
+ LARGE_INTEGER value;
+ value.LowPart = (DWORD) pos;
+ value.HighPart = (LONG) ((UInt64) pos >> 32);
+ value.LowPart = SetFilePointer(s->File, value.LowPart, &value.HighPart,
+ FILE_BEGIN);
+ if (value.LowPart == 0xFFFFFFFF) {
+ if(GetLastError() != NO_ERROR) {
+ return SZE_FAIL;
+ }
+ }
+ return SZ_OK;
+}
+
+SZ_RESULT SzFileReadImp(void *object, void **buffer,
+ size_t maxRequiredSize, size_t *processedSize) {
+ const int kBufferSize = 1 << 12;
+ static Byte g_Buffer[kBufferSize];
+ if (maxRequiredSize > kBufferSize) {
+ maxRequiredSize = kBufferSize;
+ }
+
+ CFileInStream *s = (CFileInStream *) object;
+ size_t processedSizeLoc;
+ processedSizeLoc = LzmaReadFile(s->File, g_Buffer, maxRequiredSize);
+ *buffer = g_Buffer;
+ if (processedSize != 0) {
+ *processedSize = processedSizeLoc;
+ }
+ return SZ_OK;
+}
+
+
+LzmaUtil::LzmaUtil() : archive_handle_(NULL) {}
+
+LzmaUtil::~LzmaUtil() {
+ if (archive_handle_) CloseArchive();
+}
+
+DWORD LzmaUtil::OpenArchive(const std::wstring& archivePath) {
+ DWORD ret = NO_ERROR;
+ archive_handle_ = CreateFile(archivePath.c_str(), GENERIC_READ,
+ FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (archive_handle_ == INVALID_HANDLE_VALUE) {
+ ret = GetLastError();
+ }
+ return ret;
+}
+
+// Unpacks the archive to the given location.
+DWORD LzmaUtil::UnPack(const std::wstring& location) {
+ CFileInStream archiveStream;
+ ISzAlloc allocImp;
+ ISzAlloc allocTempImp;
+ CArchiveDatabaseEx db;
+ DWORD ret = NO_ERROR;
+
+ archiveStream.File = archive_handle_;
+ archiveStream.InStream.Read = SzFileReadImp;
+ archiveStream.InStream.Seek = SzFileSeekImp;
+ allocImp.Alloc = SzAlloc;
+ allocImp.Free = SzFree;
+ allocTempImp.Alloc = SzAllocTemp;
+ allocTempImp.Free = SzFreeTemp;
+
+ CrcGenerateTable();
+ SzArDbExInit(&db);
+ if ((ret = SzArchiveOpen(&archiveStream.InStream, &db,
+ &allocImp, &allocTempImp)) != SZ_OK) {
+ return ret;
+ }
+
+ Byte *outBuffer = 0; // it must be 0 before first call for each new archive
+ UInt32 blockIndex = 0xFFFFFFFF; // can have any value if outBuffer = 0
+ size_t outBufferSize = 0; // can have any value if outBuffer = 0
+ for (unsigned int i = 0; i < db.Database.NumFiles; i++) {
+ DWORD written;
+ size_t offset;
+ size_t outSizeProcessed;
+ CFileItem *f = db.Database.Files + i;
+
+ if ((ret = SzExtract(&archiveStream.InStream, &db, i, &blockIndex,
+ &outBuffer, &outBufferSize, &offset, &outSizeProcessed,
+ &allocImp, &allocTempImp)) != SZ_OK) {
+ break;
+ }
+
+ // Append location to the file path in archive, to get full path.
+ std::wstring wfileName(location);
+ file_util::AppendToPath(&wfileName, UTF8ToWide(f->Name));
+
+ // If archive entry is directory create it and move on to the next entry.
+ if (f->IsDirectory) {
+ file_util::CreateDirectory(wfileName);
+ continue;
+ }
+
+ HANDLE hFile;
+ std::wstring directory = file_util::GetDirectoryFromPath(wfileName);
+ file_util::CreateDirectory(directory);
+
+ hFile = CreateFile(wfileName.c_str(), GENERIC_WRITE, 0, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ ret = GetLastError();
+ break;
+ }
+
+ WriteFile(hFile, outBuffer + offset, (DWORD) outSizeProcessed,
+ &written, NULL);
+ if (written != outSizeProcessed) {
+ ret = GetLastError();
+ CloseHandle(hFile);
+ break;
+ }
+
+ if (f->IsLastWriteTimeDefined) {
+ if (!SetFileTime(hFile, NULL, NULL,
+ (const FILETIME *)&(f->LastWriteTime))) {
+ ret = GetLastError();
+ CloseHandle(hFile);
+ break;
+ }
+ }
+ if (!CloseHandle(hFile)) {
+ ret = GetLastError();
+ break;
+ }
+ } // for loop
+
+ allocImp.Free(outBuffer);
+ SzArDbExFree(&db, allocImp.Free);
+ return ret;
+}
+
+void LzmaUtil::CloseArchive() {
+ CloseHandle(archive_handle_);
+ archive_handle_ = NULL;
+}
+
+} // namespace installer
diff --git a/chrome/installer/util/lzma_util.h b/chrome/installer/util/lzma_util.h
new file mode 100644
index 0000000..67b98d2
--- /dev/null
+++ b/chrome/installer/util/lzma_util.h
@@ -0,0 +1,62 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_INSTALLER_UTIL_LZMA_UTIL_H__
+#define CHROME_INSTALLER_UTIL_LZMA_UTIL_H__
+
+#include <string>
+#include <windows.h>
+
+#include "base/basictypes.h"
+
+namespace installer {
+
+// This is a utility class that acts as a wrapper around LZMA SDK library
+class LzmaUtil {
+ public:
+ LzmaUtil();
+ ~LzmaUtil();
+
+ DWORD OpenArchive(const std::wstring& archivePath);
+
+ // Unpacks the archive to the given location
+ DWORD UnPack(const std::wstring& location);
+
+ void CloseArchive();
+
+ private:
+ HANDLE archive_handle_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(LzmaUtil);
+};
+
+} // namespace installer
+
+
+#endif // CHROME_INSTALLER_UTIL_LZMA_UTIL_H__
diff --git a/chrome/installer/util/prebuild/create_string_rc.bat b/chrome/installer/util/prebuild/create_string_rc.bat
new file mode 100644
index 0000000..51d1c39
--- /dev/null
+++ b/chrome/installer/util/prebuild/create_string_rc.bat
@@ -0,0 +1,10 @@
+:: A wrapper file for running create_string_rc.py from visual studio.
+
+setlocal
+set OUTFILE=%~1
+set PYTHON=%~dp0..\..\..\..\third_party\python_24\python.exe
+
+:: Add grit to the python path so we can import FP.py.
+set PYTHONPATH=%~dp0..\..\..\..\tools\grit\grit\extern
+
+%PYTHON% create_string_rc.py %OUTFILE%
diff --git a/chrome/installer/util/prebuild/create_string_rc.py b/chrome/installer/util/prebuild/create_string_rc.py
new file mode 100644
index 0000000..a617646
--- /dev/null
+++ b/chrome/installer/util/prebuild/create_string_rc.py
@@ -0,0 +1,180 @@
+#!/usr/bin/python
+# Copyright 2008 Google Inc. All rights reserved.
+
+"""This script generates an rc file and header (setup_strings.{rc,h}) to be
+included in setup.exe. The rc file includes translations for strings pulled
+from generated_resource.grd and the localized .xtb files.
+
+The header file includes IDs for each string, but also has values to allow
+getting a string based on a language offset. For example, the header file
+looks like this:
+
+#define IDS_L10N_OFFSET_AR 0
+#define IDS_L10N_OFFSET_BG 1
+#define IDS_L10N_OFFSET_CA 2
+...
+#define IDS_L10N_OFFSET_ZH_TW 41
+
+#define IDS_MY_STRING_AR 1600
+#define IDS_MY_STRING_BG 1601
+...
+#define IDS_MY_STRING_BASE IDS_MY_STRING_AR
+
+This allows us to lookup an an ID for a string by adding IDS_MY_STRING_BASE and
+IDS_L10N_OFFSET_* for the language we are interested in.
+"""
+
+import glob
+import os
+import sys
+from xml.dom import minidom
+
+from google import path_utils
+
+import FP
+
+# The IDs of strings we want to import from generated_resources.grd and include
+# in setup.exe's resources.
+kStringIds = [
+ 'IDS_PRODUCT_NAME',
+ 'IDS_UNINSTALL_CHROME',
+]
+
+# The ID of the first resource string.
+kFirstResourceID = 1600
+
+class TranslationStruct:
+ """A helper struct that holds information about a single translation."""
+ def __init__(self, resource_id_str, language, translation):
+ self.resource_id_str = resource_id_str
+ self.language = language
+ self.translation = translation
+
+ def __cmp__(self, other):
+ """Allow TranslationStructs to be sorted by id."""
+ return cmp(self.resource_id_str, other.resource_id_str)
+
+
+def CollectTranslatedStrings():
+ """Collects all the translations for "Google Chrome" from the XTB files.
+ Returns a list of tuples of (language, translated string). The list is
+ sorted by language codes."""
+ kGeneratedResourcesPath = os.path.join(path_utils.ScriptDir(), '..', '..',
+ '..', 'app/generated_resources.grd')
+ kTranslationDirectory = os.path.join(path_utils.ScriptDir(), '..', '..',
+ '..', 'app', 'resources')
+ kTranslationFiles = glob.glob(os.path.join(kTranslationDirectory, '*.xtb'))
+
+ # Get the strings out of generated_resources.grd.
+ dom = minidom.parse(kGeneratedResourcesPath)
+ # message_nodes is a list of message dom nodes corresponding to the string
+ # ids we care about. We want to make sure that this list is in the same
+ # order as kStringIds so we can associate them together.
+ message_nodes = []
+ all_message_nodes = dom.getElementsByTagName('message')
+ for string_id in kStringIds:
+ message_nodes.append([x for x in all_message_nodes if
+ x.getAttribute('name') == string_id][0])
+ message_texts = [node.firstChild.nodeValue.strip() for node in message_nodes]
+
+ # The fingerprint of the string is the message ID in the translation files
+ # (xtb files).
+ translation_ids = [str(FP.FingerPrint(text)) for text in message_texts]
+
+ # Manually put _EN_US in the list of translated strings because it doesn't
+ # have a .xtb file.
+ translated_strings = []
+ for string_id, message_text in zip(kStringIds, message_texts):
+ translated_strings.append(TranslationStruct(string_id + '_EN_US',
+ 'EN_US',
+ message_text))
+
+ # Gather the translated strings from the .xtb files. If an .xtb file doesn't
+ # have the string we want, use the en-US string.
+ for xtb_filename in kTranslationFiles:
+ dom = minidom.parse(xtb_filename)
+ language = dom.documentElement.getAttribute('lang')
+ language = language.replace('-', '_').upper()
+ translation_nodes = {}
+ for translation_node in dom.getElementsByTagName('translation'):
+ translation_id = translation_node.getAttribute('id')
+ if translation_id in translation_ids:
+ translation_nodes[translation_id] = (translation_node.firstChild
+ .nodeValue
+ .strip())
+ for i, string_id in enumerate(kStringIds):
+ translated_string = translation_nodes.get(translation_ids[i],
+ message_texts[i])
+ translated_strings.append(TranslationStruct(string_id + '_' + language,
+ language,
+ translated_string))
+
+ translated_strings.sort()
+ return translated_strings
+
+def WriteRCFile(translated_strings, out_filename):
+ """Writes a resource (rc) file with all the language strings provided in
+ |translated_strings|."""
+ kHeaderText = (
+ u'#include "setup_strings.h"\n\n'
+ u'STRINGTABLE\n'
+ u'BEGIN\n'
+ )
+ kFooterText = (
+ u'END\n'
+ )
+ lines = [kHeaderText]
+ for translation_struct in translated_strings:
+ lines.append(u' %s "%s"\n' % (translation_struct.resource_id_str,
+ translation_struct.translation))
+ lines.append(kFooterText)
+ outfile = open(out_filename, 'wb')
+ outfile.write(''.join(lines).encode('utf-16'))
+ outfile.close()
+
+def WriteHeaderFile(translated_strings, out_filename):
+ """Writes a .h file with resource ids. This file can be included by the
+ executable to refer to identifiers."""
+ lines = []
+
+ # Write the values for how the languages ids are offset.
+ seen_languages = set()
+ offset_id = 0
+ for translation_struct in translated_strings:
+ lang = translation_struct.language
+ if lang not in seen_languages:
+ seen_languages.add(lang)
+ lines.append(u'#define IDS_L10N_OFFSET_%s %s' % (lang, offset_id))
+ offset_id += 1
+ else:
+ break
+
+ # Write the resource ids themselves.
+ resource_id = kFirstResourceID
+ for translation_struct in translated_strings:
+ lines.append(u'#define %s %s' % (translation_struct.resource_id_str,
+ resource_id))
+ resource_id += 1
+
+ # Write out base ID values.
+ for string_id in kStringIds:
+ lines.append(u'#define %s_BASE %s_%s' % (string_id,
+ string_id,
+ translated_strings[0].language))
+
+ outfile = open(out_filename, 'wb')
+ outfile.write('\n'.join(lines))
+ outfile.write('\n') # .rc files must end in a new line
+ outfile.close()
+
+def main(argv):
+ translated_strings = CollectTranslatedStrings()
+ kFilebase = os.path.join(argv[1], 'setup_strings')
+ WriteRCFile(translated_strings, kFilebase + '.rc')
+ WriteHeaderFile(translated_strings, kFilebase + '.h')
+
+if '__main__' == __name__:
+ if len(sys.argv) < 2:
+ print 'Usage:\n %s <output_directory>' % sys.argv[0]
+ sys.exit(1)
+ main(sys.argv)
diff --git a/chrome/installer/util/prebuild/util_prebuild.vcproj b/chrome/installer/util/prebuild/util_prebuild.vcproj
new file mode 100644
index 0000000..1269dde
--- /dev/null
+++ b/chrome/installer/util/prebuild/util_prebuild.vcproj
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="util_prebuild"
+ ProjectGUID="{0026A376-C4F1-4575-A1BA-578C69F07013}"
+ RootNamespace="util_prebuild"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="create_string_rc.bat $(IntDir)"
+ AdditionalDependencies="create_string_rc.py;$(SolutionDir)\app\generated_resources.grd"
+ Outputs="$(IntDir)\setup_strings.rc;$(IntDir)\setup_strings.h"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ CommandLine="create_string_rc.bat $(IntDir)"
+ AdditionalDependencies="create_string_rc.py;$(SolutionDir)\app\generated_resources.grd"
+ Outputs="$(IntDir)\setup_strings.rc;$(IntDir)\setup_strings.h"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/chrome/installer/util/prebuild/util_prebuild.vsprops b/chrome/installer/util/prebuild/util_prebuild.vsprops
new file mode 100644
index 0000000..2fa7bc5
--- /dev/null
+++ b/chrome/installer/util/prebuild/util_prebuild.vsprops
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="util_prebuild"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="$(IntDir)\..\util_prebuild\"
+ />
+</VisualStudioPropertySheet>
diff --git a/chrome/installer/util/run_all_unittests.cc b/chrome/installer/util/run_all_unittests.cc
new file mode 100644
index 0000000..69b2d3f
--- /dev/null
+++ b/chrome/installer/util/run_all_unittests.cc
@@ -0,0 +1,34 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/test_suite.h"
+
+int main(int argc, char** argv) {
+ return TestSuite(argc, argv).Run();
+}
diff --git a/chrome/installer/util/set_reg_value_work_item.cc b/chrome/installer/util/set_reg_value_work_item.cc
new file mode 100644
index 0000000..8f8d4b4
--- /dev/null
+++ b/chrome/installer/util/set_reg_value_work_item.cc
@@ -0,0 +1,176 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/registry.h"
+#include "chrome/installer/util/set_reg_value_work_item.h"
+#include "chrome/installer/util/logging_installer.h"
+
+SetRegValueWorkItem::~SetRegValueWorkItem() {
+}
+
+SetRegValueWorkItem::SetRegValueWorkItem(HKEY predefined_root,
+ std::wstring key_path,
+ std::wstring value_name,
+ std::wstring value_data,
+ bool overwrite)
+ : predefined_root_(predefined_root),
+ key_path_(key_path),
+ value_name_(value_name),
+ value_data_str_(value_data),
+ overwrite_(overwrite),
+ status_(SET_VALUE),
+ is_str_type_(true) {
+}
+
+SetRegValueWorkItem::SetRegValueWorkItem(HKEY predefined_root,
+ std::wstring key_path,
+ std::wstring value_name,
+ DWORD value_data,
+ bool overwrite)
+ : predefined_root_(predefined_root),
+ key_path_(key_path),
+ value_name_(value_name),
+ value_data_dword_(value_data),
+ overwrite_(overwrite),
+ status_(SET_VALUE),
+ is_str_type_(false) {
+}
+
+bool SetRegValueWorkItem::Do() {
+ if (status_ != SET_VALUE) {
+ // we already did something.
+ LOG(ERROR) << "multiple calls to Do()";
+ return false;
+ }
+
+ RegKey key;
+ if (!key.Open(predefined_root_, key_path_.c_str(),
+ KEY_READ | KEY_SET_VALUE)) {
+ LOG(ERROR) << "can not open " << key_path_;
+ status_ = VALUE_UNCHANGED;
+ return false;
+ }
+
+ bool result = false;
+ if (key.ValueExists(value_name_.c_str())) {
+ if (overwrite_) {
+ bool success = true;
+ // Read previous value for rollback and write new value
+ if (is_str_type_) {
+ std::wstring data;
+ if (key.ReadValue(value_name_.c_str(), &data)) {
+ previous_value_str_.assign(data);
+ }
+ success = key.WriteValue(value_name_.c_str(), value_data_str_.c_str());
+ } else {
+ DWORD data;
+ if (key.ReadValueDW(value_name_.c_str(), &data)) {
+ previous_value_dword_ = data;
+ }
+ success = key.WriteValue(value_name_.c_str(), value_data_dword_);
+ }
+ if (success) {
+ LOG(INFO) << "overwritten value for " << value_name_;
+ status_ = VALUE_OVERWRITTEN;
+ result = true;
+ } else {
+ LOG(ERROR) << "failed to overwrite value for " << value_name_;
+ status_ = VALUE_UNCHANGED;
+ result = false;
+ }
+ } else {
+ LOG(INFO) << value_name_ << " exists. not changed ";
+ status_ = VALUE_UNCHANGED;
+ result = true;
+ }
+ } else {
+ bool success = true;
+ if (is_str_type_) {
+ success = key.WriteValue(value_name_.c_str(), value_data_str_.c_str());
+ } else {
+ success = key.WriteValue(value_name_.c_str(), value_data_dword_);
+ }
+ if (success) {
+ LOG(INFO) << "created value for " << value_name_;
+ status_ = NEW_VALUE_CREATED;
+ result = true;
+ } else {
+ LOG(ERROR) << "failed to create value for " << value_name_;
+ status_ = VALUE_UNCHANGED;
+ result = false;
+ }
+ }
+
+ key.Close();
+ return result;
+}
+
+void SetRegValueWorkItem::Rollback() {
+ if (status_ == SET_VALUE || status_ == VALUE_ROLL_BACK)
+ return;
+
+ if (status_ == VALUE_UNCHANGED) {
+ status_ = VALUE_ROLL_BACK;
+ LOG(INFO) << "rollback: setting unchanged, nothing to do";
+ return;
+ }
+
+ RegKey key;
+ if (!key.Open(predefined_root_, key_path_.c_str(),
+ KEY_READ | KEY_SET_VALUE)) {
+ status_ = VALUE_ROLL_BACK;
+ LOG(INFO) << "rollback: can not open " << key_path_;
+ return;
+ }
+
+ std::wstring result_str(L" failed");
+ if (status_ == NEW_VALUE_CREATED) {
+ if (key.DeleteValue(value_name_.c_str()))
+ result_str.assign(L" succeeded");
+ LOG(INFO) << "rollback: deleting " << value_name_ << result_str;
+ } else if (status_ == VALUE_OVERWRITTEN) {
+ // try restore the previous value
+ bool success = true;
+ if (is_str_type_) {
+ success = key.WriteValue(value_name_.c_str(),
+ previous_value_str_.c_str());
+ } else {
+ success = key.WriteValue(value_name_.c_str(), previous_value_dword_);
+ }
+ if (success)
+ result_str.assign(L" succeeded");
+ LOG(INFO) << "rollback: restoring " << value_name_ << result_str;
+ } else {
+ // Not reached.
+ }
+
+ status_ = VALUE_ROLL_BACK;
+ key.Close();
+ return;
+}
diff --git a/chrome/installer/util/set_reg_value_work_item.h b/chrome/installer/util/set_reg_value_work_item.h
new file mode 100644
index 0000000..2b12509
--- /dev/null
+++ b/chrome/installer/util/set_reg_value_work_item.h
@@ -0,0 +1,101 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_INSTALLER_UTIL_SET_REG_VALUE_WORK_ITEM_H__
+#define CHROME_INSTALLER_UTIL_SET_REG_VALUE_WORK_ITEM_H__
+
+#include <string>
+#include <windows.h>
+#include "chrome/installer/util/work_item.h"
+
+// A WorkItem subclass that sets a registry value with REG_SZ or REG_DWORD
+// type at the specified path. The value is only set if the target key exists.
+class SetRegValueWorkItem : public WorkItem {
+ public:
+ virtual ~SetRegValueWorkItem();
+
+ virtual bool Do();
+
+ virtual void Rollback();
+
+ private:
+ friend class WorkItem;
+
+ enum SettingStatus {
+ // The status before Do is called.
+ SET_VALUE,
+ // One possible outcome after Do(). A new value is created under the key.
+ NEW_VALUE_CREATED,
+ // One possible outcome after Do(). The previous value under the key has
+ // been overwritten.
+ VALUE_OVERWRITTEN,
+ // One possible outcome after Do(). No change is applied, either
+ // because we are not allowed to overwrite the previous value, or due to
+ // some errors like the key does not exist.
+ VALUE_UNCHANGED,
+ // The status after Do and Rollback is called.
+ VALUE_ROLL_BACK
+ };
+
+ SetRegValueWorkItem(HKEY predefined_root, std::wstring key_path,
+ std::wstring value_name, std::wstring value_data,
+ bool overwrite);
+
+ SetRegValueWorkItem(HKEY predefined_root, std::wstring key_path,
+ std::wstring value_name, DWORD value_data,
+ bool overwrite);
+
+ // Root key of the target key under which the value is set. The root key can
+ // only be one of the predefined keys on Windows.
+ HKEY predefined_root_;
+
+ // Path of the target key under which the value is set.
+ std::wstring key_path_;
+
+ // Name of the value to be set.
+ std::wstring value_name_;
+
+ // Data of the value to be set.
+ std::wstring value_data_str_; // if data is of type REG_SZ
+ DWORD value_data_dword_; // if data is of type REG_DWORD
+
+ // Whether to overwrite the existing value under the target key.
+ bool overwrite_;
+
+ // boolean that tells whether data value is of type REG_SZ.
+ bool is_str_type_;
+
+ SettingStatus status_;
+
+ // Data of the previous value.
+ std::wstring previous_value_str_; // if data is of type REG_SZ
+ DWORD previous_value_dword_; // if data is of type REG_DWORD
+};
+
+#endif // CHROME_INSTALLER_UTIL_SET_REG_VALUE_WORK_ITEM_H__
diff --git a/chrome/installer/util/set_reg_value_work_item_unittest.cc b/chrome/installer/util/set_reg_value_work_item_unittest.cc
new file mode 100644
index 0000000..67e68e8
--- /dev/null
+++ b/chrome/installer/util/set_reg_value_work_item_unittest.cc
@@ -0,0 +1,263 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/registry.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "chrome/installer/util/set_reg_value_work_item.h"
+#include "chrome/installer/util/work_item.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+ wchar_t test_root[] = L"TempTemp";
+ wchar_t data_str_1[] = L"data_111";
+ wchar_t data_str_2[] = L"data_222";
+ DWORD dword1 = 0;
+ DWORD dword2 = 1;
+ class SetRegValueWorkItemTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Create a temporary key for testing
+ RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS);
+ key.DeleteKey(test_root);
+ ASSERT_FALSE(key.Open(HKEY_CURRENT_USER, test_root, KEY_READ));
+ ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, test_root, KEY_READ));
+
+ // Create a log file
+ std::wstring log_file;
+ ASSERT_TRUE(file_util::CreateTemporaryFileName(&log_file));
+ ASSERT_TRUE(file_util::PathExists(log_file));
+
+ logging::InitLogging(log_file.c_str(),
+ logging::LOG_ONLY_TO_FILE,
+ logging::LOCK_LOG_FILE,
+ logging::DELETE_OLD_LOG_FILE);
+ logging::SetMinLogLevel(logging::LOG_INFO);
+ }
+ virtual void TearDown() {
+ logging::CloseLogFile();
+ // Clean up the temporary key
+ RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS);
+ ASSERT_TRUE(key.DeleteKey(test_root));
+ }
+ };
+};
+
+// Write a new value without overwrite flag. The value should be set.
+TEST_F(SetRegValueWorkItemTest, WriteNewNonOverwrite) {
+ RegKey key;
+
+ std::wstring parent_key(test_root);
+ file_util::AppendToPath(&parent_key, L"WriteNewNonOverwrite");
+ ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, parent_key.c_str(), KEY_READ));
+
+ std::wstring name_str(L"name_str");
+ std::wstring data_str(data_str_1);
+ scoped_ptr<SetRegValueWorkItem> work_item1(
+ WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, parent_key,
+ name_str, data_str, false));
+
+ std::wstring name_dword(L"name_dword");
+ scoped_ptr<SetRegValueWorkItem> work_item2(
+ WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, parent_key,
+ name_dword, dword1, false));
+
+ EXPECT_TRUE(work_item1->Do());
+ EXPECT_TRUE(work_item2->Do());
+
+ std::wstring read_out;
+ DWORD read_dword;
+ EXPECT_TRUE(key.ReadValue(name_str.c_str(), &read_out));
+ EXPECT_TRUE(key.ReadValueDW(name_dword.c_str(), &read_dword));
+ EXPECT_EQ(read_out, data_str_1);
+ EXPECT_EQ(read_dword, dword1);
+
+ work_item1->Rollback();
+ work_item2->Rollback();
+
+ // Rollback should delete the value.
+ EXPECT_FALSE(key.ValueExists(name_str.c_str()));
+ EXPECT_FALSE(key.ValueExists(name_dword.c_str()));
+}
+
+// Write a new value with overwrite flag. The value should be set.
+TEST_F(SetRegValueWorkItemTest, WriteNewOverwrite) {
+ RegKey key;
+
+ std::wstring parent_key(test_root);
+ file_util::AppendToPath(&parent_key, L"WriteNewOverwrite");
+ ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, parent_key.c_str(), KEY_READ));
+
+ std::wstring name_str(L"name_str");
+ std::wstring data_str(data_str_1);
+ scoped_ptr<SetRegValueWorkItem> work_item1(
+ WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, parent_key,
+ name_str, data_str, true));
+
+ std::wstring name_dword(L"name_dword");
+ scoped_ptr<SetRegValueWorkItem> work_item2(
+ WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, parent_key,
+ name_dword, dword1, true));
+
+ EXPECT_TRUE(work_item1->Do());
+ EXPECT_TRUE(work_item2->Do());
+
+ std::wstring read_out;
+ DWORD read_dword;
+ EXPECT_TRUE(key.ReadValue(name_str.c_str(), &read_out));
+ EXPECT_TRUE(key.ReadValueDW(name_dword.c_str(), &read_dword));
+ EXPECT_EQ(read_out, data_str_1);
+ EXPECT_EQ(read_dword, dword1);
+
+ work_item1->Rollback();
+ work_item2->Rollback();
+
+ // Rollback should delete the value.
+ EXPECT_FALSE(key.ValueExists(name_str.c_str()));
+ EXPECT_FALSE(key.ValueExists(name_dword.c_str()));
+}
+
+// Write to an existing value without overwrite flag. There should be
+// no change.
+TEST_F(SetRegValueWorkItemTest, WriteExistingNonOverwrite) {
+ RegKey key;
+
+ std::wstring parent_key(test_root);
+ file_util::AppendToPath(&parent_key, L"WriteExistingNonOverwrite");
+ ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, parent_key.c_str(),
+ KEY_READ | KEY_SET_VALUE));
+
+ // First test REG_SZ value.
+ // Write data to the value we are going to set.
+ std::wstring name(L"name_str");
+ ASSERT_TRUE(key.WriteValue(name.c_str(), data_str_1));
+
+ std::wstring data(data_str_2);
+ scoped_ptr<SetRegValueWorkItem> work_item(
+ WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, parent_key,
+ name, data, false));
+ EXPECT_TRUE(work_item->Do());
+
+ std::wstring read_out;
+ EXPECT_TRUE(key.ReadValue(name.c_str(), &read_out));
+ EXPECT_EQ(0, read_out.compare(data_str_1));
+
+ work_item->Rollback();
+ EXPECT_TRUE(key.ValueExists(name.c_str()));
+ EXPECT_TRUE(key.ReadValue(name.c_str(), &read_out));
+ EXPECT_EQ(read_out, data_str_1);
+
+ // Now test REG_DWORD value.
+ // Write data to the value we are going to set.
+ name.assign(L"name_dword");
+ ASSERT_TRUE(key.WriteValue(name.c_str(), dword1));
+ work_item.reset(WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER,
+ parent_key, name, dword2, false));
+ EXPECT_TRUE(work_item->Do());
+
+ DWORD read_dword;
+ EXPECT_TRUE(key.ReadValueDW(name.c_str(), &read_dword));
+ EXPECT_EQ(read_dword, dword1);
+
+ work_item->Rollback();
+ EXPECT_TRUE(key.ValueExists(name.c_str()));
+ EXPECT_TRUE(key.ReadValueDW(name.c_str(), &read_dword));
+ EXPECT_EQ(read_dword, dword1);
+}
+
+// Write to an existing value with overwrite flag. The value should be
+// overwritten.
+TEST_F(SetRegValueWorkItemTest, WriteExistingOverwrite) {
+ RegKey key;
+
+ std::wstring parent_key(test_root);
+ file_util::AppendToPath(&parent_key, L"WriteExistingOverwrite");
+ ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, parent_key.c_str(),
+ KEY_READ | KEY_SET_VALUE));
+
+ // First test REG_SZ value.
+ // Write data to the value we are going to set.
+ std::wstring name(L"name_str");
+ ASSERT_TRUE(key.WriteValue(name.c_str(), data_str_1));
+
+ std::wstring data(data_str_2);
+ scoped_ptr<SetRegValueWorkItem> work_item(
+ WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, parent_key,
+ name, data, true));
+ EXPECT_TRUE(work_item->Do());
+
+ std::wstring read_out;
+ EXPECT_TRUE(key.ReadValue(name.c_str(), &read_out));
+ EXPECT_EQ(0, read_out.compare(data_str_2));
+
+ work_item->Rollback();
+ EXPECT_TRUE(key.ValueExists(name.c_str()));
+ EXPECT_TRUE(key.ReadValue(name.c_str(), &read_out));
+ EXPECT_EQ(read_out, data_str_1);
+
+ // Now test REG_DWORD value.
+ // Write data to the value we are going to set.
+ name.assign(L"name_dword");
+ ASSERT_TRUE(key.WriteValue(name.c_str(), dword1));
+ work_item.reset(WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER,
+ parent_key, name, dword2, true));
+ EXPECT_TRUE(work_item->Do());
+
+ DWORD read_dword;
+ EXPECT_TRUE(key.ReadValueDW(name.c_str(), &read_dword));
+ EXPECT_EQ(read_dword, dword2);
+
+ work_item->Rollback();
+ EXPECT_TRUE(key.ValueExists(name.c_str()));
+ EXPECT_TRUE(key.ReadValueDW(name.c_str(), &read_dword));
+ EXPECT_EQ(read_dword, dword1);
+}
+
+// Write a value to a non-existing key. This should fail.
+TEST_F(SetRegValueWorkItemTest, WriteNonExistingKey) {
+ RegKey key;
+
+ std::wstring parent_key(test_root);
+ file_util::AppendToPath(&parent_key, L"WriteNonExistingKey");
+
+ std::wstring name(L"name");
+ std::wstring data(data_str_1);
+ scoped_ptr<SetRegValueWorkItem> work_item(
+ WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, parent_key,
+ name, data, false));
+ EXPECT_FALSE(work_item->Do());
+
+ work_item.reset(WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER,
+ parent_key, name, dword1, false));
+ EXPECT_FALSE(work_item->Do());
+}
diff --git a/chrome/installer/util/shell_util.cc b/chrome/installer/util/shell_util.cc
new file mode 100644
index 0000000..c8ebfc8
--- /dev/null
+++ b/chrome/installer/util/shell_util.cc
@@ -0,0 +1,376 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file defines functions that integrate Chrome in Windows shell. These
+// functions can be used by Chrome as well as Chrome installer. All of the
+// work is done by the local functions defined in anonymous namespace in
+// this class.
+
+#include <windows.h>
+#include <shellapi.h>
+#include <shlobj.h>
+
+#include "shell_util.h"
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/registry.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/win_util.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/installer/util/create_reg_key_work_item.h"
+#include "chrome/installer/util/l10n_string_util.h"
+#include "chrome/installer/util/set_reg_value_work_item.h"
+#include "chrome/installer/util/util_constants.h"
+#include "chrome/installer/util/work_item.h"
+
+#include "setup_strings.h"
+
+namespace {
+
+// This class represents a single registry entry. The objective is to
+// encapsulate all the registry entries required for registering Chrome at one
+// place. This class can not be instantiated outside the class and the objects
+// of this class type can be obtained only by calling a static method of this
+// class.
+class RegistryEntry {
+ public:
+ // This method returns a list of all the registry entries that are needed
+ // to register Chrome.
+ static std::list<RegistryEntry*> GetAllEntries(const std::wstring& chrome_exe) {
+ std::list<RegistryEntry*> entries;
+ std::wstring icon_path(chrome_exe);
+ ShellUtil::GetChromeIcon(icon_path);
+ std::wstring quoted_exe_path = L"\"" + chrome_exe + L"\"";
+ std::wstring open_cmd = quoted_exe_path + L" \"%1\"";
+
+ entries.push_front(new RegistryEntry(L"Software\\Classes\\ChromeHTML",
+ L"Chrome HTML"));
+ entries.push_front(new RegistryEntry(
+ L"Software\\Classes\\ChromeHTML\\DefaultIcon", icon_path));
+ entries.push_front(new RegistryEntry(
+ L"Software\\Classes\\ChromeHTML\\shell\\open\\command", open_cmd));
+
+ entries.push_front(new RegistryEntry(
+ L"Software\\Clients\\StartMenuInternet\\chrome.exe",
+ installer_util::kApplicationName));
+ entries.push_front(new RegistryEntry(
+ L"Software\\Clients\\StartMenuInternet\\chrome.exe\\shell\\open\\command",
+ quoted_exe_path));
+ entries.push_front(new RegistryEntry(
+ L"Software\\Clients\\StartMenuInternet\\chrome.exe\\DefaultIcon",
+ icon_path));
+ entries.push_front(new RegistryEntry(
+ L"Software\\Clients\\StartMenuInternet\\chrome.exe\\InstallInfo",
+ L"ReinstallCommand",
+ quoted_exe_path + L" --" + switches::kMakeDefaultBrowser));
+ entries.push_front(new RegistryEntry(
+ L"Software\\Clients\\StartMenuInternet\\chrome.exe\\InstallInfo",
+ L"HideIconsCommand",
+ quoted_exe_path + L" --" + switches::kHideIcons));
+ entries.push_front(new RegistryEntry(
+ L"Software\\Clients\\StartMenuInternet\\chrome.exe\\InstallInfo",
+ L"ShowIconsCommand",
+ quoted_exe_path + L" --" + switches::kShowIcons));
+ entries.push_front(new RegistryEntry(
+ L"Software\\Clients\\StartMenuInternet\\chrome.exe\\InstallInfo",
+ L"IconsVisible", 1));
+
+ entries.push_front(new RegistryEntry(
+ ShellUtil::kRegRegisteredApplications,
+ installer_util::kApplicationName,
+ L"Software\\Clients\\StartMenuInternet\\chrome.exe\\Capabilities"));
+ entries.push_front(new RegistryEntry(
+ L"Software\\Clients\\StartMenuInternet\\chrome.exe\\Capabilities",
+ L"ApplicationDescription", installer_util::kApplicationName));
+ entries.push_front(new RegistryEntry(
+ L"Software\\Clients\\StartMenuInternet\\chrome.exe\\Capabilities",
+ L"ApplicationIcon", icon_path));
+ entries.push_front(new RegistryEntry(
+ L"Software\\Clients\\StartMenuInternet\\chrome.exe\\Capabilities",
+ L"ApplicationName", installer_util::kApplicationName));
+
+ entries.push_front(new RegistryEntry(
+ L"Software\\Clients\\StartMenuInternet\\chrome.exe\\Capabilities\\StartMenu",
+ L"StartMenuInternet", L"chrome.exe"));
+ for (int i = 0; ShellUtil::kFileAssociations[i] != NULL; i++) {
+ entries.push_front(new RegistryEntry(
+ L"Software\\Clients\\StartMenuInternet\\chrome.exe\\Capabilities\\FileAssociations",
+ ShellUtil::kFileAssociations[i], ShellUtil::kChromeHTMLProgId));
+ }
+ for (int i = 0; ShellUtil::kProtocolAssociations[i] != NULL; i++) {
+ entries.push_front(new RegistryEntry(
+ L"Software\\Clients\\StartMenuInternet\\chrome.exe\\Capabilities\\URLAssociations",
+ ShellUtil::kProtocolAssociations[i], ShellUtil::kChromeHTMLProgId));
+ }
+ return entries;
+ }
+
+ // Generate work_item tasks required to create current regitry entry and
+ // add them to the given work item list.
+ void AddToWorkItemList(HKEY root, WorkItemList *items) {
+ items->AddCreateRegKeyWorkItem(root, _key_path);
+ if (_is_string) {
+ items->AddSetRegValueWorkItem(root, _key_path, _name, _value, true);
+ } else {
+ items->AddSetRegValueWorkItem(root, _key_path, _name, _int_value, true);
+ }
+ }
+
+ // Check if the current registry entry exists in HKLM registry.
+ bool ExistsInHKLM() {
+ RegKey key(HKEY_LOCAL_MACHINE, _key_path.c_str());
+ bool found = false;
+ if (_is_string) {
+ std::wstring read_value;
+ found = key.ReadValue(_name.c_str(), &read_value) &&
+ read_value == _value;
+ } else {
+ DWORD read_value;
+ found = key.ReadValueDW(_name.c_str(), &read_value) &&
+ read_value == _int_value;
+ }
+ key.Close();
+ return found;
+ }
+
+ private:
+ // Create a object that represent default value of a key
+ RegistryEntry(const std::wstring& key_path, const std::wstring& value) :
+ _key_path(key_path), _name(L""), _value(value),
+ _is_string(true) {
+ }
+
+ // Create a object that represent a key of type REG_SZ
+ RegistryEntry(const std::wstring& key_path, const std::wstring& name,
+ const std::wstring& value) : _key_path(key_path),
+ _name(name), _value(value), _is_string(true) {
+ }
+
+ // Create a object that represent a key of integer type
+ RegistryEntry(const std::wstring& key_path, const std::wstring& name,
+ DWORD value) : _key_path(key_path),
+ _name(name), _int_value(value), _is_string(false) {
+ }
+
+ bool _is_string; // true if current registry entry is of type REG_SZ
+ std::wstring _key_path; // key path for the registry entry
+ std::wstring _name; // name of the registry entry
+ std::wstring _value; // string value (useful if _is_string = true)
+ DWORD _int_value; // integer value (useful if _is_string = false)
+}; // class RegistryEntry
+
+
+// This method checks if Chrome is already registered on the local machine.
+// It gets all the required registry entries for Chrome and then checks if
+// they exist in HKLM. Returns true if all the entries exist, otherwise false.
+bool IsChromeRegistered(const std::wstring& chrome_exe) {
+ bool registered = true;
+ std::list<RegistryEntry*> entries = RegistryEntry::GetAllEntries(chrome_exe);
+ for (std::list<RegistryEntry*>::iterator itr = entries.begin();
+ itr != entries.end(); ++itr) {
+ if (registered && !(*itr)->ExistsInHKLM())
+ registered = false;
+ delete (*itr);
+ }
+ LOG(INFO) << "Check for Chrome registeration returned " << registered;
+ return registered;
+}
+
+
+// This method creates the registry entries required for Add/Remove Programs->
+// Set Program Access and Defaults, Start->Default Programs on Windows Vista
+// and Chrome ProgIds for file extension and protocol handler. root_key is
+// the root registry (HKLM or HKCU).
+bool SetAccessDefaultRegEntries(HKEY root_key,
+ const std::wstring& chrome_exe) {
+ LOG(INFO) << "Registering Chrome browser " << chrome_exe;
+ // Create a list of registry entries work items so that we can rollback
+ // in case of problem.
+ scoped_ptr<WorkItemList> items(WorkItem::CreateWorkItemList());
+
+ std::list<RegistryEntry*> entries = RegistryEntry::GetAllEntries(chrome_exe);
+ for (std::list<RegistryEntry*>::iterator itr = entries.begin();
+ itr != entries.end(); ++itr) {
+ (*itr)->AddToWorkItemList(root_key, items.get());
+ delete (*itr);
+ }
+
+ // Apply all the registry changes and if there is a problem, rollback.
+ if (!items->Do()) {
+ LOG(ERROR) << "Failed to add Chrome to Set Program Access and Defaults";
+ items->Rollback();
+ return false;
+ }
+
+ return true;
+}
+
+
+// This method registers Chrome on Vista. It checks if we are currently
+// running as admin and if not, it launches setup.exe as administrator which
+// will show user standard Vista elevation prompt. If user accepts it
+// the new process will make the necessary changes and return SUCCESS that
+// we capture and return.
+ShellUtil::RegisterStatus RegisterOnVista(const std::wstring& chrome_exe,
+ bool skip_if_not_admin) {
+ if (IsUserAnAdmin() &&
+ SetAccessDefaultRegEntries(HKEY_LOCAL_MACHINE, chrome_exe))
+ return ShellUtil::SUCCESS;
+
+ if (!skip_if_not_admin) {
+ std::wstring exe_path(file_util::GetDirectoryFromPath(chrome_exe));
+ file_util::AppendToPath(&exe_path, installer_util::kSetupExe);
+ if (!file_util::PathExists(exe_path)) {
+ RegKey key(HKEY_CURRENT_USER, installer_util::kUninstallRegPath);
+ key.ReadValue(installer_util::kUninstallStringField, &exe_path);
+ exe_path = exe_path.substr(0, exe_path.find_first_of(L" --"));
+ TrimString(exe_path, L" \"", &exe_path);
+ }
+ if (file_util::PathExists(exe_path)) {
+ SHELLEXECUTEINFO info = {0};
+ info.cbSize = sizeof(SHELLEXECUTEINFO);
+ info.fMask = SEE_MASK_NOCLOSEPROCESS;
+ info.lpVerb = L"runas";
+ info.lpFile = exe_path.c_str();
+ std::wstring params(L"--");
+ params.append(installer_util::switches::kRegisterChromeBrowser);
+ params.append(L"=\"" + chrome_exe + L"\"");
+ info.lpParameters = params.c_str();
+ info.nShow = SW_SHOW;
+ if (::ShellExecuteEx(&info)) {
+ ::WaitForSingleObject(info.hProcess, INFINITE);
+ DWORD ret_val = ShellUtil::SUCCESS;
+ if (::GetExitCodeProcess(info.hProcess, &ret_val) &&
+ (ret_val == ShellUtil::SUCCESS))
+ return ShellUtil::SUCCESS;
+ }
+ }
+ }
+ return ShellUtil::FAILURE;
+}
+
+} // namespace
+
+
+const wchar_t* ShellUtil::kRegDefaultIcon = L"\\DefaultIcon";
+const wchar_t* ShellUtil::kRegShellPath = L"\\shell";
+const wchar_t* ShellUtil::kRegShellOpen = L"\\shell\\open\\command";
+const wchar_t* ShellUtil::kRegStartMenuInternet =
+ L"Software\\Clients\\StartMenuInternet";
+const wchar_t* ShellUtil::kRegClasses = L"Software\\Classes";
+const wchar_t* ShellUtil::kRegRegisteredApplications =
+ L"Software\\RegisteredApplications";
+const wchar_t* ShellUtil::kRegShellChromeHTML = L"\\shell\\ChromeHTML";
+const wchar_t* ShellUtil::kRegShellChromeHTMLCommand =
+ L"\\shell\\ChromeHTML\\command";
+
+const wchar_t* ShellUtil::kChromeHTMLProgId = L"ChromeHTML";
+const wchar_t* ShellUtil::kFileAssociations[] = {L".htm", L".html", L".shtml",
+ L".xht", L".xhtml", NULL};
+const wchar_t* ShellUtil::kProtocolAssociations[] = {L"ftp", L"http", L"https",
+ NULL};
+
+
+ShellUtil::RegisterStatus ShellUtil::AddChromeToSetAccessDefaults(
+ const std::wstring& chrome_exe, bool skip_if_not_admin) {
+ if (IsChromeRegistered(chrome_exe))
+ return ShellUtil::SUCCESS;
+
+ if (win_util::GetWinVersion() == win_util::WINVERSION_VISTA)
+ return RegisterOnVista(chrome_exe, skip_if_not_admin);
+
+ // Try adding these entries to HKLM first and if that fails try adding
+ // to HKCU.
+ if (SetAccessDefaultRegEntries(HKEY_LOCAL_MACHINE, chrome_exe))
+ return ShellUtil::SUCCESS;
+
+ if (!skip_if_not_admin &&
+ SetAccessDefaultRegEntries(HKEY_CURRENT_USER, chrome_exe))
+ return ShellUtil::REGISTERED_PER_USER;
+
+ return ShellUtil::FAILURE;
+}
+
+bool ShellUtil::GetChromeIcon(std::wstring& chrome_icon) {
+ if (chrome_icon.empty())
+ return false;
+
+ chrome_icon.append(L",0");
+ return true;
+}
+
+bool ShellUtil::GetChromeShortcutName(std::wstring* shortcut) {
+ shortcut->assign(installer_util::GetLocalizedString(IDS_PRODUCT_NAME_BASE));
+ shortcut->append(L".lnk");
+ return true;
+}
+
+bool ShellUtil::GetDesktopPath(std::wstring* path) {
+ wchar_t desktop[MAX_PATH];
+ if (FAILED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT,
+ desktop)))
+ return false;
+ *path = desktop;
+ return true;
+}
+
+bool ShellUtil::GetQuickLaunchPath(std::wstring* path) {
+ if (!PathService::Get(base::DIR_APP_DATA, path))
+ return false;
+ // This path works on Vista as well.
+ file_util::AppendToPath(path, L"Microsoft\\Internet Explorer\\Quick Launch");
+ return true;
+}
+
+bool ShellUtil::UpdateChromeShortcut(const std::wstring& chrome_exe,
+ const std::wstring& shortcut,
+ bool create_new) {
+ std::wstring chrome_path = file_util::GetDirectoryFromPath(chrome_exe);
+ if (create_new) {
+ return file_util::CreateShortcutLink(chrome_exe.c_str(), // target
+ shortcut.c_str(), // shortcut
+ chrome_path.c_str(), // working dir
+ NULL, // arguments
+ NULL, // description
+ chrome_exe.c_str(), // icon file
+ 0); // icon index
+ } else {
+ return file_util::UpdateShortcutLink(chrome_exe.c_str(), // target
+ shortcut.c_str(), // shortcut
+ chrome_path.c_str(), // working dir
+ NULL, // arguments
+ NULL, // description
+ chrome_exe.c_str(), // icon file
+ 0); // icon index
+ }
+}
diff --git a/chrome/installer/util/shell_util.h b/chrome/installer/util/shell_util.h
new file mode 100644
index 0000000..d741a76
--- /dev/null
+++ b/chrome/installer/util/shell_util.h
@@ -0,0 +1,145 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file declares methods that are useful for integrating Chrome in
+// Windows shell. These methods are all static and currently part of
+// ShellUtil class.
+
+#ifndef CHROME_INSTALLER_UTIL_SHELL_UTIL_H__
+#define CHROME_INSTALLER_UTIL_SHELL_UTIL_H__
+
+#include <windows.h>
+#include <string>
+
+#include "base/basictypes.h"
+#include "chrome/installer/util/work_item_list.h"
+
+// This is a utility class that provides common shell integration methods
+// that can be used by installer as well as Chrome.
+class ShellUtil {
+ public:
+ // Return value of AddChromeToSetAccessDefaults.
+ enum RegisterStatus {
+ SUCCESS, // Registration of Chrome successful (in HKLM)
+ FAILURE, // Registration failed (no changes made)
+ REGISTERED_PER_USER // Registered Chrome as per user (in HKCU)
+ };
+
+ // Relative path of DefaultIcon registry entry (prefixed with '\').
+ static const wchar_t* kRegDefaultIcon;
+
+ // Relative path of "shell" registry key.
+ static const wchar_t* kRegShellPath;
+
+ // Relative path of shell open command in Windows registry
+ // (i.e. \\shell\\open\\command).
+ static const wchar_t* kRegShellOpen;
+
+ // Relative path of registry key under which applications need to register
+ // to control Windows Start menu links.
+ static const wchar_t* kRegStartMenuInternet;
+
+ // Relative path of Classes registry entry under which file associations
+ // are added on Windows.
+ static const wchar_t* kRegClasses;
+
+ // Relative path of RegisteredApplications registry entry under which
+ // we add Chrome as a Windows application
+ static const wchar_t* kRegRegisteredApplications;
+
+ // Name that we give to Chrome file association handler ProgId.
+ static const wchar_t* kChromeHTMLProgId;
+
+ // Relative path of shell Chrome Progid in Windows registry
+ // (i.e. \\shell\\ChromeHTML).
+ static const wchar_t* kRegShellChromeHTML;
+
+ // Relative path of shell Chrome Progid command in Windows registry
+ // (i.e. \\shell\\ChromeHTML\\command).
+ static const wchar_t* kRegShellChromeHTMLCommand;
+
+ // File extensions that Chrome registers itself for.
+ static const wchar_t* kFileAssociations[];
+
+ // Protocols that Chrome registers itself for.
+ static const wchar_t* kProtocolAssociations[];
+
+ // This method adds Chrome to the list that shows up in Add/Remove Programs->
+ // Set Program Access and Defaults and also creates Chrome ProgIds under
+ // Software\Classes. This method requires write access to HKLM so is just
+ // best effort deal. If write to HKLM fails and skip_if_not_admin is false,
+ // this method will:
+ // - add the ProgId entries to HKCU on XP. HKCU entries will not make
+ // Chrome show in Set Program Access and Defaults but they are still useful
+ // because we can make Chrome run when user clicks on http link or html
+ // file.
+ // - will try to launch setup.exe with admin priviledges on Vista to do
+ // these tasks. Users will see standard Vista elevation prompt and if they
+ // enter the right credentials, the write operation will work.
+ // Currently skip_if_not_admin is false only when user tries to make Chrome
+ // default browser and Chrome is not registered on the machine.
+ //
+ // chrome_exe: full path to chrome.exe.
+ // skip_if_not_admin: if false will make this method try alternate methods
+ // as described above.
+ static RegisterStatus AddChromeToSetAccessDefaults(
+ const std::wstring& chrome_exe, bool skip_if_not_admin);
+
+ // This method appends the Chrome icon index inside chrome.exe to the
+ // chrome.exe path passed in as input, to generate the full path for
+ // Chrome icon that can be used as value for Windows registry keys.
+ // chrome_icon: full path to chrome.exe.
+ static bool GetChromeIcon(std::wstring& chrome_icon);
+
+ // Returns the localized name of Chrome shortcut.
+ static bool GetChromeShortcutName(std::wstring* shortcut);
+
+ // Gets the desktop path for the current user and returns it in 'path'
+ // argument. Return true if successful, otherwise returns false.
+ static bool GetDesktopPath(std::wstring* path);
+
+ // Gets the Quick Launch shortcuts path for the current user and
+ // returns it in 'path' argument. Return true if successful, otherwise
+ // returns false.
+ static bool GetQuickLaunchPath(std::wstring* path);
+
+ // Updates shortcut (or creates a new shortcut) at destination given by
+ // shortcut to a target given by chrome_exe. The arguments is left NULL
+ // for the target and icon is set as icon at index 0 from exe.
+ // If create_new is set to true, the function will create a new shortcut if
+ // if doesn't exist.
+ static bool UpdateChromeShortcut(const std::wstring& chrome_exe,
+ const std::wstring& shortcut,
+ bool create_new);
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(ShellUtil);
+};
+
+
+#endif // CHROME_INSTALLER_UTIL_SHELL_UTIL_H__
diff --git a/chrome/installer/util/using_util.vsprops b/chrome/installer/util/using_util.vsprops
new file mode 100644
index 0000000..fe84cd9
--- /dev/null
+++ b/chrome/installer/util/using_util.vsprops
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="using_util"
+ >
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="shlwapi.lib"
+ />
+</VisualStudioPropertySheet>
diff --git a/chrome/installer/util/util.vcproj b/chrome/installer/util/util.vcproj
new file mode 100644
index 0000000..a63254c
--- /dev/null
+++ b/chrome/installer/util/util.vcproj
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="util"
+ ProjectGUID="{EFBB1436-A63F-4CD8-9E99-B89226E782EC}"
+ RootNamespace="util"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)common\common.vsprops;$(SolutionDir)..\third_party\lzma_sdk\using_lzma_sdk.vsprops;prebuild\util_prebuild.vsprops"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;$(SolutionDir)common\common.vsprops;$(SolutionDir)..\third_party\lzma_sdk\using_lzma_sdk.vsprops;prebuild\util_prebuild.vsprops"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ </Configuration>
+ </Configurations>
+ <Files>
+ <File
+ RelativePath="..\..\app\google_update_settings.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\app\google_update_settings.h"
+ >
+ </File>
+ <File
+ RelativePath="copy_tree_work_item.cc"
+ >
+ </File>
+ <File
+ RelativePath="copy_tree_work_item.h"
+ >
+ </File>
+ <File
+ RelativePath="create_dir_work_item.cc"
+ >
+ </File>
+ <File
+ RelativePath="create_dir_work_item.h"
+ >
+ </File>
+ <File
+ RelativePath="create_reg_key_work_item.cc"
+ >
+ </File>
+ <File
+ RelativePath="create_reg_key_work_item.h"
+ >
+ </File>
+ <File
+ RelativePath="delete_tree_work_item.cc"
+ >
+ </File>
+ <File
+ RelativePath="delete_tree_work_item.h"
+ >
+ </File>
+ <File
+ RelativePath="helper.cc"
+ >
+ </File>
+ <File
+ RelativePath="helper.h"
+ >
+ </File>
+ <File
+ RelativePath="install_util.cc"
+ >
+ </File>
+ <File
+ RelativePath="install_util.h"
+ >
+ </File>
+ <File
+ RelativePath="l10n_string_util.cc"
+ >
+ </File>
+ <File
+ RelativePath="l10n_string_util.h"
+ >
+ </File>
+ <File
+ RelativePath="logging_installer.cc"
+ >
+ </File>
+ <File
+ RelativePath="logging_installer.h"
+ >
+ </File>
+ <File
+ RelativePath=".\lzma_util.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\lzma_util.h"
+ >
+ </File>
+ <File
+ RelativePath=".\google_update_constants.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\google_update_constants.h"
+ >
+ </File>
+ <File
+ RelativePath="set_reg_value_work_item.cc"
+ >
+ </File>
+ <File
+ RelativePath="set_reg_value_work_item.h"
+ >
+ </File>
+ <File
+ RelativePath=".\shell_util.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\shell_util.h"
+ >
+ </File>
+ <File
+ RelativePath="util_constants.cc"
+ >
+ </File>
+ <File
+ RelativePath="util_constants.h"
+ >
+ </File>
+ <File
+ RelativePath="version.cc"
+ >
+ </File>
+ <File
+ RelativePath="version.h"
+ >
+ </File>
+ <File
+ RelativePath="work_item.cc"
+ >
+ </File>
+ <File
+ RelativePath="work_item.h"
+ >
+ </File>
+ <File
+ RelativePath="work_item_list.cc"
+ >
+ </File>
+ <File
+ RelativePath="work_item_list.h"
+ >
+ </File>
+ </Files>
+</VisualStudioProject>
diff --git a/chrome/installer/util/util_constants.cc b/chrome/installer/util/util_constants.cc
new file mode 100644
index 0000000..9833671
--- /dev/null
+++ b/chrome/installer/util/util_constants.cc
@@ -0,0 +1,82 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/installer/util/util_constants.h"
+
+namespace installer_util {
+
+namespace switches {
+
+// Disable logging
+const wchar_t kDisableLogging[] = L"disable-logging";
+
+// Enable logging at the error level. This is the default behavior.
+const wchar_t kEnableLogging[] = L"enable-logging";
+
+// Specify the file path of Chrome archive for install.
+const wchar_t kInstallArchive[] = L"install-archive";
+
+// If present, specify file path to write logging info.
+const wchar_t kLogFile[] = L"log-file";
+
+// Register Chrome as a valid browser on the current sytem. This option
+// requires that setup.exe is running as admin. If this option is specified,
+// options kInstallArchive and kUninstall are ignored.
+const wchar_t kRegisterChromeBrowser[] = L"register-chrome-browser";
+
+// By default we remove all shared (between users) files, registry entries etc
+// during uninstall. If this option is specified together with kUninstall option
+// we do not clean up shared entries otherwise this option is ignored.
+const wchar_t kDoNotRemoveSharedItems[] = L"do-not-remove-shared-items";
+
+// Install Chrome to system wise location. The default is per user install.
+const wchar_t kSystemInstall[] = L"system-install";
+
+// If present, setup will uninstall chrome.
+const wchar_t kUninstall[] = L"uninstall";
+
+// Enable verbose logging (info level).
+const wchar_t kVerboseLogging[] = L"verbose-logging";
+
+} // namespace switches
+
+const wchar_t kInstallBinaryDir[] = L"Application";
+const wchar_t kInstallGoogleDir[] = L"Google";
+const wchar_t kChrome[] = L"Chrome";
+const wchar_t kChromeExe[] = L"chrome.exe";
+const wchar_t kChromeDll[] = L"chrome.dll";
+
+const wchar_t kPublisherName[] = L"Google";
+const wchar_t kApplicationName[] = L"Google Chrome";
+const wchar_t kSetupExe[] = L"setup.exe";
+
+const wchar_t kUninstallRegPath[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Chrome";
+const wchar_t kUninstallStringField[] = L"UninstallString";
+const wchar_t kUninstallDisplayNameField[] = L"DisplayName";
+} // namespace installer_util
diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h
new file mode 100644
index 0000000..af9596f
--- /dev/null
+++ b/chrome/installer/util/util_constants.h
@@ -0,0 +1,88 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Defines all install related constants that need to be used by Chrome as
+// well as Chrome Installer.
+
+#ifndef CHROME_INSTALLER_UTIL_UTIL_CONSTANTS_H__
+#define CHROME_INSTALLER_UTIL_UTIL_CONSTANTS_H__
+
+namespace installer_util {
+
+// Return status of installer
+enum InstallStatus {
+ FIRST_INSTALL_SUCCESS, // Successfully installed Chrome for the first time
+ INSTALL_REPAIRED, // Same version reinstalled for repair
+ NEW_VERSION_UPDATED, // Chrome successfully updated to new version
+ HIGHER_VERSION_EXISTS, // Higher version of Chrome already exists
+ INSTALL_FAILED, // Install/update failed
+ OS_NOT_SUPPORTED, // Current OS not supported
+ OS_ERROR, // OS API call failed
+ TEMP_DIR_FAILED, // Unable to get Temp directory
+ UNCOMPRESSION_FAILED, // Failed to uncompress Chrome archive
+ INVALID_ARCHIVE, // Something wrong with the installer archive
+ CHROME_NOT_INSTALLED, // Chrome not installed (returned in case of uninstall)
+ CHROME_RUNNING, // Chrome currently running (when trying to uninstall)
+ UNINSTALL_CONFIRMED, // User has confirmed Chrome uninstall
+ UNINSTALL_SUCCESSFUL, // Chrome successfully uninstalled
+ UNINSTALL_FAILED, // Chrome uninstallation failed
+ UNINSTALL_CANCELLED, // User cancelled Chrome uninstallation
+ UNKNOWN_STATUS, // Unknown status (this should never happen)
+};
+
+namespace switches {
+extern const wchar_t kDisableLogging[];
+extern const wchar_t kEnableLogging[];
+extern const wchar_t kInstallArchive[];
+extern const wchar_t kLogFile[];
+extern const wchar_t kRegisterChromeBrowser[];
+extern const wchar_t kDoNotRemoveSharedItems[];
+extern const wchar_t kSystemInstall[];
+extern const wchar_t kUninstall[];
+extern const wchar_t kVerboseLogging[];
+} // namespace switches
+
+extern const wchar_t kInstallBinaryDir[];
+extern const wchar_t kInstallGoogleDir[];
+extern const wchar_t kChrome[];
+extern const wchar_t kChromeExe[];
+extern const wchar_t kChromeDll[];
+
+// Bug 1214772 - these should be removed for public beta and replaced with
+// a localized string.
+extern const wchar_t kPublisherName[];
+extern const wchar_t kApplicationName[];
+extern const wchar_t kSetupExe[];
+
+extern const wchar_t kUninstallRegPath[];
+extern const wchar_t kUninstallStringField[];
+extern const wchar_t kUninstallDisplayNameField[];
+} // namespace installer_util
+
+#endif // CHROME_INSTALLER_UTIL_UTIL_CONSTANTS_H__
diff --git a/chrome/installer/util/version.cc b/chrome/installer/util/version.cc
new file mode 100644
index 0000000..925c950
--- /dev/null
+++ b/chrome/installer/util/version.cc
@@ -0,0 +1,71 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <vector>
+
+#include "base/string_util.h"
+#include "chrome/installer/util/version.h"
+
+installer::Version::Version(int64 major, int64 minor, int64 build,
+ int64 patch) :
+ major_(major), minor_(minor), build_(build), patch_(patch) {
+ version_str_.append(Int64ToWString(major_));
+ version_str_.append(L".");
+ version_str_.append(Int64ToWString(minor_));
+ version_str_.append(L".");
+ version_str_.append(Int64ToWString(build_));
+ version_str_.append(L".");
+ version_str_.append(Int64ToWString(patch_));
+}
+
+installer::Version::~Version() {
+}
+
+bool installer::Version::IsHigherThan(const installer::Version* other) const {
+ return ((major_ > other->major_) ||
+ ((major_ == other->major_) && (minor_ > other->minor_)) ||
+ ((major_ == other->major_) && (minor_ == other->minor_)
+ && (build_ > other->build_)) ||
+ ((major_ == other->major_) && (minor_ == other->minor_)
+ && (build_ == other->build_)
+ && (patch_ > other->patch_)));
+}
+
+installer::Version* installer::Version::GetVersionFromString(
+ std::wstring version_str) {
+ std::vector<std::wstring> numbers;
+ SplitString(version_str, '.', &numbers);
+
+ if (numbers.size() != 4) {
+ return NULL;
+ }
+
+ return new Version(StringToInt64(numbers[0]), StringToInt64(numbers[1]),
+ StringToInt64(numbers[2]), StringToInt64(numbers[3]));
+}
diff --git a/chrome/installer/util/version.h b/chrome/installer/util/version.h
new file mode 100644
index 0000000..fba1f9a
--- /dev/null
+++ b/chrome/installer/util/version.h
@@ -0,0 +1,71 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_INSTALLER_UTIL_VERSION_H__
+#define CHROME_INSTALLER_UTIL_VERSION_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace installer {
+
+class Version {
+public:
+ virtual ~Version();
+
+ // Check if the current version is higher than the version object passed
+ // as parameter
+ bool IsHigherThan(const Version* other) const;
+
+ // Return the string representation of this version
+ const std::wstring& GetString() const {
+ return version_str_;
+ }
+
+ // Assume that the version string is specified by four integers separated
+ // by character '.'. Return NULL if string is not of this format.
+ // Caller is responsible for freeing the Version object once done.
+ static Version* GetVersionFromString(std::wstring version_str);
+
+private:
+ int64 major_;
+ int64 minor_;
+ int64 build_;
+ int64 patch_;
+ std::wstring version_str_;
+
+ // Classes outside this file do not have any need to create objects of
+ // this type so declare constructor as private.
+ Version(int64 major, int64 minor, int64 build, int64 patch);
+};
+
+} // namespace installer
+
+#endif
diff --git a/chrome/installer/util/work_item.cc b/chrome/installer/util/work_item.cc
new file mode 100644
index 0000000..c273cf3
--- /dev/null
+++ b/chrome/installer/util/work_item.cc
@@ -0,0 +1,85 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/installer/util/work_item.h"
+#include "chrome/installer/util/copy_tree_work_item.h"
+#include "chrome/installer/util/create_dir_work_item.h"
+#include "chrome/installer/util/create_reg_key_work_item.h"
+#include "chrome/installer/util/delete_tree_work_item.h"
+#include "chrome/installer/util/set_reg_value_work_item.h"
+#include "chrome/installer/util/work_item_list.h"
+
+WorkItem::WorkItem() {
+}
+
+WorkItem::~WorkItem() {
+}
+
+CopyTreeWorkItem* WorkItem::CreateCopyTreeWorkItem(
+ std::wstring source_path, std::wstring dest_path, std::wstring temp_dir,
+ CopyOverWriteOption overwrite_option, std::wstring alternative_path) {
+ return new CopyTreeWorkItem(source_path, dest_path, temp_dir,
+ overwrite_option, alternative_path);
+}
+
+CreateDirWorkItem* WorkItem::CreateCreateDirWorkItem(std::wstring path) {
+ return new CreateDirWorkItem(path);
+}
+
+CreateRegKeyWorkItem* WorkItem::CreateCreateRegKeyWorkItem(
+ HKEY predefined_root, std::wstring path) {
+ return new CreateRegKeyWorkItem(predefined_root, path);
+}
+
+DeleteTreeWorkItem* WorkItem::CreateDeleteTreeWorkItem(std::wstring root_path,
+ std::wstring key_path) {
+ return new DeleteTreeWorkItem(root_path, key_path);
+}
+
+SetRegValueWorkItem* WorkItem::CreateSetRegValueWorkItem(
+ HKEY predefined_root, std::wstring key_path,
+ std::wstring value_name, std::wstring value_data, bool overwrite) {
+ return new SetRegValueWorkItem(predefined_root, key_path,
+ value_name, value_data, overwrite);
+}
+
+SetRegValueWorkItem* WorkItem::CreateSetRegValueWorkItem(
+ HKEY predefined_root, std::wstring key_path,
+ std::wstring value_name, DWORD value_data, bool overwrite) {
+ return new SetRegValueWorkItem(predefined_root, key_path,
+ value_name, value_data, overwrite);
+}
+
+WorkItemList* WorkItem::CreateWorkItemList() {
+ return new WorkItemList();
+}
+
+std::wstring WorkItem::Dump() {
+ return std::wstring(L"Work Item");
+}
diff --git a/chrome/installer/util/work_item.h b/chrome/installer/util/work_item.h
new file mode 100644
index 0000000..c167cf3
--- /dev/null
+++ b/chrome/installer/util/work_item.h
@@ -0,0 +1,124 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Base class for managing an action of a sequence of actions to be carried
+// out during install/update/uninstall. Supports rollback of actions if this
+// process fails.
+
+#ifndef CHROME_INSTALLER_UTIL_WORK_ITEM_H__
+#define CHROME_INSTALLER_UTIL_WORK_ITEM_H__
+
+#include <string>
+#include <windows.h>
+
+class CopyTreeWorkItem;
+class CreateDirWorkItem;
+class CreateRegKeyWorkItem;
+class DeleteTreeWorkItem;
+class SetRegValueWorkItem;
+class WorkItemList;
+
+// A base class that defines APIs to perform/rollback an action or a
+// sequence of actions during install/update/uninstall.
+class WorkItem {
+ public:
+ // Possible states
+ typedef enum CopyOverWriteOption {
+ ALWAYS, // Always overwrite regardless of what existed before.
+ NEVER, // Not used currently.
+ IF_DIFFERENT, // Overwrite if different. Currently only applies to file.
+ RENAME_IF_IN_USE // Copy to a new path instead of overwriting (only files).
+ };
+
+ virtual ~WorkItem();
+
+ // Create a CopyTreeWorkItem that recursively copies a file system hierarchy
+ // from source path to destination path. If overwrite_option is ALWAYS, the
+ // created CopyTreeWorkItem always overwrites files. If overwrite_option is
+ // RENAME_IF_IN_USE, file is copied with an alternate name specified by
+ // alternative_path.
+ static CopyTreeWorkItem* CreateCopyTreeWorkItem(std::wstring source_path,
+ std::wstring dest_path, std::wstring temp_dir,
+ CopyOverWriteOption overwrite_option,
+ std::wstring alternative_path = L"");
+
+ // Create a CreateDirWorkItem that creates a directory at the given path.
+ static CreateDirWorkItem* CreateCreateDirWorkItem(std::wstring path);
+
+ // Create a CreateRegKeyWorkItem that creates a registry key at the given
+ // path.
+ static CreateRegKeyWorkItem* CreateCreateRegKeyWorkItem(
+ HKEY predefined_root, std::wstring path);
+
+ // Create a DeleteTreeWorkItem that recursively deletes a file system
+ // hierarchy at the given root path. A key file can be optionally specified
+ // by key_path.
+ static DeleteTreeWorkItem* CreateDeleteTreeWorkItem(std::wstring root_path,
+ std::wstring key_path);
+
+ // Create a SetRegValueWorkItem that sets a registry value with REG_SZ type
+ // at the key with specified path.
+ static SetRegValueWorkItem* CreateSetRegValueWorkItem(
+ HKEY predefined_root, std::wstring key_path,
+ std::wstring value_name, std::wstring value_data, bool overwrite);
+
+ // Create a SetRegValueWorkItem that sets a registry value with REG_DWORD type
+ // at the key with specified path.
+ static SetRegValueWorkItem* CreateSetRegValueWorkItem(
+ HKEY predefined_root, std::wstring key_path,
+ std::wstring value_name, DWORD value_data, bool overwrite);
+
+ // Create an empty WorkItemList. A WorkItemList can recursively contains
+ // a list of WorkItems.
+ static WorkItemList* CreateWorkItemList();
+
+ // Perform the actions of WorkItem. Returns true if success, returns false
+ // otherwise.
+ // If the WorkItem is transactional, then Do() is done as a transaction.
+ // If it returns false, there will be no change on the system.
+ virtual bool Do() = 0;
+
+ // Rollback any actions previously carried out by this WorkItem. If the
+ // WorkItem is transactional, then the previous actions can be fully
+ // rolled back. If the WorkItem is non-transactional, the rollback is a
+ // best effort.
+ virtual void Rollback() = 0;
+
+ // Return true if the WorkItem is transactional, return false if
+ // non-transactional.
+ virtual bool IsTransactional() { return false; }
+
+ // For diagnostics.
+ virtual std::wstring Dump();
+
+ protected:
+ WorkItem();
+};
+
+#endif // CHROME_INSTALLER_UTIL_WORK_ITEM_H__
diff --git a/chrome/installer/util/work_item_list.cc b/chrome/installer/util/work_item_list.cc
new file mode 100644
index 0000000..d9c4b54
--- /dev/null
+++ b/chrome/installer/util/work_item_list.cc
@@ -0,0 +1,142 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/installer/util/logging_installer.h"
+#include "chrome/installer/util/work_item_list.h"
+
+WorkItemList::~WorkItemList() {
+ for (WorkItemIterator itr = list_.begin(); itr != list_.end(); ++itr) {
+ delete (*itr);
+ }
+ for (WorkItemIterator itr = executed_list_.begin();
+ itr != executed_list_.end(); ++itr) {
+ delete (*itr);
+ }
+}
+
+WorkItemList::WorkItemList()
+ : status_(ADD_ITEM) {
+}
+
+bool WorkItemList::Do() {
+ if (status_ != ADD_ITEM)
+ return false;
+
+ bool result = true;
+ while (!list_.empty()) {
+ WorkItem* work_item = list_.front();
+ list_.pop_front();
+ executed_list_.push_front(work_item);
+ if (!work_item->Do()) {
+ LOG(ERROR) << "list execution failed";
+ result = false;
+ break;
+ }
+ }
+
+ if (result)
+ LOG(INFO) << "list execution succeeded";
+
+ status_ = LIST_EXECUTED;
+ return result;
+}
+
+void WorkItemList::Rollback() {
+ if (status_ != LIST_EXECUTED)
+ return;
+
+ for (WorkItemIterator itr = executed_list_.begin();
+ itr != executed_list_.end(); ++itr) {
+ (*itr)->Rollback();
+ }
+
+ status_ = LIST_ROLLED_BACK;
+ return;
+}
+
+bool WorkItemList::AddWorkItem(WorkItem* work_item) {
+ if (status_ != ADD_ITEM)
+ return false;
+
+ list_.push_back(work_item);
+ return true;
+}
+
+bool WorkItemList::AddCopyTreeWorkItem(std::wstring source_path,
+ std::wstring dest_path,
+ std::wstring temp_dir,
+ CopyOverWriteOption overwrite_option,
+ std::wstring alternative_path) {
+ WorkItem* item = reinterpret_cast<WorkItem*>(
+ WorkItem::CreateCopyTreeWorkItem(source_path, dest_path, temp_dir,
+ overwrite_option, alternative_path));
+ return AddWorkItem(item);
+}
+
+bool WorkItemList::AddCreateDirWorkItem(std::wstring path) {
+ WorkItem* item = reinterpret_cast<WorkItem*>(
+ WorkItem::CreateCreateDirWorkItem(path));
+ return AddWorkItem(item);
+}
+
+bool WorkItemList::AddCreateRegKeyWorkItem(HKEY predefined_root,
+ std::wstring path) {
+ WorkItem* item = reinterpret_cast<WorkItem*>(
+ WorkItem::CreateCreateRegKeyWorkItem(predefined_root, path));
+ return AddWorkItem(item);
+}
+
+bool WorkItemList::AddDeleteTreeWorkItem(std::wstring root_path,
+ std::wstring key_path) {
+ WorkItem* item = reinterpret_cast<WorkItem*>(
+ WorkItem::CreateDeleteTreeWorkItem(root_path, key_path));
+ return AddWorkItem(item);
+}
+
+bool WorkItemList::AddSetRegValueWorkItem(HKEY predefined_root,
+ std::wstring key_path,
+ std::wstring value_name,
+ std::wstring value_data,
+ bool overwrite) {
+ WorkItem* item = reinterpret_cast<WorkItem*>(
+ WorkItem::CreateSetRegValueWorkItem(predefined_root, key_path, value_name,
+ value_data, overwrite));
+ return AddWorkItem(item);
+}
+
+bool WorkItemList::AddSetRegValueWorkItem(HKEY predefined_root,
+ std::wstring key_path,
+ std::wstring value_name,
+ DWORD value_data,
+ bool overwrite) {
+ WorkItem* item = reinterpret_cast<WorkItem*>(
+ WorkItem::CreateSetRegValueWorkItem(predefined_root, key_path, value_name,
+ value_data, overwrite));
+ return AddWorkItem(item);
+}
diff --git a/chrome/installer/util/work_item_list.h b/chrome/installer/util/work_item_list.h
new file mode 100644
index 0000000..a33a21888
--- /dev/null
+++ b/chrome/installer/util/work_item_list.h
@@ -0,0 +1,116 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_INSTALLER_UTIL_WORK_ITEM_LIST_H__
+#define CHROME_INSTALLER_UTIL_WORK_ITEM_LIST_H__
+
+#include <list>
+#include <string>
+#include <windows.h>
+#include "chrome/installer/util/work_item.h"
+
+// A WorkItem subclass that recursively contains a list of WorkItems. Thus it
+// provides functionalities to carry out or roll back the sequence of actions
+// defined by the list of WorkItems it contains.
+// The WorkItems are executed in the same order as they are added to the list.
+class WorkItemList : public WorkItem {
+ public:
+ virtual ~WorkItemList();
+
+ // Execute the WorkItems in the same order as they are added to the list.
+ // It aborts as soon as one WorkItem fails.
+ virtual bool Do();
+
+ // Rollback the WorkItems in the reverse order as they are executed.
+ virtual void Rollback();
+
+ // Add a WorkItem to the list. Return true if the WorkItem is successfully
+ // added. Return false otherwise.
+ // A WorkItem can only be added to the list before the list's DO() is called.
+ // Once a WorkItem is added to the list. The list owns the WorkItem.
+ bool AddWorkItem(WorkItem* work_item);
+
+ // Add a CopyTreeWorkItem to the list of work items.
+ bool AddCopyTreeWorkItem(std::wstring source_path, std::wstring dest_path,
+ std::wstring temp_dir,
+ CopyOverWriteOption overwrite_option,
+ std::wstring alternative_path = L"");
+
+ // Add a CreateDirWorkItem that creates a directory at the given path.
+ bool AddCreateDirWorkItem(std::wstring path);
+
+ // Add a CreateRegKeyWorkItem that creates a registry key at the given
+ // path.
+ bool AddCreateRegKeyWorkItem(HKEY predefined_root, std::wstring path);
+
+ // Add a DeleteTreeWorkItem that recursively deletes a file system
+ // hierarchy at the given root path. A key file can be optionally specified
+ // by key_path.
+ bool AddDeleteTreeWorkItem(std::wstring root_path, std::wstring key_path);
+
+ // Add a SetRegValueWorkItem that sets a registry value with REG_SZ type
+ // at the key with specified path.
+ bool AddSetRegValueWorkItem(HKEY predefined_root, std::wstring key_path,
+ std::wstring value_name, std::wstring value_data,
+ bool overwrite);
+
+ // Add a SetRegValueWorkItem that sets a registry value with REG_DWORD type
+ // at the key with specified path.
+ bool AddSetRegValueWorkItem(HKEY predefined_root, std::wstring key_path,
+ std::wstring value_name, DWORD value_data,
+ bool overwrite);
+
+ private:
+ friend class WorkItem;
+
+ typedef std::list<WorkItem*> WorkItems;
+ typedef WorkItems::iterator WorkItemIterator;
+
+ enum ListStatus {
+ // List has not been executed. Ok to add new WorkItem.
+ ADD_ITEM,
+ // List has been executed. Can not add new WorkItem.
+ LIST_EXECUTED,
+ // List has been executed and rolled back. No further action is acceptable.
+ LIST_ROLLED_BACK
+ };
+
+ WorkItemList();
+
+ ListStatus status_;
+
+ // The list of WorkItems, in the order of them being added.
+ WorkItems list_;
+
+ // The list of executed WorkItems, in the reverse order of them being
+ // executed.
+ WorkItems executed_list_;
+};
+
+#endif // CHROME_INSTALLER_UTIL_WORK_ITEM_LIST_H__
diff --git a/chrome/installer/util/work_item_list_unittest.cc b/chrome/installer/util/work_item_list_unittest.cc
new file mode 100644
index 0000000..f381b01
--- /dev/null
+++ b/chrome/installer/util/work_item_list_unittest.cc
@@ -0,0 +1,192 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/base_paths.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/registry.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "chrome/installer/util/work_item.h"
+#include "chrome/installer/util/work_item_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+ wchar_t test_root[] = L"ListList";
+ wchar_t data_str[] = L"data_111";
+
+ class WorkItemListTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Create a temporary key for testing
+ RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS);
+ key.DeleteKey(test_root);
+ ASSERT_FALSE(key.Open(HKEY_CURRENT_USER, test_root, KEY_READ));
+ ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, test_root, KEY_READ));
+
+ // Create a temp directory for test.
+ ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_));
+ file_util::AppendToPath(&test_dir_, L"WorkItemListTest");
+ file_util::Delete(test_dir_, true);
+ ASSERT_FALSE(file_util::PathExists(test_dir_));
+ CreateDirectory(test_dir_.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(test_dir_));
+
+ // Create a log file
+ std::wstring log_file;
+ ASSERT_TRUE(file_util::CreateTemporaryFileName(&log_file));
+ ASSERT_TRUE(file_util::PathExists(log_file));
+
+ logging::InitLogging(log_file.c_str(),
+ logging::LOG_ONLY_TO_FILE,
+ logging::LOCK_LOG_FILE,
+ logging::DELETE_OLD_LOG_FILE);
+ logging::SetMinLogLevel(logging::LOG_INFO);
+ }
+
+ virtual void TearDown() {
+ logging::CloseLogFile();
+ // Clean up test directory
+ ASSERT_TRUE(file_util::Delete(test_dir_, true));
+ ASSERT_FALSE(file_util::PathExists(test_dir_));
+ // Clean up the temporary key
+ RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS);
+ ASSERT_TRUE(key.DeleteKey(test_root));
+ }
+
+ std::wstring test_dir_;
+ };
+};
+
+// Execute a WorkItem list successfully and then rollback.
+TEST_F(WorkItemListTest, ExecutionSuccess) {
+ scoped_ptr<WorkItemList> work_item_list(WorkItem::CreateWorkItemList());
+ scoped_ptr<WorkItem> work_item;
+
+ std::wstring top_dir_to_create(test_dir_);
+ file_util::AppendToPath(&top_dir_to_create, L"a");
+ std::wstring dir_to_create(top_dir_to_create);
+ file_util::AppendToPath(&dir_to_create, L"b");
+ ASSERT_FALSE(file_util::PathExists(dir_to_create));
+
+ work_item.reset(reinterpret_cast<WorkItem*>(
+ WorkItem::CreateCreateDirWorkItem(dir_to_create)));
+ EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release()));
+
+ std::wstring key_to_create(test_root);
+ file_util::AppendToPath(&key_to_create, L"ExecutionSuccess");
+
+ work_item.reset(reinterpret_cast<WorkItem*>(
+ WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, key_to_create)));
+ EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release()));
+
+ std::wstring name(L"name");
+ std::wstring data(data_str);
+ work_item.reset(reinterpret_cast<WorkItem*>(
+ WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, key_to_create,
+ name, data, false)));
+ EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release()));
+
+ EXPECT_TRUE(work_item_list->Do());
+
+ // Verify all WorkItems have been executed.
+ RegKey key;
+ EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ));
+ std::wstring read_out;
+ EXPECT_TRUE(key.ReadValue(name.c_str(), &read_out));
+ EXPECT_EQ(0, read_out.compare(data_str));
+ key.Close();
+ EXPECT_TRUE(file_util::PathExists(dir_to_create));
+
+ work_item_list->Rollback();
+
+ // Verify everything is rolled back.
+ // The value must have been deleted first in roll back otherwise the key
+ // can not be deleted.
+ EXPECT_FALSE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ));
+ EXPECT_FALSE(file_util::PathExists(top_dir_to_create));
+}
+
+// Execute a WorkItem list. Fail in the middle. Rollback what has been done.
+TEST_F(WorkItemListTest, ExecutionFailAndRollback) {
+ scoped_ptr<WorkItemList> work_item_list(WorkItem::CreateWorkItemList());
+ scoped_ptr<WorkItem> work_item;
+
+ std::wstring top_dir_to_create(test_dir_);
+ file_util::AppendToPath(&top_dir_to_create, L"a");
+ std::wstring dir_to_create(top_dir_to_create);
+ file_util::AppendToPath(&dir_to_create, L"b");
+ ASSERT_FALSE(file_util::PathExists(dir_to_create));
+
+ work_item.reset(reinterpret_cast<WorkItem*>(
+ WorkItem::CreateCreateDirWorkItem(dir_to_create)));
+ EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release()));
+
+ std::wstring key_to_create(test_root);
+ file_util::AppendToPath(&key_to_create, L"ExecutionFail");
+
+ work_item.reset(reinterpret_cast<WorkItem*>(
+ WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, key_to_create)));
+ EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release()));
+
+ std::wstring not_created_key(test_root);
+ file_util::AppendToPath(&not_created_key, L"NotCreated");
+ std::wstring name(L"name");
+ std::wstring data(data_str);
+ work_item.reset(reinterpret_cast<WorkItem*>(
+ WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, not_created_key,
+ name, data, false)));
+ EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release()));
+
+ // This one will not be executed because we will fail early.
+ work_item.reset(reinterpret_cast<WorkItem*>(
+ WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER,
+ not_created_key)));
+ EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release()));
+
+ EXPECT_FALSE(work_item_list->Do());
+
+ // Verify the first 2 WorkItems have been executed.
+ RegKey key;
+ EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ));
+ key.Close();
+ EXPECT_TRUE(file_util::PathExists(dir_to_create));
+ // The last one should not be there.
+ EXPECT_FALSE(key.Open(HKEY_CURRENT_USER, not_created_key.c_str(),
+ KEY_READ));
+
+ work_item_list->Rollback();
+
+ // Verify everything is rolled back.
+ EXPECT_FALSE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ));
+ EXPECT_FALSE(file_util::PathExists(top_dir_to_create));
+}