Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / cloud_print / service / win / chrome_launcher.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 "cloud_print/service/win/chrome_launcher.h"
6
7 #include "base/base_switches.h"
8 #include "base/command_line.h"
9 #include "base/file_util.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/json/json_reader.h"
12 #include "base/json/json_writer.h"
13 #include "base/process/kill.h"
14 #include "base/process/process.h"
15 #include "base/values.h"
16 #include "base/win/registry.h"
17 #include "base/win/scoped_handle.h"
18 #include "base/win/scoped_process_information.h"
19 #include "chrome/common/chrome_constants.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/common/pref_names.h"
22 #include "chrome/installer/launcher_support/chrome_launcher_support.h"
23 #include "cloud_print/common/win/cloud_print_utils.h"
24 #include "cloud_print/service/service_constants.h"
25 #include "cloud_print/service/win/service_utils.h"
26 #include "components/cloud_devices/common/cloud_devices_urls.h"
27 #include "google_apis/gaia/gaia_urls.h"
28 #include "net/base/url_util.h"
29 #include "url/gurl.h"
30
31 namespace {
32
33 const int kShutdownTimeoutMs = 30 * 1000;
34 const int kUsageUpdateTimeoutMs = 6 * 3600 * 1000;  // 6 hours.
35
36 static const base::char16 kAutoRunKeyPath[] =
37     L"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
38
39 // Terminates any process.
40 void ShutdownChrome(HANDLE process, DWORD thread_id) {
41   if (::PostThreadMessage(thread_id, WM_QUIT, 0, 0) &&
42       WAIT_OBJECT_0 == ::WaitForSingleObject(process, kShutdownTimeoutMs)) {
43     return;
44   }
45   LOG(ERROR) << "Failed to shutdown process.";
46   base::KillProcess(process, 0, true);
47 }
48
49 BOOL CALLBACK CloseIfPidEqual(HWND wnd, LPARAM lparam) {
50   DWORD pid = 0;
51   ::GetWindowThreadProcessId(wnd, &pid);
52   if (pid == static_cast<DWORD>(lparam))
53     ::PostMessage(wnd, WM_CLOSE, 0, 0);
54   return TRUE;
55 }
56
57 void CloseAllProcessWindows(HANDLE process) {
58   ::EnumWindows(&CloseIfPidEqual, GetProcessId(process));
59 }
60
61 // Close Chrome browser window.
62 void CloseChrome(HANDLE process, DWORD thread_id) {
63   CloseAllProcessWindows(process);
64   if (WAIT_OBJECT_0 == ::WaitForSingleObject(process, kShutdownTimeoutMs)) {
65     return;
66   }
67   ShutdownChrome(process, thread_id);
68 }
69
70 bool LaunchProcess(const CommandLine& cmdline,
71                    base::win::ScopedHandle* process_handle,
72                    DWORD* thread_id) {
73   STARTUPINFO startup_info = {};
74   startup_info.cb = sizeof(startup_info);
75   startup_info.dwFlags = STARTF_USESHOWWINDOW;
76   startup_info.wShowWindow = SW_SHOW;
77
78   PROCESS_INFORMATION temp_process_info = {};
79   if (!CreateProcess(NULL,
80       const_cast<wchar_t*>(cmdline.GetCommandLineString().c_str()), NULL, NULL,
81       FALSE, 0, NULL, NULL, &startup_info, &temp_process_info)) {
82     return false;
83   }
84   base::win::ScopedProcessInformation process_info(temp_process_info);
85
86   if (process_handle)
87     process_handle->Set(process_info.TakeProcessHandle());
88
89   if (thread_id)
90     *thread_id = process_info.thread_id();
91
92   return true;
93 }
94
95 std::string ReadAndUpdateServiceState(const base::FilePath& directory,
96                                       const std::string& proxy_id) {
97   std::string json;
98   base::FilePath file_path = directory.Append(chrome::kServiceStateFileName);
99   if (!base::ReadFileToString(file_path, &json)) {
100     return std::string();
101   }
102
103   scoped_ptr<base::Value> service_state(base::JSONReader::Read(json));
104   base::DictionaryValue* dictionary = NULL;
105   if (!service_state->GetAsDictionary(&dictionary) || !dictionary) {
106     return std::string();
107   }
108
109   bool enabled = false;
110   if (!dictionary->GetBoolean(prefs::kCloudPrintProxyEnabled, &enabled) ||
111       !enabled) {
112     return std::string();
113   }
114
115   std::string refresh_token;
116   if (!dictionary->GetString(prefs::kCloudPrintRobotRefreshToken,
117                              &refresh_token) ||
118       refresh_token.empty()) {
119     return std::string();
120   }
121
122   // Remove everything except kCloudPrintRoot.
123   scoped_ptr<base::Value> cloud_print_root;
124   dictionary->Remove(prefs::kCloudPrintRoot, &cloud_print_root);
125   dictionary->Clear();
126   dictionary->Set(prefs::kCloudPrintRoot, cloud_print_root.release());
127
128   dictionary->SetBoolean(prefs::kCloudPrintXmppPingEnabled, true);
129   if (!proxy_id.empty())  // Reuse proxy id if we already had one.
130     dictionary->SetString(prefs::kCloudPrintProxyId, proxy_id);
131   std::string result;
132   base::JSONWriter::WriteWithOptions(dictionary,
133                                      base::JSONWriter::OPTIONS_PRETTY_PRINT,
134                                      &result);
135   return result;
136 }
137
138 void DeleteAutorunKeys(const base::FilePath& user_data_dir) {
139   base::win::RegKey key(HKEY_CURRENT_USER, kAutoRunKeyPath, KEY_SET_VALUE);
140   if (!key.Valid())
141     return;
142   std::vector<base::string16> to_delete;
143
144   base::FilePath abs_user_data_dir = base::MakeAbsoluteFilePath(user_data_dir);
145
146   {
147     base::win::RegistryValueIterator value(HKEY_CURRENT_USER, kAutoRunKeyPath);
148     for (; value.Valid(); ++value) {
149       if (value.Type() == REG_SZ && value.Value()) {
150         CommandLine cmd = CommandLine::FromString(value.Value());
151         if (cmd.GetSwitchValueASCII(switches::kProcessType) ==
152             switches::kServiceProcess &&
153             cmd.HasSwitch(switches::kUserDataDir)) {
154           base::FilePath path_from_reg = base::MakeAbsoluteFilePath(
155               cmd.GetSwitchValuePath(switches::kUserDataDir));
156           if (path_from_reg == abs_user_data_dir) {
157             to_delete.push_back(value.Name());
158           }
159         }
160       }
161     }
162   }
163
164   for (size_t i = 0; i < to_delete.size(); ++i) {
165     key.DeleteValue(to_delete[i].c_str());
166   }
167 }
168
169 }  // namespace
170
171 ChromeLauncher::ChromeLauncher(const base::FilePath& user_data)
172     : stop_event_(true, true),
173       user_data_(user_data) {
174 }
175
176 ChromeLauncher::~ChromeLauncher() {
177 }
178
179 bool ChromeLauncher::Start() {
180   DeleteAutorunKeys(user_data_);
181   stop_event_.Reset();
182   thread_.reset(new base::DelegateSimpleThread(this, "chrome_launcher"));
183   thread_->Start();
184   return true;
185 }
186
187 void ChromeLauncher::Stop() {
188   stop_event_.Signal();
189   thread_->Join();
190   thread_.reset();
191 }
192
193 void ChromeLauncher::Run() {
194   const base::TimeDelta default_time_out = base::TimeDelta::FromSeconds(1);
195   const base::TimeDelta max_time_out = base::TimeDelta::FromHours(1);
196
197   for (base::TimeDelta time_out = default_time_out;;
198        time_out = std::min(time_out * 2, max_time_out)) {
199     base::FilePath chrome_path = chrome_launcher_support::GetAnyChromePath();
200
201     if (!chrome_path.empty()) {
202       CommandLine cmd(chrome_path);
203       CopyChromeSwitchesFromCurrentProcess(&cmd);
204
205       // Required switches.
206       cmd.AppendSwitchASCII(switches::kProcessType, switches::kServiceProcess);
207       cmd.AppendSwitchPath(switches::kUserDataDir, user_data_);
208       cmd.AppendSwitch(switches::kNoServiceAutorun);
209
210       // Optional.
211       cmd.AppendSwitch(switches::kAutoLaunchAtStartup);
212       cmd.AppendSwitch(switches::kDisableBackgroundMode);
213       cmd.AppendSwitch(switches::kDisableDefaultApps);
214       cmd.AppendSwitch(switches::kDisableExtensions);
215       cmd.AppendSwitch(switches::kDisableGpu);
216       cmd.AppendSwitch(switches::kDisableSoftwareRasterizer);
217       cmd.AppendSwitch(switches::kDisableSync);
218       cmd.AppendSwitch(switches::kNoFirstRun);
219       cmd.AppendSwitch(switches::kNoStartupWindow);
220
221       base::win::ScopedHandle chrome_handle;
222       base::Time started = base::Time::Now();
223       DWORD thread_id = 0;
224       LaunchProcess(cmd, &chrome_handle, &thread_id);
225
226       HANDLE handles[] = {stop_event_.handle(), chrome_handle};
227       DWORD wait_result = WAIT_TIMEOUT;
228       while (wait_result == WAIT_TIMEOUT) {
229         cloud_print::SetGoogleUpdateUsage(kGoogleUpdateId);
230         wait_result = ::WaitForMultipleObjects(arraysize(handles), handles,
231                                                FALSE, kUsageUpdateTimeoutMs);
232       }
233       if (wait_result == WAIT_OBJECT_0) {
234         ShutdownChrome(chrome_handle, thread_id);
235         break;
236       } else if (wait_result == WAIT_OBJECT_0 + 1) {
237         LOG(ERROR) << "Chrome process exited.";
238       } else {
239         LOG(ERROR) << "Error waiting Chrome (" << ::GetLastError() << ").";
240       }
241       if (base::Time::Now() - started > base::TimeDelta::FromHours(1)) {
242         // Reset timeout because process worked long enough.
243         time_out = default_time_out;
244       }
245     }
246     if (stop_event_.TimedWait(time_out))
247       break;
248   }
249 }
250
251 std::string ChromeLauncher::CreateServiceStateFile(
252     const std::string& proxy_id,
253     const std::vector<std::string>& printers) {
254   base::ScopedTempDir temp_user_data;
255   if (!temp_user_data.CreateUniqueTempDir()) {
256     LOG(ERROR) << "Can't create temp dir.";
257     return std::string();
258   }
259
260   base::FilePath chrome_path = chrome_launcher_support::GetAnyChromePath();
261   if (chrome_path.empty()) {
262     LOG(ERROR) << "Can't find Chrome.";
263     return std::string();
264   }
265
266   base::FilePath printers_file = temp_user_data.path().Append(L"printers.json");
267
268   base::ListValue printer_list;
269   printer_list.AppendStrings(printers);
270   std::string printers_json;
271   base::JSONWriter::Write(&printer_list, &printers_json);
272   size_t written = base::WriteFile(printers_file,
273                                    printers_json.c_str(),
274                                    printers_json.size());
275   if (written != printers_json.size()) {
276     LOG(ERROR) << "Can't write file.";
277     return std::string();
278   }
279
280   CommandLine cmd(chrome_path);
281   CopyChromeSwitchesFromCurrentProcess(&cmd);
282   cmd.AppendSwitchPath(switches::kUserDataDir, temp_user_data.path());
283   cmd.AppendSwitchPath(switches::kCloudPrintSetupProxy, printers_file);
284   cmd.AppendSwitch(switches::kNoServiceAutorun);
285
286   // Optional.
287   cmd.AppendSwitch(switches::kDisableBackgroundMode);
288   cmd.AppendSwitch(switches::kDisableDefaultApps);
289   cmd.AppendSwitch(switches::kDisableExtensions);
290   cmd.AppendSwitch(switches::kDisableSync);
291   cmd.AppendSwitch(switches::kNoDefaultBrowserCheck);
292   cmd.AppendSwitch(switches::kNoFirstRun);
293
294   cmd.AppendArg(
295       cloud_devices::GetCloudPrintEnableWithSigninURL(proxy_id).spec());
296
297   base::win::ScopedHandle chrome_handle;
298   DWORD thread_id = 0;
299   if (!LaunchProcess(cmd, &chrome_handle, &thread_id)) {
300     LOG(ERROR) << "Unable to launch Chrome.";
301     return std::string();
302   }
303
304   for (;;) {
305     DWORD wait_result = ::WaitForSingleObject(chrome_handle, 500);
306     std::string json = ReadAndUpdateServiceState(temp_user_data.path(),
307                                                  proxy_id);
308     if (wait_result == WAIT_OBJECT_0) {
309       // Return what we have because browser is closed.
310       return json;
311     }
312     if (wait_result != WAIT_TIMEOUT) {
313       LOG(ERROR) << "Chrome launch failed.";
314       return std::string();
315     }
316     if (!json.empty()) {
317       // Close chrome because Service State is ready.
318       CloseChrome(chrome_handle, thread_id);
319       return json;
320     }
321   }
322 }
323