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(
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.
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.";
54 if (!product
->channel().Equals(binaries
->channel())) {
55 VLOG(2) << "It seems that chrome is being over installed.";
60 VLOG(2) << "It seems that the binaries are being updated.";
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
),
73 verbose_logging_(false),
74 ensure_google_update_present_(false) {
77 InstallerState::InstallerState(Level level
)
78 : operation_(UNINITIALIZED
),
79 state_type_(BrowserDistribution::CHROME_BROWSER
),
80 multi_package_distribution_(NULL
),
81 level_(UNKNOWN_LEVEL
),
82 package_type_(UNKNOWN_PACKAGE_TYPE
),
85 verbose_logging_(false),
86 ensure_google_update_present_(false) {
87 // Use set_level() so that root_key_ is updated properly.
91 void InstallerState::Initialize(const CommandLine
& command_line
,
92 const MasterPreferences
& prefs
,
93 const InstallationState
& machine_state
) {
97 if (!prefs
.GetBool(master_preferences::kSystemLevel
, &pref_bool
))
99 set_level(pref_bool
? SYSTEM_LEVEL
: USER_LEVEL
);
101 if (!prefs
.GetBool(master_preferences::kVerboseLogging
, &verbose_logging_
))
102 verbose_logging_
= false;
104 if (!prefs
.GetBool(master_preferences::kMultiInstall
, &pref_bool
))
106 set_package_type(pref_bool
? MULTI_PACKAGE
: SINGLE_PACKAGE
);
108 if (!prefs
.GetBool(master_preferences::kMsi
, &msi_
))
111 ensure_google_update_present_
=
112 command_line
.HasSwitch(installer::switches::kEnsureGoogleUpdatePresent
);
114 const bool is_uninstall
= command_line
.HasSwitch(switches::kUninstall
);
116 if (prefs
.install_chrome()) {
117 Product
* p
= AddProductFromPreferences(
118 BrowserDistribution::CHROME_BROWSER
, prefs
, machine_state
);
119 VLOG(1) << (is_uninstall
? "Uninstall" : "Install")
120 << " distribution: " << p
->distribution()->GetDisplayName();
123 if (prefs
.install_chrome_app_launcher()) {
124 Product
* p
= AddProductFromPreferences(
125 BrowserDistribution::CHROME_APP_HOST
, prefs
, machine_state
);
126 VLOG(1) << (is_uninstall
? "Uninstall" : "Install")
127 << " distribution: " << p
->distribution()->GetDisplayName();
130 if (!is_uninstall
&& is_multi_install()) {
131 bool need_binaries
= false;
132 if (FindProduct(BrowserDistribution::CHROME_APP_HOST
)) {
133 // App Host will happily use Chrome at system level, or binaries at system
134 // level, even if app host is user level.
135 const ProductState
* chrome_state
= machine_state
.GetProductState(
136 true, // system level
137 BrowserDistribution::CHROME_BROWSER
);
138 // If Chrome is at system-level, multi- or otherwise. We'll use it.
140 const ProductState
* binaries_state
= machine_state
.GetProductState(
141 true, // system level
142 BrowserDistribution::CHROME_BINARIES
);
144 need_binaries
= true;
148 // Chrome multi needs Binaries at its own level.
149 if (FindProduct(BrowserDistribution::CHROME_BROWSER
))
150 need_binaries
= true;
152 if (need_binaries
&& !FindProduct(BrowserDistribution::CHROME_BINARIES
)) {
153 // Force binaries to be installed/updated.
154 Product
* p
= AddProductFromPreferences(
155 BrowserDistribution::CHROME_BINARIES
, prefs
, machine_state
);
156 VLOG(1) << "Install distribution: "
157 << p
->distribution()->GetDisplayName();
161 if (is_uninstall
&& prefs
.is_multi_install()) {
162 if (FindProduct(BrowserDistribution::CHROME_BROWSER
)) {
163 // Uninstall each product of type |type| listed below based on the
164 // presence or absence of |switch_name| in that product's uninstall
167 BrowserDistribution::Type type
;
168 const char* switch_name
;
169 bool switch_expected
;
170 } conditional_additions
[] = {
171 // If the App Host is installed, but not the App Launcher, remove it
172 // with Chrome. Note however that for system-level Chrome uninstalls,
173 // any installed user-level App Host will remain even if there is no
174 // App Launcher present (the orphaned app_host.exe will prompt the user
175 // for further action when executed).
176 { BrowserDistribution::CHROME_APP_HOST
,
177 switches::kChromeAppLauncher
,
181 for (size_t i
= 0; i
< arraysize(conditional_additions
); ++i
) {
182 const ProductState
* product_state
= machine_state
.GetProductState(
183 system_install(), conditional_additions
[i
].type
);
184 if (product_state
!= NULL
&&
185 product_state
->uninstall_command().HasSwitch(
186 conditional_additions
[i
].switch_name
) ==
187 conditional_additions
[i
].switch_expected
&&
188 !FindProduct(conditional_additions
[i
].type
)) {
189 Product
* p
= AddProductFromPreferences(
190 conditional_additions
[i
].type
, prefs
, machine_state
);
191 VLOG(1) << "Uninstall distribution: "
192 << p
->distribution()->GetDisplayName();
197 bool keep_binaries
= false;
198 // Look for a multi-install product that is not the binaries and that is not
199 // being uninstalled. If not found, binaries are uninstalled too.
200 for (size_t i
= 0; i
< BrowserDistribution::NUM_TYPES
; ++i
) {
201 BrowserDistribution::Type type
=
202 static_cast<BrowserDistribution::Type
>(i
);
204 if (type
== BrowserDistribution::CHROME_BINARIES
)
207 const ProductState
* product_state
=
208 machine_state
.GetProductState(system_install(), type
);
209 if (product_state
== NULL
) {
210 // The product is not installed.
214 if (!product_state
->is_multi_install() &&
215 type
!= BrowserDistribution::CHROME_BROWSER
) {
216 // The product is not sharing the binaries. It is ordinarily impossible
217 // for single-install Chrome to be installed along with any
218 // multi-install product. Treat single-install Chrome the same as any
219 // multi-install product just in case the impossible happens.
223 // The product is installed.
225 if (!FindProduct(type
)) {
226 // The product is not being uninstalled.
227 if (type
!= BrowserDistribution::CHROME_APP_HOST
) {
228 keep_binaries
= true;
231 // If binaries/chrome are at system-level, we can discard them at
233 if (!machine_state
.GetProductState(
234 true, // system-level
235 BrowserDistribution::CHROME_BROWSER
) &&
236 !machine_state
.GetProductState(
237 true, // system-level
238 BrowserDistribution::CHROME_BINARIES
)) {
239 // ... otherwise keep them.
240 keep_binaries
= true;
247 // The product is being uninstalled.
249 if (!keep_binaries
&&
250 machine_state
.GetProductState(system_install(),
251 BrowserDistribution::CHROME_BINARIES
)) {
252 Product
* p
= AddProductFromPreferences(
253 BrowserDistribution::CHROME_BINARIES
, prefs
, machine_state
);
254 VLOG(1) << (is_uninstall
? "Uninstall" : "Install")
255 << " distribution: " << p
->distribution()->GetDisplayName();
259 BrowserDistribution
* operand
= NULL
;
262 operation_
= UNINSTALL
;
263 } else if (!prefs
.is_multi_install()) {
264 // For a single-install, the current browser dist is the operand.
265 operand
= BrowserDistribution::GetDistribution();
266 operation_
= SINGLE_INSTALL_OR_UPDATE
;
267 } else if (IsMultiInstallUpdate(prefs
, machine_state
)) {
268 // Updates driven by Google Update take place under the multi-installer's
270 operand
= multi_package_distribution_
;
271 operation_
= MULTI_UPDATE
;
273 operation_
= MULTI_INSTALL
;
276 // Initial, over, and un-installs will take place under one of the product app
277 // guids (Chrome, App Host, or Binaries, in order of preference).
278 if (operand
== NULL
) {
279 BrowserDistribution::Type operand_distribution_type
=
280 BrowserDistribution::CHROME_BINARIES
;
281 if (prefs
.install_chrome())
282 operand_distribution_type
= BrowserDistribution::CHROME_BROWSER
;
283 else if (prefs
.install_chrome_app_launcher())
284 operand_distribution_type
= BrowserDistribution::CHROME_APP_HOST
;
286 operand
= BrowserDistribution::GetSpecificDistribution(
287 operand_distribution_type
);
290 state_key_
= operand
->GetStateKey();
291 state_type_
= operand
->GetType();
293 // Parse --critical-update-version=W.X.Y.Z
294 std::string
critical_version_value(
295 command_line
.GetSwitchValueASCII(switches::kCriticalUpdateVersion
));
296 critical_update_version_
= Version(critical_version_value
);
299 void InstallerState::set_level(Level level
) {
303 root_key_
= HKEY_CURRENT_USER
;
306 root_key_
= HKEY_LOCAL_MACHINE
;
309 DCHECK(level
== UNKNOWN_LEVEL
);
310 level_
= UNKNOWN_LEVEL
;
316 void InstallerState::set_package_type(PackageType type
) {
317 package_type_
= type
;
320 multi_package_distribution_
= NULL
;
323 multi_package_distribution_
=
324 BrowserDistribution::GetSpecificDistribution(
325 BrowserDistribution::CHROME_BINARIES
);
328 DCHECK(type
== UNKNOWN_PACKAGE_TYPE
);
329 package_type_
= UNKNOWN_PACKAGE_TYPE
;
330 multi_package_distribution_
= NULL
;
335 // Returns the Chrome binaries directory for multi-install or |dist|'s directory
337 base::FilePath
InstallerState::GetDefaultProductInstallPath(
338 BrowserDistribution
* dist
) const {
340 DCHECK(package_type_
!= UNKNOWN_PACKAGE_TYPE
);
342 if (package_type_
== SINGLE_PACKAGE
) {
343 return GetChromeInstallPath(system_install(), dist
);
345 return GetChromeInstallPath(system_install(),
346 BrowserDistribution::GetSpecificDistribution(
347 BrowserDistribution::CHROME_BINARIES
));
351 // Evaluates a product's eligibility for participation in this operation.
352 // We never expect these checks to fail, hence they all terminate the process in
353 // debug builds. See the log messages for details.
354 bool InstallerState::CanAddProduct(const Product
& product
,
355 const base::FilePath
* product_dir
) const {
356 switch (package_type_
) {
358 if (!products_
.empty()) {
359 LOG(DFATAL
) << "Cannot process more than one single-install product.";
364 if (!product
.HasOption(kOptionMultiInstall
)) {
365 LOG(DFATAL
) << "Cannot process a single-install product with a "
366 "multi-install state.";
369 if (FindProduct(product
.distribution()->GetType()) != NULL
) {
370 LOG(DFATAL
) << "Cannot process more than one product of the same type.";
373 if (!target_path_
.empty()) {
374 base::FilePath default_dir
;
375 if (product_dir
== NULL
)
376 default_dir
= GetDefaultProductInstallPath(product
.distribution());
377 if (!base::FilePath::CompareEqualIgnoreCase(
378 (product_dir
== NULL
? default_dir
: *product_dir
).value(),
379 target_path_
.value())) {
380 LOG(DFATAL
) << "Cannot process products in different directories.";
386 DCHECK_EQ(UNKNOWN_PACKAGE_TYPE
, package_type_
);
392 // Adds |product|, installed in |product_dir| to this object's collection. If
393 // |product_dir| is NULL, the product's default install location is used.
394 // Returns NULL if |product| is incompatible with this object. Otherwise,
395 // returns a pointer to the product (ownership is held by this object).
396 Product
* InstallerState::AddProductInDirectory(
397 const base::FilePath
* product_dir
,
398 scoped_ptr
<Product
>* product
) {
399 DCHECK(product
!= NULL
);
400 DCHECK(product
->get() != NULL
);
401 const Product
& the_product
= *product
->get();
403 if (!CanAddProduct(the_product
, product_dir
))
406 if (package_type_
== UNKNOWN_PACKAGE_TYPE
) {
407 set_package_type(the_product
.HasOption(kOptionMultiInstall
) ?
408 MULTI_PACKAGE
: SINGLE_PACKAGE
);
411 if (target_path_
.empty()) {
412 if (product_dir
== NULL
)
413 target_path_
= GetDefaultProductInstallPath(the_product
.distribution());
415 target_path_
= *product_dir
;
418 if (state_key_
.empty())
419 state_key_
= the_product
.distribution()->GetStateKey();
421 products_
.push_back(product
->release());
422 return products_
[products_
.size() - 1];
425 Product
* InstallerState::AddProduct(scoped_ptr
<Product
>* product
) {
426 return AddProductInDirectory(NULL
, product
);
429 // Adds a product of type |distribution_type| constructed on the basis of
430 // |prefs|, setting this object's msi flag if the product is represented in
431 // |machine_state| and is msi-installed. Returns the product that was added,
432 // or NULL if |state| is incompatible with this object. Ownership is not passed
434 Product
* InstallerState::AddProductFromPreferences(
435 BrowserDistribution::Type distribution_type
,
436 const MasterPreferences
& prefs
,
437 const InstallationState
& machine_state
) {
438 scoped_ptr
<Product
> product_ptr(
439 new Product(BrowserDistribution::GetSpecificDistribution(
440 distribution_type
)));
441 product_ptr
->InitializeFromPreferences(prefs
);
443 Product
* product
= AddProductInDirectory(NULL
, &product_ptr
);
445 if (product
!= NULL
&& !msi_
) {
446 const ProductState
* product_state
= machine_state
.GetProductState(
447 system_install(), distribution_type
);
448 if (product_state
!= NULL
)
449 msi_
= product_state
->is_msi();
455 Product
* InstallerState::AddProductFromState(
456 BrowserDistribution::Type type
,
457 const ProductState
& state
) {
458 scoped_ptr
<Product
> product_ptr(
459 new Product(BrowserDistribution::GetSpecificDistribution(type
)));
460 product_ptr
->InitializeFromUninstallCommand(state
.uninstall_command());
462 // Strip off <version>/Installer/setup.exe; see GetInstallerDirectory().
463 base::FilePath product_dir
=
464 state
.GetSetupPath().DirName().DirName().DirName();
466 Product
* product
= AddProductInDirectory(&product_dir
, &product_ptr
);
469 msi_
|= state
.is_msi();
474 bool InstallerState::system_install() const {
475 DCHECK(level_
== USER_LEVEL
|| level_
== SYSTEM_LEVEL
);
476 return level_
== SYSTEM_LEVEL
;
479 bool InstallerState::is_multi_install() const {
480 DCHECK(package_type_
== SINGLE_PACKAGE
|| package_type_
== MULTI_PACKAGE
);
481 return package_type_
!= SINGLE_PACKAGE
;
484 bool InstallerState::RemoveProduct(const Product
* product
) {
485 ScopedVector
<Product
>::iterator it
=
486 std::find(products_
.begin(), products_
.end(), product
);
487 if (it
!= products_
.end()) {
488 products_
.weak_erase(it
);
494 const Product
* InstallerState::FindProduct(
495 BrowserDistribution::Type distribution_type
) const {
496 for (Products::const_iterator scan
= products_
.begin(), end
= products_
.end();
497 scan
!= end
; ++scan
) {
498 if ((*scan
)->is_type(distribution_type
))
504 Version
* InstallerState::GetCurrentVersion(
505 const InstallationState
& machine_state
) const {
506 DCHECK(!products_
.empty());
507 scoped_ptr
<Version
> current_version
;
508 // If we're doing a multi-install, the current version may be either an
509 // existing multi or an existing single product that is being migrated
510 // in place (i.e., Chrome). In the latter case, there is no existing
511 // CHROME_BINARIES installation so we need to search for the product.
512 BrowserDistribution::Type prod_type
;
513 if (package_type_
== MULTI_PACKAGE
) {
514 prod_type
= BrowserDistribution::CHROME_BINARIES
;
515 if (machine_state
.GetProductState(level_
== SYSTEM_LEVEL
,
516 prod_type
) == NULL
) {
517 // Search for a product on which we're operating that is installed in our
519 Products::const_iterator end
= products().end();
520 for (Products::const_iterator scan
= products().begin(); scan
!= end
;
522 BrowserDistribution::Type product_type
=
523 (*scan
)->distribution()->GetType();
524 const ProductState
* state
=
525 machine_state
.GetProductState(level_
== SYSTEM_LEVEL
, product_type
);
526 if (state
!= NULL
&& target_path_
.IsParent(state
->GetSetupPath())) {
527 prod_type
= product_type
;
533 prod_type
= products_
[0]->distribution()->GetType();
535 const ProductState
* product_state
=
536 machine_state
.GetProductState(level_
== SYSTEM_LEVEL
, prod_type
);
538 if (product_state
!= NULL
) {
539 const Version
* version
= NULL
;
541 // Be aware that there might be a pending "new_chrome.exe" already in the
542 // installation path. If so, we use old_version, which holds the version of
543 // "chrome.exe" itself.
544 if (base::PathExists(target_path().Append(kChromeNewExe
)))
545 version
= product_state
->old_version();
548 version
= &product_state
->version();
550 current_version
.reset(new Version(*version
));
553 return current_version
.release();
556 Version
InstallerState::DetermineCriticalVersion(
557 const Version
* current_version
,
558 const Version
& new_version
) const {
559 DCHECK(current_version
== NULL
|| current_version
->IsValid());
560 DCHECK(new_version
.IsValid());
561 if (critical_update_version_
.IsValid() &&
562 (current_version
== NULL
||
563 (current_version
->CompareTo(critical_update_version_
) < 0)) &&
564 new_version
.CompareTo(critical_update_version_
) >= 0) {
565 return critical_update_version_
;
570 bool InstallerState::IsChromeFrameRunning(
571 const InstallationState
& machine_state
) const {
572 return AnyExistsAndIsInUse(machine_state
, CHROME_FRAME_DLL
);
575 bool InstallerState::AreBinariesInUse(
576 const InstallationState
& machine_state
) const {
577 return AnyExistsAndIsInUse(
579 (CHROME_FRAME_HELPER_EXE
| CHROME_FRAME_HELPER_DLL
|
580 CHROME_FRAME_DLL
| CHROME_DLL
));
583 base::FilePath
InstallerState::GetInstallerDirectory(
584 const Version
& version
) const {
585 return target_path().Append(base::ASCIIToWide(version
.GetString()))
586 .Append(kInstallerDir
);
590 bool InstallerState::IsFileInUse(const base::FilePath
& file
) {
591 // Call CreateFile with a share mode of 0 which should cause this to fail
592 // with ERROR_SHARING_VIOLATION if the file exists and is in-use.
593 return !base::win::ScopedHandle(CreateFile(file
.value().c_str(),
594 GENERIC_WRITE
, 0, NULL
,
595 OPEN_EXISTING
, 0, 0)).IsValid();
598 void InstallerState::Clear() {
599 operation_
= UNINITIALIZED
;
600 target_path_
.clear();
602 state_type_
= BrowserDistribution::CHROME_BROWSER
;
604 multi_package_distribution_
= NULL
;
605 critical_update_version_
= base::Version();
606 level_
= UNKNOWN_LEVEL
;
607 package_type_
= UNKNOWN_PACKAGE_TYPE
;
610 verbose_logging_
= false;
611 ensure_google_update_present_
= false;
614 bool InstallerState::AnyExistsAndIsInUse(
615 const InstallationState
& machine_state
,
616 uint32 file_bits
) const {
617 static const wchar_t* const kBinaryFileNames
[] = {
620 kChromeFrameHelperDll
,
621 kChromeFrameHelperExe
,
623 DCHECK_NE(file_bits
, 0U);
624 DCHECK_LT(file_bits
, 1U << NUM_BINARIES
);
625 COMPILE_ASSERT(CHROME_DLL
== 1, no_youre_out_of_order
);
626 COMPILE_ASSERT(CHROME_FRAME_DLL
== 2, no_youre_out_of_order
);
627 COMPILE_ASSERT(CHROME_FRAME_HELPER_DLL
== 4, no_youre_out_of_order
);
628 COMPILE_ASSERT(CHROME_FRAME_HELPER_EXE
== 8, no_youre_out_of_order
);
630 // Check only for the current version (i.e., the version we are upgrading
631 // _from_). Later versions from pending in-use updates need not be checked
632 // since the current version is guaranteed to be in use if any such are.
634 scoped_ptr
<Version
> current_version(GetCurrentVersion(machine_state
));
635 if (!current_version
)
637 base::FilePath
directory(
638 target_path().AppendASCII(current_version
->GetString()));
639 for (int i
= 0; i
< NUM_BINARIES
; ++i
) {
640 if (!(file_bits
& (1U << i
)))
642 base::FilePath
file(directory
.Append(kBinaryFileNames
[i
]));
643 if (base::PathExists(file
) && IsFileInUse(file
))
649 void InstallerState::GetExistingExeVersions(
650 std::set
<std::string
>* existing_versions
) const {
652 static const wchar_t* const kChromeFilenames
[] = {
653 installer::kChromeExe
,
654 installer::kChromeNewExe
,
655 installer::kChromeOldExe
,
658 for (int i
= 0; i
< arraysize(kChromeFilenames
); ++i
) {
659 base::FilePath
chrome_exe(target_path().Append(kChromeFilenames
[i
]));
660 scoped_ptr
<FileVersionInfo
> file_version_info(
661 FileVersionInfo::CreateFileVersionInfo(chrome_exe
));
662 if (file_version_info
) {
663 base::string16 version_string
= file_version_info
->file_version();
664 if (!version_string
.empty() && IsStringASCII(version_string
))
665 existing_versions
->insert(WideToASCII(version_string
));
670 void InstallerState::RemoveOldVersionDirectories(
671 const Version
& new_version
,
672 Version
* existing_version
,
673 const base::FilePath
& temp_path
) const {
675 scoped_ptr
<WorkItem
> item
;
677 std::set
<std::string
> existing_version_strings
;
678 existing_version_strings
.insert(new_version
.GetString());
679 if (existing_version
)
680 existing_version_strings
.insert(existing_version
->GetString());
682 // Make sure not to delete any version dir that is "referenced" by an existing
683 // Chrome executable.
684 GetExistingExeVersions(&existing_version_strings
);
686 // Try to delete all directories that are not in the set we care to keep.
687 base::FileEnumerator
version_enum(target_path(), false,
688 base::FileEnumerator::DIRECTORIES
);
689 for (base::FilePath next_version
= version_enum
.Next(); !next_version
.empty();
690 next_version
= version_enum
.Next()) {
691 base::FilePath
dir_name(next_version
.BaseName());
692 version
= Version(WideToASCII(dir_name
.value()));
693 // Delete the version folder if it is less than the new version and not
694 // equal to the old version (if we have an old version).
695 if (version
.IsValid() &&
696 existing_version_strings
.count(version
.GetString()) == 0) {
697 // Note: temporarily log old version deletion at ERROR level to make it
698 // more likely we see this in the installer log.
699 LOG(ERROR
) << "Deleting old version directory: " << next_version
.value();
701 // Attempt to recursively delete the old version dir.
702 bool delete_succeeded
= base::DeleteFile(next_version
, true);
704 // Note: temporarily log old version deletion at ERROR level to make it
705 // more likely we see this in the installer log.
706 LOG_IF(ERROR
, !delete_succeeded
)
707 << "Failed to delete old version directory: " << next_version
.value();
712 void InstallerState::AddComDllList(
713 std::vector
<base::FilePath
>* com_dll_list
) const {
714 std::for_each(products_
.begin(), products_
.end(),
715 std::bind2nd(std::mem_fun(&Product::AddComDllList
),
719 bool InstallerState::SetChannelFlags(bool set
,
720 ChannelInfo
* channel_info
) const {
721 bool modified
= false;
722 for (Products::const_iterator scan
= products_
.begin(), end
= products_
.end();
723 scan
!= end
; ++scan
) {
724 modified
|= (*scan
)->SetChannelFlags(set
, channel_info
);
729 void InstallerState::UpdateStage(installer::InstallerStage stage
) const {
730 InstallUtil::UpdateInstallerStage(system_install(), state_key_
, stage
);
733 void InstallerState::UpdateChannels() const {
734 if (operation_
!= MULTI_INSTALL
&& operation_
!= MULTI_UPDATE
) {
735 VLOG(1) << "InstallerState::UpdateChannels noop: " << operation_
;
739 // Update the "ap" value for the product being installed/updated. We get the
740 // current value from the registry since the InstallationState instance used
741 // by the bulk of the installer does not track changes made by UpdateStage.
742 // Create the app's ClientState key if it doesn't exist.
743 ChannelInfo channel_info
;
744 base::win::RegKey state_key
;
745 LONG result
= state_key
.Create(root_key_
, state_key_
.c_str(),
746 KEY_QUERY_VALUE
| KEY_SET_VALUE
);
747 if (result
== ERROR_SUCCESS
) {
748 channel_info
.Initialize(state_key
);
750 // This is a multi-install product.
751 bool modified
= channel_info
.SetMultiInstall(true);
753 // Add the appropriate modifiers for all products and their options.
754 modified
|= SetChannelFlags(true, &channel_info
);
756 VLOG(1) << "ap: " << channel_info
.value();
758 // Write the results if needed.
760 channel_info
.Write(&state_key
);
762 // Remove the -stage: modifier since we don't want to propagate that to the
764 channel_info
.SetStage(NULL
);
766 // Synchronize the other products and the package with this one.
767 ChannelInfo other_info
;
768 for (int i
= 0; i
< BrowserDistribution::NUM_TYPES
; ++i
) {
769 BrowserDistribution::Type type
=
770 static_cast<BrowserDistribution::Type
>(i
);
771 // Skip the app_guid we started with.
772 if (type
== state_type_
)
774 BrowserDistribution
* dist
= NULL
;
775 // Always operate on the binaries.
776 if (i
== BrowserDistribution::CHROME_BINARIES
) {
777 dist
= multi_package_distribution_
;
779 const Product
* product
= FindProduct(type
);
780 // Skip this one if it's for a product we're not operating on.
783 dist
= product
->distribution();
785 result
= state_key
.Create(root_key_
, dist
->GetStateKey().c_str(),
786 KEY_QUERY_VALUE
| KEY_SET_VALUE
);
787 if (result
== ERROR_SUCCESS
) {
788 other_info
.Initialize(state_key
);
789 if (!other_info
.Equals(channel_info
))
790 channel_info
.Write(&state_key
);
792 LOG(ERROR
) << "Failed opening key " << dist
->GetStateKey()
793 << " to update app channels; result: " << result
;
797 LOG(ERROR
) << "Failed opening key " << state_key_
798 << " to update app channels; result: " << result
;
802 void InstallerState::WriteInstallerResult(
803 InstallStatus status
,
804 int string_resource_id
,
805 const std::wstring
* const launch_cmd
) const {
806 // Use a no-rollback list since this is a best-effort deal.
807 scoped_ptr
<WorkItemList
> install_list(
808 WorkItem::CreateNoRollbackWorkItemList());
809 const bool system_install
= this->system_install();
810 // Write the value for all products upon which we're operating.
811 Products::const_iterator end
= products().end();
812 for (Products::const_iterator scan
= products().begin(); scan
!= end
;
814 InstallUtil::AddInstallerResultItems(
815 system_install
, (*scan
)->distribution()->GetStateKey(), status
,
816 string_resource_id
, launch_cmd
, install_list
.get());
818 // And for the binaries if this is a multi-install.
819 if (is_multi_install()) {
820 InstallUtil::AddInstallerResultItems(
821 system_install
, multi_package_binaries_distribution()->GetStateKey(),
822 status
, string_resource_id
, launch_cmd
, install_list
.get());
824 if (!install_list
->Do())
825 LOG(ERROR
) << "Failed to record installer error information in registry.";
828 bool InstallerState::RequiresActiveSetup() const {
829 return system_install() && FindProduct(BrowserDistribution::CHROME_BROWSER
);
832 } // namespace installer