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/util.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include "base/base64.h"
11 #include "base/files/file_enumerator.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/format_macros.h"
15 #include "base/rand_util.h"
16 #include "base/strings/string16.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/third_party/icu/icu_utf.h"
20 #include "base/values.h"
21 #include "chrome/test/chromedriver/chrome/browser_info.h"
22 #include "chrome/test/chromedriver/chrome/chrome.h"
23 #include "chrome/test/chromedriver/chrome/status.h"
24 #include "chrome/test/chromedriver/chrome/ui_events.h"
25 #include "chrome/test/chromedriver/chrome/web_view.h"
26 #include "chrome/test/chromedriver/command_listener.h"
27 #include "chrome/test/chromedriver/constants/version.h"
28 #include "chrome/test/chromedriver/key_converter.h"
29 #include "chrome/test/chromedriver/session.h"
30 #include "third_party/zlib/google/zip.h"
31
32 const char kWindowHandlePrefix[] = "CDwindow-";
33
GenerateId()34 std::string GenerateId() {
35 uint64_t msb = base::RandUint64();
36 uint64_t lsb = base::RandUint64();
37 return base::StringPrintf("%016" PRIx64 "%016" PRIx64, msb, lsb);
38 }
39
40 namespace {
41 const double kCentimetersPerInch = 2.54;
42
FlattenStringArray(const base::ListValue * src,base::string16 * dest)43 Status FlattenStringArray(const base::ListValue* src, base::string16* dest) {
44 base::string16 keys;
45 for (size_t i = 0; i < src->GetSize(); ++i) {
46 base::string16 keys_list_part;
47 if (!src->GetString(i, &keys_list_part))
48 return Status(kUnknownError, "keys should be a string");
49 for (size_t j = 0; j < keys_list_part.size(); ++j) {
50 if (CBU16_IS_SURROGATE(keys_list_part[j])) {
51 return Status(
52 kUnknownError,
53 base::StringPrintf("%s only supports characters in the BMP",
54 kChromeDriverProductShortName));
55 }
56 }
57 keys.append(keys_list_part);
58 }
59 *dest = keys;
60 return Status(kOk);
61 }
62
63 } // namespace
64
SendKeysOnWindow(WebView * web_view,const base::ListValue * key_list,bool release_modifiers,int * sticky_modifiers)65 Status SendKeysOnWindow(
66 WebView* web_view,
67 const base::ListValue* key_list,
68 bool release_modifiers,
69 int* sticky_modifiers) {
70 base::string16 keys;
71 Status status = FlattenStringArray(key_list, &keys);
72 if (status.IsError())
73 return status;
74 std::vector<KeyEvent> events;
75 int sticky_modifiers_tmp = *sticky_modifiers;
76 status = ConvertKeysToKeyEvents(
77 keys, release_modifiers, &sticky_modifiers_tmp, &events);
78 if (status.IsError())
79 return status;
80 status = web_view->DispatchKeyEvents(events, false);
81 if (status.IsOk())
82 *sticky_modifiers = sticky_modifiers_tmp;
83 return status;
84 }
85
Base64Decode(const std::string & base64,std::string * bytes)86 bool Base64Decode(const std::string& base64,
87 std::string* bytes) {
88 std::string copy = base64;
89 // Some WebDriver client base64 encoders follow RFC 1521, which require that
90 // 'encoded lines be no more than 76 characters long'. Just remove any
91 // newlines.
92 base::RemoveChars(copy, "\n", ©);
93 return base::Base64Decode(copy, bytes);
94 }
95
96 namespace {
97
UnzipArchive(const base::FilePath & unzip_dir,const std::string & bytes)98 Status UnzipArchive(const base::FilePath& unzip_dir,
99 const std::string& bytes) {
100 base::ScopedTempDir dir;
101 if (!dir.CreateUniqueTempDir())
102 return Status(kUnknownError, "unable to create temp dir");
103
104 base::FilePath archive = dir.GetPath().AppendASCII("temp.zip");
105 int length = bytes.length();
106 if (base::WriteFile(archive, bytes.c_str(), length) != length)
107 return Status(kUnknownError, "could not write file to temp dir");
108
109 if (!zip::Unzip(archive, unzip_dir))
110 return Status(kUnknownError, "could not unzip archive");
111 return Status(kOk);
112 }
113
114 // Stream for writing binary data.
115 class DataOutputStream {
116 public:
DataOutputStream()117 DataOutputStream() {}
~DataOutputStream()118 ~DataOutputStream() {}
119
WriteUInt16(uint16_t data)120 void WriteUInt16(uint16_t data) { WriteBytes(&data, sizeof(data)); }
121
WriteUInt32(uint32_t data)122 void WriteUInt32(uint32_t data) { WriteBytes(&data, sizeof(data)); }
123
WriteString(const std::string & data)124 void WriteString(const std::string& data) {
125 WriteBytes(data.c_str(), data.length());
126 }
127
WriteBytes(const void * bytes,int size)128 void WriteBytes(const void* bytes, int size) {
129 if (!size)
130 return;
131 size_t next = buffer_.length();
132 buffer_.resize(next + size);
133 memcpy(&buffer_[next], bytes, size);
134 }
135
buffer() const136 const std::string& buffer() const { return buffer_; }
137
138 private:
139 std::string buffer_;
140 };
141
142 // Stream for reading binary data.
143 class DataInputStream {
144 public:
DataInputStream(const char * data,int size)145 DataInputStream(const char* data, int size)
146 : data_(data), size_(size), iter_(0) {}
~DataInputStream()147 ~DataInputStream() {}
148
ReadUInt16(uint16_t * data)149 bool ReadUInt16(uint16_t* data) { return ReadBytes(data, sizeof(*data)); }
150
ReadUInt32(uint32_t * data)151 bool ReadUInt32(uint32_t* data) { return ReadBytes(data, sizeof(*data)); }
152
ReadString(std::string * data,int length)153 bool ReadString(std::string* data, int length) {
154 if (length < 0)
155 return false;
156 // Check here to make sure we don't allocate wastefully.
157 if (iter_ + length > size_)
158 return false;
159 data->resize(length);
160 if (length == 0)
161 return true;
162 return ReadBytes(&(*data)[0], length);
163 }
164
ReadBytes(void * bytes,int size)165 bool ReadBytes(void* bytes, int size) {
166 if (iter_ + size > size_)
167 return false;
168 memcpy(bytes, &data_[iter_], size);
169 iter_ += size;
170 return true;
171 }
172
remaining() const173 int remaining() const { return size_ - iter_; }
174
175 private:
176 const char* data_;
177 int size_;
178 int iter_;
179 };
180
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
184 // file format.
185 struct ZipEntry {
186 // The given bytes must contain the whole zip entry and only the entry,
187 // although the entry may include a data descriptor.
FromBytes__anonafd7ad0c0211::ZipEntry188 static bool FromBytes(const std::string& bytes, ZipEntry* zip,
189 std::string* error_msg) {
190 DataInputStream stream(bytes.c_str(), bytes.length());
191
192 uint32_t signature;
193 if (!stream.ReadUInt32(&signature) || signature != kFileHeaderSignature) {
194 *error_msg = "invalid file header signature";
195 return false;
196 }
197 if (!stream.ReadUInt16(&zip->version_needed)) {
198 *error_msg = "invalid version";
199 return false;
200 }
201 if (!stream.ReadUInt16(&zip->bit_flag)) {
202 *error_msg = "invalid bit flag";
203 return false;
204 }
205 if (!stream.ReadUInt16(&zip->compression_method)) {
206 *error_msg = "invalid compression method";
207 return false;
208 }
209 if (!stream.ReadUInt16(&zip->mod_time)) {
210 *error_msg = "invalid file last modified time";
211 return false;
212 }
213 if (!stream.ReadUInt16(&zip->mod_date)) {
214 *error_msg = "invalid file last modified date";
215 return false;
216 }
217 if (!stream.ReadUInt32(&zip->crc)) {
218 *error_msg = "invalid crc";
219 return false;
220 }
221 uint32_t compressed_size;
222 if (!stream.ReadUInt32(&compressed_size)) {
223 *error_msg = "invalid compressed size";
224 return false;
225 }
226 if (!stream.ReadUInt32(&zip->uncompressed_size)) {
227 *error_msg = "invalid compressed size";
228 return false;
229 }
230 uint16_t name_length;
231 if (!stream.ReadUInt16(&name_length)) {
232 *error_msg = "invalid name length";
233 return false;
234 }
235 uint16_t field_length;
236 if (!stream.ReadUInt16(&field_length)) {
237 *error_msg = "invalid field length";
238 return false;
239 }
240 if (!stream.ReadString(&zip->name, name_length)) {
241 *error_msg = "invalid name";
242 return false;
243 }
244 if (!stream.ReadString(&zip->fields, field_length)) {
245 *error_msg = "invalid fields";
246 return false;
247 }
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";
252 return false;
253 }
254 compressed_size = stream.remaining() - 16;
255 if (!stream.ReadString(&zip->compressed_data, compressed_size)) {
256 *error_msg = "invalid compressed data before descriptor";
257 return false;
258 }
259 if (!stream.ReadUInt32(&signature) ||
260 signature != kDataDescriptorSignature) {
261 *error_msg = "invalid data descriptor signature";
262 return false;
263 }
264 if (!stream.ReadUInt32(&zip->crc)) {
265 *error_msg = "invalid crc";
266 return false;
267 }
268 if (!stream.ReadUInt32(&compressed_size)) {
269 *error_msg = "invalid compressed size";
270 return false;
271 }
272 if (compressed_size != zip->compressed_data.length()) {
273 *error_msg = "compressed data does not match data descriptor";
274 return false;
275 }
276 if (!stream.ReadUInt32(&zip->uncompressed_size)) {
277 *error_msg = "invalid compressed size";
278 return false;
279 }
280 } else {
281 // Just has compressed data.
282 if (!stream.ReadString(&zip->compressed_data, compressed_size)) {
283 *error_msg = "invalid compressed data";
284 return false;
285 }
286 if (stream.remaining() != 0) {
287 *error_msg = "leftover data after zip entry";
288 return false;
289 }
290 }
291 return true;
292 }
293
294 // Returns bytes for a valid zip file that just contains this zip entry.
ToZip__anonafd7ad0c0211::ZipEntry295 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_t entry_size = stream.buffer().length();
313
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_t cd_size = stream.buffer().length() - entry_size;
335
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
345
346 return stream.buffer();
347 }
348
349 static const uint32_t kFileHeaderSignature;
350 static const uint32_t kDataDescriptorSignature;
351 static const uint32_t kCentralDirSignature;
352 static const uint32_t kEndOfCentralDirSignature;
353 uint16_t version_needed;
354 uint16_t bit_flag;
355 uint16_t compression_method;
356 uint16_t mod_time;
357 uint16_t mod_date;
358 uint32_t crc;
359 uint32_t uncompressed_size;
360 std::string name;
361 std::string fields;
362 std::string compressed_data;
363 };
364
365 const uint32_t ZipEntry::kFileHeaderSignature = 0x04034b50;
366 const uint32_t ZipEntry::kDataDescriptorSignature = 0x08074b50;
367 const uint32_t ZipEntry::kCentralDirSignature = 0x02014b50;
368 const uint32_t ZipEntry::kEndOfCentralDirSignature = 0x06054b50;
369
UnzipEntry(const base::FilePath & unzip_dir,const std::string & bytes)370 Status UnzipEntry(const base::FilePath& unzip_dir,
371 const std::string& bytes) {
372 ZipEntry entry;
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);
378 }
379
380 } // namespace
381
UnzipSoleFile(const base::FilePath & unzip_dir,const std::string & bytes,base::FilePath * file)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()));
393 }
394 }
395
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");
401
402 base::FilePath second_file = enumerator.Next();
403 if (!second_file.empty())
404 return Status(kUnknownError, "contained multiple files");
405
406 *file = first_file;
407 return Status(kOk);
408 }
409
NotifyCommandListenersBeforeCommand(Session * session,const std::string & command_name)410 Status NotifyCommandListenersBeforeCommand(Session* session,
411 const std::string& command_name) {
412 for (const auto& listener : session->command_listeners) {
413 Status status = listener->BeforeCommand(command_name);
414 if (status.IsError()) {
415 // Do not continue if an error is encountered. Mark session for deletion,
416 // quit Chrome if necessary, and return a detailed error.
417 if (!session->quit) {
418 session->quit = true;
419 std::string message = base::StringPrintf("session deleted because "
420 "error encountered when notifying listeners of '%s' command",
421 command_name.c_str());
422 if (session->chrome && !session->detach) {
423 Status quit_status = session->chrome->Quit();
424 if (quit_status.IsError())
425 message += ", but failed to kill browser:" + quit_status.message();
426 }
427 status = Status(kUnknownError, message, status);
428 }
429 if (session->chrome) {
430 const BrowserInfo* browser_info = session->chrome->GetBrowserInfo();
431 status.AddDetails("Session info: " + browser_info->browser_name + "=" +
432 browser_info->browser_version);
433 }
434 return status;
435 }
436 }
437 return Status(kOk);
438 }
439
ConvertCentimeterToInch(double centimeter)440 double ConvertCentimeterToInch(double centimeter) {
441 return centimeter / kCentimetersPerInch;
442 }
443
444 namespace {
445
446 template <typename T>
GetOptionalValue(const base::DictionaryValue * dict,base::StringPiece path,T * out_value,bool * has_value,bool (base::Value::* getter)(T *)const)447 bool GetOptionalValue(const base::DictionaryValue* dict,
448 base::StringPiece path,
449 T* out_value,
450 bool* has_value,
451 bool (base::Value::*getter)(T*) const) {
452 if (has_value != nullptr)
453 *has_value = false;
454 const base::Value* value;
455 if (!dict->Get(path, &value))
456 return true;
457 if ((value->*getter)(out_value)) {
458 if (has_value != nullptr)
459 *has_value = true;
460 return true;
461 }
462 return false;
463 }
464
465 } // namespace
466
GetOptionalBool(const base::DictionaryValue * dict,base::StringPiece path,bool * out_value,bool * has_value)467 bool GetOptionalBool(const base::DictionaryValue* dict,
468 base::StringPiece path,
469 bool* out_value,
470 bool* has_value) {
471 return GetOptionalValue(dict, path, out_value, has_value,
472 &base::Value::GetAsBoolean);
473 }
474
GetOptionalInt(const base::DictionaryValue * dict,base::StringPiece path,int * out_value,bool * has_value)475 bool GetOptionalInt(const base::DictionaryValue* dict,
476 base::StringPiece path,
477 int* out_value,
478 bool* has_value) {
479 if (GetOptionalValue(dict, path, out_value, has_value,
480 &base::Value::GetAsInteger)) {
481 return true;
482 }
483 // See if we have a double that contains an int value.
484 double d;
485 if (!dict->GetDouble(path, &d))
486 return false;
487 int i = static_cast<int>(d);
488 if (i == d) {
489 *out_value = i;
490 if (has_value != nullptr)
491 *has_value = true;
492 return true;
493 }
494 return false;
495 }
496
GetOptionalDouble(const base::DictionaryValue * dict,base::StringPiece path,double * out_value,bool * has_value)497 bool GetOptionalDouble(const base::DictionaryValue* dict,
498 base::StringPiece path,
499 double* out_value,
500 bool* has_value) {
501 // base::Value::GetAsDouble already converts int to double if needed.
502 return GetOptionalValue(dict, path, out_value, has_value,
503 &base::Value::GetAsDouble);
504 }
505
GetOptionalString(const base::DictionaryValue * dict,base::StringPiece path,std::string * out_value,bool * has_value)506 bool GetOptionalString(const base::DictionaryValue* dict,
507 base::StringPiece path,
508 std::string* out_value,
509 bool* has_value) {
510 return GetOptionalValue(dict, path, out_value, has_value,
511 &base::Value::GetAsString);
512 }
513
GetOptionalDictionary(const base::DictionaryValue * dict,base::StringPiece path,const base::DictionaryValue ** out_value,bool * has_value)514 bool GetOptionalDictionary(const base::DictionaryValue* dict,
515 base::StringPiece path,
516 const base::DictionaryValue** out_value,
517 bool* has_value) {
518 return GetOptionalValue(dict, path, out_value, has_value,
519 &base::Value::GetAsDictionary);
520 }
521
GetOptionalList(const base::DictionaryValue * dict,base::StringPiece path,const base::ListValue ** out_value,bool * has_value)522 bool GetOptionalList(const base::DictionaryValue* dict,
523 base::StringPiece path,
524 const base::ListValue** out_value,
525 bool* has_value) {
526 return GetOptionalValue(dict, path, out_value, has_value,
527 &base::Value::GetAsList);
528 }
529
GetOptionalSafeInt(const base::DictionaryValue * dict,base::StringPiece path,int64_t * out_value,bool * has_value)530 bool GetOptionalSafeInt(const base::DictionaryValue* dict,
531 base::StringPiece path,
532 int64_t* out_value,
533 bool* has_value) {
534 // Check if we have a normal int, which is always a safe int.
535 int temp_int;
536 bool temp_has_value;
537 if (GetOptionalValue(dict, path, &temp_int, &temp_has_value,
538 &base::Value::GetAsInteger)) {
539 if (has_value != nullptr)
540 *has_value = temp_has_value;
541 if (temp_has_value)
542 *out_value = temp_int;
543 return true;
544 }
545
546 // Check if we have a double, which may or may not contain a safe int value.
547 double temp_double;
548 if (!dict->GetDouble(path, &temp_double))
549 return false;
550
551 // Verify that the value is an integer.
552 int64_t temp_int64 = static_cast<int64_t>(temp_double);
553 if (temp_int64 != temp_double)
554 return false;
555
556 // Verify that the value is in the range for safe integer.
557 if (temp_int64 >= (1ll << 53) || temp_int64 <= -(1ll << 53))
558 return false;
559
560 // Got a good value.
561 *out_value = temp_int64;
562 if (has_value != nullptr)
563 *has_value = true;
564 return true;
565 }
566
SetSafeInt(base::DictionaryValue * dict,const base::StringPiece path,int64_t in_value_64)567 bool SetSafeInt(base::DictionaryValue* dict,
568 const base::StringPiece path,
569 int64_t in_value_64) {
570 int int_value = static_cast<int>(in_value_64);
571 if (in_value_64 == int_value)
572 return dict->SetInteger(path, in_value_64);
573 else
574 return dict->SetDouble(path, in_value_64);
575 }
576
WebViewIdToWindowHandle(const std::string & web_view_id)577 std::string WebViewIdToWindowHandle(const std::string& web_view_id) {
578 return kWindowHandlePrefix + web_view_id;
579 }
580
WindowHandleToWebViewId(const std::string & window_handle,std::string * web_view_id)581 bool WindowHandleToWebViewId(const std::string& window_handle,
582 std::string* web_view_id) {
583 if (!base::StartsWith(window_handle, kWindowHandlePrefix,
584 base::CompareCase::SENSITIVE)) {
585 return false;
586 }
587 *web_view_id = window_handle.substr(sizeof(kWindowHandlePrefix) - 1);
588 return true;
589 }
590