[M85 Dev][EFL] Fix errors to generate ninja files
[platform/framework/web/chromium-efl.git] / chrome / browser / process_singleton_win.cc
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/process_singleton.h"
6
7 #include <windows.h>
8 #include <shellapi.h>
9 #include <stddef.h>
10
11 #include "base/base_paths.h"
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/debug/activity_tracker.h"
15 #include "base/files/file_path.h"
16 #include "base/logging.h"
17 #include "base/macros.h"
18 #include "base/metrics/histogram_functions.h"
19 #include "base/metrics/histogram_macros.h"
20 #include "base/process/process.h"
21 #include "base/process/process_info.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/time/time.h"
25 #include "base/win/registry.h"
26 #include "base/win/scoped_handle.h"
27 #include "base/win/windows_version.h"
28 #include "base/win/wmi.h"
29 #include "chrome/browser/shell_integration.h"
30 #include "chrome/browser/ui/simple_message_box.h"
31 #include "chrome/browser/win/chrome_process_finder.h"
32 #include "chrome/common/chrome_constants.h"
33 #include "chrome/common/chrome_paths.h"
34 #include "chrome/common/chrome_paths_internal.h"
35 #include "chrome/common/chrome_switches.h"
36 #include "chrome/grit/chromium_strings.h"
37 #include "content/public/common/result_codes.h"
38 #include "net/base/escape.h"
39 #include "ui/base/l10n/l10n_util.h"
40 #include "ui/gfx/win/hwnd_util.h"
41
42 namespace {
43
44 const char kLockfile[] = "lockfile";
45
46 // A helper class that acquires the given |mutex| while the AutoLockMutex is in
47 // scope.
48 class AutoLockMutex {
49  public:
50   explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) {
51     DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
52     DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
53   }
54
55   ~AutoLockMutex() {
56     BOOL released = ::ReleaseMutex(mutex_);
57     DPCHECK(released);
58   }
59
60  private:
61   HANDLE mutex_;
62   DISALLOW_COPY_AND_ASSIGN(AutoLockMutex);
63 };
64
65 // A helper class that releases the given |mutex| while the AutoUnlockMutex is
66 // in scope and immediately re-acquires it when going out of scope.
67 class AutoUnlockMutex {
68  public:
69   explicit AutoUnlockMutex(HANDLE mutex) : mutex_(mutex) {
70     BOOL released = ::ReleaseMutex(mutex_);
71     DPCHECK(released);
72   }
73
74   ~AutoUnlockMutex() {
75     DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
76     DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
77   }
78
79  private:
80   HANDLE mutex_;
81   DISALLOW_COPY_AND_ASSIGN(AutoUnlockMutex);
82 };
83
84 // Checks the visibility of the enumerated window and signals once a visible
85 // window has been found.
86 BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
87   bool* result = reinterpret_cast<bool*>(param);
88   *result = ::IsWindowVisible(window) != 0;
89   // Stops enumeration if a visible window has been found.
90   return !*result;
91 }
92
93 bool ParseCommandLine(const COPYDATASTRUCT* cds,
94                       base::CommandLine* parsed_command_line,
95                       base::FilePath* current_directory) {
96   // We should have enough room for the shortest command (min_message_size)
97   // and also be a multiple of wchar_t bytes. The shortest command
98   // possible is L"START\0\0" (empty current directory and command line).
99   static const int min_message_size = 7;
100   if (cds->cbData < min_message_size * sizeof(wchar_t) ||
101       cds->cbData % sizeof(wchar_t) != 0) {
102     LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData;
103     return false;
104   }
105
106   // We split the string into 4 parts on NULLs.
107   DCHECK(cds->lpData);
108   const std::wstring msg(static_cast<wchar_t*>(cds->lpData),
109                          cds->cbData / sizeof(wchar_t));
110   const std::wstring::size_type first_null = msg.find_first_of(L'\0');
111   if (first_null == 0 || first_null == std::wstring::npos) {
112     // no NULL byte, don't know what to do
113     LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length() <<
114       ", first null = " << first_null;
115     return false;
116   }
117
118   // Decode the command, which is everything until the first NULL.
119   if (msg.substr(0, first_null) == L"START") {
120     // Another instance is starting parse the command line & do what it would
121     // have done.
122     VLOG(1) << "Handling STARTUP request from another process";
123     const std::wstring::size_type second_null =
124         msg.find_first_of(L'\0', first_null + 1);
125     if (second_null == std::wstring::npos ||
126         first_null == msg.length() - 1 || second_null == msg.length()) {
127       LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
128         "parts separated by NULLs";
129       return false;
130     }
131
132     // Get current directory.
133     *current_directory = base::FilePath(msg.substr(first_null + 1,
134                                                    second_null - first_null));
135
136     const std::wstring::size_type third_null =
137         msg.find_first_of(L'\0', second_null + 1);
138     if (third_null == std::wstring::npos ||
139         third_null == msg.length()) {
140       LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
141         "parts separated by NULLs";
142     }
143
144     // Get command line.
145     const std::wstring cmd_line =
146         msg.substr(second_null + 1, third_null - second_null);
147     *parsed_command_line = base::CommandLine::FromString(cmd_line);
148     return true;
149   }
150   return false;
151 }
152
153 bool ProcessLaunchNotification(
154     const ProcessSingleton::NotificationCallback& notification_callback,
155     UINT message,
156     WPARAM wparam,
157     LPARAM lparam,
158     LRESULT* result) {
159   if (message != WM_COPYDATA)
160     return false;
161
162   // Handle the WM_COPYDATA message from another process.
163   const COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lparam);
164
165   base::CommandLine parsed_command_line(base::CommandLine::NO_PROGRAM);
166   base::FilePath current_directory;
167   if (!ParseCommandLine(cds, &parsed_command_line, &current_directory)) {
168     *result = TRUE;
169     return true;
170   }
171
172   *result = notification_callback.Run(parsed_command_line, current_directory) ?
173       TRUE : FALSE;
174   return true;
175 }
176
177 bool DisplayShouldKillMessageBox() {
178   return chrome::ShowQuestionMessageBox(
179              NULL, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
180              l10n_util::GetStringUTF16(IDS_BROWSER_HUNGBROWSER_MESSAGE)) !=
181          chrome::MESSAGE_BOX_RESULT_NO;
182 }
183
184 void SendRemoteProcessInteractionResultHistogram(
185     ProcessSingleton::RemoteProcessInteractionResult result) {
186   UMA_HISTOGRAM_ENUMERATION(
187       "Chrome.ProcessSingleton.RemoteProcessInteractionResult", result,
188       ProcessSingleton::REMOTE_PROCESS_INTERACTION_RESULT_COUNT);
189 }
190
191 // Function was copied from Process::Terminate.
192 void TerminateProcessWithHistograms(const base::Process& process,
193                                     int exit_code) {
194   DCHECK(process.IsValid());
195   base::TimeTicks start_time = base::TimeTicks::Now();
196   bool result = (::TerminateProcess(process.Handle(), exit_code) != FALSE);
197   DWORD terminate_error = 0;
198   if (result) {
199     DWORD wait_error = 0;
200     // The process may not end immediately due to pending I/O
201     DWORD wait_result = ::WaitForSingleObject(process.Handle(), 60 * 1000);
202     if (wait_result != WAIT_OBJECT_0) {
203       if (wait_result == WAIT_FAILED)
204         wait_error = ::GetLastError();
205       SendRemoteProcessInteractionResultHistogram(
206           ProcessSingleton::TERMINATE_WAIT_TIMEOUT);
207       DPLOG(ERROR) << "Error waiting for process exit";
208     } else {
209       SendRemoteProcessInteractionResultHistogram(
210           ProcessSingleton::TERMINATE_SUCCEEDED);
211     }
212     base::debug::GlobalActivityTracker::RecordProcessExitIfEnabled(
213         process.Pid(), exit_code);
214     UMA_HISTOGRAM_TIMES("Chrome.ProcessSingleton.TerminateProcessTime",
215                         base::TimeTicks::Now() - start_time);
216     base::UmaHistogramSparse(
217         "Chrome.ProcessSingleton.TerminationWaitErrorCode.Windows", wait_error);
218   } else {
219     terminate_error = ::GetLastError();
220     SendRemoteProcessInteractionResultHistogram(
221         ProcessSingleton::TERMINATE_FAILED);
222     DPLOG(ERROR) << "Unable to terminate process";
223   }
224   base::UmaHistogramSparse(
225       "Chrome.ProcessSingleton.TerminateProcessErrorCode.Windows",
226       terminate_error);
227 }
228
229 }  // namespace
230
231 // Microsoft's Softricity virtualization breaks the sandbox processes.
232 // So, if we detect the Softricity DLL we use WMI Win32_Process.Create to
233 // break out of the virtualization environment.
234 // http://code.google.com/p/chromium/issues/detail?id=43650
235 bool ProcessSingleton::EscapeVirtualization(
236     const base::FilePath& user_data_dir) {
237   if (::GetModuleHandle(L"sftldr_wow64.dll") ||
238       ::GetModuleHandle(L"sftldr.dll")) {
239     int process_id;
240     if (!base::win::WmiLaunchProcess(::GetCommandLineW(), &process_id))
241       return false;
242     is_virtualized_ = true;
243     // The new window was spawned from WMI, and won't be in the foreground.
244     // So, first we sleep while the new chrome.exe instance starts (because
245     // WaitForInputIdle doesn't work here). Then we poll for up to two more
246     // seconds and make the window foreground if we find it (or we give up).
247     HWND hwnd = 0;
248     ::Sleep(90);
249     for (int tries = 200; tries; --tries) {
250       hwnd = chrome::FindRunningChromeWindow(user_data_dir);
251       if (hwnd) {
252         ::SetForegroundWindow(hwnd);
253         break;
254       }
255       ::Sleep(10);
256     }
257     return true;
258   }
259   return false;
260 }
261
262 ProcessSingleton::ProcessSingleton(
263     const base::FilePath& user_data_dir,
264     const NotificationCallback& notification_callback)
265     : notification_callback_(notification_callback),
266       is_virtualized_(false),
267       lock_file_(INVALID_HANDLE_VALUE),
268       user_data_dir_(user_data_dir),
269       should_kill_remote_process_callback_(
270           base::Bind(&DisplayShouldKillMessageBox)) {
271 }
272
273 ProcessSingleton::~ProcessSingleton() {
274   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
275   if (lock_file_ != INVALID_HANDLE_VALUE)
276     ::CloseHandle(lock_file_);
277 }
278
279 // Code roughly based on Mozilla.
280 ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
281   if (is_virtualized_)
282     return PROCESS_NOTIFIED;  // We already spawned the process in this case.
283   if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_) {
284     return LOCK_ERROR;
285   } else if (!remote_window_) {
286     return PROCESS_NONE;
287   }
288
289   switch (chrome::AttemptToNotifyRunningChrome(remote_window_)) {
290     case chrome::NOTIFY_SUCCESS:
291       return PROCESS_NOTIFIED;
292     case chrome::NOTIFY_FAILED:
293       remote_window_ = NULL;
294       SendRemoteProcessInteractionResultHistogram(RUNNING_PROCESS_NOTIFY_ERROR);
295       return PROCESS_NONE;
296     case chrome::NOTIFY_WINDOW_HUNG:
297       // Fall through and potentially terminate the hung browser.
298       break;
299   }
300
301   // The window is hung.
302   DWORD process_id = 0;
303   DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id);
304   if (!thread_id || !process_id) {
305     remote_window_ = NULL;
306     SendRemoteProcessInteractionResultHistogram(REMOTE_PROCESS_NOT_FOUND);
307     return PROCESS_NONE;
308   }
309
310   // Get a handle to the process that created the window.
311   base::Process process = base::Process::Open(process_id);
312
313   // Scan for every window to find a visible one.
314   bool visible_window = false;
315   ::EnumThreadWindows(thread_id,
316                       &BrowserWindowEnumeration,
317                       reinterpret_cast<LPARAM>(&visible_window));
318
319   // If there is a visible browser window, ask the user before killing it.
320   if (visible_window && !should_kill_remote_process_callback_.Run()) {
321     SendRemoteProcessInteractionResultHistogram(USER_REFUSED_TERMINATION);
322     // The user denied. Quit silently.
323     return PROCESS_NOTIFIED;
324   }
325   UMA_HISTOGRAM_ENUMERATION(
326       "Chrome.ProcessSingleton.RemoteHungProcessTerminateReason",
327       visible_window ? USER_ACCEPTED_TERMINATION : NO_VISIBLE_WINDOW_FOUND,
328       REMOTE_HUNG_PROCESS_TERMINATE_REASON_COUNT);
329
330   // Time to take action. Kill the browser process.
331   TerminateProcessWithHistograms(process, content::RESULT_CODE_HUNG);
332
333   remote_window_ = NULL;
334   return PROCESS_NONE;
335 }
336
337 ProcessSingleton::NotifyResult
338 ProcessSingleton::NotifyOtherProcessOrCreate() {
339   const base::TimeTicks begin_ticks = base::TimeTicks::Now();
340   for (int i = 0; i < 2; ++i) {
341     if (Create()) {
342       UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToCreate",
343                                  base::TimeTicks::Now() - begin_ticks);
344       return PROCESS_NONE;  // This is the single browser process.
345     }
346     ProcessSingleton::NotifyResult result = NotifyOtherProcess();
347     if (result == PROCESS_NOTIFIED || result == LOCK_ERROR) {
348       if (result == PROCESS_NOTIFIED) {
349         UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToNotify",
350                                    base::TimeTicks::Now() - begin_ticks);
351       } else {
352         UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToFailure",
353                                    base::TimeTicks::Now() - begin_ticks);
354       }
355       // The single browser process was notified, the user chose not to
356       // terminate a hung browser, or the lock file could not be created.
357       // Nothing more to do.
358       return result;
359     }
360     DCHECK_EQ(PROCESS_NONE, result);
361     // The process could not be notified for some reason, or it was hung and
362     // terminated. Retry once if this is the first time; otherwise, fall through
363     // to report that the process must exit because the profile is in use.
364   }
365   UMA_HISTOGRAM_MEDIUM_TIMES("Chrome.ProcessSingleton.TimeToFailure",
366                              base::TimeTicks::Now() - begin_ticks);
367   return PROFILE_IN_USE;
368 }
369
370 // Look for a Chrome instance that uses the same profile directory. If there
371 // isn't one, create a message window with its title set to the profile
372 // directory path.
373 bool ProcessSingleton::Create() {
374   static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!";
375
376   remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
377   if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) {
378     // Make sure we will be the one and only process creating the window.
379     // We use a named Mutex since we are protecting against multi-process
380     // access. As documented, it's clearer to NOT request ownership on creation
381     // since it isn't guaranteed we will get it. It is better to create it
382     // without ownership and explicitly get the ownership afterward.
383     base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName));
384     if (!only_me.IsValid()) {
385       DPLOG(FATAL) << "CreateMutex failed";
386       return false;
387     }
388
389     AutoLockMutex auto_lock_only_me(only_me.Get());
390
391     // We now own the mutex so we are the only process that can create the
392     // window at this time, but we must still check if someone created it
393     // between the time where we looked for it above and the time the mutex
394     // was given to us.
395     remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
396
397     if (!remote_window_) {
398       // We have to make sure there is no Chrome instance running on another
399       // machine that uses the same profile.
400       base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile);
401       lock_file_ = ::CreateFile(lock_file_path.value().c_str(),
402                                 GENERIC_WRITE,
403                                 FILE_SHARE_READ,
404                                 NULL,
405                                 CREATE_ALWAYS,
406                                 FILE_ATTRIBUTE_NORMAL |
407                                 FILE_FLAG_DELETE_ON_CLOSE,
408                                 NULL);
409       DWORD error = ::GetLastError();
410       LOG_IF(WARNING, lock_file_ != INVALID_HANDLE_VALUE &&
411           error == ERROR_ALREADY_EXISTS) << "Lock file exists but is writable.";
412       LOG_IF(ERROR, lock_file_ == INVALID_HANDLE_VALUE)
413           << "Lock file can not be created! Error code: " << error;
414
415       if (lock_file_ != INVALID_HANDLE_VALUE) {
416         // Set the window's title to the path of our user data directory so
417         // other Chrome instances can decide if they should forward to us.
418         bool result = window_.CreateNamed(
419             base::Bind(&ProcessLaunchNotification, notification_callback_),
420             user_data_dir_.value());
421         CHECK(result && window_.hwnd());
422       }
423     }
424   }
425
426   return window_.hwnd() != NULL;
427 }
428
429 void ProcessSingleton::Cleanup() {
430 }
431
432 void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting(
433     const ShouldKillRemoteProcessCallback& display_dialog_callback) {
434   should_kill_remote_process_callback_ = display_dialog_callback;
435 }