1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/installer/util/installer_state.h"
11 #include "base/command_line.h"
12 #include "base/file_util.h"
13 #include "base/file_version_info.h"
14 #include "base/files/file_enumerator.h"
15 #include "base/logging.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/win/registry.h"
20 #include "base/win/scoped_handle.h"
21 #include "chrome/installer/util/delete_tree_work_item.h"
22 #include "chrome/installer/util/helper.h"
23 #include "chrome/installer/util/install_util.h"
24 #include "chrome/installer/util/installation_state.h"
25 #include "chrome/installer/util/master_preferences.h"
26 #include "chrome/installer/util/master_preferences_constants.h"
27 #include "chrome/installer/util/product.h"
28 #include "chrome/installer/util/work_item.h"
29 #include "chrome/installer/util/work_item_list.h"
33 bool InstallerState::IsMultiInstallUpdate(const MasterPreferences& prefs,
34 const InstallationState& machine_state) {
35 // First, is the package present?
36 const ProductState* package =
37 machine_state.GetProductState(level_ == SYSTEM_LEVEL,
38 BrowserDistribution::CHROME_BINARIES);
39 if (package == NULL) {
40 // The multi-install package has not been installed, so it certainly isn't
45 BrowserDistribution::Type types[2];
47 if (prefs.install_chrome())
48 types[num_types++] = BrowserDistribution::CHROME_BROWSER;
49 if (prefs.install_chrome_frame())
50 types[num_types++] = BrowserDistribution::CHROME_FRAME;
52 for (const BrowserDistribution::Type* scan = &types[0],
53 *end = &types[num_types]; scan != end; ++scan) {
54 const ProductState* product =
55 machine_state.GetProductState(level_ == SYSTEM_LEVEL, *scan);
56 if (product == NULL) {
57 VLOG(2) << "It seems that distribution type " << *scan
58 << " is being installed for the first time.";
61 if (!product->channel().Equals(package->channel())) {
62 VLOG(2) << "It seems that distribution type " << *scan
63 << " is being over installed.";
68 VLOG(2) << "It seems that the package is being updated.";
73 InstallerState::InstallerState()
74 : operation_(UNINITIALIZED),
75 multi_package_distribution_(NULL),
76 level_(UNKNOWN_LEVEL),
77 package_type_(UNKNOWN_PACKAGE_TYPE),
78 state_type_(BrowserDistribution::CHROME_BROWSER),
81 verbose_logging_(false),
82 ensure_google_update_present_(false) {
85 InstallerState::InstallerState(Level level)
86 : operation_(UNINITIALIZED),
87 multi_package_distribution_(NULL),
88 level_(UNKNOWN_LEVEL),
89 package_type_(UNKNOWN_PACKAGE_TYPE),
90 state_type_(BrowserDistribution::CHROME_BROWSER),
93 verbose_logging_(false),
94 ensure_google_update_present_(false) {
95 // Use set_level() so that root_key_ is updated properly.
99 void InstallerState::Initialize(const CommandLine& command_line,
100 const MasterPreferences& prefs,
101 const InstallationState& machine_state) {
103 if (!prefs.GetBool(master_preferences::kSystemLevel, &pref_bool))
105 set_level(pref_bool ? SYSTEM_LEVEL : USER_LEVEL);
107 if (!prefs.GetBool(master_preferences::kVerboseLogging, &verbose_logging_))
108 verbose_logging_ = false;
110 if (!prefs.GetBool(master_preferences::kMultiInstall, &pref_bool))
112 set_package_type(pref_bool ? MULTI_PACKAGE : SINGLE_PACKAGE);
114 if (!prefs.GetBool(master_preferences::kMsi, &msi_))
117 ensure_google_update_present_ =
118 command_line.HasSwitch(installer::switches::kEnsureGoogleUpdatePresent);
120 const bool is_uninstall = command_line.HasSwitch(switches::kUninstall);
122 if (prefs.install_chrome()) {
123 Product* p = AddProductFromPreferences(
124 BrowserDistribution::CHROME_BROWSER, prefs, machine_state);
125 VLOG(1) << (is_uninstall ? "Uninstall" : "Install")
126 << " distribution: " << p->distribution()->GetDisplayName();
128 if (prefs.install_chrome_frame()) {
129 Product* p = AddProductFromPreferences(
130 BrowserDistribution::CHROME_FRAME, prefs, machine_state);
131 VLOG(1) << (is_uninstall ? "Uninstall" : "Install")
132 << " distribution: " << p->distribution()->GetDisplayName();
135 if (prefs.install_chrome_app_launcher()) {
136 Product* p = AddProductFromPreferences(
137 BrowserDistribution::CHROME_APP_HOST, prefs, machine_state);
138 VLOG(1) << (is_uninstall ? "Uninstall" : "Install")
139 << " distribution: " << p->distribution()->GetDisplayName();
142 if (!is_uninstall && is_multi_install()) {
143 bool need_binaries = false;
144 if (FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
145 // App Host will happily use Chrome at system level, or binaries at system
146 // level, even if app host is user level.
147 const ProductState* chrome_state = machine_state.GetProductState(
148 true, // system level
149 BrowserDistribution::CHROME_BROWSER);
150 // If Chrome is at system-level, multi- or otherwise. We'll use it.
152 const ProductState* binaries_state = machine_state.GetProductState(
153 true, // system level
154 BrowserDistribution::CHROME_BINARIES);
156 need_binaries = true;
160 // Chrome/Chrome Frame multi need Binaries at their own level.
161 if (FindProduct(BrowserDistribution::CHROME_BROWSER))
162 need_binaries = true;
164 if (FindProduct(BrowserDistribution::CHROME_FRAME))
165 need_binaries = true;
167 if (need_binaries && !FindProduct(BrowserDistribution::CHROME_BINARIES)) {
168 // Force binaries to be installed/updated.
169 Product* p = AddProductFromPreferences(
170 BrowserDistribution::CHROME_BINARIES, prefs, machine_state);
171 VLOG(1) << "Install distribution: "
172 << p->distribution()->GetDisplayName();
176 if (is_uninstall && prefs.is_multi_install()) {
177 if (FindProduct(BrowserDistribution::CHROME_BROWSER)) {
178 // Uninstall each product of type |type| listed below based on the
179 // presence or absence of |switch_name| in that product's uninstall
182 BrowserDistribution::Type type;
183 const char* switch_name;
184 bool switch_expected;
185 } conditional_additions[] = {
186 // If Chrome Frame is installed in Ready Mode, remove it with Chrome.
187 { BrowserDistribution::CHROME_FRAME,
188 switches::kChromeFrameReadyMode,
190 // If the App Host is installed, but not the App Launcher, remove it
191 // with Chrome. Note however that for system-level Chrome uninstalls,
192 // any installed user-level App Host will remain even if there is no
193 // App Launcher present (the orphaned app_host.exe will prompt the user
194 // for further action when executed).
195 { BrowserDistribution::CHROME_APP_HOST,
196 switches::kChromeAppLauncher,
200 for (size_t i = 0; i < arraysize(conditional_additions); ++i) {
201 const ProductState* product_state = machine_state.GetProductState(
202 system_install(), conditional_additions[i].type);
203 if (product_state != NULL &&
204 product_state->uninstall_command().HasSwitch(
205 conditional_additions[i].switch_name) ==
206 conditional_additions[i].switch_expected &&
207 !FindProduct(conditional_additions[i].type)) {
208 Product* p = AddProductFromPreferences(
209 conditional_additions[i].type, prefs, machine_state);
210 VLOG(1) << "Uninstall distribution: "
211 << p->distribution()->GetDisplayName();
216 bool keep_binaries = false;
217 // Look for a multi-install product that is not the binaries and that is not
218 // being uninstalled. If not found, binaries are uninstalled too.
219 for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
220 BrowserDistribution::Type type =
221 static_cast<BrowserDistribution::Type>(i);
223 if (type == BrowserDistribution::CHROME_BINARIES)
226 const ProductState* product_state =
227 machine_state.GetProductState(system_install(), type);
228 if (product_state == NULL) {
229 // The product is not installed.
233 if (!product_state->is_multi_install() &&
234 type != BrowserDistribution::CHROME_BROWSER) {
235 // The product is not sharing the binaries. It is ordinarily impossible
236 // for single-install Chrome to be installed along with any
237 // multi-install product. Treat single-install Chrome the same as any
238 // multi-install product just in case the impossible happens.
242 // The product is installed.
244 if (!FindProduct(type)) {
245 // The product is not being uninstalled.
246 if (type != BrowserDistribution::CHROME_APP_HOST) {
247 keep_binaries = true;
250 // If binaries/chrome are at system-level, we can discard them at
252 if (!machine_state.GetProductState(
253 true, // system-level
254 BrowserDistribution::CHROME_BROWSER) &&
255 !machine_state.GetProductState(
256 true, // system-level
257 BrowserDistribution::CHROME_BINARIES)) {
258 // ... otherwise keep them.
259 keep_binaries = true;
266 // The product is being uninstalled.
268 if (!keep_binaries &&
269 machine_state.GetProductState(system_install(),
270 BrowserDistribution::CHROME_BINARIES)) {
271 Product* p = AddProductFromPreferences(
272 BrowserDistribution::CHROME_BINARIES, prefs, machine_state);
273 VLOG(1) << (is_uninstall ? "Uninstall" : "Install")
274 << " distribution: " << p->distribution()->GetDisplayName();
278 BrowserDistribution* operand = NULL;
281 operation_ = UNINSTALL;
282 } else if (!prefs.is_multi_install()) {
283 // For a single-install, the current browser dist is the operand.
284 operand = BrowserDistribution::GetDistribution();
285 operation_ = SINGLE_INSTALL_OR_UPDATE;
286 } else if (IsMultiInstallUpdate(prefs, machine_state)) {
287 // Updates driven by Google Update take place under the multi-installer's
289 operand = multi_package_distribution_;
290 operation_ = MULTI_UPDATE;
292 operation_ = MULTI_INSTALL;
295 // Initial, over, and un-installs will take place under one of the
296 // product app guids (Chrome, Chrome Frame, App Host, or Binaries, in order of
298 if (operand == NULL) {
299 BrowserDistribution::Type operand_distribution_type =
300 BrowserDistribution::CHROME_BINARIES;
301 if (prefs.install_chrome())
302 operand_distribution_type = BrowserDistribution::CHROME_BROWSER;
303 else if (prefs.install_chrome_frame())
304 operand_distribution_type = BrowserDistribution::CHROME_FRAME;
305 else if (prefs.install_chrome_app_launcher())
306 operand_distribution_type = BrowserDistribution::CHROME_APP_HOST;
308 operand = BrowserDistribution::GetSpecificDistribution(
309 operand_distribution_type);
312 state_key_ = operand->GetStateKey();
313 state_type_ = operand->GetType();
315 // Parse --critical-update-version=W.X.Y.Z
316 std::string critical_version_value(
317 command_line.GetSwitchValueASCII(switches::kCriticalUpdateVersion));
318 critical_update_version_ = Version(critical_version_value);
321 void InstallerState::set_level(Level level) {
325 root_key_ = HKEY_CURRENT_USER;
328 root_key_ = HKEY_LOCAL_MACHINE;
331 DCHECK(level == UNKNOWN_LEVEL);
332 level_ = UNKNOWN_LEVEL;
338 void InstallerState::set_package_type(PackageType type) {
339 package_type_ = type;
342 multi_package_distribution_ = NULL;
345 multi_package_distribution_ =
346 BrowserDistribution::GetSpecificDistribution(
347 BrowserDistribution::CHROME_BINARIES);
350 DCHECK(type == UNKNOWN_PACKAGE_TYPE);
351 package_type_ = UNKNOWN_PACKAGE_TYPE;
352 multi_package_distribution_ = NULL;
357 // Returns the Chrome binaries directory for multi-install or |dist|'s directory
359 base::FilePath InstallerState::GetDefaultProductInstallPath(
360 BrowserDistribution* dist) const {
362 DCHECK(package_type_ != UNKNOWN_PACKAGE_TYPE);
364 if (package_type_ == SINGLE_PACKAGE) {
365 return GetChromeInstallPath(system_install(), dist);
367 return GetChromeInstallPath(system_install(),
368 BrowserDistribution::GetSpecificDistribution(
369 BrowserDistribution::CHROME_BINARIES));
373 // Evaluates a product's eligibility for participation in this operation.
374 // We never expect these checks to fail, hence they all terminate the process in
375 // debug builds. See the log messages for details.
376 bool InstallerState::CanAddProduct(const Product& product,
377 const base::FilePath* product_dir) const {
378 switch (package_type_) {
380 if (!products_.empty()) {
381 LOG(DFATAL) << "Cannot process more than one single-install product.";
386 if (!product.HasOption(kOptionMultiInstall)) {
387 LOG(DFATAL) << "Cannot process a single-install product with a "
388 "multi-install state.";
391 if (FindProduct(product.distribution()->GetType()) != NULL) {
392 LOG(DFATAL) << "Cannot process more than one product of the same type.";
395 if (!target_path_.empty()) {
396 base::FilePath default_dir;
397 if (product_dir == NULL)
398 default_dir = GetDefaultProductInstallPath(product.distribution());
399 if (!base::FilePath::CompareEqualIgnoreCase(
400 (product_dir == NULL ? default_dir : *product_dir).value(),
401 target_path_.value())) {
402 LOG(DFATAL) << "Cannot process products in different directories.";
408 DCHECK_EQ(UNKNOWN_PACKAGE_TYPE, package_type_);
414 // Adds |product|, installed in |product_dir| to this object's collection. If
415 // |product_dir| is NULL, the product's default install location is used.
416 // Returns NULL if |product| is incompatible with this object. Otherwise,
417 // returns a pointer to the product (ownership is held by this object).
418 Product* InstallerState::AddProductInDirectory(
419 const base::FilePath* product_dir,
420 scoped_ptr<Product>* product) {
421 DCHECK(product != NULL);
422 DCHECK(product->get() != NULL);
423 const Product& the_product = *product->get();
425 if (!CanAddProduct(the_product, product_dir))
428 if (package_type_ == UNKNOWN_PACKAGE_TYPE) {
429 set_package_type(the_product.HasOption(kOptionMultiInstall) ?
430 MULTI_PACKAGE : SINGLE_PACKAGE);
433 if (target_path_.empty()) {
434 if (product_dir == NULL)
435 target_path_ = GetDefaultProductInstallPath(the_product.distribution());
437 target_path_ = *product_dir;
440 if (state_key_.empty())
441 state_key_ = the_product.distribution()->GetStateKey();
443 products_.push_back(product->release());
444 return products_[products_.size() - 1];
447 Product* InstallerState::AddProduct(scoped_ptr<Product>* product) {
448 return AddProductInDirectory(NULL, product);
451 // Adds a product of type |distribution_type| constructed on the basis of
452 // |prefs|, setting this object's msi flag if the product is represented in
453 // |machine_state| and is msi-installed. Returns the product that was added,
454 // or NULL if |state| is incompatible with this object. Ownership is not passed
456 Product* InstallerState::AddProductFromPreferences(
457 BrowserDistribution::Type distribution_type,
458 const MasterPreferences& prefs,
459 const InstallationState& machine_state) {
460 scoped_ptr<Product> product_ptr(
461 new Product(BrowserDistribution::GetSpecificDistribution(
462 distribution_type)));
463 product_ptr->InitializeFromPreferences(prefs);
465 Product* product = AddProductInDirectory(NULL, &product_ptr);
467 if (product != NULL && !msi_) {
468 const ProductState* product_state = machine_state.GetProductState(
469 system_install(), distribution_type);
470 if (product_state != NULL)
471 msi_ = product_state->is_msi();
477 Product* InstallerState::AddProductFromState(
478 BrowserDistribution::Type type,
479 const ProductState& state) {
480 scoped_ptr<Product> product_ptr(
481 new Product(BrowserDistribution::GetSpecificDistribution(type)));
482 product_ptr->InitializeFromUninstallCommand(state.uninstall_command());
484 // Strip off <version>/Installer/setup.exe; see GetInstallerDirectory().
485 base::FilePath product_dir =
486 state.GetSetupPath().DirName().DirName().DirName();
488 Product* product = AddProductInDirectory(&product_dir, &product_ptr);
491 msi_ |= state.is_msi();
496 bool InstallerState::system_install() const {
497 DCHECK(level_ == USER_LEVEL || level_ == SYSTEM_LEVEL);
498 return level_ == SYSTEM_LEVEL;
501 bool InstallerState::is_multi_install() const {
502 DCHECK(package_type_ == SINGLE_PACKAGE || package_type_ == MULTI_PACKAGE);
503 return package_type_ != SINGLE_PACKAGE;
506 bool InstallerState::RemoveProduct(const Product* product) {
507 ScopedVector<Product>::iterator it =
508 std::find(products_.begin(), products_.end(), product);
509 if (it != products_.end()) {
510 products_.weak_erase(it);
516 const Product* InstallerState::FindProduct(
517 BrowserDistribution::Type distribution_type) const {
518 for (Products::const_iterator scan = products_.begin(), end = products_.end();
519 scan != end; ++scan) {
520 if ((*scan)->is_type(distribution_type))
526 Version* InstallerState::GetCurrentVersion(
527 const InstallationState& machine_state) const {
528 DCHECK(!products_.empty());
529 scoped_ptr<Version> current_version;
530 // If we're doing a multi-install, the current version may be either an
531 // existing multi or an existing single product that is being migrated
532 // in place (i.e., Chrome). In the latter case, there is no existing
533 // CHROME_BINARIES installation so we need to search for the product.
534 BrowserDistribution::Type prod_type;
535 if (package_type_ == MULTI_PACKAGE) {
536 prod_type = BrowserDistribution::CHROME_BINARIES;
537 if (machine_state.GetProductState(level_ == SYSTEM_LEVEL,
538 prod_type) == NULL) {
539 // Search for a product on which we're operating that is installed in our
541 Products::const_iterator end = products().end();
542 for (Products::const_iterator scan = products().begin(); scan != end;
544 BrowserDistribution::Type product_type =
545 (*scan)->distribution()->GetType();
546 const ProductState* state =
547 machine_state.GetProductState(level_ == SYSTEM_LEVEL, product_type);
548 if (state != NULL && target_path_.IsParent(state->GetSetupPath())) {
549 prod_type = product_type;
555 prod_type = products_[0]->distribution()->GetType();
557 const ProductState* product_state =
558 machine_state.GetProductState(level_ == SYSTEM_LEVEL, prod_type);
560 if (product_state != NULL) {
561 const Version* version = NULL;
563 // Be aware that there might be a pending "new_chrome.exe" already in the
564 // installation path. If so, we use old_version, which holds the version of
565 // "chrome.exe" itself.
566 if (base::PathExists(target_path().Append(kChromeNewExe)))
567 version = product_state->old_version();
570 version = &product_state->version();
572 current_version.reset(new Version(*version));
575 return current_version.release();
578 Version InstallerState::DetermineCriticalVersion(
579 const Version* current_version,
580 const Version& new_version) const {
581 DCHECK(current_version == NULL || current_version->IsValid());
582 DCHECK(new_version.IsValid());
583 if (critical_update_version_.IsValid() &&
584 (current_version == NULL ||
585 (current_version->CompareTo(critical_update_version_) < 0)) &&
586 new_version.CompareTo(critical_update_version_) >= 0) {
587 return critical_update_version_;
592 bool InstallerState::IsChromeFrameRunning(
593 const InstallationState& machine_state) const {
594 return AnyExistsAndIsInUse(machine_state, CHROME_FRAME_DLL);
597 bool InstallerState::AreBinariesInUse(
598 const InstallationState& machine_state) const {
599 return AnyExistsAndIsInUse(
601 CHROME_FRAME_DLL | CHROME_FRAME_HELPER_EXE | CHROME_DLL);
604 base::FilePath InstallerState::GetInstallerDirectory(
605 const Version& version) const {
606 return target_path().Append(ASCIIToWide(version.GetString()))
607 .Append(kInstallerDir);
611 bool InstallerState::IsFileInUse(const base::FilePath& file) {
612 // Call CreateFile with a share mode of 0 which should cause this to fail
613 // with ERROR_SHARING_VIOLATION if the file exists and is in-use.
614 return !base::win::ScopedHandle(CreateFile(file.value().c_str(),
615 GENERIC_WRITE, 0, NULL,
616 OPEN_EXISTING, 0, 0)).IsValid();
619 bool InstallerState::AnyExistsAndIsInUse(
620 const InstallationState& machine_state,
621 uint32 file_bits) const {
622 static const wchar_t* const kBinaryFileNames[] = {
624 kChromeFrameHelperExe,
627 DCHECK_NE(file_bits, 0U);
628 DCHECK_LT(file_bits, 1U << NUM_BINARIES);
629 COMPILE_ASSERT(CHROME_FRAME_DLL == 1,no_youre_out_of_order);
630 COMPILE_ASSERT(CHROME_FRAME_HELPER_EXE == 2, no_youre_out_of_order);
631 COMPILE_ASSERT(CHROME_DLL == 4, no_youre_out_of_order);
633 // Check only for the current version (i.e., the version we are upgrading
634 // _from_). Later versions from pending in-use updates need not be checked
635 // since the current version is guaranteed to be in use if any such are.
637 scoped_ptr<Version> current_version(GetCurrentVersion(machine_state));
638 if (!current_version)
640 base::FilePath directory(
641 target_path().AppendASCII(current_version->GetString()));
642 for (int i = 0; i < NUM_BINARIES; ++i) {
643 if (!(file_bits & (1U << i)))
645 base::FilePath file(directory.Append(kBinaryFileNames[i]));
646 if (base::PathExists(file) && IsFileInUse(file))
652 void InstallerState::GetExistingExeVersions(
653 std::set<std::string>* existing_versions) const {
655 static const wchar_t* const kChromeFilenames[] = {
656 installer::kChromeExe,
657 installer::kChromeNewExe,
658 installer::kChromeOldExe,
661 for (int i = 0; i < arraysize(kChromeFilenames); ++i) {
662 base::FilePath chrome_exe(target_path().Append(kChromeFilenames[i]));
663 scoped_ptr<FileVersionInfo> file_version_info(
664 FileVersionInfo::CreateFileVersionInfo(chrome_exe));
665 if (file_version_info) {
666 string16 version_string = file_version_info->file_version();
667 if (!version_string.empty() && IsStringASCII(version_string))
668 existing_versions->insert(WideToASCII(version_string));
673 void InstallerState::RemoveOldVersionDirectories(
674 const Version& new_version,
675 Version* existing_version,
676 const base::FilePath& temp_path) const {
678 scoped_ptr<WorkItem> item;
680 std::set<std::string> existing_version_strings;
681 existing_version_strings.insert(new_version.GetString());
682 if (existing_version)
683 existing_version_strings.insert(existing_version->GetString());
685 // Make sure not to delete any version dir that is "referenced" by an existing
686 // Chrome executable.
687 GetExistingExeVersions(&existing_version_strings);
689 // Try to delete all directories that are not in the set we care to keep.
690 base::FileEnumerator version_enum(target_path(), false,
691 base::FileEnumerator::DIRECTORIES);
692 for (base::FilePath next_version = version_enum.Next(); !next_version.empty();
693 next_version = version_enum.Next()) {
694 base::FilePath dir_name(next_version.BaseName());
695 version = Version(WideToASCII(dir_name.value()));
696 // Delete the version folder if it is less than the new version and not
697 // equal to the old version (if we have an old version).
698 if (version.IsValid() &&
699 existing_version_strings.count(version.GetString()) == 0) {
700 // Note: temporarily log old version deletion at ERROR level to make it
701 // more likely we see this in the installer log.
702 LOG(ERROR) << "Deleting old version directory: " << next_version.value();
704 // Attempt to recursively delete the old version dir.
705 bool delete_succeeded = base::DeleteFile(next_version, true);
707 // Note: temporarily log old version deletion at ERROR level to make it
708 // more likely we see this in the installer log.
709 LOG_IF(ERROR, !delete_succeeded)
710 << "Failed to delete old version directory: " << next_version.value();
715 void InstallerState::AddComDllList(
716 std::vector<base::FilePath>* com_dll_list) const {
717 std::for_each(products_.begin(), products_.end(),
718 std::bind2nd(std::mem_fun(&Product::AddComDllList),
722 bool InstallerState::SetChannelFlags(bool set,
723 ChannelInfo* channel_info) const {
724 bool modified = false;
725 for (Products::const_iterator scan = products_.begin(), end = products_.end();
726 scan != end; ++scan) {
727 modified |= (*scan)->SetChannelFlags(set, channel_info);
732 void InstallerState::UpdateStage(installer::InstallerStage stage) const {
733 InstallUtil::UpdateInstallerStage(system_install(), state_key_, stage);
736 void InstallerState::UpdateChannels() const {
737 if (operation_ != MULTI_INSTALL && operation_ != MULTI_UPDATE) {
738 VLOG(1) << "InstallerState::UpdateChannels noop: " << operation_;
742 // Update the "ap" value for the product being installed/updated. We get the
743 // current value from the registry since the InstallationState instance used
744 // by the bulk of the installer does not track changes made by UpdateStage.
745 // Create the app's ClientState key if it doesn't exist.
746 ChannelInfo channel_info;
747 base::win::RegKey state_key;
748 LONG result = state_key.Create(root_key_, state_key_.c_str(),
749 KEY_QUERY_VALUE | KEY_SET_VALUE);
750 if (result == ERROR_SUCCESS) {
751 channel_info.Initialize(state_key);
753 // This is a multi-install product.
754 bool modified = channel_info.SetMultiInstall(true);
756 // Add the appropriate modifiers for all products and their options.
757 modified |= SetChannelFlags(true, &channel_info);
759 VLOG(1) << "ap: " << channel_info.value();
761 // Write the results if needed.
763 channel_info.Write(&state_key);
765 // Remove the -stage: modifier since we don't want to propagate that to the
767 channel_info.SetStage(NULL);
769 // Synchronize the other products and the package with this one.
770 ChannelInfo other_info;
771 for (int i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
772 BrowserDistribution::Type type =
773 static_cast<BrowserDistribution::Type>(i);
774 // Skip the app_guid we started with.
775 if (type == state_type_)
777 BrowserDistribution* dist = NULL;
778 // Always operate on the binaries.
779 if (i == BrowserDistribution::CHROME_BINARIES) {
780 dist = multi_package_distribution_;
782 const Product* product = FindProduct(type);
783 // Skip this one if it's for a product we're not operating on.
786 dist = product->distribution();
788 result = state_key.Create(root_key_, dist->GetStateKey().c_str(),
789 KEY_QUERY_VALUE | KEY_SET_VALUE);
790 if (result == ERROR_SUCCESS) {
791 other_info.Initialize(state_key);
792 if (!other_info.Equals(channel_info))
793 channel_info.Write(&state_key);
795 LOG(ERROR) << "Failed opening key " << dist->GetStateKey()
796 << " to update app channels; result: " << result;
800 LOG(ERROR) << "Failed opening key " << state_key_
801 << " to update app channels; result: " << result;
805 void InstallerState::WriteInstallerResult(
806 InstallStatus status,
807 int string_resource_id,
808 const std::wstring* const launch_cmd) const {
809 // Use a no-rollback list since this is a best-effort deal.
810 scoped_ptr<WorkItemList> install_list(
811 WorkItem::CreateNoRollbackWorkItemList());
812 const bool system_install = this->system_install();
813 // Write the value for all products upon which we're operating.
814 Products::const_iterator end = products().end();
815 for (Products::const_iterator scan = products().begin(); scan != end;
817 InstallUtil::AddInstallerResultItems(
818 system_install, (*scan)->distribution()->GetStateKey(), status,
819 string_resource_id, launch_cmd, install_list.get());
821 // And for the binaries if this is a multi-install.
822 if (is_multi_install()) {
823 InstallUtil::AddInstallerResultItems(
824 system_install, multi_package_binaries_distribution()->GetStateKey(),
825 status, string_resource_id, launch_cmd, install_list.get());
827 if (!install_list->Do())
828 LOG(ERROR) << "Failed to record installer error information in registry.";
831 bool InstallerState::RequiresActiveSetup() const {
832 return system_install() && FindProduct(BrowserDistribution::CHROME_BROWSER);
835 } // namespace installer