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.
5 #include "webkit/glue/glue_serialize.h"
9 #include "base/pickle.h"
10 #include "base/utf_string_conversions.h"
11 #include "googleurl/src/gurl.h"
12 #include "third_party/WebKit/Source/Platform/chromium/public/WebData.h"
13 #include "third_party/WebKit/Source/Platform/chromium/public/WebHTTPBody.h"
14 #include "third_party/WebKit/Source/Platform/chromium/public/WebPoint.h"
15 #include "third_party/WebKit/Source/Platform/chromium/public/WebString.h"
16 #include "third_party/WebKit/Source/Platform/chromium/public/WebURL.h"
17 #include "third_party/WebKit/Source/Platform/chromium/public/WebVector.h"
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebHistoryItem.h"
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSerializedScriptValue.h"
20 #include "ui/gfx/screen.h"
21 #include "webkit/base/file_path_string_conversions.h"
23 using WebKit::WebData
;
24 using WebKit::WebHistoryItem
;
25 using WebKit::WebHTTPBody
;
26 using WebKit::WebPoint
;
27 using WebKit::WebSerializedScriptValue
;
28 using WebKit::WebString
;
29 using WebKit::WebUChar
;
30 using WebKit::WebVector
;
32 namespace webkit_glue
{
36 enum IncludeFormData
{
37 NEVER_INCLUDE_FORM_DATA
,
38 INCLUDE_FORM_DATA_WITHOUT_PASSWORDS
,
39 ALWAYS_INCLUDE_FORM_DATA
42 struct SerializeObject
{
43 SerializeObject() : version(0) {}
44 SerializeObject(const char* data
, int len
)
45 : pickle(data
, len
), version(0) { iter
= PickleIterator(pickle
); }
47 std::string
GetAsString() {
48 return std::string(static_cast<const char*>(pickle
.data()), pickle
.size());
52 mutable PickleIterator iter
;
56 // TODO(mpcomplete): obsolete versions 1 and 2 after 1/1/2008.
57 // Version ID used in reading/writing history items.
58 // 1: Initial revision.
59 // 2: Added case for NULL string versus "". Version 2 code can read Version 1
60 // data, but not vice versa.
61 // 3: Version 2 was broken, it stored number of WebUChars, not number of bytes.
62 // This version checks and reads v1 and v2 correctly.
63 // 4: Adds support for storing FormData::identifier().
64 // 5: Adds support for empty FormData
65 // 6: Adds support for documentSequenceNumbers
66 // 7: Adds support for stateObject
67 // 8: Adds support for file range and modification time
68 // 9: Adds support for itemSequenceNumbers
69 // 10: Adds support for blob
70 // 11: Adds support for pageScaleFactor
71 // 12: Adds support for hasPasswordData in HTTP body
72 // 13: Adds support for URL (FileSystem URL)
73 // Should be const, but unit tests may modify it.
75 // NOTE: If the version is -1, then the pickle contains only a URL string.
76 // See CreateHistoryStateForURL.
80 // A bunch of convenience functions to read/write to SerializeObjects.
81 // The serializers assume the input data is in the correct format and so does
83 inline void WriteData(const void* data
, int length
, SerializeObject
* obj
) {
84 obj
->pickle
.WriteData(static_cast<const char*>(data
), length
);
87 inline void ReadData(const SerializeObject
* obj
, const void** data
,
90 if (obj
->pickle
.ReadData(&obj
->iter
, &tmp
, length
)) {
98 inline bool ReadBytes(const SerializeObject
* obj
, const void** data
,
101 if (!obj
->pickle
.ReadBytes(&obj
->iter
, &tmp
, length
))
107 inline void WriteInteger(int data
, SerializeObject
* obj
) {
108 obj
->pickle
.WriteInt(data
);
111 inline int ReadInteger(const SerializeObject
* obj
) {
113 if (obj
->pickle
.ReadInt(&obj
->iter
, &tmp
))
118 inline void WriteInteger64(int64 data
, SerializeObject
* obj
) {
119 obj
->pickle
.WriteInt64(data
);
122 inline int64
ReadInteger64(const SerializeObject
* obj
) {
124 obj
->pickle
.ReadInt64(&obj
->iter
, &tmp
);
128 inline void WriteReal(double data
, SerializeObject
* obj
) {
129 WriteData(&data
, sizeof(double), obj
);
132 inline double ReadReal(const SerializeObject
* obj
) {
133 const void* tmp
= NULL
;
136 ReadData(obj
, &tmp
, &length
);
137 if (tmp
&& length
>= static_cast<int>(sizeof(double))) {
138 // Use memcpy, as tmp may not be correctly aligned.
139 memcpy(&value
, tmp
, sizeof(double));
144 inline void WriteBoolean(bool data
, SerializeObject
* obj
) {
145 obj
->pickle
.WriteInt(data
? 1 : 0);
148 inline bool ReadBoolean(const SerializeObject
* obj
) {
150 if (obj
->pickle
.ReadBool(&obj
->iter
, &tmp
))
155 inline void WriteGURL(const GURL
& url
, SerializeObject
* obj
) {
156 obj
->pickle
.WriteString(url
.possibly_invalid_spec());
159 inline GURL
ReadGURL(const SerializeObject
* obj
) {
161 if (obj
->pickle
.ReadString(&obj
->iter
, &spec
))
166 // Read/WriteString pickle the WebString as <int length><WebUChar* data>.
167 // If length == -1, then the WebString itself is NULL (WebString()).
168 // Otherwise the length is the number of WebUChars (not bytes) in the WebString.
169 inline void WriteString(const WebString
& str
, SerializeObject
* obj
) {
172 // Version 1 writes <length in bytes><string data>.
173 // It saves WebString() and "" as "".
174 obj
->pickle
.WriteInt(str
.length() * sizeof(WebUChar
));
175 obj
->pickle
.WriteBytes(str
.data(), str
.length() * sizeof(WebUChar
));
178 // Version 2 writes <length in WebUChar><string data>.
179 // It uses -1 in the length field to mean WebString().
181 obj
->pickle
.WriteInt(-1);
183 obj
->pickle
.WriteInt(str
.length());
184 obj
->pickle
.WriteBytes(str
.data(),
185 str
.length() * sizeof(WebUChar
));
189 // Version 3+ writes <length in bytes><string data>.
190 // It uses -1 in the length field to mean WebString().
192 obj
->pickle
.WriteInt(-1);
194 obj
->pickle
.WriteInt(str
.length() * sizeof(WebUChar
));
195 obj
->pickle
.WriteBytes(str
.data(),
196 str
.length() * sizeof(WebUChar
));
202 // This reads a serialized WebString from obj. If a string can't be read,
203 // WebString() is returned.
204 inline WebString
ReadString(const SerializeObject
* obj
) {
207 // Versions 1, 2, and 3 all start with an integer.
208 if (!obj
->pickle
.ReadInt(&obj
->iter
, &length
))
211 // Starting with version 2, -1 means WebString().
215 // In version 2, the length field was the length in WebUChars.
216 // In version 1 and 3 it is the length in bytes.
218 if (obj
->version
== 2)
219 bytes
*= sizeof(WebUChar
);
222 if (!ReadBytes(obj
, &data
, bytes
))
224 return WebString(static_cast<const WebUChar
*>(data
),
225 bytes
/ sizeof(WebUChar
));
228 // Writes a Vector of Strings into a SerializeObject for serialization.
229 void WriteStringVector(
230 const WebVector
<WebString
>& data
, SerializeObject
* obj
) {
231 WriteInteger(static_cast<int>(data
.size()), obj
);
232 for (size_t i
= 0, c
= data
.size(); i
< c
; ++i
) {
233 unsigned ui
= static_cast<unsigned>(i
); // sigh
234 WriteString(data
[ui
], obj
);
238 WebVector
<WebString
> ReadStringVector(const SerializeObject
* obj
) {
239 int num_elements
= ReadInteger(obj
);
240 WebVector
<WebString
> result(static_cast<size_t>(num_elements
));
241 for (int i
= 0; i
< num_elements
; ++i
)
242 result
[i
] = ReadString(obj
);
246 // Writes a FormData object into a SerializeObject for serialization.
247 void WriteFormData(const WebHTTPBody
& http_body
, SerializeObject
* obj
) {
248 WriteBoolean(!http_body
.isNull(), obj
);
250 if (http_body
.isNull())
253 WriteInteger(static_cast<int>(http_body
.elementCount()), obj
);
254 WebHTTPBody::Element element
;
255 for (size_t i
= 0; http_body
.elementAt(i
, element
); ++i
) {
256 WriteInteger(element
.type
, obj
);
257 if (element
.type
== WebHTTPBody::Element::TypeData
) {
258 WriteData(element
.data
.data(), static_cast<int>(element
.data
.size()),
260 } else if (element
.type
== WebHTTPBody::Element::TypeFile
) {
261 WriteString(element
.filePath
, obj
);
262 WriteInteger64(element
.fileStart
, obj
);
263 WriteInteger64(element
.fileLength
, obj
);
264 WriteReal(element
.modificationTime
, obj
);
265 } else if (element
.type
== WebHTTPBody::Element::TypeURL
) {
266 WriteGURL(element
.url
, obj
);
267 WriteInteger64(element
.fileStart
, obj
);
268 WriteInteger64(element
.fileLength
, obj
);
269 WriteReal(element
.modificationTime
, obj
);
271 WriteGURL(element
.url
, obj
);
274 WriteInteger64(http_body
.identifier(), obj
);
275 WriteBoolean(http_body
.containsPasswordData(), obj
);
278 WebHTTPBody
ReadFormData(const SerializeObject
* obj
) {
279 // In newer versions, an initial boolean indicates if we have form data.
280 if (obj
->version
>= 5 && !ReadBoolean(obj
))
281 return WebHTTPBody();
283 // In older versions, 0 elements implied no form data.
284 int num_elements
= ReadInteger(obj
);
285 if (num_elements
== 0 && obj
->version
< 5)
286 return WebHTTPBody();
288 WebHTTPBody http_body
;
289 http_body
.initialize();
291 for (int i
= 0; i
< num_elements
; ++i
) {
292 int type
= ReadInteger(obj
);
293 if (type
== WebHTTPBody::Element::TypeData
) {
296 ReadData(obj
, &data
, &length
);
298 http_body
.appendData(WebData(static_cast<const char*>(data
), length
));
299 } else if (type
== WebHTTPBody::Element::TypeFile
) {
300 WebString file_path
= ReadString(obj
);
301 long long file_start
= 0;
302 long long file_length
= -1;
303 double modification_time
= 0.0;
304 if (obj
->version
>= 8) {
305 file_start
= ReadInteger64(obj
);
306 file_length
= ReadInteger64(obj
);
307 modification_time
= ReadReal(obj
);
309 http_body
.appendFileRange(file_path
, file_start
, file_length
,
311 } else if (type
== WebHTTPBody::Element::TypeURL
) {
312 GURL url
= ReadGURL(obj
);
313 long long file_start
= 0;
314 long long file_length
= -1;
315 double modification_time
= 0.0;
316 file_start
= ReadInteger64(obj
);
317 file_length
= ReadInteger64(obj
);
318 modification_time
= ReadReal(obj
);
319 http_body
.appendURLRange(url
, file_start
, file_length
,
321 } else if (obj
->version
>= 10) {
322 GURL blob_url
= ReadGURL(obj
);
323 http_body
.appendBlob(blob_url
);
326 if (obj
->version
>= 4)
327 http_body
.setIdentifier(ReadInteger64(obj
));
329 if (obj
->version
>= 12)
330 http_body
.setContainsPasswordData(ReadBoolean(obj
));
335 // Writes the HistoryItem data into the SerializeObject object for
337 void WriteHistoryItem(
338 const WebHistoryItem
& item
, SerializeObject
* obj
) {
339 // WARNING: This data may be persisted for later use. As such, care must be
340 // taken when changing the serialized format. If a new field needs to be
341 // written, only adding at the end will make it easier to deal with loading
342 // older versions. Similarly, this should NOT save fields with sensitive
343 // data, such as password fields.
344 WriteInteger(kVersion
, obj
);
345 WriteString(item
.urlString(), obj
);
346 WriteString(item
.originalURLString(), obj
);
347 WriteString(item
.target(), obj
);
348 WriteString(item
.parent(), obj
);
349 WriteString(item
.title(), obj
);
350 WriteString(item
.alternateTitle(), obj
);
351 WriteReal(item
.lastVisitedTime(), obj
);
352 WriteInteger(item
.scrollOffset().x
, obj
);
353 WriteInteger(item
.scrollOffset().y
, obj
);
354 WriteBoolean(item
.isTargetItem(), obj
);
355 WriteInteger(item
.visitCount(), obj
);
356 WriteString(item
.referrer(), obj
);
358 WriteStringVector(item
.documentState(), obj
);
361 WriteReal(item
.pageScaleFactor(), obj
);
363 WriteInteger64(item
.itemSequenceNumber(), obj
);
365 WriteInteger64(item
.documentSequenceNumber(), obj
);
367 bool has_state_object
= !item
.stateObject().isNull();
368 WriteBoolean(has_state_object
, obj
);
369 if (has_state_object
)
370 WriteString(item
.stateObject().toString(), obj
);
373 // Yes, the referrer is written twice. This is for backwards
374 // compatibility with the format.
375 WriteFormData(item
.httpBody(), obj
);
376 WriteString(item
.httpContentType(), obj
);
377 WriteString(item
.referrer(), obj
);
380 const WebVector
<WebHistoryItem
>& children
= item
.children();
381 WriteInteger(static_cast<int>(children
.size()), obj
);
382 for (size_t i
= 0, c
= children
.size(); i
< c
; ++i
)
383 WriteHistoryItem(children
[i
], obj
);
386 // Creates a new HistoryItem tree based on the serialized string.
387 // Assumes the data is in the format returned by WriteHistoryItem.
388 WebHistoryItem
ReadHistoryItem(
389 const SerializeObject
* obj
,
390 IncludeFormData include_form_data
,
391 bool include_scroll_offset
) {
392 // See note in WriteHistoryItem. on this.
393 obj
->version
= ReadInteger(obj
);
395 if (obj
->version
== -1) {
396 GURL url
= ReadGURL(obj
);
399 item
.setURLString(WebString::fromUTF8(url
.possibly_invalid_spec()));
403 if (obj
->version
> kVersion
|| obj
->version
< 1)
404 return WebHistoryItem();
409 item
.setURLString(ReadString(obj
));
410 item
.setOriginalURLString(ReadString(obj
));
411 item
.setTarget(ReadString(obj
));
412 item
.setParent(ReadString(obj
));
413 item
.setTitle(ReadString(obj
));
414 item
.setAlternateTitle(ReadString(obj
));
415 item
.setLastVisitedTime(ReadReal(obj
));
417 int x
= ReadInteger(obj
);
418 int y
= ReadInteger(obj
);
419 if (include_scroll_offset
)
420 item
.setScrollOffset(WebPoint(x
, y
));
422 item
.setIsTargetItem(ReadBoolean(obj
));
423 item
.setVisitCount(ReadInteger(obj
));
424 item
.setReferrer(ReadString(obj
));
426 item
.setDocumentState(ReadStringVector(obj
));
428 if (obj
->version
>= 11)
429 item
.setPageScaleFactor(ReadReal(obj
));
430 if (obj
->version
>= 9)
431 item
.setItemSequenceNumber(ReadInteger64(obj
));
432 if (obj
->version
>= 6)
433 item
.setDocumentSequenceNumber(ReadInteger64(obj
));
434 if (obj
->version
>= 7) {
435 bool has_state_object
= ReadBoolean(obj
);
436 if (has_state_object
) {
438 WebSerializedScriptValue::fromString(ReadString(obj
)));
442 // The extra referrer string is read for backwards compat.
443 const WebHTTPBody
& http_body
= ReadFormData(obj
);
444 const WebString
& http_content_type
= ReadString(obj
);
445 ALLOW_UNUSED
const WebString
& unused_referrer
= ReadString(obj
);
446 if (include_form_data
== ALWAYS_INCLUDE_FORM_DATA
||
447 (include_form_data
== INCLUDE_FORM_DATA_WITHOUT_PASSWORDS
&&
448 !http_body
.isNull() && !http_body
.containsPasswordData())) {
449 // Include the full HTTP body.
450 item
.setHTTPBody(http_body
);
451 item
.setHTTPContentType(http_content_type
);
452 } else if (!http_body
.isNull()) {
453 // Don't include the data in the HTTP body, but include its identifier. This
454 // enables fetching data from the cache.
455 WebHTTPBody empty_http_body
;
456 empty_http_body
.initialize();
457 empty_http_body
.setIdentifier(http_body
.identifier());
458 item
.setHTTPBody(empty_http_body
);
461 #if defined(OS_ANDROID)
462 if (obj
->version
== 11) {
463 // Now-unused values that shipped in this version of Chrome for Android when
464 // it was on a private branch.
468 // In this version, pageScaleFactor included deviceScaleFactor and scroll
469 // offsets were premultiplied by pageScaleFactor.
470 if (item
.pageScaleFactor()) {
471 if (include_scroll_offset
)
472 item
.setScrollOffset(
473 WebPoint(item
.scrollOffset().x
/ item
.pageScaleFactor(),
474 item
.scrollOffset().y
/ item
.pageScaleFactor()));
475 item
.setPageScaleFactor(item
.pageScaleFactor() /
476 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay()
477 .device_scale_factor());
483 int num_children
= ReadInteger(obj
);
484 for (int i
= 0; i
< num_children
; ++i
)
485 item
.appendToChildren(ReadHistoryItem(obj
,
487 include_scroll_offset
));
492 // Reconstruct a HistoryItem from a string, using our JSON Value deserializer.
493 // This assumes that the given serialized string has all the required key,value
494 // pairs, and does minimal error checking. The form data of the post is restored
495 // if |include_form_data| is |ALWAYS_INCLUDE_FORM_DATA| or if the data doesn't
496 // contain passwords and |include_form_data| is
497 // |INCLUDE_FORM_DATA_WITHOUT_PASSWORDS|. Otherwise the form data is empty. If
498 // |include_scroll_offset| is true, the scroll offset is restored.
499 WebHistoryItem
HistoryItemFromString(
500 const std::string
& serialized_item
,
501 IncludeFormData include_form_data
,
502 bool include_scroll_offset
) {
503 if (serialized_item
.empty())
504 return WebHistoryItem();
506 SerializeObject
obj(serialized_item
.data(),
507 static_cast<int>(serialized_item
.length()));
508 return ReadHistoryItem(&obj
, include_form_data
, include_scroll_offset
);
513 // Serialize a HistoryItem to a string, using our JSON Value serializer.
514 std::string
HistoryItemToString(const WebHistoryItem
& item
) {
516 return std::string();
519 WriteHistoryItem(item
, &obj
);
520 return obj
.GetAsString();
523 WebHistoryItem
HistoryItemFromString(const std::string
& serialized_item
) {
524 return HistoryItemFromString(serialized_item
, ALWAYS_INCLUDE_FORM_DATA
, true);
527 std::vector
<base::FilePath
> FilePathsFromHistoryState(
528 const std::string
& content_state
) {
529 std::vector
<base::FilePath
> to_return
;
530 // TODO(darin): We should avoid using the WebKit API here, so that we do not
531 // need to have WebKit initialized before calling this method.
532 const WebHistoryItem
& item
=
533 HistoryItemFromString(content_state
, ALWAYS_INCLUDE_FORM_DATA
, true);
535 // Couldn't parse the string.
538 const WebVector
<WebString
> file_paths
= item
.getReferencedFilePaths();
539 for (size_t i
= 0; i
< file_paths
.size(); ++i
)
540 to_return
.push_back(webkit_base::WebStringToFilePath(file_paths
[i
]));
544 // For testing purposes only.
545 void HistoryItemToVersionedString(const WebHistoryItem
& item
, int version
,
546 std::string
* serialized_item
) {
548 serialized_item
->clear();
552 // Temporarily change the version.
553 int real_version
= kVersion
;
557 WriteHistoryItem(item
, &obj
);
558 *serialized_item
= obj
.GetAsString();
560 kVersion
= real_version
;
563 int HistoryItemCurrentVersion() {
567 std::string
RemoveFormDataFromHistoryState(const std::string
& content_state
) {
568 // TODO(darin): We should avoid using the WebKit API here, so that we do not
569 // need to have WebKit initialized before calling this method.
570 const WebHistoryItem
& item
=
571 HistoryItemFromString(content_state
, NEVER_INCLUDE_FORM_DATA
, true);
573 // Couldn't parse the string, return an empty string.
574 return std::string();
577 return HistoryItemToString(item
);
580 std::string
RemovePasswordDataFromHistoryState(
581 const std::string
& content_state
) {
582 // TODO(darin): We should avoid using the WebKit API here, so that we do not
583 // need to have WebKit initialized before calling this method.
584 const WebHistoryItem
& item
=
585 HistoryItemFromString(
586 content_state
, INCLUDE_FORM_DATA_WITHOUT_PASSWORDS
, true);
588 // Couldn't parse the string, return an empty string.
589 return std::string();
592 return HistoryItemToString(item
);
595 std::string
RemoveScrollOffsetFromHistoryState(
596 const std::string
& content_state
) {
597 // TODO(darin): We should avoid using the WebKit API here, so that we do not
598 // need to have WebKit initialized before calling this method.
599 const WebHistoryItem
& item
=
600 HistoryItemFromString(content_state
, ALWAYS_INCLUDE_FORM_DATA
, false);
602 // Couldn't parse the string, return an empty string.
603 return std::string();
606 return HistoryItemToString(item
);
609 std::string
CreateHistoryStateForURL(const GURL
& url
) {
610 // We avoid using the WebKit API here, so that we do not need to have WebKit
611 // initialized before calling this method. Instead, we write a simple
612 // serialization of the given URL with a dummy version number of -1. This
613 // will be interpreted by ReadHistoryItem as a request to create a default
616 WriteInteger(-1, &obj
);
617 WriteGURL(url
, &obj
);
618 return obj
.GetAsString();
621 } // namespace webkit_glue