diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-01-27 22:17:29 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-01-27 22:17:29 +0000 |
commit | fc14cef961fa421cc1c44e373e3f3f0fb84ec545 (patch) | |
tree | b7f442cf807c187103f4340c9aa9a0b37e3c94d6 /chrome | |
parent | 933fbfb3a9813625eea8c2258748ce86210444a8 (diff) | |
download | chromium_src-fc14cef961fa421cc1c44e373e3f3f0fb84ec545.zip chromium_src-fc14cef961fa421cc1c44e373e3f3f0fb84ec545.tar.gz chromium_src-fc14cef961fa421cc1c44e373e3f3f0fb84ec545.tar.bz2 |
Move MessageWindow out into its own file. BrowserInit is now cross-platform-capable.
Fix up the header includes in browser_init.cc/h removing a bunch of windows stuff.
Review URL: http://codereview.chromium.org/19034
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8762 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/browser.vcproj | 8 | ||||
-rw-r--r-- | chrome/browser/browser_init.cc | 278 | ||||
-rw-r--r-- | chrome/browser/browser_init.h | 82 | ||||
-rw-r--r-- | chrome/browser/browser_main.cc | 5 | ||||
-rw-r--r-- | chrome/browser/message_window.cc | 284 | ||||
-rw-r--r-- | chrome/browser/message_window.h | 75 | ||||
-rw-r--r-- | chrome/common/temp_scaffolding_stubs.h | 26 |
7 files changed, 395 insertions, 363 deletions
diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj index 32f9051..b1db09b 100644 --- a/chrome/browser/browser.vcproj +++ b/chrome/browser/browser.vcproj @@ -414,6 +414,14 @@ > </File> <File + RelativePath=".\message_window.cc" + > + </File> + <File + RelativePath=".\message_window.h" + > + </File> + <File RelativePath=".\meta_table_helper.cc" > </File> diff --git a/chrome/browser/browser_init.cc b/chrome/browser/browser_init.cc index 4b061e6..637f129 100644 --- a/chrome/browser/browser_init.cc +++ b/chrome/browser/browser_init.cc @@ -4,46 +4,38 @@ #include "chrome/browser/browser_init.h" -#include <shellapi.h> - #include "base/basictypes.h" #include "base/command_line.h" #include "base/event_recorder.h" -#include "base/file_util.h" -#include "base/histogram.h" #include "base/path_service.h" -#include "base/process_util.h" #include "base/string_util.h" #include "base/win_util.h" #include "chrome/app/locales/locale_settings.h" #include "chrome/app/result_codes.h" #include "chrome/app/theme/theme_resources.h" #include "chrome/browser/automation/automation_provider.h" +#include "chrome/browser/automation/automation_provider_list.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" -#include "chrome/browser/dom_ui/new_tab_ui.h" #include "chrome/browser/extensions/extensions_service.h" -#include "chrome/browser/first_run.h" #include "chrome/browser/net/dns_global.h" #include "chrome/browser/net/url_fixer_upper.h" +#include "chrome/browser/profile.h" #include "chrome/browser/renderer_host/render_process_host.h" +#include "chrome/browser/search_engines/template_url_model.h" #include "chrome/browser/session_startup_pref.h" #include "chrome/browser/sessions/session_restore.h" #include "chrome/browser/tab_contents/infobar_delegate.h" #include "chrome/browser/tab_contents/navigation_controller.h" -#include "chrome/browser/tabs/tab_strip_model.h" #include "chrome/browser/web_app_launcher.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/l10n_util.h" -#include "chrome/common/logging_chrome.h" #include "chrome/common/pref_names.h" #include "chrome/common/pref_service.h" #include "chrome/common/resource_bundle.h" -#include "chrome/common/win_util.h" #include "net/base/cookie_monster.h" -#include "net/base/net_util.h" #include "webkit/glue/webkit_glue.h" #include "chromium_strings.h" @@ -104,15 +96,6 @@ void SetOverrideHomePage(const CommandLine& command_line, PrefService* prefs) { } } -// Checks the visiblilty of the enumerated window and signals once a visible -// window has been found. -BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) { - bool* result = reinterpret_cast<bool*>(param); - *result = IsWindowVisible(window) != 0; - // Stops enumeration if a visible window has been found. - return !*result; -} - SessionStartupPref GetSessionStartupPref(Profile* profile, const CommandLine& command_line) { SessionStartupPref pref = SessionStartupPref::GetStartupPref(profile); @@ -130,8 +113,6 @@ SessionStartupPref GetSessionStartupPref(Profile* profile, } // namespace -// MessageWindow -------------------------------------------------------------- - static bool in_startup = false; // static @@ -139,259 +120,6 @@ bool BrowserInit::InProcessStartup() { return in_startup; } -BrowserInit::MessageWindow::MessageWindow(const std::wstring& user_data_dir) - : window_(NULL), - locked_(false) { - // Look for a Chrome instance that uses the same profile directory: - remote_window_ = FindWindowEx(HWND_MESSAGE, - NULL, - chrome::kMessageWindowClass, - user_data_dir.c_str()); -} - -BrowserInit::MessageWindow::~MessageWindow() { - if (window_) - DestroyWindow(window_); -} - -bool BrowserInit::MessageWindow::NotifyOtherProcess() { - if (!remote_window_) - return false; - - // Found another window, send our command line to it - // format is "START\0<<<current directory>>>\0<<<commandline>>>". - std::wstring to_send(L"START\0", 6); // want the NULL in the string. - std::wstring cur_dir; - if (!PathService::Get(base::DIR_CURRENT, &cur_dir)) - return false; - to_send.append(cur_dir); - to_send.append(L"\0", 1); // Null separator. - to_send.append(GetCommandLineW()); - to_send.append(L"\0", 1); // Null separator. - - // Allow the current running browser window making itself the foreground - // window (otherwise it will just flash in the taskbar). - DWORD process_id = 0; - DWORD thread_id = GetWindowThreadProcessId(remote_window_, &process_id); - DCHECK(process_id); - AllowSetForegroundWindow(process_id); - - // Gives 20 seconds timeout for the current browser process to respond. - const int kTimeout = 20000; - COPYDATASTRUCT cds; - cds.dwData = 0; - cds.cbData = static_cast<DWORD>((to_send.length() + 1) * sizeof(wchar_t)); - cds.lpData = const_cast<wchar_t*>(to_send.c_str()); - DWORD_PTR result = 0; - if (SendMessageTimeout(remote_window_, - WM_COPYDATA, - NULL, - reinterpret_cast<LPARAM>(&cds), - SMTO_ABORTIFHUNG, - kTimeout, - &result)) { - return true; - } - - // The window is hung. Scan for every window to find a visible one. - bool visible_window = false; - EnumThreadWindows(thread_id, - &BrowserWindowEnumeration, - reinterpret_cast<LPARAM>(&visible_window)); - - // If there is a visible browser window, ask the user before killing it. - if (visible_window) { - std::wstring text = l10n_util::GetString(IDS_BROWSER_HUNGBROWSER_MESSAGE); - std::wstring caption = l10n_util::GetString(IDS_PRODUCT_NAME); - if (IDYES != win_util::MessageBox(NULL, text, caption, - MB_YESNO | MB_ICONSTOP | MB_TOPMOST)) { - // The user denied. Quit silently. - return true; - } - } - - // Time to take action. Kill the browser process. - base::KillProcess(process_id, ResultCodes::HUNG, true); - remote_window_ = NULL; - return false; -} - -void BrowserInit::MessageWindow::Create() { - DCHECK(!window_); - DCHECK(!remote_window_); - HINSTANCE hinst = GetModuleHandle(NULL); - - WNDCLASSEX wc = {0}; - wc.cbSize = sizeof(wc); - wc.lpfnWndProc = BrowserInit::MessageWindow::WndProcStatic; - wc.hInstance = hinst; - wc.lpszClassName = chrome::kMessageWindowClass; - RegisterClassEx(&wc); - - std::wstring user_data_dir; - PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); - - // Set the window's title to the path of our user data directory so other - // Chrome instances can decide if they should forward to us or not. - window_ = CreateWindow(chrome::kMessageWindowClass, user_data_dir.c_str(), - 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, 0); - DCHECK(window_); - - win_util::SetWindowUserData(window_, this); -} - -LRESULT BrowserInit::MessageWindow::OnCopyData(HWND hwnd, - const COPYDATASTRUCT* cds) { - // Ignore the request if the browser process is already in shutdown path. - if (!g_browser_process || g_browser_process->IsShuttingDown()) - return TRUE; - - // If locked, it means we are not ready to process this message because - // we are probably in a first run critical phase. - if (locked_) - return TRUE; - - // We should have enough room for the shortest command (min_message_size) - // and also be a multiple of wchar_t bytes. - static const int min_message_size = 7; - if (cds->cbData < min_message_size || 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. - 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. - LOG(INFO) << "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 std::wstring 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(L""); - parsed_command_line.ParseFromString(cmd_line); - PrefService* prefs = g_browser_process->local_state(); - DCHECK(prefs); - - std::wstring user_data_dir; - PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); - ProfileManager* profile_manager = g_browser_process->profile_manager(); - Profile* profile = profile_manager->GetDefaultProfile(user_data_dir); - if (!profile) { - // We should only be able to get here if the profile already exists and - // has been created. - NOTREACHED(); - return TRUE; - } - ProcessCommandLine(parsed_command_line, cur_dir, prefs, false, profile, - NULL); - return TRUE; - } - return TRUE; -} - -LRESULT CALLBACK BrowserInit::MessageWindow::WndProc(HWND hwnd, - UINT message, - WPARAM wparam, - LPARAM lparam) { - switch (message) { - case WM_COPYDATA: - return OnCopyData(reinterpret_cast<HWND>(wparam), - reinterpret_cast<COPYDATASTRUCT*>(lparam)); - default: - break; - } - - return ::DefWindowProc(hwnd, message, wparam, lparam); -} - -void BrowserInit::MessageWindow::HuntForZombieChromeProcesses() { - // Detecting dead renderers is simple: - // - The process is named chrome.exe. - // - The process' parent doesn't exist anymore. - // - The process doesn't have a chrome::kMessageWindowClass window. - // If these conditions hold, the process is a zombie renderer or plugin. - - // Retrieve the list of browser processes on start. This list is then used to - // detect zombie renderer process or plugin process. - class ZombieDetector : public base::ProcessFilter { - public: - ZombieDetector() { - for (HWND window = NULL;;) { - window = FindWindowEx(HWND_MESSAGE, - window, - chrome::kMessageWindowClass, - NULL); - if (!window) - break; - DWORD process = 0; - GetWindowThreadProcessId(window, &process); - if (process) - browsers_.push_back(process); - } - // We are also a browser, regardless of having the window or not. - browsers_.push_back(::GetCurrentProcessId()); - } - - virtual bool Includes(uint32 pid, uint32 parent_pid) const { - // Don't kill ourself eh. - if (GetCurrentProcessId() == pid) - return false; - - // Is this a browser? If so, ignore it. - if (std::find(browsers_.begin(), browsers_.end(), pid) != browsers_.end()) - return false; - - // Is the parent a browser? If so, ignore it. - if (std::find(browsers_.begin(), browsers_.end(), parent_pid) - != browsers_.end()) - return false; - - // The chrome process is orphan. - return true; - } - - protected: - std::vector<uint32> browsers_; - }; - - ZombieDetector zombie_detector; - base::KillProcesses(L"chrome.exe", ResultCodes::HUNG, &zombie_detector); -} - - // LaunchWithProfile ---------------------------------------------------------- BrowserInit::LaunchWithProfile::LaunchWithProfile(const std::wstring& cur_dir, diff --git a/chrome/browser/browser_init.h b/chrome/browser/browser_init.h index 8b27aa1..5e11811 100644 --- a/chrome/browser/browser_init.h +++ b/chrome/browser/browser_init.h @@ -2,21 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_BROWSER_INIT_H__ -#define CHROME_BROWSER_BROWSER_INIT_H__ +#ifndef CHROME_BROWSER_BROWSER_INIT_H_ +#define CHROME_BROWSER_BROWSER_INIT_H_ -#include <atlbase.h> -#include <atlwin.h> -#include <windows.h> #include <string> #include <vector> -#include "base/gfx/rect.h" -#include "chrome/browser/profile_manager.h" +#include "base/basictypes.h" class Browser; class CommandLine; class GURL; +class PrefService; class Profile; class TabContents; @@ -27,68 +24,6 @@ class BrowserInit { // Returns true if the browser is coming up. static bool InProcessStartup(); - // MessageWindow ------------------------------------------------------------- - // - // Class for dealing with the invisible global message window for IPC. This - // window allows different browser processes to communicate with each other. - // It is named according to the user data directory, so we can be sure that - // no more than one copy of the application can be running at once with a - // given data directory. - - class MessageWindow { - public: - explicit MessageWindow(const std::wstring& user_data_dir); - ~MessageWindow(); - - // Returns true if another process was found and notified, false if we - // should continue with this process. Roughly based on Mozilla - // - // TODO(brettw): this will not handle all cases. If two process start up too - // close to each other, the window might not have been created yet for the - // first one, so this function won't find it. - bool NotifyOtherProcess(); - - // Create the toplevel message window for IPC. - void Create(); - - // Blocks the dispatch of CopyData messages. - void Lock() { - locked_ = true; - } - - // Allows the dispatch of CopyData messages. - void Unlock() { - locked_ = false; - } - - // This ugly behemoth handles startup commands sent from another process. - LRESULT OnCopyData(HWND hwnd, const COPYDATASTRUCT* cds); - - // Looks for zombie renderer and plugin processes that could have survived. - void HuntForZombieChromeProcesses(); - - private: - LRESULT CALLBACK WndProc(HWND hwnd, - UINT message, - WPARAM wparam, - LPARAM lparam); - - static LRESULT CALLBACK WndProcStatic(HWND hwnd, - UINT message, - WPARAM wparam, - LPARAM lparam) { - MessageWindow* msg_wnd = reinterpret_cast<MessageWindow*>( - GetWindowLongPtr(hwnd, GWLP_USERDATA)); - return msg_wnd->WndProc(hwnd, message, wparam, lparam); - } - - HWND remote_window_; // The HWND_MESSAGE of another browser. - HWND window_; // The HWND_MESSAGE window. - bool locked_; - - DISALLOW_EVIL_CONSTRUCTORS(MessageWindow); - }; - // LaunchWithProfile --------------------------------------------------------- // // Assists launching the application and appending the initial tabs for a @@ -140,10 +75,7 @@ class BrowserInit { std::wstring command_line_; Profile* profile_; - // Bounds for the browser. - gfx::Rect start_position_; - - DISALLOW_EVIL_CONSTRUCTORS(LaunchWithProfile); + DISALLOW_COPY_AND_ASSIGN(LaunchWithProfile); }; // This function performs command-line handling and is invoked when @@ -175,8 +107,8 @@ class BrowserInit { // This class is for scoping purposes. BrowserInit(); - DISALLOW_EVIL_CONSTRUCTORS(BrowserInit); + DISALLOW_COPY_AND_ASSIGN(BrowserInit); }; -#endif // CHROME_BROWSER_BROWSER_INIT_H__ +#endif // CHROME_BROWSER_BROWSER_INIT_H_ diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index 63cba36..8431d5d 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -65,12 +65,15 @@ #include "chrome/browser/extensions/extension_protocols.h" #include "chrome/browser/first_run.h" #include "chrome/browser/jankometer.h" +#include "chrome/browser/message_window.h" #include "chrome/browser/metrics/metrics_service.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/net/dns_global.h" #include "chrome/browser/net/sdch_dictionary_fetcher.h" #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/browser/printing/print_job_manager.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/profile_manager.h" #include "chrome/browser/rlz/rlz.h" #include "chrome/browser/user_data_manager.h" #include "chrome/browser/views/user_data_dir_dialog.h" @@ -214,7 +217,7 @@ int BrowserMain(const MainFunctionParams& parameters) { std::wstring user_data_dir; PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); - BrowserInit::MessageWindow message_window(user_data_dir); + MessageWindow message_window(user_data_dir); scoped_ptr<BrowserProcess> browser_process; if (parsed_command_line.HasSwitch(switches::kImport)) { diff --git a/chrome/browser/message_window.cc b/chrome/browser/message_window.cc new file mode 100644 index 0000000..13eb763 --- /dev/null +++ b/chrome/browser/message_window.cc @@ -0,0 +1,284 @@ +// Copyright (c) 2009 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/message_window.h" + +#include "base/base_paths.h" +#include "base/command_line.h" +#include "base/process_util.h" +#include "base/win_util.h" +#include "chrome/app/result_codes.h" +#include "chrome/browser/browser_init.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/profile_manager.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/win_util.h" + +#include "chromium_strings.h" +#include "generated_resources.h" + +namespace { + +// Checks the visiblilty of the enumerated window and signals once a visible +// window has been found. +BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) { + bool* result = reinterpret_cast<bool*>(param); + *result = IsWindowVisible(window) != 0; + // Stops enumeration if a visible window has been found. + return !*result; +} + +} // namespace + +MessageWindow::MessageWindow(const std::wstring& user_data_dir) + : window_(NULL), + locked_(false) { + // Look for a Chrome instance that uses the same profile directory: + remote_window_ = FindWindowEx(HWND_MESSAGE, + NULL, + chrome::kMessageWindowClass, + user_data_dir.c_str()); +} + +MessageWindow::~MessageWindow() { + if (window_) + DestroyWindow(window_); +} + +bool MessageWindow::NotifyOtherProcess() { + if (!remote_window_) + return false; + + // Found another window, send our command line to it + // format is "START\0<<<current directory>>>\0<<<commandline>>>". + std::wstring to_send(L"START\0", 6); // want the NULL in the string. + std::wstring cur_dir; + if (!PathService::Get(base::DIR_CURRENT, &cur_dir)) + return false; + to_send.append(cur_dir); + to_send.append(L"\0", 1); // Null separator. + to_send.append(GetCommandLineW()); + to_send.append(L"\0", 1); // Null separator. + + // Allow the current running browser window making itself the foreground + // window (otherwise it will just flash in the taskbar). + DWORD process_id = 0; + DWORD thread_id = GetWindowThreadProcessId(remote_window_, &process_id); + DCHECK(process_id); + AllowSetForegroundWindow(process_id); + + // Gives 20 seconds timeout for the current browser process to respond. + const int kTimeout = 20000; + COPYDATASTRUCT cds; + cds.dwData = 0; + cds.cbData = static_cast<DWORD>((to_send.length() + 1) * sizeof(wchar_t)); + cds.lpData = const_cast<wchar_t*>(to_send.c_str()); + DWORD_PTR result = 0; + if (SendMessageTimeout(remote_window_, + WM_COPYDATA, + NULL, + reinterpret_cast<LPARAM>(&cds), + SMTO_ABORTIFHUNG, + kTimeout, + &result)) { + return true; + } + + // The window is hung. Scan for every window to find a visible one. + bool visible_window = false; + EnumThreadWindows(thread_id, + &BrowserWindowEnumeration, + reinterpret_cast<LPARAM>(&visible_window)); + + // If there is a visible browser window, ask the user before killing it. + if (visible_window) { + std::wstring text = l10n_util::GetString(IDS_BROWSER_HUNGBROWSER_MESSAGE); + std::wstring caption = l10n_util::GetString(IDS_PRODUCT_NAME); + if (IDYES != win_util::MessageBox(NULL, text, caption, + MB_YESNO | MB_ICONSTOP | MB_TOPMOST)) { + // The user denied. Quit silently. + return true; + } + } + + // Time to take action. Kill the browser process. + base::KillProcess(process_id, ResultCodes::HUNG, true); + remote_window_ = NULL; + return false; +} + +void MessageWindow::Create() { + DCHECK(!window_); + DCHECK(!remote_window_); + HINSTANCE hinst = GetModuleHandle(NULL); + + WNDCLASSEX wc = {0}; + wc.cbSize = sizeof(wc); + wc.lpfnWndProc = MessageWindow::WndProcStatic; + wc.hInstance = hinst; + wc.lpszClassName = chrome::kMessageWindowClass; + RegisterClassEx(&wc); + + std::wstring user_data_dir; + PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + + // Set the window's title to the path of our user data directory so other + // Chrome instances can decide if they should forward to us or not. + window_ = CreateWindow(chrome::kMessageWindowClass, user_data_dir.c_str(), + 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, 0); + DCHECK(window_); + + win_util::SetWindowUserData(window_, this); +} + +LRESULT MessageWindow::OnCopyData(HWND hwnd, const COPYDATASTRUCT* cds) { + // Ignore the request if the browser process is already in shutdown path. + if (!g_browser_process || g_browser_process->IsShuttingDown()) + return TRUE; + + // If locked, it means we are not ready to process this message because + // we are probably in a first run critical phase. + if (locked_) + return TRUE; + + // We should have enough room for the shortest command (min_message_size) + // and also be a multiple of wchar_t bytes. + static const int min_message_size = 7; + if (cds->cbData < min_message_size || 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. + 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. + LOG(INFO) << "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 std::wstring 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(L""); + parsed_command_line.ParseFromString(cmd_line); + PrefService* prefs = g_browser_process->local_state(); + DCHECK(prefs); + + std::wstring user_data_dir; + PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + ProfileManager* profile_manager = g_browser_process->profile_manager(); + Profile* profile = profile_manager->GetDefaultProfile(user_data_dir); + if (!profile) { + // We should only be able to get here if the profile already exists and + // has been created. + NOTREACHED(); + return TRUE; + } + BrowserInit::ProcessCommandLine(parsed_command_line, cur_dir, prefs, false, + profile, NULL); + return TRUE; + } + return TRUE; +} + +LRESULT CALLBACK MessageWindow::WndProc(HWND hwnd, UINT message, + WPARAM wparam, LPARAM lparam) { + switch (message) { + case WM_COPYDATA: + return OnCopyData(reinterpret_cast<HWND>(wparam), + reinterpret_cast<COPYDATASTRUCT*>(lparam)); + default: + break; + } + + return ::DefWindowProc(hwnd, message, wparam, lparam); +} + +void MessageWindow::HuntForZombieChromeProcesses() { + // Detecting dead renderers is simple: + // - The process is named chrome.exe. + // - The process' parent doesn't exist anymore. + // - The process doesn't have a chrome::kMessageWindowClass window. + // If these conditions hold, the process is a zombie renderer or plugin. + + // Retrieve the list of browser processes on start. This list is then used to + // detect zombie renderer process or plugin process. + class ZombieDetector : public base::ProcessFilter { + public: + ZombieDetector() { + for (HWND window = NULL;;) { + window = FindWindowEx(HWND_MESSAGE, + window, + chrome::kMessageWindowClass, + NULL); + if (!window) + break; + DWORD process = 0; + GetWindowThreadProcessId(window, &process); + if (process) + browsers_.push_back(process); + } + // We are also a browser, regardless of having the window or not. + browsers_.push_back(::GetCurrentProcessId()); + } + + virtual bool Includes(uint32 pid, uint32 parent_pid) const { + // Don't kill ourself eh. + if (GetCurrentProcessId() == pid) + return false; + + // Is this a browser? If so, ignore it. + if (std::find(browsers_.begin(), browsers_.end(), pid) != browsers_.end()) + return false; + + // Is the parent a browser? If so, ignore it. + if (std::find(browsers_.begin(), browsers_.end(), parent_pid) + != browsers_.end()) + return false; + + // The chrome process is orphan. + return true; + } + + protected: + std::vector<uint32> browsers_; + }; + + ZombieDetector zombie_detector; + base::KillProcesses(L"chrome.exe", ResultCodes::HUNG, &zombie_detector); +} diff --git a/chrome/browser/message_window.h b/chrome/browser/message_window.h new file mode 100644 index 0000000..1d0d732 --- /dev/null +++ b/chrome/browser/message_window.h @@ -0,0 +1,75 @@ +// Copyright (c) 2009 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. + +#ifndef CHROME_BROWSER_MESSAGE_WINDOW_H_ +#define CHROME_BROWSER_MESSAGE_WINDOW_H_ + +#include <string> +#include <windows.h> + +#include "base/basictypes.h" + +// MessageWindow ------------------------------------------------------------- +// +// Class for dealing with the invisible global message window for IPC. This +// window allows different browser processes to communicate with each other. +// It is named according to the user data directory, so we can be sure that +// no more than one copy of the application can be running at once with a +// given data directory. + +class MessageWindow { + public: + explicit MessageWindow(const std::wstring& user_data_dir); + ~MessageWindow(); + + // Returns true if another process was found and notified, false if we + // should continue with this process. Roughly based on Mozilla + // + // TODO(brettw): this will not handle all cases. If two process start up too + // close to each other, the window might not have been created yet for the + // first one, so this function won't find it. + bool NotifyOtherProcess(); + + // Create the toplevel message window for IPC. + void Create(); + + // Blocks the dispatch of CopyData messages. + void Lock() { + locked_ = true; + } + + // Allows the dispatch of CopyData messages. + void Unlock() { + locked_ = false; + } + + // This ugly behemoth handles startup commands sent from another process. + LRESULT OnCopyData(HWND hwnd, const COPYDATASTRUCT* cds); + + // Looks for zombie renderer and plugin processes that could have survived. + void HuntForZombieChromeProcesses(); + + private: + LRESULT CALLBACK WndProc(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam); + + static LRESULT CALLBACK WndProcStatic(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + MessageWindow* msg_wnd = reinterpret_cast<MessageWindow*>( + GetWindowLongPtr(hwnd, GWLP_USERDATA)); + return msg_wnd->WndProc(hwnd, message, wparam, lparam); + } + + HWND remote_window_; // The HWND_MESSAGE of another browser. + HWND window_; // The HWND_MESSAGE window. + bool locked_; + + DISALLOW_COPY_AND_ASSIGN(MessageWindow); +}; + +#endif // #ifndef CHROME_BROWSER_MESSAGE_WINDOW_H_ diff --git a/chrome/common/temp_scaffolding_stubs.h b/chrome/common/temp_scaffolding_stubs.h index 034de96..c4152ba 100644 --- a/chrome/common/temp_scaffolding_stubs.h +++ b/chrome/common/temp_scaffolding_stubs.h @@ -51,20 +51,22 @@ class Upgrade { static bool SwapNewChromeExeIfPresent() { return true; } }; +// TODO(port): MessageWindow is very windows-specific, but provides the concept +// of singleton browser process per user-data-dir. Investigate how +// to achieve this on other platforms and see if this API works. +class MessageWindow { + public: + explicit MessageWindow(const std::wstring& user_data_dir) { } + ~MessageWindow() { } + bool NotifyOtherProcess() { return false; } + void HuntForZombieChromeProcesses() { } + void Create() { } + void Lock() { } + void Unlock() { } +}; + class BrowserInit { public: - // TODO(port): MessageWindow is very windows specific and shouldn't be part of - // BrowserInit at all. - class MessageWindow { - public: - explicit MessageWindow(const std::wstring& user_data_dir) { } - ~MessageWindow() { } - bool NotifyOtherProcess() { return false; } - void HuntForZombieChromeProcesses() { } - void Create() { } - void Lock() { } - void Unlock() { } - }; static bool ProcessCommandLine(const CommandLine& parsed_command_line, const std::wstring& cur_dir, PrefService* prefs, bool process_startup, |