Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / installer / util / installer_state.cc
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.
4
5 #include "chrome/installer/util/installer_state.h"
6
7 #include <algorithm>
8 #include <functional>
9 #include <utility>
10
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"
30
31 namespace installer {
32
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;
44   }
45
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;
53     }
54     if (!product->channel().Equals(binaries->channel())) {
55       VLOG(2) << "It seems that chrome is being over installed.";
56       return false;
57     }
58   }
59
60   VLOG(2) << "It seems that the binaries are being updated.";
61
62   return true;
63 }
64
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),
74       ensure_google_update_present_(false) {
75 }
76
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),
83       root_key_(NULL),
84       msi_(false),
85       verbose_logging_(false),
86       ensure_google_update_present_(false) {
87   // Use set_level() so that root_key_ is updated properly.
88   set_level(level);
89 }
90
91 void InstallerState::Initialize(const CommandLine& command_line,
92                                 const MasterPreferences& prefs,
93                                 const InstallationState& machine_state) {
94   Clear();
95
96   bool pref_bool;
97   if (!prefs.GetBool(master_preferences::kSystemLevel, &pref_bool))
98     pref_bool = false;
99   set_level(pref_bool ? SYSTEM_LEVEL : USER_LEVEL);
100
101   if (!prefs.GetBool(master_preferences::kVerboseLogging, &verbose_logging_))
102     verbose_logging_ = false;
103
104   if (!prefs.GetBool(master_preferences::kMultiInstall, &pref_bool))
105     pref_bool = false;
106   set_package_type(pref_bool ? MULTI_PACKAGE : SINGLE_PACKAGE);
107
108   if (!prefs.GetBool(master_preferences::kMsi, &msi_))
109     msi_ = false;
110
111   ensure_google_update_present_ =
112       command_line.HasSwitch(installer::switches::kEnsureGoogleUpdatePresent);
113
114   const bool is_uninstall = command_line.HasSwitch(switches::kUninstall);
115
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();
121   }
122
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();
128   }
129
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.
139       if (!chrome_state) {
140         const ProductState* binaries_state = machine_state.GetProductState(
141             true,  // system level
142             BrowserDistribution::CHROME_BINARIES);
143         if (!binaries_state)
144           need_binaries = true;
145       }
146     }
147
148     // Chrome multi needs Binaries at its own level.
149     if (FindProduct(BrowserDistribution::CHROME_BROWSER))
150       need_binaries = true;
151
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();
158     }
159   }
160
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
165       // command.
166       const struct {
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,
178           false },
179       };
180
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();
193         }
194       }
195     }
196
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);
203
204       if (type == BrowserDistribution::CHROME_BINARIES)
205         continue;
206
207       const ProductState* product_state =
208           machine_state.GetProductState(system_install(), type);
209       if (product_state == NULL) {
210         // The product is not installed.
211         continue;
212       }
213
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.
220         continue;
221       }
222
223       // The product is installed.
224
225       if (!FindProduct(type)) {
226         // The product is not being uninstalled.
227         if (type != BrowserDistribution::CHROME_APP_HOST) {
228           keep_binaries = true;
229           break;
230         } else {
231           // If binaries/chrome are at system-level, we can discard them at
232           // user-level...
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;
241             break;
242           }
243
244         }
245       }
246
247       // The product is being uninstalled.
248     }
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();
256     }
257   }
258
259   BrowserDistribution* operand = NULL;
260
261   if (is_uninstall) {
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
269     // app guid.
270     operand = multi_package_distribution_;
271     operation_ = MULTI_UPDATE;
272   } else {
273     operation_ = MULTI_INSTALL;
274   }
275
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;
285
286     operand = BrowserDistribution::GetSpecificDistribution(
287         operand_distribution_type);
288   }
289
290   state_key_ = operand->GetStateKey();
291   state_type_ = operand->GetType();
292
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);
297 }
298
299 void InstallerState::set_level(Level level) {
300   level_ = level;
301   switch (level) {
302     case USER_LEVEL:
303       root_key_ = HKEY_CURRENT_USER;
304       break;
305     case SYSTEM_LEVEL:
306       root_key_ = HKEY_LOCAL_MACHINE;
307       break;
308     default:
309       DCHECK(level == UNKNOWN_LEVEL);
310       level_ = UNKNOWN_LEVEL;
311       root_key_ = NULL;
312       break;
313   }
314 }
315
316 void InstallerState::set_package_type(PackageType type) {
317   package_type_ = type;
318   switch (type) {
319     case SINGLE_PACKAGE:
320       multi_package_distribution_ = NULL;
321       break;
322     case MULTI_PACKAGE:
323       multi_package_distribution_ =
324           BrowserDistribution::GetSpecificDistribution(
325               BrowserDistribution::CHROME_BINARIES);
326       break;
327     default:
328       DCHECK(type == UNKNOWN_PACKAGE_TYPE);
329       package_type_ = UNKNOWN_PACKAGE_TYPE;
330       multi_package_distribution_ = NULL;
331       break;
332   }
333 }
334
335 // Returns the Chrome binaries directory for multi-install or |dist|'s directory
336 // otherwise.
337 base::FilePath InstallerState::GetDefaultProductInstallPath(
338     BrowserDistribution* dist) const {
339   DCHECK(dist);
340   DCHECK(package_type_ != UNKNOWN_PACKAGE_TYPE);
341
342   if (package_type_ == SINGLE_PACKAGE) {
343     return GetChromeInstallPath(system_install(), dist);
344   } else {
345     return GetChromeInstallPath(system_install(),
346         BrowserDistribution::GetSpecificDistribution(
347             BrowserDistribution::CHROME_BINARIES));
348   }
349 }
350
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_) {
357     case SINGLE_PACKAGE:
358       if (!products_.empty()) {
359         LOG(DFATAL) << "Cannot process more than one single-install product.";
360         return false;
361       }
362       break;
363     case MULTI_PACKAGE:
364       if (!product.HasOption(kOptionMultiInstall)) {
365         LOG(DFATAL) << "Cannot process a single-install product with a "
366                        "multi-install state.";
367         return false;
368       }
369       if (FindProduct(product.distribution()->GetType()) != NULL) {
370         LOG(DFATAL) << "Cannot process more than one product of the same type.";
371         return false;
372       }
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.";
381           return false;
382         }
383       }
384       break;
385     default:
386       DCHECK_EQ(UNKNOWN_PACKAGE_TYPE, package_type_);
387       break;
388   }
389   return true;
390 }
391
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();
402
403   if (!CanAddProduct(the_product, product_dir))
404     return NULL;
405
406   if (package_type_ == UNKNOWN_PACKAGE_TYPE) {
407     set_package_type(the_product.HasOption(kOptionMultiInstall) ?
408                          MULTI_PACKAGE : SINGLE_PACKAGE);
409   }
410
411   if (target_path_.empty()) {
412     if (product_dir == NULL)
413       target_path_ = GetDefaultProductInstallPath(the_product.distribution());
414     else
415       target_path_ = *product_dir;
416   }
417
418   if (state_key_.empty())
419     state_key_ = the_product.distribution()->GetStateKey();
420
421   products_.push_back(product->release());
422   return products_[products_.size() - 1];
423 }
424
425 Product* InstallerState::AddProduct(scoped_ptr<Product>* product) {
426   return AddProductInDirectory(NULL, product);
427 }
428
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
433 // to the caller.
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);
442
443   Product* product = AddProductInDirectory(NULL, &product_ptr);
444
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();
450   }
451
452   return product;
453 }
454
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());
461
462   // Strip off <version>/Installer/setup.exe; see GetInstallerDirectory().
463   base::FilePath product_dir =
464       state.GetSetupPath().DirName().DirName().DirName();
465
466   Product* product = AddProductInDirectory(&product_dir, &product_ptr);
467
468   if (product != NULL)
469     msi_ |= state.is_msi();
470
471   return product;
472 }
473
474 bool InstallerState::system_install() const {
475   DCHECK(level_ == USER_LEVEL || level_ == SYSTEM_LEVEL);
476   return level_ == SYSTEM_LEVEL;
477 }
478
479 bool InstallerState::is_multi_install() const {
480   DCHECK(package_type_ == SINGLE_PACKAGE || package_type_ == MULTI_PACKAGE);
481   return package_type_ != SINGLE_PACKAGE;
482 }
483
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);
489     return true;
490   }
491   return false;
492 }
493
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))
499        return *scan;
500   }
501   return NULL;
502 }
503
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
518       // target directory.
519       Products::const_iterator end = products().end();
520       for (Products::const_iterator scan = products().begin(); scan != end;
521            ++scan) {
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;
528           break;
529         }
530       }
531     }
532   } else {
533     prod_type = products_[0]->distribution()->GetType();
534   }
535   const ProductState* product_state =
536       machine_state.GetProductState(level_ == SYSTEM_LEVEL, prod_type);
537
538   if (product_state != NULL) {
539     const Version* version = NULL;
540
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();
546
547     if (version == NULL)
548       version = &product_state->version();
549
550     current_version.reset(new Version(*version));
551   }
552
553   return current_version.release();
554 }
555
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_;
566   }
567   return Version();
568 }
569
570 bool InstallerState::IsChromeFrameRunning(
571     const InstallationState& machine_state) const {
572   return AnyExistsAndIsInUse(machine_state, CHROME_FRAME_DLL);
573 }
574
575 bool InstallerState::AreBinariesInUse(
576     const InstallationState& machine_state) const {
577   return AnyExistsAndIsInUse(
578       machine_state,
579       (CHROME_FRAME_HELPER_EXE | CHROME_FRAME_HELPER_DLL |
580        CHROME_FRAME_DLL | CHROME_DLL));
581 }
582
583 base::FilePath InstallerState::GetInstallerDirectory(
584     const Version& version) const {
585   return target_path().Append(base::ASCIIToWide(version.GetString()))
586       .Append(kInstallerDir);
587 }
588
589 // static
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();
596 }
597
598 void InstallerState::Clear() {
599   operation_ = UNINITIALIZED;
600   target_path_.clear();
601   state_key_.clear();
602   state_type_ = BrowserDistribution::CHROME_BROWSER;
603   products_.clear();
604   multi_package_distribution_ = NULL;
605   critical_update_version_ = base::Version();
606   level_ = UNKNOWN_LEVEL;
607   package_type_ = UNKNOWN_PACKAGE_TYPE;
608   root_key_ = NULL;
609   msi_ = false;
610   verbose_logging_ = false;
611   ensure_google_update_present_ = false;
612 }
613
614 bool InstallerState::AnyExistsAndIsInUse(
615     const InstallationState& machine_state,
616     uint32 file_bits) const {
617   static const wchar_t* const kBinaryFileNames[] = {
618     kChromeDll,
619     kChromeFrameDll,
620     kChromeFrameHelperDll,
621     kChromeFrameHelperExe,
622   };
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);
629
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.
633   scoped_ptr<Version> current_version(GetCurrentVersion(machine_state));
634   if (!current_version)
635     return false;
636   base::FilePath directory(
637       target_path().AppendASCII(current_version->GetString()));
638   for (int i = 0; i < NUM_BINARIES; ++i) {
639     if (!(file_bits & (1U << i)))
640       continue;
641     base::FilePath file(directory.Append(kBinaryFileNames[i]));
642     if (base::PathExists(file) && IsFileInUse(file))
643       return true;
644   }
645   return false;
646 }
647
648 void InstallerState::GetExistingExeVersions(
649     std::set<std::string>* existing_versions) const {
650
651   static const wchar_t* const kChromeFilenames[] = {
652     installer::kChromeExe,
653     installer::kChromeNewExe,
654     installer::kChromeOldExe,
655   };
656
657   for (int i = 0; i < arraysize(kChromeFilenames); ++i) {
658     base::FilePath chrome_exe(target_path().Append(kChromeFilenames[i]));
659     scoped_ptr<FileVersionInfo> file_version_info(
660         FileVersionInfo::CreateFileVersionInfo(chrome_exe));
661     if (file_version_info) {
662       base::string16 version_string = file_version_info->file_version();
663       if (!version_string.empty() && base::IsStringASCII(version_string))
664         existing_versions->insert(base::UTF16ToASCII(version_string));
665     }
666   }
667 }
668
669 void InstallerState::RemoveOldVersionDirectories(
670     const Version& new_version,
671     Version* existing_version,
672     const base::FilePath& temp_path) const {
673   Version version;
674   scoped_ptr<WorkItem> item;
675
676   std::set<std::string> existing_version_strings;
677   existing_version_strings.insert(new_version.GetString());
678   if (existing_version)
679     existing_version_strings.insert(existing_version->GetString());
680
681   // Make sure not to delete any version dir that is "referenced" by an existing
682   // Chrome executable.
683   GetExistingExeVersions(&existing_version_strings);
684
685   // Try to delete all directories that are not in the set we care to keep.
686   base::FileEnumerator version_enum(target_path(), false,
687                                     base::FileEnumerator::DIRECTORIES);
688   for (base::FilePath next_version = version_enum.Next(); !next_version.empty();
689        next_version = version_enum.Next()) {
690     base::FilePath dir_name(next_version.BaseName());
691     version = Version(base::UTF16ToASCII(dir_name.value()));
692     // Delete the version folder if it is less than the new version and not
693     // equal to the old version (if we have an old version).
694     if (version.IsValid() &&
695         existing_version_strings.count(version.GetString()) == 0) {
696       // Note: temporarily log old version deletion at ERROR level to make it
697       // more likely we see this in the installer log.
698       LOG(ERROR) << "Deleting old version directory: " << next_version.value();
699
700       // Attempt to recursively delete the old version dir.
701       bool delete_succeeded = base::DeleteFile(next_version, true);
702
703       // Note: temporarily log old version deletion at ERROR level to make it
704       // more likely we see this in the installer log.
705       LOG_IF(ERROR, !delete_succeeded)
706           << "Failed to delete old version directory: " << next_version.value();
707     }
708   }
709 }
710
711 void InstallerState::AddComDllList(
712     std::vector<base::FilePath>* com_dll_list) const {
713   std::for_each(products_.begin(), products_.end(),
714                 std::bind2nd(std::mem_fun(&Product::AddComDllList),
715                              com_dll_list));
716 }
717
718 bool InstallerState::SetChannelFlags(bool set,
719                                      ChannelInfo* channel_info) const {
720   bool modified = false;
721   for (Products::const_iterator scan = products_.begin(), end = products_.end();
722        scan != end; ++scan) {
723      modified |= (*scan)->SetChannelFlags(set, channel_info);
724   }
725   return modified;
726 }
727
728 void InstallerState::UpdateStage(installer::InstallerStage stage) const {
729   InstallUtil::UpdateInstallerStage(system_install(), state_key_, stage);
730 }
731
732 void InstallerState::UpdateChannels() const {
733   if (operation_ != MULTI_INSTALL && operation_ != MULTI_UPDATE) {
734     VLOG(1) << "InstallerState::UpdateChannels noop: " << operation_;
735     return;
736   }
737
738   // Update the "ap" value for the product being installed/updated.  We get the
739   // current value from the registry since the InstallationState instance used
740   // by the bulk of the installer does not track changes made by UpdateStage.
741   // Create the app's ClientState key if it doesn't exist.
742   ChannelInfo channel_info;
743   base::win::RegKey state_key;
744   LONG result =
745       state_key.Create(root_key_,
746                        state_key_.c_str(),
747                        KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_WOW64_32KEY);
748   if (result == ERROR_SUCCESS) {
749     channel_info.Initialize(state_key);
750
751     // This is a multi-install product.
752     bool modified = channel_info.SetMultiInstall(true);
753
754     // Add the appropriate modifiers for all products and their options.
755     modified |= SetChannelFlags(true, &channel_info);
756
757     VLOG(1) << "ap: " << channel_info.value();
758
759     // Write the results if needed.
760     if (modified)
761       channel_info.Write(&state_key);
762
763     // Remove the -stage: modifier since we don't want to propagate that to the
764     // other app_guids.
765     channel_info.SetStage(NULL);
766
767     // Synchronize the other products and the package with this one.
768     ChannelInfo other_info;
769     for (int i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
770       BrowserDistribution::Type type =
771           static_cast<BrowserDistribution::Type>(i);
772       // Skip the app_guid we started with.
773       if (type == state_type_)
774         continue;
775       BrowserDistribution* dist = NULL;
776       // Always operate on the binaries.
777       if (i == BrowserDistribution::CHROME_BINARIES) {
778         dist = multi_package_distribution_;
779       } else {
780         const Product* product = FindProduct(type);
781         // Skip this one if it's for a product we're not operating on.
782         if (product == NULL)
783           continue;
784         dist = product->distribution();
785       }
786       result =
787           state_key.Create(root_key_,
788                            dist->GetStateKey().c_str(),
789                            KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_WOW64_32KEY);
790       if (result == ERROR_SUCCESS) {
791         other_info.Initialize(state_key);
792         if (!other_info.Equals(channel_info))
793           channel_info.Write(&state_key);
794       } else {
795         LOG(ERROR) << "Failed opening key " << dist->GetStateKey()
796                    << " to update app channels; result: " << result;
797       }
798     }
799   } else {
800     LOG(ERROR) << "Failed opening key " << state_key_
801                << " to update app channels; result: " << result;
802   }
803 }
804
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;
816        ++scan) {
817     InstallUtil::AddInstallerResultItems(
818         system_install, (*scan)->distribution()->GetStateKey(), status,
819         string_resource_id, launch_cmd, install_list.get());
820   }
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());
826   }
827   if (!install_list->Do())
828     LOG(ERROR) << "Failed to record installer error information in registry.";
829 }
830
831 bool InstallerState::RequiresActiveSetup() const {
832   return system_install() && FindProduct(BrowserDistribution::CHROME_BROWSER);
833 }
834
835 }  // namespace installer