Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(777)

Side by Side Diff: chrome/installer/setup/install_worker.cc

Issue 6153003: Refactor install.cc into the work item parts and the non-work item parts. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 // This file contains the definitions of the installer functions that build
6 // the WorkItemList used to install the application.
7
8 #include "chrome/installer/setup/install_worker.h"
9
10 #include <shlobj.h>
11 #include <time.h>
12 #include <vector>
13
14 #include "base/command_line.h"
15 #include "base/file_path.h"
16 #include "base/logging.h"
17 #include "base/path_service.h"
18 #include "base/string_util.h"
19 #include "base/utf_string_conversions.h"
20 #include "base/version.h"
21 #include "base/win/registry.h"
22 #include "chrome/installer/setup/install.h"
23 #include "chrome/installer/setup/setup_constants.h"
24 #include "chrome/installer/util/chrome_frame_distribution.h"
25 #include "chrome/installer/util/conditional_work_item_list.h"
26 #include "chrome/installer/util/create_reg_key_work_item.h"
27 #include "chrome/installer/util/google_update_constants.h"
28 #include "chrome/installer/util/helper.h"
29 #include "chrome/installer/util/installation_state.h"
30 #include "chrome/installer/util/installer_state.h"
31 #include "chrome/installer/util/install_util.h"
32 #include "chrome/installer/util/master_preferences.h"
33 #include "chrome/installer/util/master_preferences_constants.h"
34 #include "chrome/installer/util/package.h"
35 #include "chrome/installer/util/package_properties.h"
36 #include "chrome/installer/util/product.h"
37 #include "chrome/installer/util/set_reg_value_work_item.h"
38 #include "chrome/installer/util/shell_util.h"
39 #include "chrome/installer/util/util_constants.h"
40 #include "chrome/installer/util/work_item_list.h"
41
42 using base::win::RegKey;
43
44 namespace {
45
46 // This method tells if we are running on 64 bit platform so that we can copy
47 // one extra exe. If the API call to determine 64 bit fails, we play it safe
48 // and return true anyway so that the executable can be copied.
49 bool Is64bit() {
50 typedef BOOL (WINAPI *WOW_FUNC)(HANDLE, PBOOL);
tommi (sloooow) - chröme 2011/01/12 19:02:49 BOOL* instead of PBOOL.
robertshield 2011/01/13 17:06:32 Done.
51 BOOL is64 = FALSE;
amit 2011/01/12 17:41:22 nit: is_64bit :)
robertshield 2011/01/13 17:06:32 Done.
52
53 HANDLE handle = GetCurrentProcess();
tommi (sloooow) - chröme 2011/01/12 19:02:49 'handle' is less descriptive than just calling Get
robertshield 2011/01/13 17:06:32 Done. (In case it wasn't clear, this code is copy
54 HMODULE module = GetModuleHandle(L"kernel32.dll");
55 WOW_FUNC p = reinterpret_cast<WOW_FUNC>(GetProcAddress(module,
tommi (sloooow) - chröme 2011/01/12 19:02:49 nit: is_wow_64 instead of p
robertshield 2011/01/13 17:06:32 Done.
56 "IsWow64Process"));
57 if ((p != NULL) && (!(p)(handle, &is64) || (is64 != FALSE))) {
tommi (sloooow) - chröme 2011/01/12 19:02:49 You don't really need the if and two return statem
robertshield 2011/01/13 17:06:32 Done.
58 return true;
59 }
60
61 return false;
62 }
63
64 } // namespace
65
66 namespace installer {
67
erikwright (departed) 2011/01/12 16:31:49 I assume these are identical to the originals?
tommi (sloooow) - chröme 2011/01/12 19:02:49 +1 (to save on review time)
robertshield 2011/01/13 17:06:32 Yes, copy/pasted.
robertshield 2011/01/13 17:06:32 Done.
68 // Local helper to call AddRegisterComDllWorkItems for all DLLs in a set of
69 // products managed by a given package.
70 void AddRegisterComDllWorkItemsForPackage(const Package& package,
71 const Version* old_version,
72 const Version& new_version,
73 WorkItemList* work_item_list) {
74 // First collect the list of DLLs to be registered from each product.
75 const Products& products = package.products();
76 Products::const_iterator product_iter(products.begin());
77 std::vector<FilePath> com_dll_list;
78 for (; product_iter != products.end(); ++product_iter) {
79 BrowserDistribution* dist = product_iter->get()->distribution();
80 std::vector<FilePath> dist_dll_list(dist->GetComDllList());
81 com_dll_list.insert(com_dll_list.end(), dist_dll_list.begin(),
82 dist_dll_list.end());
83 }
84
85 // Then, if we got some, attempt to unregister the DLLs from the old
86 // version directory and then re-register them in the new one.
87 // Note that if we are migrating the install directory then we will not
88 // successfully unregister the old DLLs.
89 // TODO(robertshield): See whether we need to fix the migration case.
90 // TODO(robertshield): If we ever remove a DLL from a product, this will
91 // not unregister it on update. We should build the unregistration list from
92 // saved state instead of assuming it is the same as the registration list.
93 if (!com_dll_list.empty()) {
94 if (old_version) {
95 FilePath old_dll_path(
96 package.path().Append(UTF8ToWide(old_version->GetString())));
97
98 installer::AddRegisterComDllWorkItems(old_dll_path,
99 com_dll_list,
100 package.system_level(),
101 false, // Unregister
102 true, // May fail
103 work_item_list);
104 }
105
106 FilePath dll_path(
107 package.path().Append(UTF8ToWide(new_version.GetString())));
108 installer::AddRegisterComDllWorkItems(dll_path,
109 com_dll_list,
110 package.system_level(),
111 true, // Register
112 false, // Must succeed.
113 work_item_list);
114 }
115 }
116
117 void AddInstallerCopyTasks(const FilePath& setup_path,
118 const FilePath& archive_path,
119 const FilePath& temp_path,
120 const Version& new_version,
121 WorkItemList* install_list,
122 const Package& package) {
123 DCHECK(install_list);
124 FilePath installer_dir(package.GetInstallerDirectory(new_version));
125 install_list->AddCreateDirWorkItem(installer_dir);
126
127 FilePath exe_dst(installer_dir.Append(setup_path.BaseName()));
128 FilePath archive_dst(installer_dir.Append(archive_path.BaseName()));
129
130 install_list->AddCopyTreeWorkItem(setup_path.value(), exe_dst.value(),
131 temp_path.value(), WorkItem::ALWAYS);
132 if (package.system_level()) {
133 install_list->AddCopyTreeWorkItem(archive_path.value(), archive_dst.value(),
134 temp_path.value(), WorkItem::ALWAYS);
135 } else {
136 install_list->AddMoveTreeWorkItem(archive_path.value(), archive_dst.value(),
137 temp_path.value());
138 }
139 }
140
141 // This method adds work items to create (or update) Chrome uninstall entry in
142 // either the Control Panel->Add/Remove Programs list or in the Omaha client
143 // state key if running under an MSI installer.
144 void AddUninstallShortcutWorkItems(const FilePath& setup_path,
145 const Version& new_version,
146 WorkItemList* install_list,
147 const Product& product) {
148 HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE :
149 HKEY_CURRENT_USER;
150 BrowserDistribution* browser_dist = product.distribution();
151 DCHECK(browser_dist);
152
153 // When we are installed via an MSI, we need to store our uninstall strings
154 // in the Google Update client state key. We do this even for non-MSI
155 // managed installs to avoid breaking the edge case whereby an MSI-managed
156 // install is updated by a non-msi installer (which would confuse the MSI
157 // machinery if these strings were not also updated).
158 // Do not quote the command line for the MSI invocation.
159 FilePath install_path(product.package().path());
160 FilePath installer_path(
161 product.package().GetInstallerDirectory(new_version));
162 installer_path = installer_path.Append(setup_path.BaseName());
163
164 CommandLine uninstall_arguments(CommandLine::NO_PROGRAM);
165 AppendUninstallCommandLineFlags(&uninstall_arguments, product);
166
167 if (product.is_chrome()) {
168 // The Chrome uninstallation command serves as the master uninstall
169 // command for Chrome + all other products (i.e. Chrome Frame) that do
170 // not have an uninstall entry in the Add/Remove Programs dialog.
171 const Products& products = product.package().products();
172 for (size_t i = 0; i < products.size(); ++i) {
173 const Product& p = *products[i];
174 if (!p.is_chrome() && !p.ShouldCreateUninstallEntry()) {
175 p.distribution()->AppendUninstallCommandLineFlags(&uninstall_arguments);
176 }
177 }
178 }
179
180 std::wstring update_state_key(browser_dist->GetStateKey());
181 install_list->AddCreateRegKeyWorkItem(reg_root, update_state_key);
182 install_list->AddSetRegValueWorkItem(reg_root, update_state_key,
183 installer::kUninstallStringField, installer_path.value(), true);
184 install_list->AddSetRegValueWorkItem(reg_root, update_state_key,
185 installer::kUninstallArgumentsField,
186 uninstall_arguments.command_line_string(), true);
187
188 if (product.ShouldCreateUninstallEntry()) {
189 // We need to quote the command line for the Add/Remove Programs dialog.
190 CommandLine quoted_uninstall_cmd(installer_path);
191 DCHECK_EQ(quoted_uninstall_cmd.command_line_string()[0], '"');
192 quoted_uninstall_cmd.AppendArguments(uninstall_arguments, false);
193
194 std::wstring uninstall_reg = browser_dist->GetUninstallRegPath();
195 install_list->AddCreateRegKeyWorkItem(reg_root, uninstall_reg);
196 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
197 installer::kUninstallDisplayNameField,
198 browser_dist->GetAppShortCutName(), true);
199 install_list->AddSetRegValueWorkItem(reg_root,
200 uninstall_reg, installer::kUninstallStringField,
201 quoted_uninstall_cmd.command_line_string(), true);
202 install_list->AddSetRegValueWorkItem(reg_root,
203 uninstall_reg,
204 L"InstallLocation",
205 install_path.value(),
206 true);
207
208 // DisplayIcon, NoModify and NoRepair
209 FilePath chrome_icon(install_path.Append(installer::kChromeExe));
210 ShellUtil::GetChromeIcon(product.distribution(), chrome_icon.value());
211 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
tommi (sloooow) - chröme 2011/01/12 19:02:49 observation - we should factor out all these facto
robertshield 2011/01/13 17:06:32 I'm mocking the WorkItemList at the moment which a
212 L"DisplayIcon", chrome_icon.value(),
213 true);
214 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
215 L"NoModify", 1, true);
216 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
217 L"NoRepair", 1, true);
218
219 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
220 L"Publisher",
221 browser_dist->GetPublisherName(),
222 true);
223 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
224 L"Version",
225 UTF8ToWide(new_version.GetString()),
226 true);
227 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
228 L"DisplayVersion",
229 UTF8ToWide(new_version.GetString()),
230 true);
231 time_t rawtime = time(NULL);
232 struct tm timeinfo = {0};
233 localtime_s(&timeinfo, &rawtime);
234 wchar_t buffer[9];
235 if (wcsftime(buffer, 9, L"%Y%m%d", &timeinfo) == 8) {
236 install_list->AddSetRegValueWorkItem(reg_root, uninstall_reg,
237 L"InstallDate",
238 buffer, false);
239 }
240 }
241 }
242
243
tommi (sloooow) - chröme 2011/01/12 19:02:49 one empty line
robertshield 2011/01/13 17:06:32 Done.
244
245 // Create Version key for a product (if not already present) and sets the new
246 // product version as the last step.
247 void AddVersionKeyWorkItems(HKEY root,
248 const Product& product,
249 const Version& new_version,
250 WorkItemList* list) {
251 // Create Version key for each distribution (if not already present) and set
252 // the new product version as the last step.
253 std::wstring version_key(product.distribution()->GetVersionKey());
254 list->AddCreateRegKeyWorkItem(root, version_key);
255
256 std::wstring product_name(product.distribution()->GetAppShortCutName());
257 list->AddSetRegValueWorkItem(root, version_key, google_update::kRegNameField,
258 product_name, true); // overwrite name also
259 list->AddSetRegValueWorkItem(root, version_key,
260 google_update::kRegOopcrashesField, 1,
261 false); // set during first install
262 list->AddSetRegValueWorkItem(root, version_key,
263 google_update::kRegVersionField,
264 UTF8ToWide(new_version.GetString()),
265 true); // overwrite version
266 }
267
268 void AddProductSpecificWorkItems(bool install,
269 const FilePath& setup_path,
270 const Version& new_version,
271 const Package& package,
272 WorkItemList* list) {
273 const Products& products = package.products();
274 for (size_t i = 0; i < products.size(); ++i) {
275 const Product& p = *products[i];
276 if (p.is_chrome_frame()) {
277 AddChromeFrameWorkItems(install, setup_path, new_version, p, list);
278 }
279 }
280 }
281
tommi (sloooow) - chröme 2011/01/12 19:02:49 one empty line
robertshield 2011/01/13 17:06:32 Done.
282
283 // Adds work items that make registry adjustments for Google Update. When a
284 // product is installed (including overinstall), Google Update will write the
285 // channel ("ap") value into either Chrome or Chrome Frame's ClientState key.
286 // In the multi-install case, this value is used as the basis upon which the
287 // package's channel value is built (by adding the ordered list of installed
288 // products and their options).
289 void AddGoogleUpdateWorkItems(const InstallationState& original_state,
290 const InstallerState& installer_state,
291 const Package& package,
292 WorkItemList* install_list) {
293 // Is a multi-install product being installed or over-installed?
294 if (installer_state.operation() != InstallerState::MULTI_INSTALL)
295 return;
296
297 const HKEY reg_root = package.system_level() ? HKEY_LOCAL_MACHINE :
298 HKEY_CURRENT_USER;
299 const std::wstring key_path = installer_state.state_key();
300 ChannelInfo channel_info;
301
302 // Update the "ap" value for the product being installed/updated.
303 // It is completely acceptable for there to be no "ap" value or even no
304 // ClientState key. Note that we check the registry rather than
305 // original_state since on a fresh install the "ap" value will be present
306 // sans "pv" value.
307 channel_info.Initialize(RegKey(reg_root, key_path.c_str(), KEY_QUERY_VALUE));
308
309 // This is a multi-install product.
310 bool modified = channel_info.SetMultiInstall(true);
311
312 // Add the appropriate modifiers for all products and their options.
313 Products::const_iterator scan = package.products().begin();
314 const Products::const_iterator end = package.products().end();
315 for (; scan != end; ++scan) {
316 modified |= scan->get()->distribution()->SetChannelFlags(true,
317 &channel_info);
318 }
319
320 // Write the results if needed.
321 if (modified) {
322 install_list->AddSetRegValueWorkItem(reg_root, key_path,
323 google_update::kRegApField,
324 channel_info.value(), true);
325 }
326
327 // Synchronize the other products and the package with this one.
328 std::wstring other_key;
329 std::vector<std::wstring> keys;
330
331 keys.reserve(package.products().size());
332 other_key = package.properties()->GetStateKey();
333 if (other_key != key_path)
334 keys.push_back(other_key);
335 scan = package.products().begin();
336 for (; scan != end; ++scan) {
337 other_key = scan->get()->distribution()->GetStateKey();
338 if (other_key != key_path)
339 keys.push_back(other_key);
340 }
341
342 RegKey key;
343 ChannelInfo other_info;
344 std::vector<std::wstring>::const_iterator kscan = keys.begin();
345 std::vector<std::wstring>::const_iterator kend = keys.end();
346 for (; kscan != kend; ++kscan) {
347 // Handle the case where the ClientState key doesn't exist by creating it.
348 // This takes care of the multi-installer's package key, which is not
349 // created by Google Update for us.
350 if (!key.Open(reg_root, kscan->c_str(), KEY_QUERY_VALUE) ||
351 !other_info.Initialize(key)) {
352 other_info.set_value(std::wstring());
353 }
354 if (!other_info.Equals(channel_info)) {
355 if (!key.Valid())
356 install_list->AddCreateRegKeyWorkItem(reg_root, *kscan);
357 install_list->AddSetRegValueWorkItem(reg_root, *kscan,
358 google_update::kRegApField,
359 channel_info.value(), true);
360 }
361 }
362 // TODO(grt): check for other keys/values we should put in the package's
363 // ClientState and/or Clients key.
364 }
365
366 // This is called when an MSI installation is run. It may be that a user is
367 // attempting to install the MSI on top of a non-MSI managed installation.
368 // If so, try and remove any existing uninstallation shortcuts, as we want the
369 // uninstall to be managed entirely by the MSI machinery (accessible via the
370 // Add/Remove programs dialog).
371 void AddDeleteUninstallShortcutsForMSIWorkItems(const Product& product,
372 WorkItemList* work_item_list) {
373 DCHECK(product.IsMsi()) << "This must only be called for MSI installations!";
374
375 // First attempt to delete the old installation's ARP dialog entry.
376 HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE :
377 HKEY_CURRENT_USER;
378 base::win::RegKey root_key(reg_root, L"", KEY_ALL_ACCESS);
379 std::wstring uninstall_reg(product.distribution()->GetUninstallRegPath());
380
381 WorkItem* delete_reg_key = work_item_list->AddDeleteRegKeyWorkItem(
382 reg_root, uninstall_reg);
383 delete_reg_key->set_ignore_failure(true);
384
385 // Then attempt to delete the old installation's start menu shortcut.
386 FilePath uninstall_link;
387 if (product.system_level()) {
388 PathService::Get(base::DIR_COMMON_START_MENU, &uninstall_link);
389 } else {
390 PathService::Get(base::DIR_START_MENU, &uninstall_link);
391 }
392
393 if (uninstall_link.empty()) {
394 LOG(ERROR) << "Failed to get location for shortcut.";
395 } else {
396 uninstall_link = uninstall_link.Append(
397 product.distribution()->GetAppShortCutName());
398 uninstall_link = uninstall_link.Append(
399 product.distribution()->GetUninstallLinkName() + L".lnk");
400 VLOG(1) << "Deleting old uninstall shortcut (if present): "
401 << uninstall_link.value();
402 WorkItem* delete_link = work_item_list->AddDeleteTreeWorkItem(
403 uninstall_link);
404 delete_link->set_ignore_failure(true);
405 delete_link->set_log_message(
406 "Failed to delete old uninstall shortcut.");
407 }
408 }
409
tommi (sloooow) - chröme 2011/01/12 19:02:49 one empty line (and throughout)
robertshield 2011/01/13 17:06:32 Done.
410
411 // After a successful copying of all the files, this function is called to
412 // do a few post install tasks:
413 // - Handle the case of in-use-update by updating "opv" (old version) key or
414 // deleting it if not required.
415 // - Register any new dlls and unregister old dlls.
416 // - If this is an MSI install, ensures that the MSI marker is set, and sets
417 // it if not.
418 // If these operations are successful, the function returns true, otherwise
419 // false.
420 bool AppendPostInstallTasks(bool multi_install,
421 const FilePath& setup_path,
422 const FilePath& new_chrome_exe,
423 const Version* current_version,
424 const Version& new_version,
425 const Package& package,
426 WorkItemList* post_install_task_list) {
427 DCHECK(post_install_task_list);
428 HKEY root = package.system_level() ? HKEY_LOCAL_MACHINE :
429 HKEY_CURRENT_USER;
430 const Products& products = package.products();
431
tommi (sloooow) - chröme 2011/01/12 19:02:49 here too :)
robertshield 2011/01/13 17:06:32 Done.
432
433 // Append work items that will only be executed if this was an update.
434 // We update the 'opv' key with the current version that is active and 'cmd'
435 // key with the rename command to run.
436 {
437 scoped_ptr<WorkItemList> in_use_update_work_items(
438 WorkItem::CreateConditionalWorkItemList(
439 new ConditionRunIfFileExists(new_chrome_exe)));
440 in_use_update_work_items->set_log_message("InUseUpdateWorkItemList");
441
442 FilePath installer_path(package.GetInstallerDirectory(new_version)
443 .Append(setup_path.BaseName()));
444
445 CommandLine rename(installer_path);
446 rename.AppendSwitch(installer::switches::kRenameChromeExe);
447 if (package.system_level())
448 rename.AppendSwitch(installer::switches::kSystemLevel);
449
450 if (InstallUtil::IsChromeSxSProcess())
451 rename.AppendSwitch(installer::switches::kChromeSxS);
452
453 if (multi_install)
454 rename.AppendSwitch(installer::switches::kMultiInstall);
455
456 std::wstring version_key;
457 for (size_t i = 0; i < products.size(); ++i) {
458 BrowserDistribution* dist = products[i]->distribution();
459 version_key = dist->GetVersionKey();
460
461 if (current_version != NULL) {
462 in_use_update_work_items->AddSetRegValueWorkItem(root, version_key,
463 google_update::kRegOldVersionField,
464 UTF8ToWide(current_version->GetString()), true);
465 }
466
467 // Adding this registry entry for all products is overkill.
468 // However, as it stands, we don't have a way to know which distribution
469 // will check the key and run the command, so we add it for all.
470 // After the first run, the subsequent runs should just be noops.
471 // (see Upgrade::SwapNewChromeExeIfPresent).
472 in_use_update_work_items->AddSetRegValueWorkItem(
473 root,
474 version_key,
475 google_update::kRegRenameCmdField,
476 rename.command_line_string(),
477 true);
478 }
479
480 if (multi_install) {
481 PackageProperties* props = package.properties();
482 if (props->ReceivesUpdates() && current_version != NULL) {
483 in_use_update_work_items->AddSetRegValueWorkItem(
484 root,
485 props->GetVersionKey(),
486 google_update::kRegOldVersionField,
487 UTF8ToWide(current_version->GetString()),
488 true);
489 // TODO(tommi): We should move the rename command here. We also need to
490 // update Upgrade::SwapNewChromeExeIfPresent.
491 }
492 }
493
494 post_install_task_list->AddWorkItem(in_use_update_work_items.release());
495 }
496
497
498 // Append work items that will be executed if this was NOT an in-use update.
499 {
500 scoped_ptr<WorkItemList> regular_update_work_items(
501 WorkItem::CreateConditionalWorkItemList(
502 new Not(new ConditionRunIfFileExists(new_chrome_exe))));
503 regular_update_work_items->set_log_message(
504 "RegularUpdateWorkItemList");
505
506 // Since this was not an in-use-update, delete 'opv' and 'cmd' keys.
507 for (size_t i = 0; i < products.size(); ++i) {
508 BrowserDistribution* dist = products[i]->distribution();
509 std::wstring version_key(dist->GetVersionKey());
510 regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
511 google_update::kRegOldVersionField,
512 true);
513 regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
514 google_update::kRegRenameCmdField,
515 true);
516 }
517
518 post_install_task_list->AddWorkItem(regular_update_work_items.release());
519 }
520
521 AddRegisterComDllWorkItemsForPackage(package, current_version, new_version,
522 post_install_task_list);
523
524 for (size_t i = 0; i < products.size(); ++i) {
525 const Product* product = products[i];
526 // If we're told that we're an MSI install, make sure to set the marker
527 // in the client state key so that future updates do the right thing.
528 if (product->IsMsi()) {
529 AddSetMsiMarkerWorkItem(*product, true, post_install_task_list);
530
531 // We want MSI installs to take over the Add/Remove Programs shortcut.
532 // Make a best-effort attempt to delete any shortcuts left over from
533 // previous non-MSI installations for the same type of install (system or
534 // per user).
535 AddDeleteUninstallShortcutsForMSIWorkItems(*product,
536 post_install_task_list);
537 }
538 }
539
540 return true;
541 }
542
543 void AddInstallWorkItems(const InstallationState& original_state,
544 const InstallerState& installer_state,
545 bool multi_install,
546 const FilePath& setup_path,
547 const FilePath& archive_path,
548 const FilePath& src_path,
549 const FilePath& temp_dir,
550 const Version& new_version,
551 scoped_ptr<Version>* current_version,
552 const Package& package,
553 WorkItemList* install_list) {
554 DCHECK(install_list);
555
556 // A temp directory that work items need and the actual install directory.
557 install_list->AddCreateDirWorkItem(temp_dir);
558 install_list->AddCreateDirWorkItem(package.path());
559
560 // Delete any new_chrome.exe if present (we will end up creating a new one
561 // if required) and then copy chrome.exe
562 FilePath new_chrome_exe(
563 package.path().Append(installer::kChromeNewExe));
564
565 install_list->AddDeleteTreeWorkItem(new_chrome_exe);
566 install_list->AddCopyTreeWorkItem(
567 src_path.Append(installer::kChromeExe).value(),
568 package.path().Append(installer::kChromeExe).value(),
569 temp_dir.value(), WorkItem::NEW_NAME_IF_IN_USE, new_chrome_exe.value());
570
571 // Extra executable for 64 bit systems.
572 if (Is64bit()) {
573 install_list->AddCopyTreeWorkItem(
574 src_path.Append(installer::kWowHelperExe).value(),
575 package.path().Append(installer::kWowHelperExe).value(),
576 temp_dir.value(), WorkItem::ALWAYS);
577 }
578
579 // If it is system level install copy the version folder (since we want to
580 // take the permissions of %ProgramFiles% folder) otherwise just move it.
581 if (package.system_level()) {
582 install_list->AddCopyTreeWorkItem(
583 src_path.Append(UTF8ToWide(new_version.GetString())).value(),
584 package.path().Append(UTF8ToWide(new_version.GetString())).value(),
585 temp_dir.value(), WorkItem::ALWAYS);
586 } else {
587 install_list->AddMoveTreeWorkItem(
588 src_path.Append(UTF8ToWide(new_version.GetString())).value(),
589 package.path().Append(UTF8ToWide(new_version.GetString())).value(),
590 temp_dir.value());
591 }
592
593 // Copy the default Dictionaries only if the folder doesn't exist already.
594 install_list->AddCopyTreeWorkItem(
595 src_path.Append(installer::kDictionaries).value(),
596 package.path().Append(installer::kDictionaries).value(),
597 temp_dir.value(), WorkItem::IF_NOT_PRESENT);
598
599 // Delete any old_chrome.exe if present.
600 install_list->AddDeleteTreeWorkItem(
601 package.path().Append(installer::kChromeOldExe));
602
603 // Copy installer in install directory and
604 // add shortcut in Control Panel->Add/Remove Programs.
605 AddInstallerCopyTasks(setup_path, archive_path, temp_dir, new_version,
606 install_list, package);
607
608 HKEY root = package.system_level() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
609
610 const Products& products = package.products();
611 for (size_t i = 0; i < products.size(); ++i) {
612 const Product* product = products[i];
613
614 AddUninstallShortcutWorkItems(setup_path, new_version, install_list,
615 *product);
616
617 AddVersionKeyWorkItems(root, *product, new_version, install_list);
618 }
619
620 if (multi_install) {
621 PackageProperties* props = package.properties();
622 if (props->ReceivesUpdates()) {
623 std::wstring version_key(props->GetVersionKey());
624 install_list->AddCreateRegKeyWorkItem(root, version_key);
625 install_list->AddSetRegValueWorkItem(root, version_key,
626 google_update::kRegVersionField,
627 UTF8ToWide(new_version.GetString()),
628 true); // overwrite version
629 install_list->AddSetRegValueWorkItem(root, version_key,
630 google_update::kRegNameField,
631 ASCIIToWide(installer::PackageProperties::kPackageProductName),
632 true); // overwrite name also
633 }
634 }
635
636 // Add any remaining work items that involve special settings for
637 // each product.
638 AddProductSpecificWorkItems(true, setup_path, new_version, package,
639 install_list);
640
641 AddGoogleUpdateWorkItems(original_state, installer_state, package,
642 install_list);
643
644 // Append the tasks that run after the installation.
645 AppendPostInstallTasks(multi_install,
646 setup_path,
647 new_chrome_exe,
648 current_version->get(),
649 new_version,
650 package,
651 install_list);
652 }
653
654
655 void AddRegisterComDllWorkItems(const FilePath& dll_folder,
656 const std::vector<FilePath>& dll_list,
657 bool system_level,
658 bool do_register,
659 bool ignore_failures,
660 WorkItemList* work_item_list) {
661 DCHECK(work_item_list);
662 if (dll_list.empty()) {
663 VLOG(1) << "No COM DLLs to register";
664 } else {
665 std::vector<FilePath>::const_iterator dll_iter(dll_list.begin());
666 for (; dll_iter != dll_list.end(); ++dll_iter) {
667 FilePath dll_path = dll_folder.Append(*dll_iter);
668 WorkItem* work_item = work_item_list->AddSelfRegWorkItem(
669 dll_path.value(), do_register, !system_level);
670 DCHECK(work_item);
671 work_item->set_ignore_failure(ignore_failures);
672 }
673 }
674 }
675
676 void AddSetMsiMarkerWorkItem(const Product& product,
677 bool set,
678 WorkItemList* work_item_list) {
679 DCHECK(work_item_list);
680 BrowserDistribution* dist = product.distribution();
681 HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE :
682 HKEY_CURRENT_USER;
683 DWORD msi_value = set ? 1 : 0;
684 WorkItem* set_msi_work_item = work_item_list->AddSetRegValueWorkItem(
685 reg_root, dist->GetStateKey(), google_update::kRegMSIField,
686 msi_value, true);
687 DCHECK(set_msi_work_item);
688 set_msi_work_item->set_ignore_failure(true);
689 set_msi_work_item->set_log_message("Could not write MSI marker!");
690 }
691
692 void AddChromeFrameWorkItems(bool install,
693 const FilePath& setup_path,
694 const Version& new_version,
695 const Product& product,
696 WorkItemList* list) {
697 DCHECK(product.is_chrome_frame());
698 if (!product.package().multi_install()) {
699 VLOG(1) << "Not adding GCF specific work items for single install.";
700 return;
701 }
702
703 // TODO(tommi): This assumes we know exactly how ShouldCreateUninstallEntry
704 // is implemented. Since there is logic in ChromeFrameDistribution for how
705 // to determine when this is enabled, this is how we have to figure out if
706 // this feature is enabled right now, but it's a hack and we need a cleaner
707 // way to figure this out.
708 // Note that we cannot just check the master preferences for
709 // kChromeFrameReadyMode, since there are other things that need to be correct
710 // in the environment in order to enable this feature.
711 bool ready_mode = !product.distribution()->ShouldCreateUninstallEntry();
712
713 HKEY root = product.package().system_level() ? HKEY_LOCAL_MACHINE :
714 HKEY_CURRENT_USER;
715 bool update_chrome_uninstall_command = false;
716 if (ready_mode) {
717 // If GCF is being installed in ready mode, we write an entry to the
718 // multi-install state key. If the value already exists, we will not
719 // overwrite it since the user might have opted out.
720 list->AddCreateRegKeyWorkItem(root,
721 product.package().properties()->GetStateKey());
722 list->AddSetRegValueWorkItem(root,
723 product.package().properties()->GetStateKey(),
724 installer::kChromeFrameReadyModeField,
725 install ? 1 : 0, // The value we want to set.
726 install ? false : true); // Overwrite existing value.
727 if (!install) {
728 // If Chrome is not also being uninstalled, we need to update its command
729 // line so that it doesn't include uninstalling Chrome Frame now.
730 update_chrome_uninstall_command =
731 (installer::FindProduct(product.package().products(),
732 BrowserDistribution::CHROME_BROWSER) == NULL);
733 }
734 } else {
735 // It doesn't matter here if we're installing or uninstalling Chrome Frame.
736 // If ready mode isn't specified on the command line for installs, we need
737 // to delete the ready mode flag from the registry if it exists - this
738 // constitutes an opt-in for the user. If we're uninstalling CF and ready
739 // mode isn't specified on the command line, that means that CF wasn't
740 // installed with ready mode enabled (the --ready-mode switch should be set
741 // in the registry) so deleting the value should have no effect.
742 // In both cases (install/uninstall), we need to make sure that Chrome's
743 // uninstallation command line does not include the --chrome-frame switch
744 // so that uninstalling Chrome will no longer uninstall Chrome Frame.
745
746 if (RegKey(root, product.package().properties()->GetStateKey().c_str(),
747 KEY_QUERY_VALUE).Valid()) {
748 list->AddDeleteRegValueWorkItem(root,
749 product.package().properties()->GetStateKey(),
750 installer::kChromeFrameReadyModeField, false);
751 }
752
753 const Product* chrome = installer::FindProduct(product.package().products(),
754 BrowserDistribution::CHROME_BROWSER);
755 if (chrome) {
756 // Chrome is already a part of this installation run, so we can assume
757 // that the uninstallation arguments will be updated correctly.
758 } else {
759 // Chrome is not a part of this installation run, so we have to explicitly
760 // check if Chrome is installed, and if so, update its uninstallation
761 // command lines.
762 BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution(
763 BrowserDistribution::CHROME_BROWSER,
764 MasterPreferences::ForCurrentProcess());
765 update_chrome_uninstall_command =
766 IsInstalledAsMulti(product.system_level(), dist);
767 }
768 }
769
770 if (update_chrome_uninstall_command) {
771 // Chrome is not a part of this installation run, so we have to explicitly
772 // check if Chrome is installed, and if so, update its uninstallation
773 // command lines.
774 const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess();
775 BrowserDistribution* chrome_dist =
776 BrowserDistribution::GetSpecificDistribution(
777 BrowserDistribution::CHROME_BROWSER, prefs);
778 const Package& pack = product.package();
779 scoped_refptr<Package> package(new Package(pack.multi_install(),
780 pack.system_level(), pack.path(), pack.properties()));
781 scoped_refptr<Product> chrome_product(new Product(chrome_dist, package));
782 AddUninstallShortcutWorkItems(setup_path, new_version, list,
783 *chrome_product.get());
784 }
785 }
786
787 void AppendUninstallCommandLineFlags(CommandLine* uninstall_cmd,
788 const Product& product) {
789 DCHECK(uninstall_cmd);
790
791 uninstall_cmd->AppendSwitch(installer::switches::kUninstall);
792
793 // Append the product-specific uninstall flags.
794 product.distribution()->AppendUninstallCommandLineFlags(uninstall_cmd);
795 if (product.IsMsi()) {
796 uninstall_cmd->AppendSwitch(installer::switches::kMsi);
797 // See comment in uninstall.cc where we check for the kDeleteProfile switch.
798 if (product.is_chrome_frame()) {
799 uninstall_cmd->AppendSwitch(installer::switches::kDeleteProfile);
800 }
801 }
802 if (product.system_level())
803 uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel);
804
805 // Propagate switches obtained from preferences as well.
806 const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess();
807 if (prefs.is_multi_install()) {
808 uninstall_cmd->AppendSwitch(installer::switches::kMultiInstall);
809 }
810 bool value = false;
811 if (prefs.GetBool(installer::master_preferences::kVerboseLogging,
812 &value) && value)
813 uninstall_cmd->AppendSwitch(installer::switches::kVerboseLogging);
814 }
815
816 } // namespace installer
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698