summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorgrt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-11 18:23:30 +0000
committergrt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-11 18:23:30 +0000
commitbaf405574a8a4f40dde15ac35ba4ad0cf34846cc (patch)
treeb0190fb67df57ca85a68dfd44f2959315520d856 /chrome
parent9800de5ee99882a819eb2673fdf4245cd5c3c770 (diff)
downloadchromium_src-baf405574a8a4f40dde15ac35ba4ad0cf34846cc.zip
chromium_src-baf405574a8a4f40dde15ac35ba4ad0cf34846cc.tar.gz
chromium_src-baf405574a8a4f40dde15ac35ba4ad0cf34846cc.tar.bz2
Add -stage:ID modifier to products' Google Update "ap" values to better track installation failures.
BUG=none TEST=Normal install/uninstall scenarios all function as before. Any that crash leave a "-stage:ID" modifier on the "ap" value. Review URL: http://codereview.chromium.org/6612068 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@77831 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/installer/setup/install.cc11
-rw-r--r--chrome/installer/setup/install_worker.cc79
-rw-r--r--chrome/installer/setup/setup_main.cc21
-rw-r--r--chrome/installer/setup/setup_util.cc13
-rw-r--r--chrome/installer/setup/setup_util.h28
-rw-r--r--chrome/installer/setup/setup_util_unittest.cc5
-rw-r--r--chrome/installer/util/browser_distribution.h1
-rw-r--r--chrome/installer/util/channel_info.cc110
-rw-r--r--chrome/installer/util/channel_info.h9
-rw-r--r--chrome/installer/util/channel_info_unittest.cc108
-rw-r--r--chrome/installer/util/install_util.cc55
-rw-r--r--chrome/installer/util/install_util.h7
-rw-r--r--chrome/installer/util/install_util_unittest.cc51
-rw-r--r--chrome/installer/util/installer_state.cc75
-rw-r--r--chrome/installer/util/installer_state.h8
-rw-r--r--chrome/installer/util/util_constants.h20
16 files changed, 477 insertions, 124 deletions
diff --git a/chrome/installer/setup/install.cc b/chrome/installer/setup/install.cc
index e9b394b..9a1c45f 100644
--- a/chrome/installer/setup/install.cc
+++ b/chrome/installer/setup/install.cc
@@ -262,6 +262,8 @@ installer::InstallStatus InstallNewVersion(
scoped_ptr<Version>* current_version) {
DCHECK(current_version);
+ installer_state.UpdateStage(installer::BUILDING);
+
current_version->reset(installer_state.GetCurrentVersion(original_state));
scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList());
@@ -281,7 +283,10 @@ installer::InstallStatus InstallNewVersion(
FilePath new_chrome_exe(
installer_state.target_path().Append(installer::kChromeNewExe));
+ installer_state.UpdateStage(installer::EXECUTING);
+
if (!install_list->Do()) {
+ installer_state.UpdateStage(installer::ROLLINGBACK);
installer::InstallStatus result =
file_util::PathExists(new_chrome_exe) && current_version->get() &&
new_version.Equals(*current_version->get()) ?
@@ -293,6 +298,8 @@ installer::InstallStatus InstallNewVersion(
return result;
}
+ installer_state.UpdateStage(installer::FINISHING);
+
installer::RefreshElevationPolicy();
if (!current_version->get()) {
@@ -358,6 +365,10 @@ InstallStatus InstallOrUpdateProduct(
// TODO(robertshield): Everything below this line should instead be captured
// by WorkItems.
if (!InstallUtil::GetInstallReturnCode(result)) {
+ // Update the modifiers on the channel values for the product(s) being
+ // installed and for the binaries in case of multi-install.
+ installer_state.UpdateChannels();
+
if (result == FIRST_INSTALL_SUCCESS && !prefs_path.empty())
CopyPreferenceFileForFirstRun(installer_state, prefs_path);
diff --git a/chrome/installer/setup/install_worker.cc b/chrome/installer/setup/install_worker.cc
index 24fbf6d..37d9f73 100644
--- a/chrome/installer/setup/install_worker.cc
+++ b/chrome/installer/setup/install_worker.cc
@@ -264,81 +264,12 @@ void AddGoogleUpdateWorkItems(const InstallationState& original_state,
return;
}
- // Update the "ap" value for the product being installed/updated. We get
- // this via GetNonVersionedProductState since the product whose ap value
- // we are querying may be in the process of being installed for the first
- // time.
- BrowserDistribution::Type client_state_distribution =
- installer_state.state_type();
- const ProductState* target_product_state =
- original_state.GetNonVersionedProductState(
- installer_state.system_install(), client_state_distribution);
- ChannelInfo channel_info(target_product_state->channel());
-
- // This is a multi-install product.
- bool modified = channel_info.SetMultiInstall(true);
-
- // Add the appropriate modifiers for all products and their options.
- modified |= installer_state.SetChannelFlags(true, &channel_info);
-
- VLOG(1) << "ap: " << channel_info.value();
-
- const HKEY reg_root = installer_state.root_key();
- const std::wstring& key_path(installer_state.state_key());
-
- // Write the results if needed.
- if (modified) {
- install_list->AddSetRegValueWorkItem(reg_root, key_path,
- google_update::kRegApField,
- channel_info.value(), true);
- } else {
- VLOG(1) << "Channel flags not modified";
- }
-
- // Synchronize the other products and the package with this one.
- std::vector<std::wstring> state_key_paths;
-
- state_key_paths.reserve(installer_state.products().size());
- std::wstring multi_key(
- installer_state.multi_package_binaries_distribution()->GetStateKey());
- if (multi_key != key_path)
- state_key_paths.push_back(multi_key);
-
- std::wstring other_key;
- Products::const_iterator scan = installer_state.products().begin();
- Products::const_iterator end = installer_state.products().end();
- for (; scan != end; ++scan) {
- other_key = (*scan)->distribution()->GetStateKey();
- if (other_key != key_path)
- state_key_paths.push_back(other_key);
- }
-
- RegKey key;
- ChannelInfo other_info;
- std::vector<std::wstring>::const_iterator kscan = state_key_paths.begin();
- std::vector<std::wstring>::const_iterator kend = state_key_paths.end();
- for (; kscan != kend; ++kscan) {
- // Handle the case where the ClientState key doesn't exist by creating it.
- // This takes care of the multi-installer's package key, which is not
- // created by Google Update for us.
- if ((key.Open(reg_root, kscan->c_str(), KEY_QUERY_VALUE) != ERROR_SUCCESS)
- || (!other_info.Initialize(key))) {
- other_info.set_value(std::wstring());
- }
- if (!other_info.Equals(channel_info)) {
- if (!key.Valid()) {
- install_list->AddCreateRegKeyWorkItem(reg_root, *kscan);
- }
- install_list->AddSetRegValueWorkItem(reg_root, *kscan,
- google_update::kRegApField,
- channel_info.value(), true);
- }
- }
-
// Creating the ClientState key for binaries, if we're migrating to multi then
// copy over Chrome's brand code if it has one. Chrome Frame currently never
// has a brand code.
- if (multi_key != key_path) {
+ if (installer_state.state_type() != BrowserDistribution::CHROME_BINARIES) {
+ std::wstring multi_key(
+ installer_state.multi_package_binaries_distribution()->GetStateKey());
const ProductState* chrome_product_state =
original_state.GetNonVersionedProductState(
installer_state.system_install(),
@@ -346,9 +277,11 @@ void AddGoogleUpdateWorkItems(const InstallationState& original_state,
const std::wstring& brand(chrome_product_state->brand());
if (!brand.empty()) {
+ install_list->AddCreateRegKeyWorkItem(installer_state.root_key(),
+ multi_key);
// Write Chrome's brand code to the multi key. Never overwrite the value
// if one is already present (although this shouldn't happen).
- install_list->AddSetRegValueWorkItem(reg_root,
+ install_list->AddSetRegValueWorkItem(installer_state.root_key(),
multi_key,
google_update::kRegBrandField,
brand,
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc
index edd5b6b..48ff47d 100644
--- a/chrome/installer/setup/setup_main.cc
+++ b/chrome/installer/setup/setup_main.cc
@@ -91,6 +91,9 @@ DWORD UnPackArchive(const FilePath& archive,
const FilePath& output_directory,
installer::ArchiveType* archive_type) {
DCHECK(archive_type);
+
+ installer_state.UpdateStage(installer::UNCOMPRESSING);
+
// First uncompress the payload. This could be a differential
// update (patch.7z) or full archive (chrome.7z). If this uncompress fails
// return with error.
@@ -120,9 +123,10 @@ DWORD UnPackArchive(const FilePath& archive,
archive_version->GetString()));
existing_archive = existing_archive.Append(installer::kInstallerDir);
existing_archive = existing_archive.Append(installer::kChromeArchive);
- if (int i = installer::ApplyDiffPatch(FilePath(existing_archive),
+ if (int i = installer::ApplyDiffPatch(existing_archive,
FilePath(unpacked_file),
- FilePath(uncompressed_archive))) {
+ uncompressed_archive,
+ &installer_state)) {
LOG(ERROR) << "Binary patching failed with error " << i;
return i;
}
@@ -130,6 +134,8 @@ DWORD UnPackArchive(const FilePath& archive,
*archive_type = installer::FULL_ARCHIVE_TYPE;
}
+ installer_state.UpdateStage(installer::UNPACKING);
+
// Unpack the uncompressed archive.
return LzmaUtil::UnPackArchive(uncompressed_archive.value(),
output_directory.value(), &unpacked_file);
@@ -671,16 +677,21 @@ installer::InstallStatus InstallProducts(
const MasterPreferences& prefs,
InstallerState* installer_state) {
DCHECK(installer_state);
+ const bool system_install = installer_state->system_install();
installer::InstallStatus install_status = installer::UNKNOWN_STATUS;
installer::ArchiveType archive_type = installer::UNKNOWN_ARCHIVE_TYPE;
bool incremental_install = false;
+ installer_state->UpdateStage(installer::PRECONDITIONS);
+ // The stage provides more fine-grained information than -multifail, so remove
+ // the -multifail suffix from the Google Update "ap" value.
+ BrowserDistribution::GetSpecificDistribution(installer_state->state_type())
+ ->UpdateInstallStatus(system_install, archive_type, install_status);
if (CheckPreInstallConditions(original_state, installer_state,
&install_status)) {
install_status = InstallProductsHelper(
original_state, cmd_line, prefs, *installer_state, &archive_type);
}
- const bool system_install = installer_state->system_install();
const Products& products = installer_state->products();
for (size_t i = 0; i < products.size(); ++i) {
@@ -693,6 +704,7 @@ installer::InstallStatus InstallProducts(
system_install, archive_type, install_status);
}
+ installer_state->UpdateStage(installer::NO_STAGE);
return install_status;
}
@@ -779,7 +791,8 @@ bool HandleNonInstallCmdLineOptions(const InstallationState& original_state,
installer::switches::kNewSetupExe);
if (!installer::ApplyDiffPatch(old_setup_exe,
FilePath(uncompressed_patch),
- new_setup_exe))
+ new_setup_exe,
+ installer_state))
status = installer::NEW_VERSION_UPDATED;
}
if (!temp_path.Delete()) {
diff --git a/chrome/installer/setup/setup_util.cc b/chrome/installer/setup/setup_util.cc
index c0e4ff4..9395c28 100644
--- a/chrome/installer/setup/setup_util.cc
+++ b/chrome/installer/setup/setup_util.cc
@@ -9,16 +9,21 @@
#include "base/file_util.h"
#include "base/logging.h"
#include "base/string_util.h"
+#include "chrome/installer/util/installer_state.h"
#include "chrome/installer/util/util_constants.h"
#include "courgette/courgette.h"
#include "third_party/bspatch/mbspatch.h"
int installer::ApplyDiffPatch(const FilePath& src,
- const FilePath& patch,
- const FilePath& dest) {
+ const FilePath& patch,
+ const FilePath& dest,
+ const InstallerState* installer_state) {
VLOG(1) << "Applying patch " << patch.value() << " to file " << src.value()
<< " and generating file " << dest.value();
+ if (installer_state != NULL)
+ installer_state->UpdateStage(installer::ENSEMBLE_PATCHING);
+
// Try Courgette first. Courgette checks the patch file first and fails
// quickly if the patch file does not have a valid Courgette header.
courgette::Status patch_status =
@@ -29,6 +34,10 @@ int installer::ApplyDiffPatch(const FilePath& src,
return 0;
VLOG(1) << "Failed to apply patch " << patch.value() << " using courgette.";
+
+ if (installer_state != NULL)
+ installer_state->UpdateStage(installer::BINARY_PATCHING);
+
return ApplyBinaryPatch(src.value().c_str(), patch.value().c_str(),
dest.value().c_str());
}
diff --git a/chrome/installer/setup/setup_util.h b/chrome/installer/setup/setup_util.h
index 22ddc14..267235f 100644
--- a/chrome/installer/setup/setup_util.h
+++ b/chrome/installer/setup/setup_util.h
@@ -13,17 +13,23 @@
class FilePath;
namespace installer {
- // Apply a diff patch to source file. First tries to apply it using courgette
- // since it checks for courgette header and fails quickly. If that fails
- // tries to apply the patch using regular bsdiff. Returns status code.
- int ApplyDiffPatch(const FilePath& src,
- const FilePath& patch,
- const FilePath& dest);
-
- // Find the version of Chrome from an install source directory.
- // Chrome_path should contain at least one version folder.
- // Returns the maximum version found or NULL if no version is found.
- Version* GetMaxVersionFromArchiveDir(const FilePath& chrome_path);
+
+class InstallerState;
+
+// Apply a diff patch to source file. First tries to apply it using courgette
+// since it checks for courgette header and fails quickly. If that fails
+// tries to apply the patch using regular bsdiff. Returns status code.
+// The installer stage is updated if |installer_state| is non-NULL.
+int ApplyDiffPatch(const FilePath& src,
+ const FilePath& patch,
+ const FilePath& dest,
+ const InstallerState* installer_state);
+
+// Find the version of Chrome from an install source directory.
+// Chrome_path should contain at least one version folder.
+// Returns the maximum version found or NULL if no version is found.
+Version* GetMaxVersionFromArchiveDir(const FilePath& chrome_path);
+
} // namespace installer
#endif // CHROME_INSTALLER_SETUP_SETUP_UTIL_H_
diff --git a/chrome/installer/setup/setup_util_unittest.cc b/chrome/installer/setup/setup_util_unittest.cc
index 37b1909..0730ba8 100644
--- a/chrome/installer/setup/setup_util_unittest.cc
+++ b/chrome/installer/setup/setup_util_unittest.cc
@@ -49,11 +49,12 @@ TEST_F(SetupUtilTest, ApplyDiffPatchTest) {
FilePath src = data_dir_.AppendASCII("archive1.7z");
FilePath patch = data_dir_.AppendASCII("archive.diff");
FilePath dest = work_dir.AppendASCII("archive2.7z");
- EXPECT_EQ(installer::ApplyDiffPatch(src, patch, dest), 0);
+ EXPECT_EQ(installer::ApplyDiffPatch(src, patch, dest, NULL), 0);
FilePath base = data_dir_.AppendASCII("archive2.7z");
EXPECT_TRUE(file_util::ContentsEqual(dest, base));
- EXPECT_EQ(installer::ApplyDiffPatch(FilePath(), FilePath(), FilePath()), 6);
+ EXPECT_EQ(installer::ApplyDiffPatch(FilePath(), FilePath(), FilePath(), NULL),
+ 6);
}
// Test that we are parsing Chrome version correctly.
diff --git a/chrome/installer/util/browser_distribution.h b/chrome/installer/util/browser_distribution.h
index 71f9acf..6c0df20 100644
--- a/chrome/installer/util/browser_distribution.h
+++ b/chrome/installer/util/browser_distribution.h
@@ -36,6 +36,7 @@ class BrowserDistribution {
CHROME_BROWSER,
CHROME_FRAME,
CHROME_BINARIES,
+ NUM_TYPES
};
static BrowserDistribution* GetDistribution();
diff --git a/chrome/installer/util/channel_info.cc b/chrome/installer/util/channel_info.cc
index 98f54d8..f2bb28d 100644
--- a/chrome/installer/util/channel_info.cc
+++ b/chrome/installer/util/channel_info.cc
@@ -19,6 +19,7 @@ const wchar_t kModChrome[] = L"-chrome";
const wchar_t kModChromeFrame[] = L"-chromeframe";
const wchar_t kModMultiInstall[] = L"-multi";
const wchar_t kModReadyMode[] = L"-readymode";
+const wchar_t kModStage[] = L"-stage:";
const wchar_t kSfxFull[] = L"-full";
const wchar_t kSfxMultiFail[] = L"-multifail";
@@ -28,6 +29,7 @@ const wchar_t* const kChannels[] = {
};
const wchar_t* const kModifiers[] = {
+ kModStage,
kModMultiInstall,
kModChrome,
kModChromeFrame,
@@ -38,6 +40,7 @@ const wchar_t* const kModifiers[] = {
};
enum ModifierIndex {
+ MOD_STAGE,
MOD_MULTI_INSTALL,
MOD_CHROME,
MOD_CHROME_FRAME,
@@ -52,29 +55,44 @@ COMPILE_ASSERT(NUM_MODIFIERS == arraysize(kModifiers),
kModifiers_disagrees_with_ModifierIndex_comma_they_must_match_bang);
// Returns true if the modifier is found, in which case |position| holds the
-// location at which the modifier was found.
+// location at which the modifier was found. The number of characters in the
+// modifier is returned in |length|, if non-NULL.
bool FindModifier(ModifierIndex index,
const std::wstring& ap_value,
- std::wstring::size_type* position) {
+ std::wstring::size_type* position,
+ std::wstring::size_type* length) {
DCHECK(position != NULL);
+ std::wstring::size_type mod_position = std::wstring::npos;
std::wstring::size_type mod_length =
std::wstring::traits_type::length(kModifiers[index]);
- for (std::wstring::size_type pos = 0; ; ) {
- *position = ap_value.find(kModifiers[index], pos, mod_length);
- if (*position == std::wstring::npos)
+ const bool mod_takes_arg = (kModifiers[index][mod_length - 1] == L':');
+ std::wstring::size_type pos = 0;
+ do {
+ mod_position = ap_value.find(kModifiers[index], pos, mod_length);
+ if (mod_position == std::wstring::npos)
+ return false; // Modifier not found.
+ pos = mod_position + mod_length;
+ // Modifiers that take an argument gobble up to the next separator or to the
+ // end.
+ if (mod_takes_arg) {
+ pos = ap_value.find(L'-', pos);
+ if (pos == std::wstring::npos)
+ pos = ap_value.size();
break;
- // The modifier must be either at the end of the string or followed by -.
- pos = *position + mod_length;
- if (pos == ap_value.size() || ap_value[pos] == L'-')
- return true;
- }
- return false;
+ }
+ // Regular modifiers must be followed by '-' or the end of the string.
+ } while (pos != ap_value.size() && ap_value[pos] != L'-');
+ DCHECK_NE(mod_position, std::wstring::npos);
+ *position = mod_position;
+ if (length != NULL)
+ *length = pos - mod_position;
+ return true;
}
bool HasModifier(ModifierIndex index, const std::wstring& ap_value) {
DCHECK(index >= 0 && index < NUM_MODIFIERS);
std::wstring::size_type position;
- return FindModifier(index, ap_value, &position);
+ return FindModifier(index, ap_value, &position, NULL);
}
std::wstring::size_type FindInsertionPoint(ModifierIndex index,
@@ -83,7 +101,7 @@ std::wstring::size_type FindInsertionPoint(ModifierIndex index,
std::wstring::size_type result;
for (int scan = index + 1; scan < NUM_MODIFIERS; ++scan) {
- if (FindModifier(static_cast<ModifierIndex>(scan), ap_value, &result))
+ if (FindModifier(static_cast<ModifierIndex>(scan), ap_value, &result, NULL))
return result;
}
@@ -95,7 +113,8 @@ bool SetModifier(ModifierIndex index, bool set, std::wstring* ap_value) {
DCHECK(index >= 0 && index < NUM_MODIFIERS);
DCHECK(ap_value);
std::wstring::size_type position;
- bool have_modifier = FindModifier(index, *ap_value, &position);
+ std::wstring::size_type length;
+ bool have_modifier = FindModifier(index, *ap_value, &position, &length);
if (set) {
if (!have_modifier) {
ap_value->insert(FindInsertionPoint(index, *ap_value), kModifiers[index]);
@@ -103,8 +122,7 @@ bool SetModifier(ModifierIndex index, bool set, std::wstring* ap_value) {
}
} else {
if (have_modifier) {
- ap_value->erase(position,
- std::wstring::traits_type::length(kModifiers[index]));
+ ap_value->erase(position, length);
return true;
}
}
@@ -120,8 +138,16 @@ bool ChannelInfo::Initialize(const RegKey& key) {
}
bool ChannelInfo::Write(RegKey* key) const {
- return (key->WriteValue(google_update::kRegApField, value_.c_str()) ==
- ERROR_SUCCESS);
+ DCHECK(key);
+ // Google Update deletes the value when it is empty, so we may as well, too.
+ LONG result = value_.empty() ?
+ key->DeleteValue(google_update::kRegApField) :
+ key->WriteValue(google_update::kRegApField, value_.c_str());
+ if (result != ERROR_SUCCESS) {
+ LOG(ERROR) << "Failed writing channel info; result: " << result;
+ return false;
+ }
+ return true;
}
bool ChannelInfo::GetChannelName(std::wstring* channel_name) const {
@@ -157,12 +183,12 @@ bool ChannelInfo::EqualsBaseOf(const ChannelInfo& other) const {
std::wstring::size_type this_base_end;
std::wstring::size_type other_base_end;
- if (!FindModifier(MOD_MULTI_INSTALL, value_, &this_base_end))
- this_base_end = FindInsertionPoint(MOD_MULTI_INSTALL, value_);
- if (!FindModifier(MOD_MULTI_INSTALL, other.value_, &other_base_end))
- other_base_end = FindInsertionPoint(MOD_MULTI_INSTALL, other.value_);
+ if (!FindModifier(MOD_STAGE, value_, &this_base_end, NULL))
+ this_base_end = FindInsertionPoint(MOD_STAGE, value_);
+ if (!FindModifier(MOD_STAGE, other.value_, &other_base_end, NULL))
+ other_base_end = FindInsertionPoint(MOD_STAGE, other.value_);
return value_.compare(0, this_base_end,
- other.value_.c_str(), other_base_end) == 0;
+ other.value_, 0, other_base_end) == 0;
}
bool ChannelInfo::IsCeee() const {
@@ -205,6 +231,44 @@ bool ChannelInfo::SetReadyMode(bool value) {
return SetModifier(MOD_READY_MODE, value, &value_);
}
+bool ChannelInfo::SetStage(const wchar_t* stage) {
+ std::wstring::size_type position;
+ std::wstring::size_type length;
+ bool have_modifier = FindModifier(MOD_STAGE, value_, &position, &length);
+ if (stage != NULL && *stage != L'\0') {
+ std::wstring stage_str(kModStage);
+ stage_str.append(stage);
+ if (!have_modifier) {
+ value_.insert(FindInsertionPoint(MOD_STAGE, value_), stage_str);
+ return true;
+ }
+ if (value_.compare(position, length, stage_str) != 0) {
+ value_.replace(position, length, stage_str);
+ return true;
+ }
+ } else {
+ if (have_modifier) {
+ value_.erase(position, length);
+ return true;
+ }
+ }
+ return false;
+}
+
+std::wstring ChannelInfo::GetStage() const {
+ std::wstring::size_type position;
+ std::wstring::size_type length;
+
+ if (FindModifier(MOD_STAGE, value_, &position, &length)) {
+ // Return the portion after the prefix.
+ std::wstring::size_type pfx_length =
+ std::wstring::traits_type::length(kModStage);
+ DCHECK_LE(pfx_length, length);
+ return value_.substr(position + pfx_length, length - pfx_length);
+ }
+ return std::wstring();
+}
+
bool ChannelInfo::HasFullSuffix() const {
return HasModifier(SFX_FULL, value_);
}
diff --git a/chrome/installer/util/channel_info.h b/chrome/installer/util/channel_info.h
index a5b895d..e9c46e1 100644
--- a/chrome/installer/util/channel_info.h
+++ b/chrome/installer/util/channel_info.h
@@ -80,6 +80,15 @@ class ChannelInfo {
// modified.
bool SetReadyMode(bool value);
+ // Adds the -stage: modifier with the given string (if |stage| is non-NULL) or
+ // removes the -stage: modifier (otherwise), returning true if the value is
+ // modified.
+ bool SetStage(const wchar_t* stage);
+
+ // Returns the string identifying the current stage, or an empty string if the
+ // -stage: modifier is not present in the value.
+ std::wstring GetStage() const;
+
// Returns true if the -full suffix is present in the value.
bool HasFullSuffix() const;
diff --git a/chrome/installer/util/channel_info_unittest.cc b/chrome/installer/util/channel_info_unittest.cc
index 5c03040..2dec71a 100644
--- a/chrome/installer/util/channel_info_unittest.cc
+++ b/chrome/installer/util/channel_info_unittest.cc
@@ -8,6 +8,8 @@
#include "chrome/installer/util/channel_info.h"
#include "testing/gtest/include/gtest/gtest.h"
+using installer::ChannelInfo;
+
namespace {
const std::wstring kChannelStable;
@@ -17,7 +19,7 @@ const std::wstring kChannelDev(L"dev");
} // namespace
TEST(ChannelInfoTest, Channels) {
- installer::ChannelInfo ci;
+ ChannelInfo ci;
std::wstring channel;
ci.set_value(L"");
@@ -58,7 +60,7 @@ TEST(ChannelInfoTest, Channels) {
}
TEST(ChannelInfoTest, CEEE) {
- installer::ChannelInfo ci;
+ ChannelInfo ci;
ci.set_value(L"");
EXPECT_TRUE(ci.SetCeee(true));
@@ -90,7 +92,7 @@ TEST(ChannelInfoTest, CEEE) {
}
TEST(ChannelInfoTest, FullInstall) {
- installer::ChannelInfo ci;
+ ChannelInfo ci;
ci.set_value(L"");
EXPECT_TRUE(ci.SetFullSuffix(true));
@@ -122,7 +124,7 @@ TEST(ChannelInfoTest, FullInstall) {
}
TEST(ChannelInfoTest, MultiInstall) {
- installer::ChannelInfo ci;
+ ChannelInfo ci;
ci.set_value(L"");
EXPECT_TRUE(ci.SetMultiInstall(true));
@@ -154,7 +156,7 @@ TEST(ChannelInfoTest, MultiInstall) {
}
TEST(ChannelInfoTest, Combinations) {
- installer::ChannelInfo ci;
+ ChannelInfo ci;
ci.set_value(L"2.0-beta-chromeframe");
EXPECT_FALSE(ci.IsChrome());
@@ -163,14 +165,22 @@ TEST(ChannelInfoTest, Combinations) {
}
TEST(ChannelInfoTest, EqualsBaseOf) {
- installer::ChannelInfo ci1;
- installer::ChannelInfo ci2;
+ ChannelInfo ci1;
+ ChannelInfo ci2;
std::pair<std::wstring, std::wstring> trues[] = {
std::make_pair(std::wstring(L""), std::wstring(L"")),
std::make_pair(std::wstring(L"2.0-beta"), std::wstring(L"2.0-beta")),
std::make_pair(std::wstring(L"-full"), std::wstring(L"-full")),
- std::make_pair(std::wstring(L""), std::wstring(L"-multi"))
+ std::make_pair(std::wstring(L""), std::wstring(L"-multi")),
+ std::make_pair(std::wstring(L"2.0-beta"),
+ std::wstring(L"2.0-beta-stage:finishing")),
+ std::make_pair(std::wstring(L"2.0-beta-stage:finishing"),
+ std::wstring(L"2.0-beta")),
+ std::make_pair(std::wstring(L"2.0-beta-stage:executing"),
+ std::wstring(L"2.0-beta-stage:finishing")),
+ std::make_pair(std::wstring(L"2.0-beta-stage:spamming"),
+ std::wstring(L"2.0-beta-stage:")),
};
for (int i = 0; i < arraysize(trues); ++i) {
std::pair<std::wstring, std::wstring>& the_pair = trues[i];
@@ -184,7 +194,9 @@ TEST(ChannelInfoTest, EqualsBaseOf) {
std::make_pair(std::wstring(L""), std::wstring(L"2.0-beta")),
std::make_pair(std::wstring(L"2.0-gamma"), std::wstring(L"2.0-beta")),
std::make_pair(std::wstring(L"spam-full"), std::wstring(L"-full")),
- std::make_pair(std::wstring(L"multi"), std::wstring(L"-multi"))
+ std::make_pair(std::wstring(L"multi"), std::wstring(L"-multi")),
+ std::make_pair(std::wstring(L"2.0-beta"),
+ std::wstring(L"2.0-stage:finishing")),
};
for (int i = 0; i < arraysize(falses); ++i) {
std::pair<std::wstring, std::wstring>& the_pair = falses[i];
@@ -194,3 +206,81 @@ TEST(ChannelInfoTest, EqualsBaseOf) {
<< the_pair.second;
}
}
+
+TEST(ChannelInfoTest, GetStage) {
+ ChannelInfo ci;
+
+ ci.set_value(L"");
+ EXPECT_EQ(L"", ci.GetStage());
+ ci.set_value(L"-stage");
+ EXPECT_EQ(L"", ci.GetStage());
+ ci.set_value(L"-stage:");
+ EXPECT_EQ(L"", ci.GetStage());
+ ci.set_value(L"-stage:spammy");
+ EXPECT_EQ(L"spammy", ci.GetStage());
+
+ ci.set_value(L"-multi");
+ EXPECT_EQ(L"", ci.GetStage());
+ ci.set_value(L"-stage-multi");
+ EXPECT_EQ(L"", ci.GetStage());
+ ci.set_value(L"-stage:-multi");
+ EXPECT_EQ(L"", ci.GetStage());
+ ci.set_value(L"-stage:spammy-multi");
+ EXPECT_EQ(L"spammy", ci.GetStage());
+
+ ci.set_value(L"2.0-beta-multi");
+ EXPECT_EQ(L"", ci.GetStage());
+ ci.set_value(L"2.0-beta-stage-multi");
+ EXPECT_EQ(L"", ci.GetStage());
+ ci.set_value(L"2.0-beta-stage:-multi");
+ EXPECT_EQ(L"", ci.GetStage());
+ ci.set_value(L"2.0-beta-stage:spammy-multi");
+ EXPECT_EQ(L"spammy", ci.GetStage());
+}
+
+TEST(ChannelInfoTest, SetStage) {
+ ChannelInfo ci;
+
+ ci.set_value(L"");
+ EXPECT_FALSE(ci.SetStage(NULL));
+ EXPECT_EQ(L"", ci.value());
+ EXPECT_TRUE(ci.SetStage(L"spammy"));
+ EXPECT_EQ(L"-stage:spammy", ci.value());
+ EXPECT_FALSE(ci.SetStage(L"spammy"));
+ EXPECT_EQ(L"-stage:spammy", ci.value());
+ EXPECT_TRUE(ci.SetStage(NULL));
+ EXPECT_EQ(L"", ci.value());
+ EXPECT_TRUE(ci.SetStage(L"spammy"));
+ EXPECT_TRUE(ci.SetStage(L""));
+ EXPECT_EQ(L"", ci.value());
+
+ ci.set_value(L"-multi");
+ EXPECT_FALSE(ci.SetStage(NULL));
+ EXPECT_EQ(L"-multi", ci.value());
+ EXPECT_TRUE(ci.SetStage(L"spammy"));
+ EXPECT_EQ(L"-stage:spammy-multi", ci.value());
+ EXPECT_FALSE(ci.SetStage(L"spammy"));
+ EXPECT_EQ(L"-stage:spammy-multi", ci.value());
+ EXPECT_TRUE(ci.SetStage(NULL));
+ EXPECT_EQ(L"-multi", ci.value());
+ EXPECT_TRUE(ci.SetStage(L"spammy"));
+ EXPECT_TRUE(ci.SetStage(L""));
+ EXPECT_EQ(L"-multi", ci.value());
+
+ ci.set_value(L"2.0-beta-multi");
+ EXPECT_FALSE(ci.SetStage(NULL));
+ EXPECT_EQ(L"2.0-beta-multi", ci.value());
+ EXPECT_TRUE(ci.SetStage(L"spammy"));
+ EXPECT_EQ(L"2.0-beta-stage:spammy-multi", ci.value());
+ EXPECT_FALSE(ci.SetStage(L"spammy"));
+ EXPECT_EQ(L"2.0-beta-stage:spammy-multi", ci.value());
+ EXPECT_TRUE(ci.SetStage(NULL));
+ EXPECT_EQ(L"2.0-beta-multi", ci.value());
+ EXPECT_TRUE(ci.SetStage(L"spammy"));
+ EXPECT_TRUE(ci.SetStage(L""));
+ EXPECT_EQ(L"2.0-beta-multi", ci.value());
+
+ ci.set_value(L"2.0-beta-stage:-multi");
+ EXPECT_TRUE(ci.SetStage(NULL));
+ EXPECT_EQ(L"2.0-beta-multi", ci.value());
+}
diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc
index 4aec77b..ea5d9ce 100644
--- a/chrome/installer/util/install_util.cc
+++ b/chrome/installer/util/install_util.cc
@@ -33,6 +33,36 @@
using base::win::RegKey;
using installer::ProductState;
+namespace {
+
+const wchar_t kStageBinaryPatching[] = L"binary_patching";
+const wchar_t kStageBuilding[] = L"building";
+const wchar_t kStageEnsemblePatching[] = L"ensemble_patching";
+const wchar_t kStageExecuting[] = L"executing";
+const wchar_t kStageFinishing[] = L"finishing";
+const wchar_t kStagePreconditions[] = L"preconditions";
+const wchar_t kStageRollingback[] = L"rollingback";
+const wchar_t kStageUncompressing[] = L"uncompressing";
+const wchar_t kStageUnpacking[] = L"unpacking";
+
+const wchar_t* const kStages[] = {
+ NULL,
+ kStagePreconditions,
+ kStageUncompressing,
+ kStageEnsemblePatching,
+ kStageBinaryPatching,
+ kStageUnpacking,
+ kStageBuilding,
+ kStageExecuting,
+ kStageRollingback,
+ kStageFinishing
+};
+
+COMPILE_ASSERT(installer::NUM_STAGES == arraysize(kStages),
+ kStages_disagrees_with_Stage_comma_they_must_match_bang);
+
+} // namespace
+
bool InstallUtil::ExecuteExeAsAdmin(const CommandLine& cmd, DWORD* exit_code) {
FilePath::StringType program(cmd.GetProgram().value());
DCHECK(!program.empty());
@@ -144,6 +174,31 @@ void InstallUtil::WriteInstallerResult(bool system_install,
LOG(ERROR) << "Failed to record installer error information in registry.";
}
+void InstallUtil::UpdateInstallerStage(bool system_install,
+ const std::wstring& state_key_path,
+ installer::InstallerStage stage) {
+ DCHECK_LE(static_cast<installer::InstallerStage>(0), stage);
+ DCHECK_GT(installer::NUM_STAGES, stage);
+ const HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+ RegKey state_key;
+ LONG result = state_key.Open(root, state_key_path.c_str(),
+ KEY_QUERY_VALUE | KEY_SET_VALUE);
+ if (result == ERROR_SUCCESS) {
+ // TODO(grt): switch to using Google Update's new InstallerExtraCode1 value
+ // once it exists. In the meantime, encode the stage into the channel name.
+ installer::ChannelInfo channel_info;
+ // This will return false if the "ap" value isn't present, which is fine.
+ channel_info.Initialize(state_key);
+ if (channel_info.SetStage(kStages[stage]) &&
+ !channel_info.Write(&state_key)) {
+ LOG(ERROR) << "Failed writing installer stage to " << state_key_path;
+ }
+ } else {
+ LOG(ERROR) << "Failed opening " << state_key_path
+ << " to update installer stage; result: " << result;
+ }
+}
+
bool InstallUtil::IsPerUserInstall(const wchar_t* const exe_path) {
wchar_t program_files_path[MAX_PATH] = {0};
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL,
diff --git a/chrome/installer/util/install_util.h b/chrome/installer/util/install_util.h
index 98cbc6d..e03a4f9 100644
--- a/chrome/installer/util/install_util.h
+++ b/chrome/installer/util/install_util.h
@@ -64,6 +64,13 @@ class InstallUtil {
int string_resource_id,
const std::wstring* const launch_cmd);
+ // Update the installer stage reported by Google Update. |state_key_path|
+ // should be obtained via the state_key method of an InstallerState instance
+ // created before the machine state is modified by the installer.
+ static void UpdateInstallerStage(bool system_install,
+ const std::wstring& state_key_path,
+ installer::InstallerStage stage);
+
// Returns true if this installation path is per user, otherwise returns
// false (per machine install, meaning: the exe_path contains path to
// Program Files).
diff --git a/chrome/installer/util/install_util_unittest.cc b/chrome/installer/util/install_util_unittest.cc
index 37e38bc..f67d689 100644
--- a/chrome/installer/util/install_util_unittest.cc
+++ b/chrome/installer/util/install_util_unittest.cc
@@ -8,6 +8,7 @@
#include "base/command_line.h"
#include "base/win/registry.h"
#include "chrome/installer/util/browser_distribution.h"
+#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/installation_state.h"
#include "chrome/installer/util/installer_state.h"
#include "chrome/installer/util/install_util.h"
@@ -121,3 +122,53 @@ TEST_F(InstallUtilTest, GetCurrentDate) {
EXPECT_TRUE(SystemTimeToFileTime(&systime, &ft));
}
}
+
+TEST_F(InstallUtilTest, UpdateInstallerStage) {
+ const bool system_level = false;
+ const HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+ std::wstring state_key_path(L"PhonyClientState");
+
+ // Update the stage when there's no "ap" value.
+ {
+ TempRegKeyOverride override(root, L"root_inst_res");
+ RegKey(root, state_key_path.c_str(), KEY_SET_VALUE);
+ InstallUtil::UpdateInstallerStage(system_level, state_key_path,
+ installer::BUILDING);
+ std::wstring value;
+ EXPECT_EQ(ERROR_SUCCESS,
+ RegKey(root, state_key_path.c_str(), KEY_QUERY_VALUE)
+ .ReadValue(google_update::kRegApField, &value));
+ EXPECT_EQ(L"-stage:building", value);
+ }
+ TempRegKeyOverride::DeleteAllTempKeys();
+
+ // Update the stage when there is an "ap" value.
+ {
+ TempRegKeyOverride override(root, L"root_inst_res");
+ RegKey(root, state_key_path.c_str(), KEY_SET_VALUE)
+ .WriteValue(google_update::kRegApField, L"2.0-dev");
+ InstallUtil::UpdateInstallerStage(system_level, state_key_path,
+ installer::BUILDING);
+ std::wstring value;
+ EXPECT_EQ(ERROR_SUCCESS,
+ RegKey(root, state_key_path.c_str(), KEY_QUERY_VALUE)
+ .ReadValue(google_update::kRegApField, &value));
+ EXPECT_EQ(L"2.0-dev-stage:building", value);
+ }
+ TempRegKeyOverride::DeleteAllTempKeys();
+
+ // Clear the stage.
+ {
+ TempRegKeyOverride override(root, L"root_inst_res");
+ RegKey(root, state_key_path.c_str(), KEY_SET_VALUE)
+ .WriteValue(google_update::kRegApField, L"2.0-dev-stage:building");
+ InstallUtil::UpdateInstallerStage(system_level, state_key_path,
+ installer::NO_STAGE);
+ std::wstring value;
+ EXPECT_EQ(ERROR_SUCCESS,
+ RegKey(root, state_key_path.c_str(), KEY_QUERY_VALUE)
+ .ReadValue(google_update::kRegApField, &value));
+ EXPECT_EQ(L"2.0-dev", value);
+ }
+ TempRegKeyOverride::DeleteAllTempKeys();
+}
diff --git a/chrome/installer/util/installer_state.cc b/chrome/installer/util/installer_state.cc
index df2da8d..4f1a772 100644
--- a/chrome/installer/util/installer_state.cc
+++ b/chrome/installer/util/installer_state.cc
@@ -14,8 +14,10 @@
#include "base/scoped_ptr.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
+#include "base/win/registry.h"
#include "chrome/installer/util/delete_tree_work_item.h"
#include "chrome/installer/util/helper.h"
+#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/installation_state.h"
#include "chrome/installer/util/master_preferences.h"
#include "chrome/installer/util/master_preferences_constants.h"
@@ -447,4 +449,77 @@ bool InstallerState::SetChannelFlags(bool set,
return modified;
}
+void InstallerState::UpdateStage(installer::InstallerStage stage) const {
+ InstallUtil::UpdateInstallerStage(system_install(), state_key_, stage);
+}
+
+void InstallerState::UpdateChannels() const {
+ if (operation_ != MULTI_INSTALL && operation_ != MULTI_UPDATE) {
+ VLOG(1) << "InstallerState::UpdateChannels noop: " << operation_;
+ return;
+ }
+
+ // Update the "ap" value for the product being installed/updated. We get the
+ // current value from the registry since the InstallationState instance used
+ // by the bulk of the installer does not track changes made by UpdateStage.
+ // Create the app's ClientState key if it doesn't exist.
+ ChannelInfo channel_info;
+ base::win::RegKey state_key;
+ LONG result = state_key.Create(root_key_, state_key_.c_str(),
+ KEY_QUERY_VALUE | KEY_SET_VALUE);
+ if (result == ERROR_SUCCESS) {
+ channel_info.Initialize(state_key);
+
+ // This is a multi-install product.
+ bool modified = channel_info.SetMultiInstall(true);
+
+ // Add the appropriate modifiers for all products and their options.
+ modified |= SetChannelFlags(true, &channel_info);
+
+ VLOG(1) << "ap: " << channel_info.value();
+
+ // Write the results if needed.
+ if (modified)
+ channel_info.Write(&state_key);
+
+ // Remove the -stage: modifier since we don't want to propagate that to the
+ // other app_guids.
+ channel_info.SetStage(NULL);
+
+ // Synchronize the other products and the package with this one.
+ ChannelInfo other_info;
+ for (int i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
+ BrowserDistribution::Type type =
+ static_cast<BrowserDistribution::Type>(i);
+ // Skip the app_guid we started with.
+ if (type == state_type_)
+ continue;
+ BrowserDistribution* dist = NULL;
+ // Always operate on the binaries.
+ if (i == BrowserDistribution::CHROME_BINARIES) {
+ dist = multi_package_distribution_;
+ } else {
+ const Product* product = FindProduct(type);
+ // Skip this one if it's for a product we're not operating on.
+ if (product == NULL)
+ continue;
+ dist = product->distribution();
+ }
+ result = state_key.Create(root_key_, dist->GetStateKey().c_str(),
+ KEY_QUERY_VALUE | KEY_SET_VALUE);
+ if (result == ERROR_SUCCESS) {
+ other_info.Initialize(state_key);
+ if (!other_info.Equals(channel_info))
+ channel_info.Write(&state_key);
+ } else {
+ LOG(ERROR) << "Failed opening key " << dist->GetStateKey()
+ << " to update app channels; result: " << result;
+ }
+ }
+ } else {
+ LOG(ERROR) << "Failed opening key " << state_key_
+ << " to update app channels; result: " << result;
+ }
+}
+
} // namespace installer
diff --git a/chrome/installer/util/installer_state.h b/chrome/installer/util/installer_state.h
index 8a2dbea..4d74b42 100644
--- a/chrome/installer/util/installer_state.h
+++ b/chrome/installer/util/installer_state.h
@@ -16,6 +16,7 @@
#include "base/scoped_vector.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/product.h"
+#include "chrome/installer/util/util_constants.h"
#if defined(OS_WIN)
#include <windows.h> // NOLINT
@@ -158,6 +159,13 @@ class InstallerState {
bool SetChannelFlags(bool set, ChannelInfo* channel_info) const;
+ // See InstallUtil::UpdateInstallerStage.
+ void UpdateStage(installer::InstallerStage stage) const;
+
+ // For a MULTI_INSTALL or MULTI_UPDATE operation, updates the Google Update
+ // "ap" values for all products being operated on.
+ void UpdateChannels() const;
+
protected:
FilePath GetDefaultProductInstallPath(BrowserDistribution* dist) const;
bool CanAddProduct(const Product& product, const FilePath* product_dir) const;
diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h
index 5c83f30..b7e5347 100644
--- a/chrome/installer/util/util_constants.h
+++ b/chrome/installer/util/util_constants.h
@@ -9,6 +9,8 @@
#define CHROME_INSTALLER_UTIL_UTIL_CONSTANTS_H_
#pragma once
+#include "base/basictypes.h"
+
namespace installer {
// Return status of installer
@@ -80,6 +82,24 @@ enum ArchiveType {
INCREMENTAL_ARCHIVE_TYPE // Incremental or differential archive.
};
+// Stages of an installation reported through Google Update on failure.
+enum InstallerStage {
+ NO_STAGE, // 0: No stage to report.
+ PRECONDITIONS, // 1: Evaluating pre-install conditions.
+ UNCOMPRESSING, // 2: Uncompressing chrome.packed.7z.
+ ENSEMBLE_PATCHING, // 3: Patching chrome.7z using courgette.
+ BINARY_PATCHING, // 4: Patching chrome.7z using bspatch.
+ UNPACKING, // 5: Unpacking chrome.7z.
+ BUILDING, // 6: Building the install work item list.
+ EXECUTING, // 7: Executing the install work item list.
+ ROLLINGBACK, // 8: Rolling-back the install work item list.
+ FINISHING, // 9: Finishing after the install work item list.
+ NUM_STAGES // 10: The number of stages.
+};
+
+COMPILE_ASSERT(FINISHING == 9,
+ never_ever_ever_change_InstallerStage_values_bang);
+
namespace switches {
extern const char kCeee[];
extern const char kChrome[];