OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/first_run/first_run.h" | 5 #include "chrome/browser/first_run/first_run.h" |
6 | 6 |
7 #include <windows.h> | 7 #include <windows.h> |
8 #include <shellapi.h> | 8 #include <shellapi.h> |
9 #include <shlobj.h> | 9 #include <shlobj.h> |
10 | 10 |
11 #include "base/callback.h" | 11 #include "base/callback.h" |
12 #include "base/environment.h" | 12 #include "base/environment.h" |
13 #include "base/file_util.h" | 13 #include "base/file_util.h" |
14 #include "base/files/file_path.h" | 14 #include "base/files/file_path.h" |
15 #include "base/path_service.h" | 15 #include "base/path_service.h" |
16 #include "base/prefs/pref_service.h" | 16 #include "base/prefs/pref_service.h" |
17 #include "base/process_util.h" | 17 #include "base/process_util.h" |
18 #include "base/stringprintf.h" | 18 #include "base/stringprintf.h" |
19 #include "base/strings/string_number_conversions.h" | 19 #include "base/strings/string_number_conversions.h" |
20 #include "base/strings/string_split.h" | 20 #include "base/strings/string_split.h" |
21 #include "base/threading/sequenced_worker_pool.h" | 21 #include "base/threading/sequenced_worker_pool.h" |
22 #include "base/time.h" | 22 #include "base/time.h" |
23 #include "base/utf_string_conversions.h" | 23 #include "base/utf_string_conversions.h" |
24 #include "base/win/metro.h" | 24 #include "base/win/metro.h" |
25 #include "base/win/object_watcher.h" | 25 #include "base/win/object_watcher.h" |
26 #include "base/win/windows_version.h" | 26 #include "base/win/windows_version.h" |
27 #include "chrome/browser/browser_process.h" | 27 #include "chrome/browser/browser_process.h" |
28 #include "chrome/browser/first_run/first_run_internal.h" | 28 #include "chrome/browser/first_run/first_run_internal.h" |
29 #include "chrome/browser/importer/importer_host.h" | 29 #include "chrome/browser/importer/importer_host.h" |
30 #include "chrome/browser/importer/importer_list.h" | 30 #include "chrome/browser/importer/importer_list.h" |
Miranda Callahan
2013/04/24 15:16:40
can remove these two importer includes.
gab
2013/04/24 20:24:59
Nice catch, forgot to go over this file's includes
| |
31 #include "chrome/browser/process_singleton.h" | 31 #include "chrome/browser/process_singleton.h" |
32 #include "chrome/browser/profiles/profile.h" | 32 #include "chrome/browser/profiles/profile.h" |
33 #include "chrome/browser/shell_integration.h" | 33 #include "chrome/browser/shell_integration.h" |
34 #include "chrome/common/chrome_constants.h" | 34 #include "chrome/common/chrome_constants.h" |
35 #include "chrome/common/chrome_paths.h" | 35 #include "chrome/common/chrome_paths.h" |
36 #include "chrome/common/chrome_result_codes.h" | 36 #include "chrome/common/chrome_result_codes.h" |
37 #include "chrome/common/chrome_switches.h" | 37 #include "chrome/common/chrome_switches.h" |
38 #include "chrome/common/pref_names.h" | 38 #include "chrome/common/pref_names.h" |
39 #include "chrome/common/worker_thread_ticker.h" | 39 #include "chrome/common/worker_thread_ticker.h" |
Miranda Callahan
2013/04/24 15:16:40
can remove this include.
gab
2013/04/24 20:24:59
Done.
| |
40 #include "chrome/installer/util/browser_distribution.h" | 40 #include "chrome/installer/util/browser_distribution.h" |
41 #include "chrome/installer/util/google_update_settings.h" | 41 #include "chrome/installer/util/google_update_settings.h" |
42 #include "chrome/installer/util/install_util.h" | 42 #include "chrome/installer/util/install_util.h" |
43 #include "chrome/installer/util/master_preferences.h" | 43 #include "chrome/installer/util/master_preferences.h" |
44 #include "chrome/installer/util/master_preferences_constants.h" | 44 #include "chrome/installer/util/master_preferences_constants.h" |
45 #include "chrome/installer/util/shell_util.h" | 45 #include "chrome/installer/util/shell_util.h" |
46 #include "chrome/installer/util/util_constants.h" | 46 #include "chrome/installer/util/util_constants.h" |
47 #include "content/public/browser/browser_thread.h" | 47 #include "content/public/browser/browser_thread.h" |
48 #include "content/public/browser/notification_service.h" | 48 #include "content/public/browser/notification_service.h" |
49 #include "content/public/browser/user_metrics.h" | 49 #include "content/public/browser/user_metrics.h" |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
165 // accepted. | 165 // accepted. |
166 bool CreateEULASentinel() { | 166 bool CreateEULASentinel() { |
167 base::FilePath eula_sentinel; | 167 base::FilePath eula_sentinel; |
168 if (!GetEULASentinelFilePath(&eula_sentinel)) | 168 if (!GetEULASentinelFilePath(&eula_sentinel)) |
169 return false; | 169 return false; |
170 | 170 |
171 return (file_util::CreateDirectory(eula_sentinel.DirName()) && | 171 return (file_util::CreateDirectory(eula_sentinel.DirName()) && |
172 file_util::WriteFile(eula_sentinel, "", 0) != -1); | 172 file_util::WriteFile(eula_sentinel, "", 0) != -1); |
173 } | 173 } |
174 | 174 |
175 // This class is used by first_run::ImportSettings to determine when the import | |
176 // process has ended and what was the result of the operation as reported by | |
177 // the process exit code. This class executes in the context of the main chrome | |
178 // process. | |
179 class ImportProcessRunner : public base::win::ObjectWatcher::Delegate { | |
180 public: | |
181 // The constructor takes the importer process to watch and then it does a | |
182 // message loop blocking wait until the process ends. This object now owns | |
183 // the import_process handle. | |
184 explicit ImportProcessRunner(base::ProcessHandle import_process) | |
185 : import_process_(import_process), | |
186 exit_code_(content::RESULT_CODE_NORMAL_EXIT) { | |
187 watcher_.StartWatching(import_process, this); | |
188 MessageLoop::current()->Run(); | |
189 } | |
190 virtual ~ImportProcessRunner() { | |
191 ::CloseHandle(import_process_); | |
192 } | |
193 // Returns the child process exit code. There are 2 expected values: | |
194 // NORMAL_EXIT, or IMPORTER_HUNG. | |
195 int exit_code() const { return exit_code_; } | |
196 | |
197 // The child process has terminated. Find the exit code and quit the loop. | |
198 virtual void OnObjectSignaled(HANDLE object) OVERRIDE { | |
199 DCHECK(object == import_process_); | |
200 if (!::GetExitCodeProcess(import_process_, &exit_code_)) { | |
201 NOTREACHED(); | |
202 } | |
203 MessageLoop::current()->Quit(); | |
204 } | |
205 | |
206 private: | |
207 base::win::ObjectWatcher watcher_; | |
208 base::ProcessHandle import_process_; | |
209 DWORD exit_code_; | |
210 }; | |
211 | |
212 // Check every 3 seconds if the importer UI has hung. | |
213 const int kPollHangFrequency = 3000; | |
214 | |
215 // This class specializes on finding hung 'owned' windows. Unfortunately, the | |
216 // HungWindowDetector class cannot be used here because it assumes child | |
217 // windows and not owned top-level windows. | |
218 // This code is executed in the context of the main browser process and will | |
219 // terminate the importer process if it is hung. | |
220 class HungImporterMonitor : public WorkerThreadTicker::Callback { | |
221 public: | |
222 // The ctor takes the owner popup window and the process handle of the | |
223 // process to kill in case the popup or its owned active popup become | |
224 // unresponsive. | |
225 HungImporterMonitor(HWND owner_window, base::ProcessHandle import_process) | |
226 : owner_window_(owner_window), | |
227 import_process_(import_process), | |
228 ticker_(kPollHangFrequency) { | |
229 ticker_.RegisterTickHandler(this); | |
230 ticker_.Start(); | |
231 } | |
232 virtual ~HungImporterMonitor() { | |
233 ticker_.Stop(); | |
234 ticker_.UnregisterTickHandler(this); | |
235 } | |
236 | |
237 private: | |
238 virtual void OnTick() OVERRIDE { | |
239 if (!import_process_) | |
240 return; | |
241 // We find the top active popup that we own, this will be either the | |
242 // owner_window_ itself or the dialog window of the other process. In | |
243 // both cases it is worth hung testing because both windows share the | |
244 // same message queue and at some point the other window could be gone | |
245 // while the other process still not pumping messages. | |
246 HWND active_window = ::GetLastActivePopup(owner_window_); | |
247 if (::IsHungAppWindow(active_window) || ::IsHungAppWindow(owner_window_)) { | |
248 ::TerminateProcess(import_process_, chrome::RESULT_CODE_IMPORTER_HUNG); | |
249 import_process_ = NULL; | |
250 } | |
251 } | |
252 | |
253 HWND owner_window_; | |
254 base::ProcessHandle import_process_; | |
255 WorkerThreadTicker ticker_; | |
256 DISALLOW_COPY_AND_ASSIGN(HungImporterMonitor); | |
257 }; | |
258 | |
259 std::string EncodeImportParams(int importer_type, | |
260 int options, | |
261 bool skip_first_run_ui) { | |
262 return base::StringPrintf("%d@%d@%d", importer_type, options, | |
263 skip_first_run_ui ? 1 : 0); | |
264 } | |
265 | |
266 bool DecodeImportParams(const std::string& encoded, | |
267 int* importer_type, | |
268 int* options, | |
269 bool* skip_first_run_ui) { | |
270 std::vector<std::string> parts; | |
271 base::SplitString(encoded, '@', &parts); | |
272 int skip_first_run_ui_int; | |
273 if ((parts.size() != 3) || !base::StringToInt(parts[0], importer_type) || | |
274 !base::StringToInt(parts[1], options) || | |
275 !base::StringToInt(parts[2], &skip_first_run_ui_int)) | |
276 return false; | |
277 *skip_first_run_ui = !!skip_first_run_ui_int; | |
278 return true; | |
279 } | |
280 | |
281 #if !defined(USE_AURA) | |
282 // Imports browser items in this process. The browser and the items to | |
283 // import are encoded in the command line. | |
284 int ImportFromBrowser(Profile* profile, | |
285 const CommandLine& cmdline) { | |
286 std::string import_info = cmdline.GetSwitchValueASCII(switches::kImport); | |
287 if (import_info.empty()) { | |
288 NOTREACHED(); | |
289 return false; | |
290 } | |
291 int importer_type = 0; | |
292 int items_to_import = 0; | |
293 bool skip_first_run_ui = false; | |
294 if (!DecodeImportParams(import_info, &importer_type, &items_to_import, | |
295 &skip_first_run_ui)) { | |
296 NOTREACHED(); | |
297 return false; | |
298 } | |
299 scoped_refptr<ImporterHost> importer_host(new ImporterHost); | |
300 | |
301 scoped_refptr<ImporterList> importer_list(new ImporterList(NULL)); | |
302 importer_list->DetectSourceProfilesHack(); | |
303 | |
304 // If |skip_first_run_ui|, we run in headless mode. This means that if | |
305 // there is user action required the import is automatically canceled. | |
306 if (skip_first_run_ui) | |
307 importer_host->set_headless(); | |
308 | |
309 first_run::internal::ImportEndedObserver observer; | |
310 importer_host->SetObserver(&observer); | |
311 importer_host->StartImportSettings( | |
312 importer_list->GetSourceProfileForImporterType(importer_type), profile, | |
313 static_cast<uint16>(items_to_import), new ProfileWriter(profile)); | |
314 // If the import process has not errored out, block on it. | |
315 if (!observer.ended()) { | |
316 observer.set_should_quit_message_loop(); | |
317 MessageLoop::current()->Run(); | |
318 } | |
319 // TODO(gab): This method will be go away as part of http://crbug.com/219419/, | |
320 // so it is fine to hardcode |RESULT_CODE_NORMAL_EXIT| here for now. | |
321 return content::RESULT_CODE_NORMAL_EXIT; | |
322 } | |
323 #endif // !defined(USE_AURA) | |
324 | |
325 bool ImportSettingsWin(Profile* profile, | |
326 int importer_type, | |
327 int items_to_import, | |
328 const base::FilePath& import_bookmarks_path, | |
329 bool skip_first_run_ui) { | |
330 if (!items_to_import && import_bookmarks_path.empty()) { | |
331 return true; | |
332 } | |
333 | |
334 const CommandLine& cmdline = *CommandLine::ForCurrentProcess(); | |
335 base::FilePath chrome_exe(cmdline.GetProgram()); | |
336 // |chrome_exe| cannot be a relative path as chrome.exe already changed its | |
337 // CWD in LoadChromeWithDirectory(), making the relative path used on the | |
338 // command-line invalid. The base name is sufficient given chrome.exe is in | |
339 // the CWD. | |
340 if (!chrome_exe.IsAbsolute()) | |
341 chrome_exe = chrome_exe.BaseName(); | |
342 CommandLine import_cmd(chrome_exe); | |
343 | |
344 const char* kSwitchNames[] = { | |
345 switches::kUserDataDir, | |
346 switches::kChromeFrame, | |
347 switches::kCountry, | |
348 }; | |
349 import_cmd.CopySwitchesFrom(cmdline, kSwitchNames, arraysize(kSwitchNames)); | |
350 | |
351 // Allow tests to introduce additional switches. | |
352 import_cmd.AppendArguments(first_run::GetExtraArgumentsForImportProcess(), | |
353 false); | |
354 | |
355 // Since ImportSettings is called before the local state is stored on disk | |
356 // we pass the language as an argument. GetApplicationLocale checks the | |
357 // current command line as fallback. | |
358 import_cmd.AppendSwitchASCII(switches::kLang, | |
359 g_browser_process->GetApplicationLocale()); | |
360 | |
361 if (items_to_import) { | |
362 import_cmd.AppendSwitchASCII(switches::kImport, | |
363 EncodeImportParams(importer_type, items_to_import, skip_first_run_ui)); | |
364 } | |
365 | |
366 if (!import_bookmarks_path.empty()) { | |
367 import_cmd.AppendSwitchPath(switches::kImportFromFile, | |
368 import_bookmarks_path); | |
369 } | |
370 | |
371 // The importer doesn't need to do any background networking tasks so disable | |
372 // them. | |
373 import_cmd.CommandLine::AppendSwitch(switches::kDisableBackgroundNetworking); | |
374 | |
375 // Time to launch the process that is going to do the import. | |
376 base::ProcessHandle import_process; | |
377 if (!base::LaunchProcess(import_cmd, base::LaunchOptions(), &import_process)) | |
378 return false; | |
379 | |
380 // We block inside the import_runner ctor, pumping messages until the | |
381 // importer process ends. This can happen either by completing the import | |
382 // or by hang_monitor killing it. | |
383 ImportProcessRunner import_runner(import_process); | |
384 | |
385 // Import process finished. Reload the prefs, because importer may set | |
386 // the pref value. | |
387 if (profile) | |
388 profile->GetPrefs()->ReloadPersistentPrefs(); | |
389 | |
390 return (import_runner.exit_code() == content::RESULT_CODE_NORMAL_EXIT); | |
391 } | |
392 | |
393 } // namespace | 175 } // namespace |
394 | 176 |
395 namespace first_run { | 177 namespace first_run { |
396 namespace internal { | 178 namespace internal { |
397 | 179 |
398 void DoPostImportPlatformSpecificTasks() { | 180 void DoPostImportPlatformSpecificTasks() { |
399 // Trigger the Active Setup command for system-level Chromes to finish | 181 // Trigger the Active Setup command for system-level Chromes to finish |
400 // configuring this user's install (e.g. per-user shortcuts). | 182 // configuring this user's install (e.g. per-user shortcuts). |
401 // Delay the task slightly to give Chrome launch I/O priority while also | 183 // Delay the task slightly to give Chrome launch I/O priority while also |
402 // making sure shortcuts are created promptly to avoid annoying the user by | 184 // making sure shortcuts are created promptly to avoid annoying the user by |
403 // re-creating shortcuts he previously deleted. | 185 // re-creating shortcuts he previously deleted. |
404 static const int64 kTiggerActiveSetupDelaySeconds = 5; | 186 static const int64 kTiggerActiveSetupDelaySeconds = 5; |
405 base::FilePath chrome_exe; | 187 base::FilePath chrome_exe; |
406 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { | 188 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { |
407 NOTREACHED(); | 189 NOTREACHED(); |
408 } else if (!InstallUtil::IsPerUserInstall(chrome_exe.value().c_str())) { | 190 } else if (!InstallUtil::IsPerUserInstall(chrome_exe.value().c_str())) { |
409 content::BrowserThread::GetBlockingPool()->PostDelayedTask( | 191 content::BrowserThread::GetBlockingPool()->PostDelayedTask( |
410 FROM_HERE, | 192 FROM_HERE, |
411 base::Bind(&InstallUtil::TriggerActiveSetupCommand), | 193 base::Bind(&InstallUtil::TriggerActiveSetupCommand), |
412 base::TimeDelta::FromSeconds(kTiggerActiveSetupDelaySeconds)); | 194 base::TimeDelta::FromSeconds(kTiggerActiveSetupDelaySeconds)); |
413 } | 195 } |
414 } | 196 } |
415 | 197 |
416 bool ImportSettings(Profile* profile, | |
417 scoped_refptr<ImporterHost> importer_host, | |
418 scoped_refptr<ImporterList> importer_list, | |
419 int items_to_import) { | |
420 return ImportSettingsWin( | |
421 profile, | |
422 importer_list->GetSourceProfileAt(0).importer_type, | |
423 items_to_import, | |
424 base::FilePath(), | |
425 false); | |
426 } | |
427 | |
428 bool GetFirstRunSentinelFilePath(base::FilePath* path) { | 198 bool GetFirstRunSentinelFilePath(base::FilePath* path) { |
429 return GetSentinelFilePath(chrome::kFirstRunSentinel, path); | 199 return GetSentinelFilePath(chrome::kFirstRunSentinel, path); |
430 } | 200 } |
431 | 201 |
432 void SetImportPreferencesAndLaunchImport( | |
433 MasterPrefs* out_prefs, | |
434 installer::MasterPreferences* install_prefs) { | |
435 std::string import_bookmarks_path; | |
436 install_prefs->GetString( | |
437 installer::master_preferences::kDistroImportBookmarksFromFilePref, | |
438 &import_bookmarks_path); | |
439 | |
440 if (!IsOrganicFirstRun()) { | |
441 // If search engines aren't explicitly imported, don't import. | |
442 if (!(out_prefs->do_import_items & importer::SEARCH_ENGINES)) { | |
443 out_prefs->dont_import_items |= importer::SEARCH_ENGINES; | |
444 } | |
445 // If home page isn't explicitly imported, don't import. | |
446 if (!(out_prefs->do_import_items & importer::HOME_PAGE)) { | |
447 out_prefs->dont_import_items |= importer::HOME_PAGE; | |
448 } | |
449 // If history isn't explicitly forbidden, do import. | |
450 if (!(out_prefs->dont_import_items & importer::HISTORY)) { | |
451 out_prefs->do_import_items |= importer::HISTORY; | |
452 } | |
453 } | |
454 | |
455 if (out_prefs->do_import_items || !import_bookmarks_path.empty()) { | |
456 // There is something to import from the default browser. This launches | |
457 // the importer process and blocks until done or until it fails. | |
458 scoped_refptr<ImporterList> importer_list(new ImporterList(NULL)); | |
459 importer_list->DetectSourceProfilesHack(); | |
460 if (!ImportSettingsWin( | |
461 NULL, importer_list->GetSourceProfileAt(0).importer_type, | |
462 out_prefs->do_import_items, base::FilePath::FromWStringHack(UTF8ToWide( | |
463 import_bookmarks_path)), true)) { | |
464 LOG(WARNING) << "silent import failed"; | |
465 } | |
466 } | |
467 } | |
468 | |
469 bool ShowPostInstallEULAIfNeeded(installer::MasterPreferences* install_prefs) { | 202 bool ShowPostInstallEULAIfNeeded(installer::MasterPreferences* install_prefs) { |
470 if (IsEULANotAccepted(install_prefs)) { | 203 if (IsEULANotAccepted(install_prefs)) { |
471 // Show the post-installation EULA. This is done by setup.exe and the | 204 // Show the post-installation EULA. This is done by setup.exe and the |
472 // result determines if we continue or not. We wait here until the user | 205 // result determines if we continue or not. We wait here until the user |
473 // dismisses the dialog. | 206 // dismisses the dialog. |
474 | 207 |
475 // The actual eula text is in a resource in chrome. We extract it to | 208 // The actual eula text is in a resource in chrome. We extract it to |
476 // a text file so setup.exe can use it as an inner frame. | 209 // a text file so setup.exe can use it as an inner frame. |
477 base::FilePath inner_html; | 210 base::FilePath inner_html; |
478 if (WriteEULAtoTempFile(&inner_html)) { | 211 if (WriteEULAtoTempFile(&inner_html)) { |
(...skipping 21 matching lines...) Expand all Loading... | |
500 base::FilePath MasterPrefsPath() { | 233 base::FilePath MasterPrefsPath() { |
501 // The standard location of the master prefs is next to the chrome binary. | 234 // The standard location of the master prefs is next to the chrome binary. |
502 base::FilePath master_prefs; | 235 base::FilePath master_prefs; |
503 if (!PathService::Get(base::DIR_EXE, &master_prefs)) | 236 if (!PathService::Get(base::DIR_EXE, &master_prefs)) |
504 return base::FilePath(); | 237 return base::FilePath(); |
505 return master_prefs.AppendASCII(installer::kDefaultMasterPrefs); | 238 return master_prefs.AppendASCII(installer::kDefaultMasterPrefs); |
506 } | 239 } |
507 | 240 |
508 } // namespace internal | 241 } // namespace internal |
509 } // namespace first_run | 242 } // namespace first_run |
510 | |
511 namespace first_run { | |
512 | |
513 int ImportNow(Profile* profile, const CommandLine& cmdline) { | |
514 int return_code = internal::ImportBookmarkFromFileIfNeeded(profile, cmdline); | |
515 #if !defined(USE_AURA) | |
516 if (cmdline.HasSwitch(switches::kImport)) { | |
517 return_code = ImportFromBrowser(profile, cmdline); | |
518 } | |
519 #endif | |
520 return return_code; | |
521 } | |
522 | |
523 } // namespace first_run | |
OLD | NEW |