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.
5 #include "chrome/test/chromedriver/util.h"
7 #include "base/base64.h"
8 #include "base/file_util.h"
9 #include "base/files/file_enumerator.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/format_macros.h"
12 #include "base/memory/scoped_vector.h"
13 #include "base/rand_util.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/third_party/icu/icu_utf.h"
18 #include "base/values.h"
19 #include "chrome/test/chromedriver/chrome/chrome.h"
20 #include "chrome/test/chromedriver/chrome/status.h"
21 #include "chrome/test/chromedriver/chrome/ui_events.h"
22 #include "chrome/test/chromedriver/chrome/version.h"
23 #include "chrome/test/chromedriver/chrome/web_view.h"
24 #include "chrome/test/chromedriver/command_listener.h"
25 #include "chrome/test/chromedriver/key_converter.h"
26 #include "chrome/test/chromedriver/session.h"
27 #include "third_party/zlib/google/zip.h"
29 std::string GenerateId() {
30 uint64 msb = base::RandUint64();
31 uint64 lsb = base::RandUint64();
32 return base::StringPrintf("%016" PRIx64 "%016" PRIx64, msb, lsb);
37 Status FlattenStringArray(const base::ListValue* src, base::string16* dest) {
39 for (size_t i = 0; i < src->GetSize(); ++i) {
40 base::string16 keys_list_part;
41 if (!src->GetString(i, &keys_list_part))
42 return Status(kUnknownError, "keys should be a string");
43 for (size_t j = 0; j < keys_list_part.size(); ++j) {
44 if (CBU16_IS_SURROGATE(keys_list_part[j])) {
45 return Status(kUnknownError,
46 "ChromeDriver only supports characters in the BMP");
49 keys.append(keys_list_part);
57 Status SendKeysOnWindow(
59 const base::ListValue* key_list,
60 bool release_modifiers,
61 int* sticky_modifiers) {
63 Status status = FlattenStringArray(key_list, &keys);
66 std::list<KeyEvent> events;
67 int sticky_modifiers_tmp = *sticky_modifiers;
68 status = ConvertKeysToKeyEvents(
69 keys, release_modifiers, &sticky_modifiers_tmp, &events);
72 status = web_view->DispatchKeyEvents(events);
74 *sticky_modifiers = sticky_modifiers_tmp;
78 bool Base64Decode(const std::string& base64,
80 std::string copy = base64;
81 // Some WebDriver client base64 encoders follow RFC 1521, which require that
82 // 'encoded lines be no more than 76 characters long'. Just remove any
84 base::RemoveChars(copy, "\n", ©);
85 return base::Base64Decode(copy, bytes);
90 Status UnzipArchive(const base::FilePath& unzip_dir,
91 const std::string& bytes) {
92 base::ScopedTempDir dir;
93 if (!dir.CreateUniqueTempDir())
94 return Status(kUnknownError, "unable to create temp dir");
96 base::FilePath archive = dir.path().AppendASCII("temp.zip");
97 int length = bytes.length();
98 if (base::WriteFile(archive, bytes.c_str(), length) != length)
99 return Status(kUnknownError, "could not write file to temp dir");
101 if (!zip::Unzip(archive, unzip_dir))
102 return Status(kUnknownError, "could not unzip archive");
106 // Stream for writing binary data.
107 class DataOutputStream {
109 DataOutputStream() {}
110 ~DataOutputStream() {}
112 void WriteUInt16(uint16 data) {
113 WriteBytes(&data, sizeof(data));
116 void WriteUInt32(uint32 data) {
117 WriteBytes(&data, sizeof(data));
120 void WriteString(const std::string& data) {
121 WriteBytes(data.c_str(), data.length());
124 void WriteBytes(const void* bytes, int size) {
127 size_t next = buffer_.length();
128 buffer_.resize(next + size);
129 memcpy(&buffer_[next], bytes, size);
132 const std::string& buffer() const { return buffer_; }
138 // Stream for reading binary data.
139 class DataInputStream {
141 DataInputStream(const char* data, int size)
142 : data_(data), size_(size), iter_(0) {}
143 ~DataInputStream() {}
145 bool ReadUInt16(uint16* data) {
146 return ReadBytes(data, sizeof(*data));
149 bool ReadUInt32(uint32* data) {
150 return ReadBytes(data, sizeof(*data));
153 bool ReadString(std::string* data, int length) {
156 // Check here to make sure we don't allocate wastefully.
157 if (iter_ + length > size_)
159 data->resize(length);
162 return ReadBytes(&(*data)[0], length);
165 bool ReadBytes(void* bytes, int size) {
166 if (iter_ + size > size_)
168 memcpy(bytes, &data_[iter_], size);
173 int remaining() const { return size_ - iter_; }
181 // A file entry within a zip archive. This may be incomplete and is not
182 // guaranteed to be able to parse all types of zip entries.
183 // See http://www.pkware.com/documents/casestudies/APPNOTE.TXT for the zip
186 // The given bytes must contain the whole zip entry and only the entry,
187 // although the entry may include a data descriptor.
188 static bool FromBytes(const std::string& bytes, ZipEntry* zip,
189 std::string* error_msg) {
190 DataInputStream stream(bytes.c_str(), bytes.length());
193 if (!stream.ReadUInt32(&signature) || signature != kFileHeaderSignature) {
194 *error_msg = "invalid file header signature";
197 if (!stream.ReadUInt16(&zip->version_needed)) {
198 *error_msg = "invalid version";
201 if (!stream.ReadUInt16(&zip->bit_flag)) {
202 *error_msg = "invalid bit flag";
205 if (!stream.ReadUInt16(&zip->compression_method)) {
206 *error_msg = "invalid compression method";
209 if (!stream.ReadUInt16(&zip->mod_time)) {
210 *error_msg = "invalid file last modified time";
213 if (!stream.ReadUInt16(&zip->mod_date)) {
214 *error_msg = "invalid file last modified date";
217 if (!stream.ReadUInt32(&zip->crc)) {
218 *error_msg = "invalid crc";
221 uint32 compressed_size;
222 if (!stream.ReadUInt32(&compressed_size)) {
223 *error_msg = "invalid compressed size";
226 if (!stream.ReadUInt32(&zip->uncompressed_size)) {
227 *error_msg = "invalid compressed size";
231 if (!stream.ReadUInt16(&name_length)) {
232 *error_msg = "invalid name length";
236 if (!stream.ReadUInt16(&field_length)) {
237 *error_msg = "invalid field length";
240 if (!stream.ReadString(&zip->name, name_length)) {
241 *error_msg = "invalid name";
244 if (!stream.ReadString(&zip->fields, field_length)) {
245 *error_msg = "invalid fields";
248 if (zip->bit_flag & 0x8) {
249 // Has compressed data and a separate data descriptor.
250 if (stream.remaining() < 16) {
251 *error_msg = "too small for data descriptor";
254 compressed_size = stream.remaining() - 16;
255 if (!stream.ReadString(&zip->compressed_data, compressed_size)) {
256 *error_msg = "invalid compressed data before descriptor";
259 if (!stream.ReadUInt32(&signature) ||
260 signature != kDataDescriptorSignature) {
261 *error_msg = "invalid data descriptor signature";
264 if (!stream.ReadUInt32(&zip->crc)) {
265 *error_msg = "invalid crc";
268 if (!stream.ReadUInt32(&compressed_size)) {
269 *error_msg = "invalid compressed size";
272 if (compressed_size != zip->compressed_data.length()) {
273 *error_msg = "compressed data does not match data descriptor";
276 if (!stream.ReadUInt32(&zip->uncompressed_size)) {
277 *error_msg = "invalid compressed size";
281 // Just has compressed data.
282 if (!stream.ReadString(&zip->compressed_data, compressed_size)) {
283 *error_msg = "invalid compressed data";
286 if (stream.remaining() != 0) {
287 *error_msg = "leftover data after zip entry";
294 // Returns bytes for a valid zip file that just contains this zip entry.
295 std::string ToZip() {
296 // Write zip entry with no data descriptor.
297 DataOutputStream stream;
298 stream.WriteUInt32(kFileHeaderSignature);
299 stream.WriteUInt16(version_needed);
300 stream.WriteUInt16(bit_flag);
301 stream.WriteUInt16(compression_method);
302 stream.WriteUInt16(mod_time);
303 stream.WriteUInt16(mod_date);
304 stream.WriteUInt32(crc);
305 stream.WriteUInt32(compressed_data.length());
306 stream.WriteUInt32(uncompressed_size);
307 stream.WriteUInt16(name.length());
308 stream.WriteUInt16(fields.length());
309 stream.WriteString(name);
310 stream.WriteString(fields);
311 stream.WriteString(compressed_data);
312 uint32 entry_size = stream.buffer().length();
314 // Write central directory.
315 stream.WriteUInt32(kCentralDirSignature);
316 stream.WriteUInt16(0x14); // Version made by. Unused at version 0.
317 stream.WriteUInt16(version_needed);
318 stream.WriteUInt16(bit_flag);
319 stream.WriteUInt16(compression_method);
320 stream.WriteUInt16(mod_time);
321 stream.WriteUInt16(mod_date);
322 stream.WriteUInt32(crc);
323 stream.WriteUInt32(compressed_data.length());
324 stream.WriteUInt32(uncompressed_size);
325 stream.WriteUInt16(name.length());
326 stream.WriteUInt16(fields.length());
327 stream.WriteUInt16(0); // Comment length.
328 stream.WriteUInt16(0); // Disk number where file starts.
329 stream.WriteUInt16(0); // Internal file attr.
330 stream.WriteUInt32(0); // External file attr.
331 stream.WriteUInt32(0); // Offset to file.
332 stream.WriteString(name);
333 stream.WriteString(fields);
334 uint32 cd_size = stream.buffer().length() - entry_size;
336 // End of central directory.
337 stream.WriteUInt32(kEndOfCentralDirSignature);
338 stream.WriteUInt16(0); // num of this disk
339 stream.WriteUInt16(0); // disk where cd starts
340 stream.WriteUInt16(1); // number of cds on this disk
341 stream.WriteUInt16(1); // total cds
342 stream.WriteUInt32(cd_size); // size of cd
343 stream.WriteUInt32(entry_size); // offset of cd
344 stream.WriteUInt16(0); // comment len
346 return stream.buffer();
349 static const uint32 kFileHeaderSignature;
350 static const uint32 kDataDescriptorSignature;
351 static const uint32 kCentralDirSignature;
352 static const uint32 kEndOfCentralDirSignature;
353 uint16 version_needed;
355 uint16 compression_method;
359 uint32 uncompressed_size;
362 std::string compressed_data;
365 const uint32 ZipEntry::kFileHeaderSignature = 0x04034b50;
366 const uint32 ZipEntry::kDataDescriptorSignature = 0x08074b50;
367 const uint32 ZipEntry::kCentralDirSignature = 0x02014b50;
368 const uint32 ZipEntry::kEndOfCentralDirSignature = 0x06054b50;
370 Status UnzipEntry(const base::FilePath& unzip_dir,
371 const std::string& bytes) {
373 std::string zip_error_msg;
374 if (!ZipEntry::FromBytes(bytes, &entry, &zip_error_msg))
375 return Status(kUnknownError, zip_error_msg);
376 std::string archive = entry.ToZip();
377 return UnzipArchive(unzip_dir, archive);
382 Status UnzipSoleFile(const base::FilePath& unzip_dir,
383 const std::string& bytes,
384 base::FilePath* file) {
385 std::string archive_error, entry_error;
386 Status status = UnzipArchive(unzip_dir, bytes);
387 if (status.IsError()) {
388 Status entry_status = UnzipEntry(unzip_dir, bytes);
389 if (entry_status.IsError()) {
390 return Status(kUnknownError, base::StringPrintf(
391 "archive error: (%s), entry error: (%s)",
392 status.message().c_str(), entry_status.message().c_str()));
396 base::FileEnumerator enumerator(unzip_dir, false /* recursive */,
397 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
398 base::FilePath first_file = enumerator.Next();
399 if (first_file.empty())
400 return Status(kUnknownError, "contained 0 files");
402 base::FilePath second_file = enumerator.Next();
403 if (!second_file.empty())
404 return Status(kUnknownError, "contained multiple files");
410 Status NotifyCommandListenersBeforeCommand(Session* session,
411 const std::string& command_name) {
412 for (ScopedVector<CommandListener>::const_iterator it =
413 session->command_listeners.begin();
414 it != session->command_listeners.end();
416 Status status = (*it)->BeforeCommand(command_name);
417 if (status.IsError()) {
418 // Do not continue if an error is encountered. Mark session for deletion,
419 // quit Chrome if necessary, and return a detailed error.
420 if (!session->quit) {
421 session->quit = true;
422 std::string message = base::StringPrintf("session deleted because "
423 "error encountered when notifying listeners of '%s' command",
424 command_name.c_str());
425 if (session->chrome && !session->detach) {
426 Status quit_status = session->chrome->Quit();
427 if (quit_status.IsError())
428 message += ", but failed to kill browser:" + quit_status.message();
430 status = Status(kUnknownError, message, status);
432 if (session->chrome) {
433 const BrowserInfo* browser_info = session->chrome->GetBrowserInfo();
434 status.AddDetails("Session info: " + browser_info->browser_name + "=" +
435 browser_info->browser_version);