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()->GetAppShortCutName();
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()->GetAppShortCutName();
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()->GetAppShortCutName();
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()->GetAppShortCutName();
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()->GetAppShortCutName();
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()->GetAppShortCutName();
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 // We check only for the current version (e.g. the version we are upgrading
595 // _from_). We don't need to check interstitial versions if any (as would
596 // occur in the case of multiple updates) since if they are in use, we are
597 // guaranteed that the current version is in use too.
599 scoped_ptr
<Version
> current_version(GetCurrentVersion(machine_state
));
600 if (current_version
!= NULL
) {
601 base::FilePath
cf_install_path(
602 target_path().AppendASCII(current_version
->GetString())
603 .Append(kChromeFrameDll
));
604 in_use
= base::PathExists(cf_install_path
) &&
605 IsFileInUse(cf_install_path
);
610 base::FilePath
InstallerState::GetInstallerDirectory(
611 const Version
& version
) const {
612 return target_path().Append(ASCIIToWide(version
.GetString()))
613 .Append(kInstallerDir
);
617 bool InstallerState::IsFileInUse(const base::FilePath
& file
) {
618 // Call CreateFile with a share mode of 0 which should cause this to fail
619 // with ERROR_SHARING_VIOLATION if the file exists and is in-use.
620 return !base::win::ScopedHandle(CreateFile(file
.value().c_str(),
621 GENERIC_WRITE
, 0, NULL
,
622 OPEN_EXISTING
, 0, 0)).IsValid();
625 void InstallerState::GetExistingExeVersions(
626 std::set
<std::string
>* existing_versions
) const {
628 static const wchar_t* const kChromeFilenames
[] = {
629 installer::kChromeExe
,
630 installer::kChromeNewExe
,
631 installer::kChromeOldExe
,
634 for (int i
= 0; i
< arraysize(kChromeFilenames
); ++i
) {
635 base::FilePath
chrome_exe(target_path().Append(kChromeFilenames
[i
]));
636 scoped_ptr
<FileVersionInfo
> file_version_info(
637 FileVersionInfo::CreateFileVersionInfo(chrome_exe
));
638 if (file_version_info
) {
639 string16 version_string
= file_version_info
->file_version();
640 if (!version_string
.empty() && IsStringASCII(version_string
))
641 existing_versions
->insert(WideToASCII(version_string
));
646 void InstallerState::RemoveOldVersionDirectories(
647 const Version
& new_version
,
648 Version
* existing_version
,
649 const base::FilePath
& temp_path
) const {
651 scoped_ptr
<WorkItem
> item
;
653 std::set
<std::string
> existing_version_strings
;
654 existing_version_strings
.insert(new_version
.GetString());
655 if (existing_version
)
656 existing_version_strings
.insert(existing_version
->GetString());
658 // Make sure not to delete any version dir that is "referenced" by an existing
659 // Chrome executable.
660 GetExistingExeVersions(&existing_version_strings
);
662 // Try to delete all directories that are not in the set we care to keep.
663 base::FileEnumerator
version_enum(target_path(), false,
664 base::FileEnumerator::DIRECTORIES
);
665 for (base::FilePath next_version
= version_enum
.Next(); !next_version
.empty();
666 next_version
= version_enum
.Next()) {
667 base::FilePath
dir_name(next_version
.BaseName());
668 version
= Version(WideToASCII(dir_name
.value()));
669 // Delete the version folder if it is less than the new version and not
670 // equal to the old version (if we have an old version).
671 if (version
.IsValid() &&
672 existing_version_strings
.count(version
.GetString()) == 0) {
673 // Note: temporarily log old version deletion at ERROR level to make it
674 // more likely we see this in the installer log.
675 LOG(ERROR
) << "Deleting old version directory: " << next_version
.value();
677 // Attempt to recursively delete the old version dir.
678 bool delete_succeeded
= base::DeleteFile(next_version
, true);
680 // Note: temporarily log old version deletion at ERROR level to make it
681 // more likely we see this in the installer log.
682 LOG_IF(ERROR
, !delete_succeeded
)
683 << "Failed to delete old version directory: " << next_version
.value();
688 void InstallerState::AddComDllList(
689 std::vector
<base::FilePath
>* com_dll_list
) const {
690 std::for_each(products_
.begin(), products_
.end(),
691 std::bind2nd(std::mem_fun(&Product::AddComDllList
),
695 bool InstallerState::SetChannelFlags(bool set
,
696 ChannelInfo
* channel_info
) const {
697 bool modified
= false;
698 for (Products::const_iterator scan
= products_
.begin(), end
= products_
.end();
699 scan
!= end
; ++scan
) {
700 modified
|= (*scan
)->SetChannelFlags(set
, channel_info
);
705 void InstallerState::UpdateStage(installer::InstallerStage stage
) const {
706 InstallUtil::UpdateInstallerStage(system_install(), state_key_
, stage
);
709 void InstallerState::UpdateChannels() const {
710 if (operation_
!= MULTI_INSTALL
&& operation_
!= MULTI_UPDATE
) {
711 VLOG(1) << "InstallerState::UpdateChannels noop: " << operation_
;
715 // Update the "ap" value for the product being installed/updated. We get the
716 // current value from the registry since the InstallationState instance used
717 // by the bulk of the installer does not track changes made by UpdateStage.
718 // Create the app's ClientState key if it doesn't exist.
719 ChannelInfo channel_info
;
720 base::win::RegKey state_key
;
721 LONG result
= state_key
.Create(root_key_
, state_key_
.c_str(),
722 KEY_QUERY_VALUE
| KEY_SET_VALUE
);
723 if (result
== ERROR_SUCCESS
) {
724 channel_info
.Initialize(state_key
);
726 // This is a multi-install product.
727 bool modified
= channel_info
.SetMultiInstall(true);
729 // Add the appropriate modifiers for all products and their options.
730 modified
|= SetChannelFlags(true, &channel_info
);
732 VLOG(1) << "ap: " << channel_info
.value();
734 // Write the results if needed.
736 channel_info
.Write(&state_key
);
738 // Remove the -stage: modifier since we don't want to propagate that to the
740 channel_info
.SetStage(NULL
);
742 // Synchronize the other products and the package with this one.
743 ChannelInfo other_info
;
744 for (int i
= 0; i
< BrowserDistribution::NUM_TYPES
; ++i
) {
745 BrowserDistribution::Type type
=
746 static_cast<BrowserDistribution::Type
>(i
);
747 // Skip the app_guid we started with.
748 if (type
== state_type_
)
750 BrowserDistribution
* dist
= NULL
;
751 // Always operate on the binaries.
752 if (i
== BrowserDistribution::CHROME_BINARIES
) {
753 dist
= multi_package_distribution_
;
755 const Product
* product
= FindProduct(type
);
756 // Skip this one if it's for a product we're not operating on.
759 dist
= product
->distribution();
761 result
= state_key
.Create(root_key_
, dist
->GetStateKey().c_str(),
762 KEY_QUERY_VALUE
| KEY_SET_VALUE
);
763 if (result
== ERROR_SUCCESS
) {
764 other_info
.Initialize(state_key
);
765 if (!other_info
.Equals(channel_info
))
766 channel_info
.Write(&state_key
);
768 LOG(ERROR
) << "Failed opening key " << dist
->GetStateKey()
769 << " to update app channels; result: " << result
;
773 LOG(ERROR
) << "Failed opening key " << state_key_
774 << " to update app channels; result: " << result
;
778 void InstallerState::WriteInstallerResult(
779 InstallStatus status
,
780 int string_resource_id
,
781 const std::wstring
* const launch_cmd
) const {
782 DWORD installer_result
=
783 (InstallUtil::GetInstallReturnCode(status
) == 0) ? 0 : 1;
784 // Use a no-rollback list since this is a best-effort deal.
785 scoped_ptr
<WorkItemList
> install_list(
786 WorkItem::CreateNoRollbackWorkItemList());
787 const bool system_install
= this->system_install();
788 // Write the value for all products upon which we're operating.
789 Products::const_iterator end
= products().end();
790 for (Products::const_iterator scan
= products().begin(); scan
!= end
;
792 InstallUtil::AddInstallerResultItems(
793 system_install
, (*scan
)->distribution()->GetStateKey(), status
,
794 string_resource_id
, launch_cmd
, install_list
.get());
796 // And for the binaries if this is a multi-install.
797 if (is_multi_install()) {
798 InstallUtil::AddInstallerResultItems(
799 system_install
, multi_package_binaries_distribution()->GetStateKey(),
800 status
, string_resource_id
, launch_cmd
, install_list
.get());
802 if (!install_list
->Do())
803 LOG(ERROR
) << "Failed to record installer error information in registry.";
806 bool InstallerState::RequiresActiveSetup() const {
807 return system_install() && FindProduct(BrowserDistribution::CHROME_BROWSER
);
810 } // namespace installer