diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/first_run.cc | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/first_run.cc')
-rw-r--r-- | chrome/browser/first_run.cc | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/chrome/browser/first_run.cc b/chrome/browser/first_run.cc new file mode 100644 index 0000000..950391b --- /dev/null +++ b/chrome/browser/first_run.cc @@ -0,0 +1,389 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> +#include <shlobj.h> + +#include <sstream> + +#include "chrome/browser/first_run.h" + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/process_util.h" +#include "base/string_util.h" +#include "chrome/app/result_codes.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/hang_monitor/hung_window_detector.h" +#include "chrome/browser/importer.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/views/first_run_view.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/pref_service.h" +#include "chrome/installer/util/shell_util.h" +#include "chrome/views/accelerator_handler.h" +#include "chrome/views/window.h" + +namespace { + +// The kSentinelFile file absence will tell us it is a first run. +const wchar_t kSentinelFile[] = L"First Run"; + +// These two names are used for upgrades in-place of the chrome exe. +const wchar_t kChromeUpgradeExe[] = L"new_chrome.exe"; +const wchar_t kChromeBackupExe[] = L"old_chrome.exe"; + +// Gives the full path to the sentinel file. The file might not exist. +bool GetFirstRunSentinelFilePath(std::wstring* path) { + std::wstring first_run_sentinel; + if (!PathService::Get(base::DIR_EXE, &first_run_sentinel)) + return false; + file_util::AppendToPath(&first_run_sentinel, kSentinelFile); + *path = first_run_sentinel; + return true; +} + +bool GetNewerChromeFile(std::wstring* path) { + if (!PathService::Get(base::DIR_EXE, path)) + return false; + file_util::AppendToPath(path, kChromeUpgradeExe); + return true; +} + +bool GetBackupChromeFile(std::wstring* path) { + if (!PathService::Get(base::DIR_EXE, path)) + return false; + file_util::AppendToPath(path, kChromeBackupExe); + return true; +} + +} // namespace + +bool FirstRun::IsChromeFirstRun() { + std::wstring first_run_sentinel; + if (!GetFirstRunSentinelFilePath(&first_run_sentinel)) + return false; + if (file_util::PathExists(first_run_sentinel)) + return false; + return true; +} + +bool FirstRun::CreateChromeDesktopShortcut() { + std::wstring chrome_exe, shortcut_path, shortcut_name; + if (!PathService::Get(base::FILE_EXE, &chrome_exe) || + !ShellUtil::GetDesktopPath(&shortcut_path) || + !ShellUtil::GetChromeShortcutName(&shortcut_name)) + return false; + file_util::AppendToPath(&shortcut_path, shortcut_name); + return ShellUtil::UpdateChromeShortcut(chrome_exe, shortcut_path, true); +} + +bool FirstRun::RemoveChromeDesktopShortcut() { + std::wstring shortcut_path, shortcut_name; + if (!ShellUtil::GetDesktopPath(&shortcut_path) || + !ShellUtil::GetChromeShortcutName(&shortcut_name)) + return false; + file_util::AppendToPath(&shortcut_path, shortcut_name); + return file_util::Delete(shortcut_path, false); +} + +bool FirstRun::CreateChromeQuickLaunchShortcut() { + std::wstring chrome_exe, shortcut_path, shortcut_name; + if (!PathService::Get(base::FILE_EXE, &chrome_exe) || + !ShellUtil::GetQuickLaunchPath(&shortcut_path) || + !ShellUtil::GetChromeShortcutName(&shortcut_name)) + return false; + file_util::AppendToPath(&shortcut_path, shortcut_name); + return ShellUtil::UpdateChromeShortcut(chrome_exe, shortcut_path, true); +} + +bool FirstRun::RemoveChromeQuickLaunchShortcut() { + std::wstring shortcut_path, shortcut_name; + if (!ShellUtil::GetQuickLaunchPath(&shortcut_path) || + !ShellUtil::GetChromeShortcutName(&shortcut_name)) + return false; + file_util::AppendToPath(&shortcut_path, shortcut_name); + return file_util::Delete(shortcut_path, false); +} + +bool FirstRun::RemoveSentinel() { + std::wstring first_run_sentinel; + if (!GetFirstRunSentinelFilePath(&first_run_sentinel)) + return false; + return file_util::Delete(first_run_sentinel, false); +} + +bool FirstRun::CreateSentinel() { + std::wstring first_run_sentinel; + if (!GetFirstRunSentinelFilePath(&first_run_sentinel)) + return false; + HANDLE file = ::CreateFileW(first_run_sentinel.c_str(), + FILE_READ_DATA | FILE_WRITE_DATA, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, 0, NULL); + if (INVALID_HANDLE_VALUE == file) + return false; + ::CloseHandle(file); + return true; +} + +bool Upgrade::SwapNewChromeExeIfPresent() { + std::wstring new_chrome_exe; + if (!GetNewerChromeFile(&new_chrome_exe)) + return false; + if (!file_util::PathExists(new_chrome_exe)) + return false; + std::wstring old_chrome_exe; + if (!PathService::Get(base::FILE_EXE, &old_chrome_exe)) + return false; + std::wstring backup_exe; + if (!GetBackupChromeFile(&backup_exe)) + return false; + if (::ReplaceFileW(old_chrome_exe.c_str(), new_chrome_exe.c_str(), + backup_exe.c_str(), REPLACEFILE_IGNORE_MERGE_ERRORS, + NULL, NULL)) { + return true; + } + return false; +} + +bool Upgrade::RelaunchChromeBrowser(const CommandLine& command_line) { + return process_util::LaunchApp(command_line.command_line_string(), + false, false, NULL); +} + +void OpenFirstRunDialog(Profile* profile) { + FirstRunView* first_run_view = new FirstRunView(profile); + ChromeViews::Window* first_run_dialog = + ChromeViews::Window::CreateChromeWindow(NULL, gfx::Rect(), + first_run_view, first_run_view); + first_run_dialog->Show(); + first_run_view->set_dialog(first_run_dialog); + // We must now run a message loop (will be terminated when the First Run UI + // is closed) so that the window can receive messages and we block the + // browser window from showing up. We pass the accelerator handler here so + // that keyboard accelerators (Enter, Esc, etc) work in the dialog box. + MessageLoop::current()->Run(g_browser_process->accelerator_handler()); +} + +namespace { + +// This class is used by FirstRun::ImportSettings to determine when the import +// process has ended and what was the result of the operation as reported by +// the process exit code. This class executes in the context of the main chrome +// process. +class ImportProcessRunner : public MessageLoop::Watcher { + public: + // The constructor takes the importer process to watch and then it does a + // message loop blocking wait until the process ends. This object now owns + // the import_process handle. + explicit ImportProcessRunner(ProcessHandle import_process) + : import_process_(import_process), + exit_code_(ResultCodes::NORMAL_EXIT) { + MessageLoop::current()->WatchObject(import_process, this); + MessageLoop::current()->Run(); + } + virtual ~ImportProcessRunner() { + ::CloseHandle(import_process_); + } + // Returns the child process exit code. There are 3 expected values: + // NORMAL_EXIT, IMPORTER_CANCEL or IMPORTER_HUNG. + int exit_code() const { + return exit_code_; + } + // The child process has terminated. Find the exit code and quit the loop. + virtual void OnObjectSignaled(HANDLE object) { + MessageLoop::current()->WatchObject(object, NULL); + DCHECK(object == import_process_); + if (!::GetExitCodeProcess(import_process_, &exit_code_)) { + NOTREACHED(); + } + MessageLoop::current()->Quit(); + } + + private: + ProcessHandle import_process_; + DWORD exit_code_; +}; + +// Check every 3 seconds if the importer UI has hung. +const int kPollHangFrequency = 3000; + +// This class specializes on finding hung 'owned' windows. Unfortunately, the +// HungWindowDetector class cannot be used here because it assumes child +// windows and not owned top-level windows. +// This code is executed in the context of the main browser process and will +// terminate the importer process if it is hung. +class HungImporterMonitor : public WorkerThreadTicker::Callback { + public: + // The ctor takes the owner popup window and the process handle of the + // process to kill in case the popup or its owned active popup become + // unresponsive. + HungImporterMonitor(HWND owner_window, ProcessHandle import_process) + : owner_window_(owner_window), + import_process_(import_process), + ticker_(kPollHangFrequency) { + ticker_.RegisterTickHandler(this); + ticker_.Start(); + } + virtual ~HungImporterMonitor() { + ticker_.Stop(); + ticker_.UnregisterTickHandler(this); + } + + private: + virtual void OnTick() { + if (!import_process_) + return; + // We find the top active popup that we own, this will be either the + // owner_window_ itself or the dialog window of the other process. In + // both cases it is worth hung testing because both windows share the + // same message queue and at some point the other window could be gone + // while the other process still not pumping messages. + HWND active_window = ::GetLastActivePopup(owner_window_); + if (::IsHungAppWindow(active_window) || ::IsHungAppWindow(owner_window_)) { + ::TerminateProcess(import_process_, ResultCodes::IMPORTER_HUNG); + import_process_ = NULL; + } + } + + HWND owner_window_; + ProcessHandle import_process_; + WorkerThreadTicker ticker_; + DISALLOW_EVIL_CONSTRUCTORS(HungImporterMonitor); +}; + +// This class is used by FirstRun::ImportWithUI to get notified of the outcome +// of the import operation. It differs from ImportProcessRunner in that this +// class executes in the context of importing child process. +// The values that it handles are meant to be used as the process exit code. +class FirstRunImportObserver : public ImportObserver { + public: + FirstRunImportObserver() : import_result_(ResultCodes::NORMAL_EXIT) { + } + int import_result() const { + return import_result_; + } + virtual void ImportCanceled() { + import_result_ = ResultCodes::IMPORTER_CANCEL; + Finish(); + } + virtual void ImportComplete() { + import_result_ = ResultCodes::NORMAL_EXIT; + Finish(); + } + private: + void Finish() { + MessageLoop::current()->Quit(); + } + + int import_result_; + DISALLOW_EVIL_CONSTRUCTORS(FirstRunImportObserver); +}; + +std::wstring EncodeImportParams(int browser, int options, HWND window) { + return StringPrintf(L"%d@%d@%d", browser, options, window); +} + +bool DecodeImportParams(const std::wstring& encoded, + int* browser, int* options, HWND* window) { + std::vector<std::wstring> v; + SplitString(encoded, L'@', &v); + if (v.size() != 3) + return false; + *browser = static_cast<int>(StringToInt64(v[0])); + *options = static_cast<int>(StringToInt64(v[1])); + *window = reinterpret_cast<HWND>(StringToInt64(v[2])); + return true; +} + +} // namespace + +bool FirstRun::ImportSettings(Profile* profile, int browser, + int items_to_import, HWND parent_window) { + CommandLine cmdline; + std::wstring import_cmd(cmdline.program()); + std::wstring data_dir(cmdline.GetSwitchValue(switches::kUserDataDir)); + if (!data_dir.empty()) { + CommandLine::AppendSwitchWithValue(&import_cmd, switches::kUserDataDir, + data_dir); + } + CommandLine::AppendSwitchWithValue(&import_cmd, switches::kImport, + EncodeImportParams(browser, items_to_import, parent_window)); + + // Time to launch the process that is going to do the import. + ProcessHandle import_process; + if (!process_util::LaunchApp(import_cmd, false, false, &import_process)) + return false; + + // Activate the importer monitor. It awakes periodically in another thread + // and checks that the importer UI is still pumping messages. + HungImporterMonitor hang_monitor(parent_window, import_process); + + // We block inside the import_runner ctor, pumping messages until the + // importer process ends. This can happen either by completing the import + // or by hang_monitor killing it. + ImportProcessRunner import_runner(import_process); + + // Import process finished. Reload the prefs, because importer may set + // the pref value. + profile->GetPrefs()->ReloadPersistentPrefs(); + return (import_runner.exit_code() == ResultCodes::NORMAL_EXIT); +} + +int FirstRun::ImportWithUI(Profile* profile, const CommandLine& cmdline) { + std::wstring import_info = cmdline.GetSwitchValue(switches::kImport); + if (import_info.empty()) { + NOTREACHED(); + return false; + } + int browser = 0; + int items_to_import = 0; + HWND parent_window = NULL; + if (!DecodeImportParams(import_info, &browser, &items_to_import, + &parent_window)) { + NOTREACHED(); + return false; + } + scoped_refptr<ImporterHost> importer_host = new ImporterHost(); + FirstRunImportObserver observer; + StartImportingWithUI( + parent_window, + items_to_import, + importer_host, + importer_host->GetSourceProfileInfoAt(browser), + profile, + &observer, + true); + MessageLoop::current()->Run(); + return observer.import_result(); +} |