cc: Added inline to Tile::IsReadyToDraw
[chromium-blink-merge.git] / cloud_print / service / win / chrome_launcher.cc
blob51501df82b7388556a8fd15b0f8593d0d3ac30e4
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.
5 #include "cloud_print/service/win/chrome_launcher.h"
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 "google_apis/gaia/gaia_urls.h"
27 #include "net/base/url_util.h"
28 #include "url/gurl.h"
30 namespace {
32 const int kShutdownTimeoutMs = 30 * 1000;
33 const int kUsageUpdateTimeoutMs = 6 * 3600 * 1000; // 6 hours.
35 static const char16 kAutoRunKeyPath[] =
36 L"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
38 // Terminates any process.
39 void ShutdownChrome(HANDLE process, DWORD thread_id) {
40 if (::PostThreadMessage(thread_id, WM_QUIT, 0, 0) &&
41 WAIT_OBJECT_0 == ::WaitForSingleObject(process, kShutdownTimeoutMs)) {
42 return;
44 LOG(ERROR) << "Failed to shutdown process.";
45 base::KillProcess(process, 0, true);
48 BOOL CALLBACK CloseIfPidEqual(HWND wnd, LPARAM lparam) {
49 DWORD pid = 0;
50 ::GetWindowThreadProcessId(wnd, &pid);
51 if (pid == static_cast<DWORD>(lparam))
52 ::PostMessage(wnd, WM_CLOSE, 0, 0);
53 return TRUE;
56 void CloseAllProcessWindows(HANDLE process) {
57 ::EnumWindows(&CloseIfPidEqual, GetProcessId(process));
60 // Close Chrome browser window.
61 void CloseChrome(HANDLE process, DWORD thread_id) {
62 CloseAllProcessWindows(process);
63 if (WAIT_OBJECT_0 == ::WaitForSingleObject(process, kShutdownTimeoutMs)) {
64 return;
66 ShutdownChrome(process, thread_id);
69 bool LaunchProcess(const CommandLine& cmdline,
70 base::ProcessHandle* process_handle,
71 DWORD* thread_id) {
72 STARTUPINFO startup_info = {};
73 startup_info.cb = sizeof(startup_info);
74 startup_info.dwFlags = STARTF_USESHOWWINDOW;
75 startup_info.wShowWindow = SW_SHOW;
77 base::win::ScopedProcessInformation process_info;
78 if (!CreateProcess(NULL,
79 const_cast<wchar_t*>(cmdline.GetCommandLineString().c_str()), NULL, NULL,
80 FALSE, 0, NULL, NULL, &startup_info, process_info.Receive())) {
81 return false;
84 if (process_handle)
85 *process_handle = process_info.TakeProcessHandle();
87 if (thread_id)
88 *thread_id = process_info.thread_id();
90 return true;
93 GURL GetCloudPrintServiceEnableURL(const std::string& proxy_id) {
94 GURL url(
95 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
96 switches::kCloudPrintServiceURL));
97 if (url.is_empty())
98 url = GURL("https://www.google.com/cloudprint");
99 url = net::AppendQueryParameter(url, "proxy", proxy_id);
100 std::string url_path(url.path() + "/enable_chrome_connector/enable.html");
101 GURL::Replacements replacements;
102 replacements.SetPathStr(url_path);
103 return url.ReplaceComponents(replacements);
106 GURL GetCloudPrintServiceEnableURLWithSignin(const std::string& proxy_id) {
107 GURL url(GaiaUrls::GetInstance()->service_login_url());
108 url = net::AppendQueryParameter(url, "service", "cloudprint");
109 url = net::AppendQueryParameter(url, "sarp", "1");
110 return net::AppendQueryParameter(
111 url, "continue", GetCloudPrintServiceEnableURL(proxy_id).spec());
114 std::string ReadAndUpdateServiceState(const base::FilePath& directory,
115 const std::string& proxy_id) {
116 std::string json;
117 base::FilePath file_path = directory.Append(chrome::kServiceStateFileName);
118 if (!base::ReadFileToString(file_path, &json)) {
119 return std::string();
122 scoped_ptr<base::Value> service_state(base::JSONReader::Read(json));
123 base::DictionaryValue* dictionary = NULL;
124 if (!service_state->GetAsDictionary(&dictionary) || !dictionary) {
125 return std::string();
128 bool enabled = false;
129 if (!dictionary->GetBoolean(prefs::kCloudPrintProxyEnabled, &enabled) ||
130 !enabled) {
131 return std::string();
134 std::string refresh_token;
135 if (!dictionary->GetString(prefs::kCloudPrintRobotRefreshToken,
136 &refresh_token) ||
137 refresh_token.empty()) {
138 return std::string();
141 // Remove everything except kCloudPrintRoot.
142 scoped_ptr<base::Value> cloud_print_root;
143 dictionary->Remove(prefs::kCloudPrintRoot, &cloud_print_root);
144 dictionary->Clear();
145 dictionary->Set(prefs::kCloudPrintRoot, cloud_print_root.release());
147 dictionary->SetBoolean(prefs::kCloudPrintXmppPingEnabled, true);
148 if (!proxy_id.empty()) // Reuse proxy id if we already had one.
149 dictionary->SetString(prefs::kCloudPrintProxyId, proxy_id);
150 std::string result;
151 base::JSONWriter::WriteWithOptions(dictionary,
152 base::JSONWriter::OPTIONS_PRETTY_PRINT,
153 &result);
154 return result;
157 void DeleteAutorunKeys(const base::FilePath& user_data_dir) {
158 base::win::RegKey key(HKEY_CURRENT_USER, kAutoRunKeyPath, KEY_SET_VALUE);
159 if (!key.Valid())
160 return;
161 std::vector<string16> to_delete;
163 base::FilePath abs_user_data_dir = base::MakeAbsoluteFilePath(user_data_dir);
166 base::win::RegistryValueIterator value(HKEY_CURRENT_USER, kAutoRunKeyPath);
167 for (; value.Valid(); ++value) {
168 if (value.Type() == REG_SZ && value.Value()) {
169 CommandLine cmd = CommandLine::FromString(value.Value());
170 if (cmd.GetSwitchValueASCII(switches::kProcessType) ==
171 switches::kServiceProcess &&
172 cmd.HasSwitch(switches::kUserDataDir)) {
173 base::FilePath path_from_reg = base::MakeAbsoluteFilePath(
174 cmd.GetSwitchValuePath(switches::kUserDataDir));
175 if (path_from_reg == abs_user_data_dir) {
176 to_delete.push_back(value.Name());
183 for (size_t i = 0; i < to_delete.size(); ++i) {
184 key.DeleteValue(to_delete[i].c_str());
188 } // namespace
190 ChromeLauncher::ChromeLauncher(const base::FilePath& user_data)
191 : stop_event_(true, true),
192 user_data_(user_data) {
195 ChromeLauncher::~ChromeLauncher() {
198 bool ChromeLauncher::Start() {
199 DeleteAutorunKeys(user_data_);
200 stop_event_.Reset();
201 thread_.reset(new base::DelegateSimpleThread(this, "chrome_launcher"));
202 thread_->Start();
203 return true;
206 void ChromeLauncher::Stop() {
207 stop_event_.Signal();
208 thread_->Join();
209 thread_.reset();
212 void ChromeLauncher::Run() {
213 const base::TimeDelta default_time_out = base::TimeDelta::FromSeconds(1);
214 const base::TimeDelta max_time_out = base::TimeDelta::FromHours(1);
216 for (base::TimeDelta time_out = default_time_out;;
217 time_out = std::min(time_out * 2, max_time_out)) {
218 base::FilePath chrome_path = chrome_launcher_support::GetAnyChromePath();
220 if (!chrome_path.empty()) {
221 CommandLine cmd(chrome_path);
222 CopyChromeSwitchesFromCurrentProcess(&cmd);
224 // Required switches.
225 cmd.AppendSwitchASCII(switches::kProcessType, switches::kServiceProcess);
226 cmd.AppendSwitchPath(switches::kUserDataDir, user_data_);
227 cmd.AppendSwitch(switches::kNoServiceAutorun);
229 // Optional.
230 cmd.AppendSwitch(switches::kAutoLaunchAtStartup);
231 cmd.AppendSwitch(switches::kDisableBackgroundMode);
232 cmd.AppendSwitch(switches::kDisableDefaultApps);
233 cmd.AppendSwitch(switches::kDisableExtensions);
234 cmd.AppendSwitch(switches::kDisableGpu);
235 cmd.AppendSwitch(switches::kDisableSoftwareRasterizer);
236 cmd.AppendSwitch(switches::kDisableSync);
237 cmd.AppendSwitch(switches::kNoFirstRun);
238 cmd.AppendSwitch(switches::kNoStartupWindow);
240 base::win::ScopedHandle chrome_handle;
241 base::Time started = base::Time::Now();
242 DWORD thread_id = 0;
243 LaunchProcess(cmd, chrome_handle.Receive(), &thread_id);
245 HANDLE handles[] = {stop_event_.handle(), chrome_handle};
246 DWORD wait_result = WAIT_TIMEOUT;
247 while (wait_result == WAIT_TIMEOUT) {
248 cloud_print::SetGoogleUpdateUsage(kGoogleUpdateId);
249 wait_result = ::WaitForMultipleObjects(arraysize(handles), handles,
250 FALSE, kUsageUpdateTimeoutMs);
252 if (wait_result == WAIT_OBJECT_0) {
253 ShutdownChrome(chrome_handle, thread_id);
254 break;
255 } else if (wait_result == WAIT_OBJECT_0 + 1) {
256 LOG(ERROR) << "Chrome process exited.";
257 } else {
258 LOG(ERROR) << "Error waiting Chrome (" << ::GetLastError() << ").";
260 if (base::Time::Now() - started > base::TimeDelta::FromHours(1)) {
261 // Reset timeout because process worked long enough.
262 time_out = default_time_out;
265 if (stop_event_.TimedWait(time_out))
266 break;
270 std::string ChromeLauncher::CreateServiceStateFile(
271 const std::string& proxy_id,
272 const std::vector<std::string>& printers) {
273 std::string result;
275 base::ScopedTempDir temp_user_data;
276 if (!temp_user_data.CreateUniqueTempDir()) {
277 LOG(ERROR) << "Can't create temp dir.";
278 return result;
281 base::FilePath chrome_path = chrome_launcher_support::GetAnyChromePath();
283 if (chrome_path.empty()) {
284 LOG(ERROR) << "Can't find Chrome.";
285 return result;
288 base::FilePath printers_file = temp_user_data.path().Append(L"printers.json");
290 base::ListValue printer_list;
291 printer_list.AppendStrings(printers);
292 std::string printers_json;
293 base::JSONWriter::Write(&printer_list, &printers_json);
294 size_t written = file_util::WriteFile(printers_file,
295 printers_json.c_str(),
296 printers_json.size());
297 if (written != printers_json.size()) {
298 LOG(ERROR) << "Can't write file.";
299 return result;
302 CommandLine cmd(chrome_path);
303 CopyChromeSwitchesFromCurrentProcess(&cmd);
304 cmd.AppendSwitchPath(switches::kUserDataDir, temp_user_data.path());
305 cmd.AppendSwitchPath(switches::kCloudPrintSetupProxy, printers_file);
306 cmd.AppendSwitch(switches::kNoServiceAutorun);
308 // Optional.
309 cmd.AppendSwitch(switches::kDisableBackgroundMode);
310 cmd.AppendSwitch(switches::kDisableDefaultApps);
311 cmd.AppendSwitch(switches::kDisableExtensions);
312 cmd.AppendSwitch(switches::kDisableSync);
313 cmd.AppendSwitch(switches::kNoDefaultBrowserCheck);
314 cmd.AppendSwitch(switches::kNoFirstRun);
316 cmd.AppendArg(GetCloudPrintServiceEnableURLWithSignin(proxy_id).spec());
318 base::win::ScopedHandle chrome_handle;
319 DWORD thread_id = 0;
320 if (!LaunchProcess(cmd, chrome_handle.Receive(), &thread_id)) {
321 LOG(ERROR) << "Unable to launch Chrome.";
322 return result;
325 for (;;) {
326 DWORD wait_result = ::WaitForSingleObject(chrome_handle, 500);
327 std::string json = ReadAndUpdateServiceState(temp_user_data.path(),
328 proxy_id);
329 if (wait_result == WAIT_OBJECT_0) {
330 // Return what we have because browser is closed.
331 return json;
332 } else if (wait_result == WAIT_TIMEOUT) {
333 if (!json.empty()) {
334 // Close chrome because Service State is ready.
335 CloseChrome(chrome_handle, thread_id);
336 return json;
338 } else {
339 LOG(ERROR) << "Chrome launch failed.";
340 return result;
343 NOTREACHED();
344 return std::string();