summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/chrome_browser_main.cc5
-rw-r--r--chrome/browser/first_run/try_chrome_dialog_view.cc10
-rw-r--r--chrome/browser/process_singleton.cc24
-rw-r--r--chrome/browser/process_singleton.h24
-rw-r--r--chrome/browser/process_singleton_linux.cc41
-rw-r--r--chrome/browser/process_singleton_mac.cc6
-rw-r--r--chrome/browser/process_singleton_win.cc188
-rw-r--r--chrome/chrome_browser.gypi1
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, &current_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, &current_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',