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/files/file_enumerator.h"
9 #include "base/files/file_util.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/browser_info.h"
20 #include "chrome/test/chromedriver/chrome/chrome.h"
21 #include "chrome/test/chromedriver/chrome/status.h"
22 #include "chrome/test/chromedriver/chrome/ui_events.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
);