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