diff options
-rw-r--r-- | chrome/browser/chrome_browser_main.cc | 5 | ||||
-rw-r--r-- | chrome/browser/first_run/try_chrome_dialog_view.cc | 10 | ||||
-rw-r--r-- | chrome/browser/process_singleton.cc | 24 | ||||
-rw-r--r-- | chrome/browser/process_singleton.h | 24 | ||||
-rw-r--r-- | chrome/browser/process_singleton_linux.cc | 41 | ||||
-rw-r--r-- | chrome/browser/process_singleton_mac.cc | 6 | ||||
-rw-r--r-- | chrome/browser/process_singleton_win.cc | 188 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 1 |
8 files changed, 191 insertions, 108 deletions
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc index 111768c..8946237 100644 --- a/chrome/browser/chrome_browser_main.cc +++ b/chrome/browser/chrome_browser_main.cc @@ -1144,6 +1144,9 @@ int ChromeBrowserMainParts::PreCreateThreadsImpl() { #endif process_singleton_.reset(new ProcessSingleton(user_data_dir_)); + // Ensure ProcessSingleton won't process messages too early. It will be + // unlocked in PostBrowserStart(). + process_singleton_->Lock(NULL); is_first_run_ = first_run::IsChromeFirstRun() || parsed_command_line().HasSwitch(switches::kFirstRun); @@ -1375,6 +1378,8 @@ void ChromeBrowserMainParts::PreBrowserStart() { void ChromeBrowserMainParts::PostBrowserStart() { for (size_t i = 0; i < chrome_extra_parts_.size(); ++i) chrome_extra_parts_[i]->PostBrowserStart(); + // Allow ProcessSingleton to process messages. + process_singleton_->Unlock(); } int ChromeBrowserMainParts::PreMainMessageLoopRunImpl() { diff --git a/chrome/browser/first_run/try_chrome_dialog_view.cc b/chrome/browser/first_run/try_chrome_dialog_view.cc index f1da73d..71135a7 100644 --- a/chrome/browser/first_run/try_chrome_dialog_view.cc +++ b/chrome/browser/first_run/try_chrome_dialog_view.cc @@ -227,12 +227,14 @@ TryChromeDialogView::Result TryChromeDialogView::ShowModal( SetToastRegion(popup_->GetNativeView(), preferred.width(), preferred.height()); - // Time to show the window in a modal loop. We don't want this chrome - // instance trying to serve WM_COPYDATA requests, as we'll surely crash. - process_singleton->Lock(popup_->GetNativeView()); + // Time to show the window in a modal loop. The ProcessSingleton should + // already be locked and it will not process WM_COPYDATA requests. Change the + // window to bring to foreground if a request arrives. + CHECK(process_singleton->locked()); + process_singleton->SetForegroundWindow(popup_->GetNativeView()); popup_->Show(); MessageLoop::current()->Run(); - process_singleton->Unlock(); + process_singleton->SetForegroundWindow(NULL); return result_; } diff --git a/chrome/browser/process_singleton.cc b/chrome/browser/process_singleton.cc new file mode 100644 index 0000000..dcda573 --- /dev/null +++ b/chrome/browser/process_singleton.cc @@ -0,0 +1,24 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/process_singleton.h" + +void ProcessSingleton::Unlock() { + DCHECK(CalledOnValidThread()); + locked_ = false; + foreground_window_ = NULL; + // Replay the command lines of the messages which were received while the + // ProcessSingleton was locked. Only replay each message once. + std::set<DelayedStartupMessage> replayed_messages; + for (std::vector<DelayedStartupMessage>::const_iterator it = + saved_startup_messages_.begin(); + it != saved_startup_messages_.end(); ++it) { + if (replayed_messages.find(*it) != + replayed_messages.end()) + continue; + ProcessCommandLine(CommandLine(it->first), it->second); + replayed_messages.insert(*it); + } + saved_startup_messages_.clear(); +} diff --git a/chrome/browser/process_singleton.h b/chrome/browser/process_singleton.h index a3b6ca1..2a7e9b0 100644 --- a/chrome/browser/process_singleton.h +++ b/chrome/browser/process_singleton.h @@ -12,7 +12,12 @@ #include <windows.h> #endif // defined(OS_WIN) +#include <set> +#include <vector> + #include "base/basictypes.h" +#include "base/command_line.h" +#include "base/file_path.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/threading/non_thread_safe.h" @@ -106,13 +111,16 @@ class ProcessSingleton : public base::NonThreadSafe { foreground_window_ = foreground_window; } - // Allows the dispatch of CopyData messages. - void Unlock() { + // Changes the foreground window without changing the locked state. + void SetForegroundWindow(gfx::NativeWindow foreground_window) { DCHECK(CalledOnValidThread()); - locked_ = false; - foreground_window_ = NULL; + foreground_window_ = foreground_window; } + // Allows the dispatch of CopyData messages and replays the messages which + // were received when the ProcessSingleton was locked. + void Unlock(); + bool locked() { DCHECK(CalledOnValidThread()); return locked_; @@ -123,6 +131,10 @@ class ProcessSingleton : public base::NonThreadSafe { #endif private: + typedef std::pair<CommandLine::StringVector, FilePath> DelayedStartupMessage; + void ProcessCommandLine(const CommandLine& command_line, + const FilePath& current_directory); + #if !defined(OS_MACOSX) // Timeout for the current browser process to respond. 20 seconds should be // enough. It's only used in Windows and Linux implementations. @@ -168,6 +180,10 @@ class ProcessSingleton : public base::NonThreadSafe { int lock_fd_; #endif + // If messages are received in the locked state, the corresponding command + // lines are saved here to be replayed later. + std::vector<DelayedStartupMessage> saved_startup_messages_; + DISALLOW_COPY_AND_ASSIGN(ProcessSingleton); }; diff --git a/chrome/browser/process_singleton_linux.cc b/chrome/browser/process_singleton_linux.cc index ca9f9a3..0fb297f 100644 --- a/chrome/browser/process_singleton_linux.cc +++ b/chrome/browser/process_singleton_linux.cc @@ -623,6 +623,8 @@ void ProcessSingleton::LinuxWatcher::HandleMessage( // we are probably in a first run critical phase. if (parent_->locked()) { DLOG(WARNING) << "Browser is locked"; + parent_->saved_startup_messages_.push_back( + std::make_pair(argv, FilePath(current_dir))); // Send back "ACK" message to prevent the client process from starting up. reader->FinishWithACK(kACKToken, arraysize(kACKToken) - 1); return; @@ -639,23 +641,7 @@ void ProcessSingleton::LinuxWatcher::HandleMessage( } CommandLine parsed_command_line(argv); - PrefService* prefs = g_browser_process->local_state(); - DCHECK(prefs); - - // Ignore the request if the process was passed the --product-version flag. - // Normally we wouldn't get here if that flag had been passed, but it can - // happen if it is passed to an older version of chrome. Since newer versions - // of chrome do this in the background, we want to avoid spawning extra - // windows. - if (parsed_command_line.HasSwitch(switches::kProductVersion)) { - DLOG(WARNING) << "Remote process was passed product version flag, " - << "but ignored it. Doing nothing."; - } else { - // Run the browser startup sequence again, with the command line of the - // signalling process. - BrowserInit::ProcessCommandLineAlreadyRunning( - parsed_command_line, FilePath(current_dir)); - } + parent_->ProcessCommandLine(parsed_command_line, FilePath(current_dir)); // Send back "ACK" message to prevent the client process from starting up. reader->FinishWithACK(kACKToken, arraysize(kACKToken) - 1); @@ -1001,3 +987,24 @@ void ProcessSingleton::Cleanup() { UnlinkPath(cookie_path_); UnlinkPath(lock_path_); } + +void ProcessSingleton::ProcessCommandLine(const CommandLine& command_line, + const FilePath& current_directory) { + PrefService* prefs = g_browser_process->local_state(); + DCHECK(prefs); + + // Ignore the request if the process was passed the --product-version flag. + // Normally we wouldn't get here if that flag had been passed, but it can + // happen if it is passed to an older version of chrome. Since newer versions + // of chrome do this in the background, we want to avoid spawning extra + // windows. + if (command_line.HasSwitch(switches::kProductVersion)) { + DLOG(WARNING) << "Remote process was passed product version flag, " + << "but ignored it. Doing nothing."; + } else { + // Run the browser startup sequence again, with the command line of the + // signalling process. + BrowserInit::ProcessCommandLineAlreadyRunning(command_line, + current_directory); + } +} diff --git a/chrome/browser/process_singleton_mac.cc b/chrome/browser/process_singleton_mac.cc index 8639711..51525a2 100644 --- a/chrome/browser/process_singleton_mac.cc +++ b/chrome/browser/process_singleton_mac.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -114,3 +114,7 @@ void ProcessSingleton::Cleanup() { } lock_fd_ = -1; } + +void ProcessSingleton::ProcessCommandLine(const CommandLine& command_line, + const FilePath& current_directory) { +} diff --git a/chrome/browser/process_singleton_win.cc b/chrome/browser/process_singleton_win.cc index 4d20415..fc55aac 100644 --- a/chrome/browser/process_singleton_win.cc +++ b/chrome/browser/process_singleton_win.cc @@ -57,6 +57,66 @@ LRESULT CALLBACK ThunkWndProc(HWND hwnd, UINT message, return singleton->WndProc(hwnd, message, wparam, lparam); } +bool ParseCommandLine(const COPYDATASTRUCT* cds, + CommandLine* parsed_command_line, + FilePath* current_directory) { + // We should have enough room for the shortest command (min_message_size) + // and also be a multiple of wchar_t bytes. The shortest command + // possible is L"START\0\0" (empty current directory and command line). + static const int min_message_size = 7; + if (cds->cbData < min_message_size * sizeof(wchar_t) || + cds->cbData % sizeof(wchar_t) != 0) { + LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData; + return false; + } + + // We split the string into 4 parts on NULLs. + DCHECK(cds->lpData); + const std::wstring msg(static_cast<wchar_t*>(cds->lpData), + cds->cbData / sizeof(wchar_t)); + const std::wstring::size_type first_null = msg.find_first_of(L'\0'); + if (first_null == 0 || first_null == std::wstring::npos) { + // no NULL byte, don't know what to do + LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length() << + ", first null = " << first_null; + return false; + } + + // Decode the command, which is everything until the first NULL. + if (msg.substr(0, first_null) == L"START") { + // Another instance is starting parse the command line & do what it would + // have done. + VLOG(1) << "Handling STARTUP request from another process"; + const std::wstring::size_type second_null = + msg.find_first_of(L'\0', first_null + 1); + if (second_null == std::wstring::npos || + first_null == msg.length() - 1 || second_null == msg.length()) { + LOG(WARNING) << "Invalid format for start command, we need a string in 4 " + "parts separated by NULLs"; + return false; + } + + // Get current directory. + *current_directory = FilePath(msg.substr(first_null + 1, + second_null - first_null)); + + const std::wstring::size_type third_null = + msg.find_first_of(L'\0', second_null + 1); + if (third_null == std::wstring::npos || + third_null == msg.length()) { + LOG(WARNING) << "Invalid format for start command, we need a string in 4 " + "parts separated by NULLs"; + } + + // Get command line. + const std::wstring cmd_line = + msg.substr(second_null + 1, third_null - second_null); + *parsed_command_line = CommandLine::FromString(cmd_line); + return true; + } + return false; +} + } // namespace // Microsoft's Softricity virtualization breaks the sandbox processes. @@ -268,8 +328,17 @@ LRESULT ProcessSingleton::OnCopyData(HWND hwnd, const COPYDATASTRUCT* cds) { NOTIMPLEMENTED(); #else // Attempt to place ourselves in the foreground / flash the task bar. - if (IsWindow(foreground_window_)) + if (foreground_window_ != NULL && IsWindow(foreground_window_)) { SetForegroundWindow(foreground_window_); + } else { + // Read the command line and store it. It will be replayed when the + // ProcessSingleton becomes unlocked. + CommandLine parsed_command_line(CommandLine::NO_PROGRAM); + FilePath current_directory; + if (ParseCommandLine(cds, &parsed_command_line, ¤t_directory)) + saved_startup_messages_.push_back( + std::make_pair(parsed_command_line.argv(), current_directory)); + } #endif return TRUE; } @@ -280,88 +349,11 @@ LRESULT ProcessSingleton::OnCopyData(HWND hwnd, const COPYDATASTRUCT* cds) { return FALSE; } - // We should have enough room for the shortest command (min_message_size) - // and also be a multiple of wchar_t bytes. The shortest command - // possible is L"START\0\0" (empty current directory and command line). - static const int min_message_size = 7; - if (cds->cbData < min_message_size * sizeof(wchar_t) || - cds->cbData % sizeof(wchar_t) != 0) { - LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData; - return TRUE; - } - - // We split the string into 4 parts on NULLs. - DCHECK(cds->lpData); - const std::wstring msg(static_cast<wchar_t*>(cds->lpData), - cds->cbData / sizeof(wchar_t)); - const std::wstring::size_type first_null = msg.find_first_of(L'\0'); - if (first_null == 0 || first_null == std::wstring::npos) { - // no NULL byte, don't know what to do - LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length() << - ", first null = " << first_null; - return TRUE; - } - - // Decode the command, which is everything until the first NULL. - if (msg.substr(0, first_null) == L"START") { - // Another instance is starting parse the command line & do what it would - // have done. - VLOG(1) << "Handling STARTUP request from another process"; - const std::wstring::size_type second_null = - msg.find_first_of(L'\0', first_null + 1); - if (second_null == std::wstring::npos || - first_null == msg.length() - 1 || second_null == msg.length()) { - LOG(WARNING) << "Invalid format for start command, we need a string in 4 " - "parts separated by NULLs"; - return TRUE; - } - - // Get current directory. - const FilePath cur_dir(msg.substr(first_null + 1, - second_null - first_null)); - - const std::wstring::size_type third_null = - msg.find_first_of(L'\0', second_null + 1); - if (third_null == std::wstring::npos || - third_null == msg.length()) { - LOG(WARNING) << "Invalid format for start command, we need a string in 4 " - "parts separated by NULLs"; - } - - // Get command line. - const std::wstring cmd_line = - msg.substr(second_null + 1, third_null - second_null); - - CommandLine parsed_command_line = CommandLine::FromString(cmd_line); - PrefService* prefs = g_browser_process->local_state(); - DCHECK(prefs); - - // Handle the --uninstall-extension startup action. This needs to done here - // in the process that is running with the target profile, otherwise the - // uninstall will fail to unload and remove all components. - if (parsed_command_line.HasSwitch(switches::kUninstallExtension)) { - // The uninstall extension switch can't be combined with the profile - // directory switch. - DCHECK(!parsed_command_line.HasSwitch(switches::kProfileDirectory)); - - Profile* profile = ProfileManager::GetLastUsedProfile(); - if (!profile) { - // We should only be able to get here if the profile already exists and - // has been created. - NOTREACHED(); - return TRUE; - } - - ExtensionsStartupUtil ext_startup_util; - ext_startup_util.UninstallExtension(parsed_command_line, profile); - return TRUE; - } - - // Run the browser startup sequence again, with the command line of the - // signalling process. - BrowserInit::ProcessCommandLineAlreadyRunning(parsed_command_line, cur_dir); + CommandLine parsed_command_line(CommandLine::NO_PROGRAM); + FilePath current_directory; + if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory)) return TRUE; - } + ProcessCommandLine(parsed_command_line, current_directory); return TRUE; } @@ -377,3 +369,35 @@ LRESULT ProcessSingleton::WndProc(HWND hwnd, UINT message, return ::DefWindowProc(hwnd, message, wparam, lparam); } + +void ProcessSingleton::ProcessCommandLine(const CommandLine& command_line, + const FilePath& current_directory) { + PrefService* prefs = g_browser_process->local_state(); + DCHECK(prefs); + + // Handle the --uninstall-extension startup action. This needs to done here + // in the process that is running with the target profile, otherwise the + // uninstall will fail to unload and remove all components. + if (command_line.HasSwitch(switches::kUninstallExtension)) { + // The uninstall extension switch can't be combined with the profile + // directory switch. + DCHECK(!command_line.HasSwitch(switches::kProfileDirectory)); + + Profile* profile = ProfileManager::GetLastUsedProfile(); + if (!profile) { + // We should only be able to get here if the profile already exists and + // has been created. + NOTREACHED(); + return; + } + + ExtensionsStartupUtil ext_startup_util; + ext_startup_util.UninstallExtension(command_line, profile); + return; + } + + // Run the browser startup sequence again, with the command line of the + // signalling process. + BrowserInit::ProcessCommandLineAlreadyRunning(command_line, + current_directory); +} diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 50a01db..a97196d 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -2065,6 +2065,7 @@ 'browser/printing/printing_message_filter.h', 'browser/process_info_snapshot.h', 'browser/process_info_snapshot_mac.cc', + 'browser/process_singleton.cc', 'browser/process_singleton.h', 'browser/process_singleton_linux.cc', 'browser/process_singleton_mac.cc', |