Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(380)

Side by Side Diff: chrome/test/chromedriver/chrome_desktop_impl.cc

Issue 12848005: [chromedriver] Separate stuff of chrome from chromedriver. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address comments and fix compile error on mac. Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013 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/test/chromedriver/chrome_desktop_impl.h"
6
7 #include "base/base64.h"
8 #include "base/command_line.h"
9 #include "base/file_util.h"
10 #include "base/files/file_path.h"
11 #include "base/format_macros.h"
12 #include "base/json/json_reader.h"
13 #include "base/json/json_writer.h"
14 #include "base/process.h"
15 #include "base/process_util.h"
16 #include "base/string_number_conversions.h"
17 #include "base/string_util.h"
18 #include "base/stringprintf.h"
19 #include "base/sys_info.h"
20 #include "base/utf_string_conversions.h"
21 #include "base/values.h"
22 #include "chrome/common/zip.h"
23 #include "chrome/test/chromedriver/chrome_finder.h"
24 #include "chrome/test/chromedriver/net/sync_websocket_impl.h"
25 #include "chrome/test/chromedriver/net/url_request_context_getter.h"
26 #include "chrome/test/chromedriver/status.h"
27 #include "chrome/test/chromedriver/user_data_dir.h"
28
29 ChromeDesktopImpl::ChromeDesktopImpl(
30 URLRequestContextGetter* context_getter,
31 int port,
32 const SyncWebSocketFactory& socket_factory)
33 : ChromeImpl(context_getter, port, socket_factory) {}
34
35 ChromeDesktopImpl::~ChromeDesktopImpl() {
36 base::CloseProcessHandle(process_);
37 }
38
39 Status ChromeDesktopImpl::Launch(const base::FilePath& exe,
40 const base::ListValue* args,
41 const base::ListValue* extensions,
42 const base::DictionaryValue* prefs,
43 const base::DictionaryValue* local_state) {
44 base::FilePath program = exe;
45 if (program.empty()) {
46 if (!FindChrome(&program))
47 return Status(kUnknownError, "cannot find Chrome binary");
48 }
49 LOG(INFO) << "Using chrome from " << program.value();
50
51 CommandLine command(program);
52 command.AppendSwitchASCII("remote-debugging-port",
53 base::IntToString(GetPort()));
54 command.AppendSwitch("no-first-run");
55 command.AppendSwitch("enable-logging");
56 command.AppendSwitchASCII("logging-level", "1");
57 command.AppendArg("data:text/html;charset=utf-8,");
58
59 if (args) {
60 Status status = internal::ProcessCommandLineArgs(args, &command);
61 if (status.IsError())
62 return status;
63 }
64
65 if (!command.HasSwitch("user-data-dir")) {
66 if (!user_data_dir_.CreateUniqueTempDir())
67 return Status(kUnknownError, "cannot create temp dir for user data dir");
68 command.AppendSwitchPath("user-data-dir", user_data_dir_.path());
69 Status status = internal::PrepareUserDataDir(
70 user_data_dir_.path(), prefs, local_state);
71 if (status.IsError())
72 return status;
73 }
74
75 if (extensions) {
76 if (!extension_dir_.CreateUniqueTempDir())
77 return Status(kUnknownError,
78 "cannot create temp dir for unpacking extensions");
79 Status status = internal::ProcessExtensions(
80 extensions, extension_dir_.path(), &command);
81 if (status.IsError())
82 return status;
83 }
84
85 base::LaunchOptions options;
86 if (!base::LaunchProcess(command, options, &process_))
87 return Status(kUnknownError, "chrome failed to start");
88
89 Status status = Init();
90 if (status.IsError()) {
91 Quit();
92 return status;
93 }
94 return Status(kOk);
95 }
96
97 std::string ChromeDesktopImpl::GetOperatingSystemName() {
98 return base::SysInfo::OperatingSystemName();
99 }
100
101 Status ChromeDesktopImpl::Quit() {
102 if (!base::KillProcess(process_, 0, true)) {
103 int exit_code;
104 if (base::GetTerminationStatus(process_, &exit_code) ==
105 base::TERMINATION_STATUS_STILL_RUNNING)
106 return Status(kUnknownError, "cannot kill Chrome");
107 }
108 return Status(kOk);
109 }
110
111 namespace internal {
112
113 Status ProcessCommandLineArgs(const base::ListValue* args,
114 CommandLine* command) {
115 for (size_t i = 0; i < args->GetSize(); ++i) {
116 std::string arg_string;
117 if (!args->GetString(i, &arg_string))
118 return Status(kUnknownError, "invalid chrome command line argument");
119 size_t separator_index = arg_string.find("=");
120 if (separator_index != std::string::npos) {
121 CommandLine::StringType arg_string_native;
122 if (!args->GetString(i, &arg_string_native))
123 return Status(kUnknownError, "invalid chrome command line argument");
124 command->AppendSwitchNative(
125 arg_string.substr(0, separator_index),
126 arg_string_native.substr(separator_index + 1));
127 } else {
128 command->AppendSwitch(arg_string);
129 }
130 }
131 return Status(kOk);
132 }
133
134 Status ProcessExtensions(const base::ListValue* extensions,
135 const base::FilePath& temp_dir,
136 CommandLine* command) {
137 std::vector<base::FilePath::StringType> extension_paths;
138 for (size_t i = 0; i < extensions->GetSize(); ++i) {
139 std::string extension_base64;
140 if (!extensions->GetString(i, &extension_base64)) {
141 return Status(kUnknownError,
142 "each extension must be a base64 encoded string");
143 }
144
145 // Decodes extension string.
146 // Some WebDriver client base64 encoders follow RFC 1521, which require that
147 // 'encoded lines be no more than 76 characters long'. Just remove any
148 // newlines.
149 RemoveChars(extension_base64, "\n", &extension_base64);
150 std::string decoded_extension;
151 if (!base::Base64Decode(extension_base64, &decoded_extension))
152 return Status(kUnknownError, "failed to base64 decode extension");
153
154 // Writes decoded extension into a temporary .crx file.
155 base::ScopedTempDir temp_crx_dir;
156 if (!temp_crx_dir.CreateUniqueTempDir())
157 return Status(kUnknownError,
158 "cannot create temp dir for writing extension CRX file");
159 base::FilePath extension_crx = temp_crx_dir.path().AppendASCII("temp.crx");
160 int size = static_cast<int>(decoded_extension.length());
161 if (file_util::WriteFile(extension_crx, decoded_extension.c_str(), size)
162 != size)
163 return Status(kUnknownError, "failed to write extension file");
164
165 // Unzips the temporary .crx file.
166 base::FilePath extension_dir = temp_dir.AppendASCII(
167 base::StringPrintf("extension%" PRIuS, i));
168 if (!zip::Unzip(extension_crx, extension_dir))
169 return Status(kUnknownError, "failed to unzip the extension CRX file");
170 extension_paths.push_back(extension_dir.value());
171 }
172
173 // Sets paths of unpacked extensions to the command line.
174 if (!extension_paths.empty()) {
175 base::FilePath::StringType extension_paths_value = JoinString(
176 extension_paths, FILE_PATH_LITERAL(','));
177 command->AppendSwitchNative("load-extension", extension_paths_value);
178 }
179
180 return Status(kOk);
181 }
182
183 Status WritePrefsFile(
184 const std::string& template_string,
185 const base::DictionaryValue* custom_prefs,
186 const base::FilePath& path) {
187 int code;
188 std::string error_msg;
189 scoped_ptr<base::Value> template_value(base::JSONReader::ReadAndReturnError(
190 template_string, 0, &code, &error_msg));
191 base::DictionaryValue* prefs;
192 if (!template_value || !template_value->GetAsDictionary(&prefs)) {
193 return Status(kUnknownError,
194 "cannot parse internal JSON template: " + error_msg);
195 }
196
197 if (custom_prefs)
198 prefs->MergeDictionary(custom_prefs);
199
200 std::string prefs_str;
201 base::JSONWriter::Write(prefs, &prefs_str);
202 if (static_cast<int>(prefs_str.length()) != file_util::WriteFile(
203 path, prefs_str.c_str(), prefs_str.length())) {
204 return Status(kUnknownError, "failed to write prefs file");
205 }
206 return Status(kOk);
207 }
208
209 Status PrepareUserDataDir(
210 const base::FilePath& user_data_dir,
211 const base::DictionaryValue* custom_prefs,
212 const base::DictionaryValue* custom_local_state) {
213 base::FilePath default_dir = user_data_dir.AppendASCII("Default");
214 if (!file_util::CreateDirectory(default_dir))
215 return Status(kUnknownError, "cannot create default profile directory");
216
217 Status status = WritePrefsFile(
218 kPreferences,
219 custom_prefs,
220 default_dir.AppendASCII("Preferences"));
221 if (status.IsError())
222 return status;
223
224 status = WritePrefsFile(
225 kLocalState,
226 custom_local_state,
227 user_data_dir.AppendASCII("Local State"));
228 if (status.IsError())
229 return status;
230
231 // Write empty "First Run" file, otherwise Chrome will wipe the default
232 // profile that was written.
233 if (file_util::WriteFile(
234 user_data_dir.AppendASCII("First Run"), "", 0) != 0) {
235 return Status(kUnknownError, "failed to write first run file");
236 }
237 return Status(kOk);
238 }
239
240 } // namespace internal
OLDNEW
« no previous file with comments | « chrome/test/chromedriver/chrome_desktop_impl.h ('k') | chrome/test/chromedriver/chrome_desktop_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698