|
OLD | NEW |
---|---|
(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 | |
OLD | NEW |