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

Side by Side Diff: chrome/browser/first_run/first_run_win.cc

Issue 12670013: Out-of-process import on Windows. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase on top of https://codereview.chromium.org/15736014/ Created 7 years, 7 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
OLDNEW
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>
8 #include <shellapi.h> 7 #include <shellapi.h>
9 #include <shlobj.h>
10 8
9 #include "base/base_paths.h"
11 #include "base/callback.h" 10 #include "base/callback.h"
12 #include "base/environment.h"
13 #include "base/file_util.h" 11 #include "base/file_util.h"
14 #include "base/files/file_path.h" 12 #include "base/files/file_path.h"
15 #include "base/path_service.h" 13 #include "base/path_service.h"
16 #include "base/prefs/pref_service.h" 14 #include "base/prefs/pref_service.h"
15 #include "base/process.h"
17 #include "base/process_util.h" 16 #include "base/process_util.h"
18 #include "base/stringprintf.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_split.h"
21 #include "base/threading/sequenced_worker_pool.h" 17 #include "base/threading/sequenced_worker_pool.h"
22 #include "base/time.h" 18 #include "base/time.h"
23 #include "base/utf_string_conversions.h"
24 #include "base/win/metro.h" 19 #include "base/win/metro.h"
25 #include "base/win/object_watcher.h"
26 #include "base/win/windows_version.h"
27 #include "chrome/browser/browser_process.h"
28 #include "chrome/browser/first_run/first_run_internal.h" 20 #include "chrome/browser/first_run/first_run_internal.h"
29 #include "chrome/browser/importer/importer_host.h"
30 #include "chrome/browser/importer/importer_list.h"
31 #include "chrome/browser/process_singleton.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/browser/shell_integration.h"
34 #include "chrome/common/chrome_constants.h" 21 #include "chrome/common/chrome_constants.h"
35 #include "chrome/common/chrome_paths.h" 22 #include "chrome/common/chrome_paths.h"
36 #include "chrome/common/chrome_result_codes.h"
37 #include "chrome/common/chrome_switches.h" 23 #include "chrome/common/chrome_switches.h"
38 #include "chrome/common/pref_names.h"
39 #include "chrome/common/worker_thread_ticker.h"
40 #include "chrome/installer/util/browser_distribution.h"
41 #include "chrome/installer/util/google_update_settings.h" 24 #include "chrome/installer/util/google_update_settings.h"
42 #include "chrome/installer/util/install_util.h" 25 #include "chrome/installer/util/install_util.h"
43 #include "chrome/installer/util/master_preferences.h" 26 #include "chrome/installer/util/master_preferences.h"
44 #include "chrome/installer/util/master_preferences_constants.h" 27 #include "chrome/installer/util/master_preferences_constants.h"
45 #include "chrome/installer/util/shell_util.h"
46 #include "chrome/installer/util/util_constants.h" 28 #include "chrome/installer/util/util_constants.h"
47 #include "content/public/browser/browser_thread.h" 29 #include "content/public/browser/browser_thread.h"
48 #include "content/public/browser/notification_service.h"
49 #include "content/public/browser/user_metrics.h"
50 #include "google_update/google_update_idl.h" 30 #include "google_update/google_update_idl.h"
51 #include "grit/chromium_strings.h"
52 #include "grit/generated_resources.h"
53 #include "grit/locale_settings.h" 31 #include "grit/locale_settings.h"
54 #include "grit/theme_resources.h"
55 #include "ui/base/l10n/l10n_util.h" 32 #include "ui/base/l10n/l10n_util.h"
56 #include "ui/base/layout.h"
57 #include "ui/base/ui_base_switches.h"
58 #include "ui/base/win/shell.h" 33 #include "ui/base/win/shell.h"
59 34
60 namespace { 35 namespace {
61 36
62 // Launches the setup exe with the given parameter/value on the command-line. 37 // Launches the setup exe with the given parameter/value on the command-line.
63 // For non-metro Windows, it waits for its termination, returns its exit code 38 // For non-metro Windows, it waits for its termination, returns its exit code
64 // in |*ret_code|, and returns true if the exit code is valid. 39 // in |*ret_code|, and returns true if the exit code is valid.
65 // For metro Windows, it launches setup via ShellExecuteEx and returns in order 40 // For metro Windows, it launches setup via ShellExecuteEx and returns in order
66 // to bounce the user back to the desktop, then returns immediately. 41 // to bounce the user back to the desktop, then returns immediately.
67 bool LaunchSetupForEula(const base::FilePath::StringType& value, 42 bool LaunchSetupForEula(const base::FilePath::StringType& value,
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 // accepted. 140 // accepted.
166 bool CreateEULASentinel() { 141 bool CreateEULASentinel() {
167 base::FilePath eula_sentinel; 142 base::FilePath eula_sentinel;
168 if (!GetEULASentinelFilePath(&eula_sentinel)) 143 if (!GetEULASentinelFilePath(&eula_sentinel))
169 return false; 144 return false;
170 145
171 return (file_util::CreateDirectory(eula_sentinel.DirName()) && 146 return (file_util::CreateDirectory(eula_sentinel.DirName()) &&
172 file_util::WriteFile(eula_sentinel, "", 0) != -1); 147 file_util::WriteFile(eula_sentinel, "", 0) != -1);
173 } 148 }
174 149
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
300 // Deletes itself.
301 ImporterHost* importer_host = new ImporterHost;
302
303 scoped_refptr<ImporterList> importer_list(new ImporterList(NULL));
304 importer_list->DetectSourceProfilesHack();
305
306 // If |skip_first_run_ui|, we run in headless mode. This means that if
307 // there is user action required the import is automatically canceled.
308 if (skip_first_run_ui)
309 importer_host->set_headless();
310
311 first_run::internal::ImportEndedObserver observer;
312 importer_host->SetObserver(&observer);
313 importer_host->StartImportSettings(
314 importer_list->GetSourceProfileForImporterType(importer_type), profile,
315 static_cast<uint16>(items_to_import), new ProfileWriter(profile));
316 // If the import process has not errored out, block on it.
317 if (!observer.ended()) {
318 observer.set_should_quit_message_loop();
319 MessageLoop::current()->Run();
320 }
321 // TODO(gab): This method will be go away as part of http://crbug.com/219419/,
322 // so it is fine to hardcode |RESULT_CODE_NORMAL_EXIT| here for now.
323 return content::RESULT_CODE_NORMAL_EXIT;
324 }
325 #endif // !defined(USE_AURA)
326
327 bool ImportSettingsWin(Profile* profile,
328 int importer_type,
329 int items_to_import,
330 const base::FilePath& import_bookmarks_path,
331 bool skip_first_run_ui) {
332 if (!items_to_import && import_bookmarks_path.empty()) {
333 return true;
334 }
335
336 const CommandLine& cmdline = *CommandLine::ForCurrentProcess();
337 base::FilePath chrome_exe(cmdline.GetProgram());
338 // |chrome_exe| cannot be a relative path as chrome.exe already changed its
339 // CWD in LoadChromeWithDirectory(), making the relative path used on the
340 // command-line invalid. The base name is sufficient given chrome.exe is in
341 // the CWD.
342 if (!chrome_exe.IsAbsolute())
343 chrome_exe = chrome_exe.BaseName();
344 CommandLine import_cmd(chrome_exe);
345
346 const char* kSwitchNames[] = {
347 switches::kUserDataDir,
348 switches::kChromeFrame,
349 switches::kCountry,
350 };
351 import_cmd.CopySwitchesFrom(cmdline, kSwitchNames, arraysize(kSwitchNames));
352
353 // Allow tests to introduce additional switches.
354 import_cmd.AppendArguments(first_run::GetExtraArgumentsForImportProcess(),
355 false);
356
357 // Since ImportSettings is called before the local state is stored on disk
358 // we pass the language as an argument. GetApplicationLocale checks the
359 // current command line as fallback.
360 import_cmd.AppendSwitchASCII(switches::kLang,
361 g_browser_process->GetApplicationLocale());
362
363 if (items_to_import) {
364 import_cmd.AppendSwitchASCII(switches::kImport,
365 EncodeImportParams(importer_type, items_to_import, skip_first_run_ui));
366 }
367
368 if (!import_bookmarks_path.empty()) {
369 import_cmd.AppendSwitchPath(switches::kImportFromFile,
370 import_bookmarks_path);
371 }
372
373 // The importer doesn't need to do any background networking tasks so disable
374 // them.
375 import_cmd.CommandLine::AppendSwitch(switches::kDisableBackgroundNetworking);
376
377 // Time to launch the process that is going to do the import.
378 base::ProcessHandle import_process;
379 if (!base::LaunchProcess(import_cmd, base::LaunchOptions(), &import_process))
380 return false;
381
382 // We block inside the import_runner ctor, pumping messages until the
383 // importer process ends. This can happen either by completing the import
384 // or by hang_monitor killing it.
385 ImportProcessRunner import_runner(import_process);
386
387 // Import process finished. Reload the prefs, because importer may set
388 // the pref value.
389 if (profile)
390 profile->GetPrefs()->ReloadPersistentPrefs();
391
392 return (import_runner.exit_code() == content::RESULT_CODE_NORMAL_EXIT);
393 }
394
395 } // namespace 150 } // namespace
396 151
397 namespace first_run { 152 namespace first_run {
398 namespace internal { 153 namespace internal {
399 154
400 void DoPostImportPlatformSpecificTasks(Profile* /* profile */) { 155 void DoPostImportPlatformSpecificTasks(Profile* /* profile */) {
401 // Trigger the Active Setup command for system-level Chromes to finish 156 // Trigger the Active Setup command for system-level Chromes to finish
402 // configuring this user's install (e.g. per-user shortcuts). 157 // configuring this user's install (e.g. per-user shortcuts).
403 // Delay the task slightly to give Chrome launch I/O priority while also 158 // Delay the task slightly to give Chrome launch I/O priority while also
404 // making sure shortcuts are created promptly to avoid annoying the user by 159 // making sure shortcuts are created promptly to avoid annoying the user by
405 // re-creating shortcuts he previously deleted. 160 // re-creating shortcuts he previously deleted.
406 static const int64 kTiggerActiveSetupDelaySeconds = 5; 161 static const int64 kTiggerActiveSetupDelaySeconds = 5;
407 base::FilePath chrome_exe; 162 base::FilePath chrome_exe;
408 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { 163 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
409 NOTREACHED(); 164 NOTREACHED();
410 } else if (!InstallUtil::IsPerUserInstall(chrome_exe.value().c_str())) { 165 } else if (!InstallUtil::IsPerUserInstall(chrome_exe.value().c_str())) {
411 content::BrowserThread::GetBlockingPool()->PostDelayedTask( 166 content::BrowserThread::GetBlockingPool()->PostDelayedTask(
412 FROM_HERE, 167 FROM_HERE,
413 base::Bind(&InstallUtil::TriggerActiveSetupCommand), 168 base::Bind(&InstallUtil::TriggerActiveSetupCommand),
414 base::TimeDelta::FromSeconds(kTiggerActiveSetupDelaySeconds)); 169 base::TimeDelta::FromSeconds(kTiggerActiveSetupDelaySeconds));
415 } 170 }
416 } 171 }
417 172
418 bool ImportSettings(Profile* profile,
419 ImporterHost* importer_host,
420 scoped_refptr<ImporterList> importer_list,
421 int items_to_import) {
422 return ImportSettingsWin(
423 profile,
424 importer_list->GetSourceProfileAt(0).importer_type,
425 items_to_import,
426 base::FilePath(),
427 false);
428 }
429
430 bool GetFirstRunSentinelFilePath(base::FilePath* path) { 173 bool GetFirstRunSentinelFilePath(base::FilePath* path) {
431 return GetSentinelFilePath(chrome::kFirstRunSentinel, path); 174 return GetSentinelFilePath(chrome::kFirstRunSentinel, path);
432 } 175 }
433 176
434 void SetImportPreferencesAndLaunchImport(
435 MasterPrefs* out_prefs,
436 installer::MasterPreferences* install_prefs) {
437 std::string import_bookmarks_path;
438 install_prefs->GetString(
439 installer::master_preferences::kDistroImportBookmarksFromFilePref,
440 &import_bookmarks_path);
441
442 if (!IsOrganicFirstRun()) {
443 // If search engines aren't explicitly imported, don't import.
444 if (!(out_prefs->do_import_items & importer::SEARCH_ENGINES)) {
445 out_prefs->dont_import_items |= importer::SEARCH_ENGINES;
446 }
447 // If home page isn't explicitly imported, don't import.
448 if (!(out_prefs->do_import_items & importer::HOME_PAGE)) {
449 out_prefs->dont_import_items |= importer::HOME_PAGE;
450 }
451 // If history isn't explicitly forbidden, do import.
452 if (!(out_prefs->dont_import_items & importer::HISTORY)) {
453 out_prefs->do_import_items |= importer::HISTORY;
454 }
455 }
456
457 if (out_prefs->do_import_items || !import_bookmarks_path.empty()) {
458 // There is something to import from the default browser. This launches
459 // the importer process and blocks until done or until it fails.
460 scoped_refptr<ImporterList> importer_list(new ImporterList(NULL));
461 importer_list->DetectSourceProfilesHack();
462 if (!ImportSettingsWin(
463 NULL, importer_list->GetSourceProfileAt(0).importer_type,
464 out_prefs->do_import_items, base::FilePath::FromWStringHack(UTF8ToWide(
465 import_bookmarks_path)), true)) {
466 LOG(WARNING) << "silent import failed";
467 }
468 }
469 }
470
471 bool ShowPostInstallEULAIfNeeded(installer::MasterPreferences* install_prefs) { 177 bool ShowPostInstallEULAIfNeeded(installer::MasterPreferences* install_prefs) {
472 if (IsEULANotAccepted(install_prefs)) { 178 if (IsEULANotAccepted(install_prefs)) {
473 // Show the post-installation EULA. This is done by setup.exe and the 179 // Show the post-installation EULA. This is done by setup.exe and the
474 // result determines if we continue or not. We wait here until the user 180 // result determines if we continue or not. We wait here until the user
475 // dismisses the dialog. 181 // dismisses the dialog.
476 182
477 // The actual eula text is in a resource in chrome. We extract it to 183 // The actual eula text is in a resource in chrome. We extract it to
478 // a text file so setup.exe can use it as an inner frame. 184 // a text file so setup.exe can use it as an inner frame.
479 base::FilePath inner_html; 185 base::FilePath inner_html;
480 if (WriteEULAtoTempFile(&inner_html)) { 186 if (WriteEULAtoTempFile(&inner_html)) {
(...skipping 21 matching lines...) Expand all
502 base::FilePath MasterPrefsPath() { 208 base::FilePath MasterPrefsPath() {
503 // The standard location of the master prefs is next to the chrome binary. 209 // The standard location of the master prefs is next to the chrome binary.
504 base::FilePath master_prefs; 210 base::FilePath master_prefs;
505 if (!PathService::Get(base::DIR_EXE, &master_prefs)) 211 if (!PathService::Get(base::DIR_EXE, &master_prefs))
506 return base::FilePath(); 212 return base::FilePath();
507 return master_prefs.AppendASCII(installer::kDefaultMasterPrefs); 213 return master_prefs.AppendASCII(installer::kDefaultMasterPrefs);
508 } 214 }
509 215
510 } // namespace internal 216 } // namespace internal
511 } // namespace first_run 217 } // namespace first_run
512
513 namespace first_run {
514
515 int ImportNow(Profile* profile, const CommandLine& cmdline) {
516 int return_code = internal::ImportBookmarkFromFileIfNeeded(profile, cmdline);
517 #if !defined(USE_AURA)
518 if (cmdline.HasSwitch(switches::kImport)) {
519 return_code = ImportFromBrowser(profile, cmdline);
520 }
521 #endif
522 return return_code;
523 }
524
525 } // namespace first_run
OLDNEW
« no previous file with comments | « chrome/browser/first_run/first_run_posix.cc ('k') | chrome/browser/importer/external_process_importer_bridge.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698