Roll src/third_party/WebKit 116cf7f:79abaa8 (svn 189234:189235)
[chromium-blink-merge.git] / chrome / installer / util / installer_state.cc
blob5488d83db6f36749e31a29d770eae71ce5bc93f8
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"
7 #include <algorithm>
8 #include <functional>
9 #include <utility>
11 #include "base/command_line.h"
12 #include "base/file_version_info.h"
13 #include "base/files/file_enumerator.h"
14 #include "base/files/file_util.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"
31 namespace installer {
33 bool InstallerState::IsMultiInstallUpdate(
34 const MasterPreferences& prefs,
35 const InstallationState& machine_state) {
36 // First, are the binaries present?
37 const ProductState* binaries =
38 machine_state.GetProductState(level_ == SYSTEM_LEVEL,
39 BrowserDistribution::CHROME_BINARIES);
40 if (binaries == NULL) {
41 // The multi-install binaries have not been installed, so they certainly
42 // aren't being updated.
43 return false;
46 if (prefs.install_chrome()) {
47 const ProductState* product =
48 machine_state.GetProductState(level_ == SYSTEM_LEVEL,
49 BrowserDistribution::CHROME_BROWSER);
50 if (product == NULL) {
51 VLOG(2) << "It seems that chrome is being installed for the first time.";
52 return false;
54 if (!product->channel().Equals(binaries->channel())) {
55 VLOG(2) << "It seems that chrome is being over installed.";
56 return false;
60 VLOG(2) << "It seems that the binaries are being updated.";
62 return true;
65 InstallerState::InstallerState()
66 : operation_(UNINITIALIZED),
67 state_type_(BrowserDistribution::CHROME_BROWSER),
68 multi_package_distribution_(NULL),
69 level_(UNKNOWN_LEVEL),
70 package_type_(UNKNOWN_PACKAGE_TYPE),
71 root_key_(NULL),
72 msi_(false),
73 verbose_logging_(false) {
76 InstallerState::InstallerState(Level level)
77 : operation_(UNINITIALIZED),
78 state_type_(BrowserDistribution::CHROME_BROWSER),
79 multi_package_distribution_(NULL),
80 level_(UNKNOWN_LEVEL),
81 package_type_(UNKNOWN_PACKAGE_TYPE),
82 root_key_(NULL),
83 msi_(false),
84 verbose_logging_(false) {
85 // Use set_level() so that root_key_ is updated properly.
86 set_level(level);
89 void InstallerState::Initialize(const base::CommandLine& command_line,
90 const MasterPreferences& prefs,
91 const InstallationState& machine_state) {
92 Clear();
94 bool pref_bool;
95 if (!prefs.GetBool(master_preferences::kSystemLevel, &pref_bool))
96 pref_bool = false;
97 set_level(pref_bool ? SYSTEM_LEVEL : USER_LEVEL);
99 if (!prefs.GetBool(master_preferences::kVerboseLogging, &verbose_logging_))
100 verbose_logging_ = false;
102 if (!prefs.GetBool(master_preferences::kMultiInstall, &pref_bool))
103 pref_bool = false;
104 set_package_type(pref_bool ? MULTI_PACKAGE : SINGLE_PACKAGE);
106 if (!prefs.GetBool(master_preferences::kMsi, &msi_))
107 msi_ = false;
109 const bool is_uninstall = command_line.HasSwitch(switches::kUninstall);
111 if (prefs.install_chrome()) {
112 Product* p = AddProductFromPreferences(
113 BrowserDistribution::CHROME_BROWSER, prefs, machine_state);
114 VLOG(1) << (is_uninstall ? "Uninstall" : "Install")
115 << " distribution: " << p->distribution()->GetDisplayName();
118 // Binaries are only used by Chrome.
119 if (is_multi_install() &&
120 FindProduct(BrowserDistribution::CHROME_BROWSER)) {
121 Product* p = AddProductFromPreferences(
122 BrowserDistribution::CHROME_BINARIES, prefs, machine_state);
123 VLOG(1) << (is_uninstall ? "Uninstall" : "Install")
124 << " distribution: " << p->distribution()->GetDisplayName();
127 BrowserDistribution* operand = NULL;
129 if (is_uninstall) {
130 operation_ = UNINSTALL;
131 } else if (!prefs.is_multi_install()) {
132 // For a single-install, the current browser dist is the operand.
133 operand = BrowserDistribution::GetDistribution();
134 operation_ = SINGLE_INSTALL_OR_UPDATE;
135 } else if (IsMultiInstallUpdate(prefs, machine_state)) {
136 // Updates driven by Google Update take place under the multi-installer's
137 // app guid.
138 operand = multi_package_distribution_;
139 operation_ = MULTI_UPDATE;
140 } else {
141 operation_ = MULTI_INSTALL;
144 // Initial, over, and un-installs will take place under Chrome or Binaries
145 // app guid.
146 if (operand == NULL) {
147 operand = BrowserDistribution::GetSpecificDistribution(
148 prefs.install_chrome() ?
149 BrowserDistribution::CHROME_BROWSER :
150 BrowserDistribution::CHROME_BINARIES);
153 state_key_ = operand->GetStateKey();
154 state_type_ = operand->GetType();
156 // Parse --critical-update-version=W.X.Y.Z
157 std::string critical_version_value(
158 command_line.GetSwitchValueASCII(switches::kCriticalUpdateVersion));
159 critical_update_version_ = Version(critical_version_value);
162 void InstallerState::set_level(Level level) {
163 level_ = level;
164 switch (level) {
165 case USER_LEVEL:
166 root_key_ = HKEY_CURRENT_USER;
167 break;
168 case SYSTEM_LEVEL:
169 root_key_ = HKEY_LOCAL_MACHINE;
170 break;
171 default:
172 DCHECK(level == UNKNOWN_LEVEL);
173 level_ = UNKNOWN_LEVEL;
174 root_key_ = NULL;
175 break;
179 void InstallerState::set_package_type(PackageType type) {
180 package_type_ = type;
181 switch (type) {
182 case SINGLE_PACKAGE:
183 multi_package_distribution_ = NULL;
184 break;
185 case MULTI_PACKAGE:
186 multi_package_distribution_ =
187 BrowserDistribution::GetSpecificDistribution(
188 BrowserDistribution::CHROME_BINARIES);
189 break;
190 default:
191 DCHECK(type == UNKNOWN_PACKAGE_TYPE);
192 package_type_ = UNKNOWN_PACKAGE_TYPE;
193 multi_package_distribution_ = NULL;
194 break;
198 // Returns the Chrome binaries directory for multi-install or |dist|'s directory
199 // otherwise.
200 base::FilePath InstallerState::GetDefaultProductInstallPath(
201 BrowserDistribution* dist) const {
202 DCHECK(dist);
203 DCHECK(package_type_ != UNKNOWN_PACKAGE_TYPE);
205 if (package_type_ == SINGLE_PACKAGE) {
206 return GetChromeInstallPath(system_install(), dist);
207 } else {
208 return GetChromeInstallPath(system_install(),
209 BrowserDistribution::GetSpecificDistribution(
210 BrowserDistribution::CHROME_BINARIES));
214 // Evaluates a product's eligibility for participation in this operation.
215 // We never expect these checks to fail, hence they all terminate the process in
216 // debug builds. See the log messages for details.
217 bool InstallerState::CanAddProduct(const Product& product,
218 const base::FilePath* product_dir) const {
219 switch (package_type_) {
220 case SINGLE_PACKAGE:
221 if (!products_.empty()) {
222 LOG(DFATAL) << "Cannot process more than one single-install product.";
223 return false;
225 break;
226 case MULTI_PACKAGE:
227 if (!product.HasOption(kOptionMultiInstall)) {
228 LOG(DFATAL) << "Cannot process a single-install product with a "
229 "multi-install state.";
230 return false;
232 if (FindProduct(product.distribution()->GetType()) != NULL) {
233 LOG(DFATAL) << "Cannot process more than one product of the same type.";
234 return false;
236 if (!target_path_.empty()) {
237 base::FilePath default_dir;
238 if (product_dir == NULL)
239 default_dir = GetDefaultProductInstallPath(product.distribution());
240 if (!base::FilePath::CompareEqualIgnoreCase(
241 (product_dir == NULL ? default_dir : *product_dir).value(),
242 target_path_.value())) {
243 LOG(DFATAL) << "Cannot process products in different directories.";
244 return false;
247 break;
248 default:
249 DCHECK_EQ(UNKNOWN_PACKAGE_TYPE, package_type_);
250 break;
252 return true;
255 // Adds |product|, installed in |product_dir| to this object's collection. If
256 // |product_dir| is NULL, the product's default install location is used.
257 // Returns NULL if |product| is incompatible with this object. Otherwise,
258 // returns a pointer to the product (ownership is held by this object).
259 Product* InstallerState::AddProductInDirectory(
260 const base::FilePath* product_dir,
261 scoped_ptr<Product>* product) {
262 DCHECK(product != NULL);
263 DCHECK(product->get() != NULL);
264 const Product& the_product = *product->get();
266 if (!CanAddProduct(the_product, product_dir))
267 return NULL;
269 if (package_type_ == UNKNOWN_PACKAGE_TYPE) {
270 set_package_type(the_product.HasOption(kOptionMultiInstall) ?
271 MULTI_PACKAGE : SINGLE_PACKAGE);
274 if (target_path_.empty()) {
275 if (product_dir == NULL)
276 target_path_ = GetDefaultProductInstallPath(the_product.distribution());
277 else
278 target_path_ = *product_dir;
281 if (state_key_.empty())
282 state_key_ = the_product.distribution()->GetStateKey();
284 products_.push_back(product->release());
285 return products_[products_.size() - 1];
288 Product* InstallerState::AddProduct(scoped_ptr<Product>* product) {
289 return AddProductInDirectory(NULL, product);
292 // Adds a product of type |distribution_type| constructed on the basis of
293 // |prefs|, setting this object's msi flag if the product is represented in
294 // |machine_state| and is msi-installed. Returns the product that was added,
295 // or NULL if |state| is incompatible with this object. Ownership is not passed
296 // to the caller.
297 Product* InstallerState::AddProductFromPreferences(
298 BrowserDistribution::Type distribution_type,
299 const MasterPreferences& prefs,
300 const InstallationState& machine_state) {
301 scoped_ptr<Product> product_ptr(
302 new Product(BrowserDistribution::GetSpecificDistribution(
303 distribution_type)));
304 product_ptr->InitializeFromPreferences(prefs);
306 Product* product = AddProductInDirectory(NULL, &product_ptr);
308 if (product != NULL && !msi_) {
309 const ProductState* product_state = machine_state.GetProductState(
310 system_install(), distribution_type);
311 if (product_state != NULL)
312 msi_ = product_state->is_msi();
315 return product;
318 Product* InstallerState::AddProductFromState(
319 BrowserDistribution::Type type,
320 const ProductState& state) {
321 scoped_ptr<Product> product_ptr(
322 new Product(BrowserDistribution::GetSpecificDistribution(type)));
323 product_ptr->InitializeFromUninstallCommand(state.uninstall_command());
325 // Strip off <version>/Installer/setup.exe; see GetInstallerDirectory().
326 base::FilePath product_dir =
327 state.GetSetupPath().DirName().DirName().DirName();
329 Product* product = AddProductInDirectory(&product_dir, &product_ptr);
331 if (product != NULL)
332 msi_ |= state.is_msi();
334 return product;
337 bool InstallerState::system_install() const {
338 DCHECK(level_ == USER_LEVEL || level_ == SYSTEM_LEVEL);
339 return level_ == SYSTEM_LEVEL;
342 bool InstallerState::is_multi_install() const {
343 DCHECK(package_type_ == SINGLE_PACKAGE || package_type_ == MULTI_PACKAGE);
344 return package_type_ != SINGLE_PACKAGE;
347 bool InstallerState::RemoveProduct(const Product* product) {
348 ScopedVector<Product>::iterator it =
349 std::find(products_.begin(), products_.end(), product);
350 if (it != products_.end()) {
351 products_.weak_erase(it);
352 return true;
354 return false;
357 const Product* InstallerState::FindProduct(
358 BrowserDistribution::Type distribution_type) const {
359 for (Products::const_iterator scan = products_.begin(), end = products_.end();
360 scan != end; ++scan) {
361 if ((*scan)->is_type(distribution_type))
362 return *scan;
364 return NULL;
367 Version* InstallerState::GetCurrentVersion(
368 const InstallationState& machine_state) const {
369 DCHECK(!products_.empty());
370 scoped_ptr<Version> current_version;
371 // If we're doing a multi-install, the current version may be either an
372 // existing multi or an existing single product that is being migrated
373 // in place (i.e., Chrome). In the latter case, there is no existing
374 // CHROME_BINARIES installation so we need to search for the product.
375 BrowserDistribution::Type prod_type;
376 if (package_type_ == MULTI_PACKAGE) {
377 prod_type = BrowserDistribution::CHROME_BINARIES;
378 if (machine_state.GetProductState(level_ == SYSTEM_LEVEL,
379 prod_type) == NULL) {
380 // Search for a product on which we're operating that is installed in our
381 // target directory.
382 Products::const_iterator end = products().end();
383 for (Products::const_iterator scan = products().begin(); scan != end;
384 ++scan) {
385 BrowserDistribution::Type product_type =
386 (*scan)->distribution()->GetType();
387 const ProductState* state =
388 machine_state.GetProductState(level_ == SYSTEM_LEVEL, product_type);
389 if (state != NULL && target_path_.IsParent(state->GetSetupPath())) {
390 prod_type = product_type;
391 break;
395 } else {
396 prod_type = products_[0]->distribution()->GetType();
398 const ProductState* product_state =
399 machine_state.GetProductState(level_ == SYSTEM_LEVEL, prod_type);
401 if (product_state != NULL) {
402 const Version* version = NULL;
404 // Be aware that there might be a pending "new_chrome.exe" already in the
405 // installation path. If so, we use old_version, which holds the version of
406 // "chrome.exe" itself.
407 if (base::PathExists(target_path().Append(kChromeNewExe)))
408 version = product_state->old_version();
410 if (version == NULL)
411 version = &product_state->version();
413 current_version.reset(new Version(*version));
416 return current_version.release();
419 Version InstallerState::DetermineCriticalVersion(
420 const Version* current_version,
421 const Version& new_version) const {
422 DCHECK(current_version == NULL || current_version->IsValid());
423 DCHECK(new_version.IsValid());
424 if (critical_update_version_.IsValid() &&
425 (current_version == NULL ||
426 (current_version->CompareTo(critical_update_version_) < 0)) &&
427 new_version.CompareTo(critical_update_version_) >= 0) {
428 return critical_update_version_;
430 return Version();
433 bool InstallerState::IsChromeFrameRunning(
434 const InstallationState& machine_state) const {
435 return AnyExistsAndIsInUse(machine_state, CHROME_FRAME_DLL);
438 bool InstallerState::AreBinariesInUse(
439 const InstallationState& machine_state) const {
440 return AnyExistsAndIsInUse(
441 machine_state,
442 (CHROME_FRAME_HELPER_EXE | CHROME_FRAME_HELPER_DLL |
443 CHROME_FRAME_DLL | CHROME_DLL));
446 base::FilePath InstallerState::GetInstallerDirectory(
447 const Version& version) const {
448 return target_path().AppendASCII(version.GetString()).Append(kInstallerDir);
451 // static
452 bool InstallerState::IsFileInUse(const base::FilePath& file) {
453 // Call CreateFile with a share mode of 0 which should cause this to fail
454 // with ERROR_SHARING_VIOLATION if the file exists and is in-use.
455 return !base::win::ScopedHandle(CreateFile(file.value().c_str(),
456 GENERIC_WRITE, 0, NULL,
457 OPEN_EXISTING, 0, 0)).IsValid();
460 void InstallerState::Clear() {
461 operation_ = UNINITIALIZED;
462 target_path_.clear();
463 state_key_.clear();
464 state_type_ = BrowserDistribution::CHROME_BROWSER;
465 products_.clear();
466 multi_package_distribution_ = NULL;
467 critical_update_version_ = base::Version();
468 level_ = UNKNOWN_LEVEL;
469 package_type_ = UNKNOWN_PACKAGE_TYPE;
470 root_key_ = NULL;
471 msi_ = false;
472 verbose_logging_ = false;
475 bool InstallerState::AnyExistsAndIsInUse(
476 const InstallationState& machine_state,
477 uint32 file_bits) const {
478 static const wchar_t* const kBinaryFileNames[] = {
479 kChromeDll,
480 kChromeFrameDll,
481 kChromeFrameHelperDll,
482 kChromeFrameHelperExe,
484 DCHECK_NE(file_bits, 0U);
485 DCHECK_LT(file_bits, 1U << NUM_BINARIES);
486 COMPILE_ASSERT(CHROME_DLL == 1, no_youre_out_of_order);
487 COMPILE_ASSERT(CHROME_FRAME_DLL == 2, no_youre_out_of_order);
488 COMPILE_ASSERT(CHROME_FRAME_HELPER_DLL == 4, no_youre_out_of_order);
489 COMPILE_ASSERT(CHROME_FRAME_HELPER_EXE == 8, no_youre_out_of_order);
491 // Check only for the current version (i.e., the version we are upgrading
492 // _from_). Later versions from pending in-use updates need not be checked
493 // since the current version is guaranteed to be in use if any such are.
494 scoped_ptr<Version> current_version(GetCurrentVersion(machine_state));
495 if (!current_version)
496 return false;
497 base::FilePath directory(
498 target_path().AppendASCII(current_version->GetString()));
499 for (int i = 0; i < NUM_BINARIES; ++i) {
500 if (!(file_bits & (1U << i)))
501 continue;
502 base::FilePath file(directory.Append(kBinaryFileNames[i]));
503 if (base::PathExists(file) && IsFileInUse(file))
504 return true;
506 return false;
509 void InstallerState::GetExistingExeVersions(
510 std::set<std::string>* existing_versions) const {
512 static const wchar_t* const kChromeFilenames[] = {
513 installer::kChromeExe,
514 installer::kChromeNewExe,
515 installer::kChromeOldExe,
518 for (int i = 0; i < arraysize(kChromeFilenames); ++i) {
519 base::FilePath chrome_exe(target_path().Append(kChromeFilenames[i]));
520 scoped_ptr<FileVersionInfo> file_version_info(
521 FileVersionInfo::CreateFileVersionInfo(chrome_exe));
522 if (file_version_info) {
523 base::string16 version_string = file_version_info->file_version();
524 if (!version_string.empty() && base::IsStringASCII(version_string))
525 existing_versions->insert(base::UTF16ToASCII(version_string));
530 void InstallerState::RemoveOldVersionDirectories(
531 const Version& new_version,
532 Version* existing_version,
533 const base::FilePath& temp_path) const {
534 Version version;
535 scoped_ptr<WorkItem> item;
537 std::set<std::string> existing_version_strings;
538 existing_version_strings.insert(new_version.GetString());
539 if (existing_version)
540 existing_version_strings.insert(existing_version->GetString());
542 // Make sure not to delete any version dir that is "referenced" by an existing
543 // Chrome executable.
544 GetExistingExeVersions(&existing_version_strings);
546 // Try to delete all directories that are not in the set we care to keep.
547 base::FileEnumerator version_enum(target_path(), false,
548 base::FileEnumerator::DIRECTORIES);
549 for (base::FilePath next_version = version_enum.Next(); !next_version.empty();
550 next_version = version_enum.Next()) {
551 base::FilePath dir_name(next_version.BaseName());
552 version = Version(base::UTF16ToASCII(dir_name.value()));
553 // Delete the version folder if it is less than the new version and not
554 // equal to the old version (if we have an old version).
555 if (version.IsValid() &&
556 existing_version_strings.count(version.GetString()) == 0) {
557 // Note: temporarily log old version deletion at ERROR level to make it
558 // more likely we see this in the installer log.
559 LOG(ERROR) << "Deleting old version directory: " << next_version.value();
561 // Attempt to recursively delete the old version dir.
562 bool delete_succeeded = base::DeleteFile(next_version, true);
564 // Note: temporarily log old version deletion at ERROR level to make it
565 // more likely we see this in the installer log.
566 LOG_IF(ERROR, !delete_succeeded)
567 << "Failed to delete old version directory: " << next_version.value();
572 void InstallerState::AddComDllList(
573 std::vector<base::FilePath>* com_dll_list) const {
574 std::for_each(products_.begin(), products_.end(),
575 std::bind2nd(std::mem_fun(&Product::AddComDllList),
576 com_dll_list));
579 bool InstallerState::SetChannelFlags(bool set,
580 ChannelInfo* channel_info) const {
581 bool modified = false;
582 for (Products::const_iterator scan = products_.begin(), end = products_.end();
583 scan != end; ++scan) {
584 modified |= (*scan)->SetChannelFlags(set, channel_info);
586 return modified;
589 void InstallerState::UpdateStage(installer::InstallerStage stage) const {
590 InstallUtil::UpdateInstallerStage(system_install(), state_key_, stage);
593 void InstallerState::UpdateChannels() const {
594 if (operation_ != MULTI_INSTALL && operation_ != MULTI_UPDATE) {
595 VLOG(1) << "InstallerState::UpdateChannels noop: " << operation_;
596 return;
599 // Update the "ap" value for the product being installed/updated. We get the
600 // current value from the registry since the InstallationState instance used
601 // by the bulk of the installer does not track changes made by UpdateStage.
602 // Create the app's ClientState key if it doesn't exist.
603 ChannelInfo channel_info;
604 base::win::RegKey state_key;
605 LONG result =
606 state_key.Create(root_key_,
607 state_key_.c_str(),
608 KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_WOW64_32KEY);
609 if (result == ERROR_SUCCESS) {
610 channel_info.Initialize(state_key);
612 // This is a multi-install product.
613 bool modified = channel_info.SetMultiInstall(true);
615 // Add the appropriate modifiers for all products and their options.
616 modified |= SetChannelFlags(true, &channel_info);
618 VLOG(1) << "ap: " << channel_info.value();
620 // Write the results if needed.
621 if (modified)
622 channel_info.Write(&state_key);
624 // Remove the -stage: modifier since we don't want to propagate that to the
625 // other app_guids.
626 channel_info.SetStage(NULL);
628 // Synchronize the other products and the package with this one.
629 ChannelInfo other_info;
630 for (int i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
631 BrowserDistribution::Type type =
632 static_cast<BrowserDistribution::Type>(i);
633 // Skip the app_guid we started with.
634 if (type == state_type_)
635 continue;
636 BrowserDistribution* dist = NULL;
637 // Always operate on the binaries.
638 if (i == BrowserDistribution::CHROME_BINARIES) {
639 dist = multi_package_distribution_;
640 } else {
641 const Product* product = FindProduct(type);
642 // Skip this one if it's for a product we're not operating on.
643 if (product == NULL)
644 continue;
645 dist = product->distribution();
647 result =
648 state_key.Create(root_key_,
649 dist->GetStateKey().c_str(),
650 KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_WOW64_32KEY);
651 if (result == ERROR_SUCCESS) {
652 other_info.Initialize(state_key);
653 if (!other_info.Equals(channel_info))
654 channel_info.Write(&state_key);
655 } else {
656 LOG(ERROR) << "Failed opening key " << dist->GetStateKey()
657 << " to update app channels; result: " << result;
660 } else {
661 LOG(ERROR) << "Failed opening key " << state_key_
662 << " to update app channels; result: " << result;
666 void InstallerState::WriteInstallerResult(
667 InstallStatus status,
668 int string_resource_id,
669 const std::wstring* const launch_cmd) const {
670 // Use a no-rollback list since this is a best-effort deal.
671 scoped_ptr<WorkItemList> install_list(
672 WorkItem::CreateNoRollbackWorkItemList());
673 const bool system_install = this->system_install();
674 // Write the value for all products upon which we're operating.
675 Products::const_iterator end = products().end();
676 for (Products::const_iterator scan = products().begin(); scan != end;
677 ++scan) {
678 InstallUtil::AddInstallerResultItems(
679 system_install, (*scan)->distribution()->GetStateKey(), status,
680 string_resource_id, launch_cmd, install_list.get());
682 // And for the binaries if this is a multi-install.
683 if (is_multi_install()) {
684 InstallUtil::AddInstallerResultItems(
685 system_install, multi_package_binaries_distribution()->GetStateKey(),
686 status, string_resource_id, launch_cmd, install_list.get());
688 if (!install_list->Do())
689 LOG(ERROR) << "Failed to record installer error information in registry.";
692 bool InstallerState::RequiresActiveSetup() const {
693 return system_install() && FindProduct(BrowserDistribution::CHROME_BROWSER);
696 } // namespace installer