OLD | NEW |
| (Empty) |
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 "chrome/test/webdriver/webdriver_util.h" | |
6 | |
7 #include "base/base64.h" | |
8 #include "base/basictypes.h" | |
9 #include "base/file_util.h" | |
10 #include "base/files/file_enumerator.h" | |
11 #include "base/files/scoped_temp_dir.h" | |
12 #include "base/format_macros.h" | |
13 #include "base/json/json_reader.h" | |
14 #include "base/json/json_writer.h" | |
15 #include "base/memory/scoped_ptr.h" | |
16 #include "base/rand_util.h" | |
17 #include "base/strings/string_number_conversions.h" | |
18 #include "base/strings/string_split.h" | |
19 #include "base/strings/string_util.h" | |
20 #include "base/strings/stringprintf.h" | |
21 #include "base/third_party/icu/icu_utf.h" | |
22 #include "chrome/common/automation_id.h" | |
23 #include "chrome/test/automation/automation_json_requests.h" | |
24 #include "third_party/zlib/google/zip.h" | |
25 | |
26 using base::DictionaryValue; | |
27 using base::ListValue; | |
28 using base::Value; | |
29 | |
30 namespace webdriver { | |
31 | |
32 SkipParsing* kSkipParsing = NULL; | |
33 | |
34 std::string GenerateRandomID() { | |
35 uint64 msb = base::RandUint64(); | |
36 uint64 lsb = base::RandUint64(); | |
37 return base::StringPrintf("%016" PRIx64 "%016" PRIx64, msb, lsb); | |
38 } | |
39 | |
40 bool Base64Decode(const std::string& base64, | |
41 std::string* bytes) { | |
42 std::string copy = base64; | |
43 // Some WebDriver client base64 encoders follow RFC 1521, which require that | |
44 // 'encoded lines be no more than 76 characters long'. Just remove any | |
45 // newlines. | |
46 RemoveChars(copy, "\n", ©); | |
47 return base::Base64Decode(copy, bytes); | |
48 } | |
49 | |
50 namespace { | |
51 | |
52 bool UnzipArchive(const base::FilePath& unzip_dir, | |
53 const std::string& bytes, | |
54 std::string* error_msg) { | |
55 base::ScopedTempDir dir; | |
56 if (!dir.CreateUniqueTempDir()) { | |
57 *error_msg = "Unable to create temp dir"; | |
58 return false; | |
59 } | |
60 base::FilePath archive = dir.path().AppendASCII("temp.zip"); | |
61 int length = bytes.length(); | |
62 if (file_util::WriteFile(archive, bytes.c_str(), length) != length) { | |
63 *error_msg = "Could not write file to temp dir"; | |
64 return false; | |
65 } | |
66 if (!zip::Unzip(archive, unzip_dir)) { | |
67 *error_msg = "Could not unzip archive"; | |
68 return false; | |
69 } | |
70 return true; | |
71 } | |
72 | |
73 } // namespace | |
74 | |
75 bool Base64DecodeAndUnzip(const base::FilePath& unzip_dir, | |
76 const std::string& base64, | |
77 std::string* error_msg) { | |
78 std::string zip_data; | |
79 if (!Base64Decode(base64, &zip_data)) { | |
80 *error_msg = "Could not decode base64 zip data"; | |
81 return false; | |
82 } | |
83 return UnzipArchive(unzip_dir, zip_data, error_msg); | |
84 } | |
85 | |
86 namespace { | |
87 | |
88 // Stream for writing binary data. | |
89 class DataOutputStream { | |
90 public: | |
91 DataOutputStream() {} | |
92 ~DataOutputStream() {} | |
93 | |
94 void WriteUInt16(uint16 data) { | |
95 WriteBytes(&data, sizeof(data)); | |
96 } | |
97 | |
98 void WriteUInt32(uint32 data) { | |
99 WriteBytes(&data, sizeof(data)); | |
100 } | |
101 | |
102 void WriteString(const std::string& data) { | |
103 WriteBytes(data.c_str(), data.length()); | |
104 } | |
105 | |
106 void WriteBytes(const void* bytes, int size) { | |
107 size_t next = buffer_.length(); | |
108 buffer_.resize(next + size); | |
109 memcpy(&buffer_[next], bytes, size); | |
110 } | |
111 | |
112 const std::string& buffer() const { return buffer_; } | |
113 | |
114 private: | |
115 std::string buffer_; | |
116 }; | |
117 | |
118 // Stream for reading binary data. | |
119 class DataInputStream { | |
120 public: | |
121 DataInputStream(const char* data, int size) | |
122 : data_(data), size_(size), iter_(0) {} | |
123 ~DataInputStream() {} | |
124 | |
125 bool ReadUInt16(uint16* data) { | |
126 return ReadBytes(data, sizeof(*data)); | |
127 } | |
128 | |
129 bool ReadUInt32(uint32* data) { | |
130 return ReadBytes(data, sizeof(*data)); | |
131 } | |
132 | |
133 bool ReadString(std::string* data, int length) { | |
134 if (length < 0) | |
135 return false; | |
136 // Check here to make sure we don't allocate wastefully. | |
137 if (iter_ + length > size_) | |
138 return false; | |
139 data->resize(length); | |
140 return ReadBytes(&(*data)[0], length); | |
141 } | |
142 | |
143 bool ReadBytes(void* bytes, int size) { | |
144 if (iter_ + size > size_) | |
145 return false; | |
146 memcpy(bytes, &data_[iter_], size); | |
147 iter_ += size; | |
148 return true; | |
149 } | |
150 | |
151 int remaining() const { return size_ - iter_; } | |
152 | |
153 private: | |
154 const char* data_; | |
155 int size_; | |
156 int iter_; | |
157 }; | |
158 | |
159 // A file entry within a zip archive. This may be incomplete and is not | |
160 // guaranteed to be able to parse all types of zip entries. | |
161 // See http://www.pkware.com/documents/casestudies/APPNOTE.TXT for the zip | |
162 // file format. | |
163 struct ZipEntry { | |
164 // The given bytes must contain the whole zip entry and only the entry, | |
165 // although the entry may include a data descriptor. | |
166 static bool FromBytes(const std::string& bytes, ZipEntry* zip, | |
167 std::string* error_msg) { | |
168 DataInputStream stream(bytes.c_str(), bytes.length()); | |
169 | |
170 uint32 signature; | |
171 if (!stream.ReadUInt32(&signature) || signature != kFileHeaderSignature) { | |
172 *error_msg = "Invalid file header signature"; | |
173 return false; | |
174 } | |
175 if (!stream.ReadUInt16(&zip->version_needed)) { | |
176 *error_msg = "Invalid version"; | |
177 return false; | |
178 } | |
179 if (!stream.ReadUInt16(&zip->bit_flag)) { | |
180 *error_msg = "Invalid bit flag"; | |
181 return false; | |
182 } | |
183 if (!stream.ReadUInt16(&zip->compression_method)) { | |
184 *error_msg = "Invalid compression method"; | |
185 return false; | |
186 } | |
187 if (!stream.ReadUInt16(&zip->mod_time)) { | |
188 *error_msg = "Invalid file last modified time"; | |
189 return false; | |
190 } | |
191 if (!stream.ReadUInt16(&zip->mod_date)) { | |
192 *error_msg = "Invalid file last modified date"; | |
193 return false; | |
194 } | |
195 if (!stream.ReadUInt32(&zip->crc)) { | |
196 *error_msg = "Invalid crc"; | |
197 return false; | |
198 } | |
199 uint32 compressed_size; | |
200 if (!stream.ReadUInt32(&compressed_size)) { | |
201 *error_msg = "Invalid compressed size"; | |
202 return false; | |
203 } | |
204 if (!stream.ReadUInt32(&zip->uncompressed_size)) { | |
205 *error_msg = "Invalid compressed size"; | |
206 return false; | |
207 } | |
208 uint16 name_length; | |
209 if (!stream.ReadUInt16(&name_length)) { | |
210 *error_msg = "Invalid name length"; | |
211 return false; | |
212 } | |
213 uint16 field_length; | |
214 if (!stream.ReadUInt16(&field_length)) { | |
215 *error_msg = "Invalid field length"; | |
216 return false; | |
217 } | |
218 if (!stream.ReadString(&zip->name, name_length)) { | |
219 *error_msg = "Invalid name"; | |
220 return false; | |
221 } | |
222 if (!stream.ReadString(&zip->fields, field_length)) { | |
223 *error_msg = "Invalid fields"; | |
224 return false; | |
225 } | |
226 if (zip->bit_flag & 0x8) { | |
227 // Has compressed data and a separate data descriptor. | |
228 if (stream.remaining() < 16) { | |
229 *error_msg = "Too small for data descriptor"; | |
230 return false; | |
231 } | |
232 compressed_size = stream.remaining() - 16; | |
233 if (!stream.ReadString(&zip->compressed_data, compressed_size)) { | |
234 *error_msg = "Invalid compressed data before descriptor"; | |
235 return false; | |
236 } | |
237 if (!stream.ReadUInt32(&signature) || | |
238 signature != kDataDescriptorSignature) { | |
239 *error_msg = "Invalid data descriptor signature"; | |
240 return false; | |
241 } | |
242 if (!stream.ReadUInt32(&zip->crc)) { | |
243 *error_msg = "Invalid crc"; | |
244 return false; | |
245 } | |
246 if (!stream.ReadUInt32(&compressed_size)) { | |
247 *error_msg = "Invalid compressed size"; | |
248 return false; | |
249 } | |
250 if (compressed_size != zip->compressed_data.length()) { | |
251 *error_msg = "Compressed data does not match data descriptor"; | |
252 return false; | |
253 } | |
254 if (!stream.ReadUInt32(&zip->uncompressed_size)) { | |
255 *error_msg = "Invalid compressed size"; | |
256 return false; | |
257 } | |
258 } else { | |
259 // Just has compressed data. | |
260 if (!stream.ReadString(&zip->compressed_data, compressed_size)) { | |
261 *error_msg = "Invalid compressed data"; | |
262 return false; | |
263 } | |
264 if (stream.remaining() != 0) { | |
265 *error_msg = "Leftover data after zip entry"; | |
266 return false; | |
267 } | |
268 } | |
269 return true; | |
270 } | |
271 | |
272 // Returns bytes for a valid zip file that just contains this zip entry. | |
273 std::string ToZip() { | |
274 // Write zip entry with no data descriptor. | |
275 DataOutputStream stream; | |
276 stream.WriteUInt32(kFileHeaderSignature); | |
277 stream.WriteUInt16(version_needed); | |
278 stream.WriteUInt16(bit_flag); | |
279 stream.WriteUInt16(compression_method); | |
280 stream.WriteUInt16(mod_time); | |
281 stream.WriteUInt16(mod_date); | |
282 stream.WriteUInt32(crc); | |
283 stream.WriteUInt32(compressed_data.length()); | |
284 stream.WriteUInt32(uncompressed_size); | |
285 stream.WriteUInt16(name.length()); | |
286 stream.WriteUInt16(fields.length()); | |
287 stream.WriteString(name); | |
288 stream.WriteString(fields); | |
289 stream.WriteString(compressed_data); | |
290 uint32 entry_size = stream.buffer().length(); | |
291 | |
292 // Write central directory. | |
293 stream.WriteUInt32(kCentralDirSignature); | |
294 stream.WriteUInt16(0x14); // Version made by. Unused at version 0. | |
295 stream.WriteUInt16(version_needed); | |
296 stream.WriteUInt16(bit_flag); | |
297 stream.WriteUInt16(compression_method); | |
298 stream.WriteUInt16(mod_time); | |
299 stream.WriteUInt16(mod_date); | |
300 stream.WriteUInt32(crc); | |
301 stream.WriteUInt32(compressed_data.length()); | |
302 stream.WriteUInt32(uncompressed_size); | |
303 stream.WriteUInt16(name.length()); | |
304 stream.WriteUInt16(fields.length()); | |
305 stream.WriteUInt16(0); // Comment length. | |
306 stream.WriteUInt16(0); // Disk number where file starts. | |
307 stream.WriteUInt16(0); // Internal file attr. | |
308 stream.WriteUInt32(0); // External file attr. | |
309 stream.WriteUInt32(0); // Offset to file. | |
310 stream.WriteString(name); | |
311 stream.WriteString(fields); | |
312 uint32 cd_size = stream.buffer().length() - entry_size; | |
313 | |
314 // End of central directory. | |
315 stream.WriteUInt32(kEndOfCentralDirSignature); | |
316 stream.WriteUInt16(0); // num of this disk | |
317 stream.WriteUInt16(0); // disk where cd starts | |
318 stream.WriteUInt16(1); // number of cds on this disk | |
319 stream.WriteUInt16(1); // total cds | |
320 stream.WriteUInt32(cd_size); // size of cd | |
321 stream.WriteUInt32(entry_size); // offset of cd | |
322 stream.WriteUInt16(0); // comment len | |
323 | |
324 return stream.buffer(); | |
325 } | |
326 | |
327 static const uint32 kFileHeaderSignature; | |
328 static const uint32 kDataDescriptorSignature; | |
329 static const uint32 kCentralDirSignature; | |
330 static const uint32 kEndOfCentralDirSignature; | |
331 uint16 version_needed; | |
332 uint16 bit_flag; | |
333 uint16 compression_method; | |
334 uint16 mod_time; | |
335 uint16 mod_date; | |
336 uint32 crc; | |
337 uint32 uncompressed_size; | |
338 std::string name; | |
339 std::string fields; | |
340 std::string compressed_data; | |
341 }; | |
342 | |
343 const uint32 ZipEntry::kFileHeaderSignature = 0x04034b50; | |
344 const uint32 ZipEntry::kDataDescriptorSignature = 0x08074b50; | |
345 const uint32 ZipEntry::kCentralDirSignature = 0x02014b50; | |
346 const uint32 ZipEntry::kEndOfCentralDirSignature = 0x06054b50; | |
347 | |
348 bool UnzipEntry(const base::FilePath& unzip_dir, | |
349 const std::string& bytes, | |
350 std::string* error_msg) { | |
351 ZipEntry entry; | |
352 std::string zip_error_msg; | |
353 if (!ZipEntry::FromBytes(bytes, &entry, &zip_error_msg)) { | |
354 *error_msg = "Error while reading zip entry: " + zip_error_msg; | |
355 return false; | |
356 } | |
357 std::string archive = entry.ToZip(); | |
358 return UnzipArchive(unzip_dir, archive, error_msg); | |
359 } | |
360 | |
361 } // namespace | |
362 | |
363 bool UnzipSoleFile(const base::FilePath& unzip_dir, | |
364 const std::string& bytes, | |
365 base::FilePath* file, | |
366 std::string* error_msg) { | |
367 std::string archive_error, entry_error; | |
368 if (!UnzipArchive(unzip_dir, bytes, &archive_error) && | |
369 !UnzipEntry(unzip_dir, bytes, &entry_error)) { | |
370 *error_msg = base::StringPrintf( | |
371 "Failed to unzip file: Archive error: (%s) Entry error: (%s)", | |
372 archive_error.c_str(), entry_error.c_str()); | |
373 return false; | |
374 } | |
375 | |
376 base::FileEnumerator enumerator(unzip_dir, false /* recursive */, | |
377 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); | |
378 base::FilePath first_file = enumerator.Next(); | |
379 if (first_file.empty()) { | |
380 *error_msg = "Zip contained 0 files"; | |
381 return false; | |
382 } | |
383 base::FilePath second_file = enumerator.Next(); | |
384 if (!second_file.empty()) { | |
385 *error_msg = "Zip contained multiple files"; | |
386 return false; | |
387 } | |
388 *file = first_file; | |
389 return true; | |
390 } | |
391 | |
392 std::string JsonStringify(const Value* value) { | |
393 std::string json; | |
394 base::JSONWriter::Write(value, &json); | |
395 return json; | |
396 } | |
397 | |
398 namespace { | |
399 | |
400 // Truncates the given string to 100 characters, adding an ellipsis if | |
401 // truncation was necessary. | |
402 void TruncateString(std::string* data) { | |
403 const size_t kMaxLength = 100; | |
404 if (data->length() > kMaxLength) { | |
405 data->resize(kMaxLength); | |
406 data->replace(kMaxLength - 3, 3, "..."); | |
407 } | |
408 } | |
409 | |
410 // Truncates all strings contained in the given value. | |
411 void TruncateContainedStrings(Value* value) { | |
412 ListValue* list = NULL; | |
413 DictionaryValue* dict = NULL; | |
414 if (value->GetAsDictionary(&dict)) { | |
415 for (DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) { | |
416 std::string data; | |
417 if (it.value().GetAsString(&data)) { | |
418 TruncateString(&data); | |
419 dict->SetWithoutPathExpansion(it.key(), new base::StringValue(data)); | |
420 } else { | |
421 Value* child = NULL; | |
422 dict->GetWithoutPathExpansion(it.key(), &child); | |
423 TruncateContainedStrings(child); | |
424 } | |
425 } | |
426 } else if (value->GetAsList(&list)) { | |
427 for (size_t i = 0; i < list->GetSize(); ++i) { | |
428 Value* child; | |
429 if (!list->Get(i, &child)) | |
430 continue; | |
431 std::string data; | |
432 if (child->GetAsString(&data)) { | |
433 TruncateString(&data); | |
434 list->Set(i, new base::StringValue(data)); | |
435 } else { | |
436 TruncateContainedStrings(child); | |
437 } | |
438 } | |
439 } | |
440 } | |
441 | |
442 } // namespace | |
443 | |
444 std::string JsonStringifyForDisplay(const Value* value) { | |
445 scoped_ptr<Value> copy; | |
446 if (value->IsType(Value::TYPE_STRING)) { | |
447 std::string data; | |
448 value->GetAsString(&data); | |
449 TruncateString(&data); | |
450 copy.reset(new base::StringValue(data)); | |
451 } else { | |
452 copy.reset(value->DeepCopy()); | |
453 TruncateContainedStrings(copy.get()); | |
454 } | |
455 std::string json; | |
456 base::JSONWriter::WriteWithOptions(copy.get(), | |
457 base::JSONWriter::OPTIONS_PRETTY_PRINT, | |
458 &json); | |
459 return json; | |
460 } | |
461 | |
462 const char* GetJsonTypeName(Value::Type type) { | |
463 switch (type) { | |
464 case Value::TYPE_NULL: | |
465 return "null"; | |
466 case Value::TYPE_BOOLEAN: | |
467 return "boolean"; | |
468 case Value::TYPE_INTEGER: | |
469 return "integer"; | |
470 case Value::TYPE_DOUBLE: | |
471 return "double"; | |
472 case Value::TYPE_STRING: | |
473 return "string"; | |
474 case Value::TYPE_BINARY: | |
475 return "binary"; | |
476 case Value::TYPE_DICTIONARY: | |
477 return "dictionary"; | |
478 case Value::TYPE_LIST: | |
479 return "list"; | |
480 } | |
481 return "unknown"; | |
482 } | |
483 | |
484 std::string AutomationIdToString(const AutomationId& id) { | |
485 return base::StringPrintf("%d-%s", id.type(), id.id().c_str()); | |
486 } | |
487 | |
488 bool StringToAutomationId(const std::string& string_id, AutomationId* id) { | |
489 std::vector<std::string> split_id; | |
490 base::SplitString(string_id, '-', &split_id); | |
491 if (split_id.size() != 2) | |
492 return false; | |
493 int type; | |
494 if (!base::StringToInt(split_id[0], &type)) | |
495 return false; | |
496 *id = AutomationId(static_cast<AutomationId::Type>(type), split_id[1]); | |
497 return true; | |
498 } | |
499 | |
500 std::string WebViewIdToString(const WebViewId& view_id) { | |
501 return base::StringPrintf( | |
502 "%s%s", | |
503 view_id.old_style() ? "t" : "f", | |
504 AutomationIdToString(view_id.GetId()).c_str()); | |
505 } | |
506 | |
507 bool StringToWebViewId(const std::string& string_id, WebViewId* view_id) { | |
508 if (string_id.empty() || (string_id[0] != 'f' && string_id[0] != 't')) | |
509 return false; | |
510 bool old_style = string_id[0] == 't'; | |
511 AutomationId id; | |
512 if (!StringToAutomationId(string_id.substr(1), &id)) | |
513 return false; | |
514 | |
515 if (old_style) { | |
516 int tab_id; | |
517 if (!base::StringToInt(id.id(), &tab_id)) | |
518 return false; | |
519 *view_id = WebViewId::ForOldStyleTab(tab_id); | |
520 } else { | |
521 *view_id = WebViewId::ForView(id); | |
522 } | |
523 return true; | |
524 } | |
525 | |
526 Error* FlattenStringArray(const ListValue* src, string16* dest) { | |
527 string16 keys; | |
528 for (size_t i = 0; i < src->GetSize(); ++i) { | |
529 string16 keys_list_part; | |
530 src->GetString(i, &keys_list_part); | |
531 for (size_t j = 0; j < keys_list_part.size(); ++j) { | |
532 if (CBU16_IS_SURROGATE(keys_list_part[j])) { | |
533 return new Error(kBadRequest, | |
534 "ChromeDriver only supports characters in the BMP"); | |
535 } | |
536 } | |
537 keys.append(keys_list_part); | |
538 } | |
539 *dest = keys; | |
540 return NULL; | |
541 } | |
542 | |
543 ValueParser::ValueParser() { } | |
544 | |
545 ValueParser::~ValueParser() { } | |
546 | |
547 } // namespace webdriver | |
548 | |
549 bool ValueConversionTraits<webdriver::SkipParsing>::SetFromValue( | |
550 const Value* value, const webdriver::SkipParsing* t) { | |
551 return true; | |
552 } | |
553 | |
554 bool ValueConversionTraits<webdriver::SkipParsing>::CanConvert( | |
555 const Value* value) { | |
556 return true; | |
557 } | |
OLD | NEW |