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/WebKit/chromium/public/platform/WebData.h"
13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebHistoryItem.h"
14 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebHTTPBody.h"
15 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPoint.h"
16 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSerializedScriptValue.h"
17 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
18 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURL.h"
19 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h"
20 #include "webkit/glue/webkit_glue.h"
22 using WebKit::WebData
;
23 using WebKit::WebHistoryItem
;
24 using WebKit::WebHTTPBody
;
25 using WebKit::WebPoint
;
26 using WebKit::WebSerializedScriptValue
;
27 using WebKit::WebString
;
28 using WebKit::WebUChar
;
29 using WebKit::WebVector
;
31 namespace webkit_glue
{
35 enum IncludeFormData
{
36 NEVER_INCLUDE_FORM_DATA
,
37 INCLUDE_FORM_DATA_WITHOUT_PASSWORDS
,
38 ALWAYS_INCLUDE_FORM_DATA
41 struct SerializeObject
{
42 SerializeObject() : version(0) {}
43 SerializeObject(const char* data
, int len
)
44 : pickle(data
, len
), version(0) { iter
= PickleIterator(pickle
); }
46 std::string
GetAsString() {
47 return std::string(static_cast<const char*>(pickle
.data()), pickle
.size());
51 mutable PickleIterator iter
;
55 // TODO(mpcomplete): obsolete versions 1 and 2 after 1/1/2008.
56 // Version ID used in reading/writing history items.
57 // 1: Initial revision.
58 // 2: Added case for NULL string versus "". Version 2 code can read Version 1
59 // data, but not vice versa.
60 // 3: Version 2 was broken, it stored number of WebUChars, not number of bytes.
61 // This version checks and reads v1 and v2 correctly.
62 // 4: Adds support for storing FormData::identifier().
63 // 5: Adds support for empty FormData
64 // 6: Adds support for documentSequenceNumbers
65 // 7: Adds support for stateObject
66 // 8: Adds support for file range and modification time
67 // 9: Adds support for itemSequenceNumbers
68 // 10: Adds support for blob
69 // 11: Adds support for pageScaleFactor
70 // 12: Adds support for hasPasswordData in HTTP body
71 // Should be const, but unit tests may modify it.
73 // NOTE: If the version is -1, then the pickle contains only a URL string.
74 // See CreateHistoryStateForURL.
78 // A bunch of convenience functions to read/write to SerializeObjects.
79 // The serializers assume the input data is in the correct format and so does
81 inline void WriteData(const void* data
, int length
, SerializeObject
* obj
) {
82 obj
->pickle
.WriteData(static_cast<const char*>(data
), length
);
85 inline void ReadData(const SerializeObject
* obj
, const void** data
,
88 if (obj
->pickle
.ReadData(&obj
->iter
, &tmp
, length
)) {
96 inline bool ReadBytes(const SerializeObject
* obj
, const void** data
,
99 if (!obj
->pickle
.ReadBytes(&obj
->iter
, &tmp
, length
))
105 inline void WriteInteger(int data
, SerializeObject
* obj
) {
106 obj
->pickle
.WriteInt(data
);
109 inline int ReadInteger(const SerializeObject
* obj
) {
111 if (obj
->pickle
.ReadInt(&obj
->iter
, &tmp
))
116 inline void WriteInteger64(int64 data
, SerializeObject
* obj
) {
117 obj
->pickle
.WriteInt64(data
);
120 inline int64
ReadInteger64(const SerializeObject
* obj
) {
122 obj
->pickle
.ReadInt64(&obj
->iter
, &tmp
);
126 inline void WriteReal(double data
, SerializeObject
* obj
) {
127 WriteData(&data
, sizeof(double), obj
);
130 inline double ReadReal(const SerializeObject
* obj
) {
131 const void* tmp
= NULL
;
133 ReadData(obj
, &tmp
, &length
);
134 if (tmp
&& length
> 0 && length
>= static_cast<int>(sizeof(0.0)))
135 return *static_cast<const double*>(tmp
);
140 inline void WriteBoolean(bool data
, SerializeObject
* obj
) {
141 obj
->pickle
.WriteInt(data
? 1 : 0);
144 inline bool ReadBoolean(const SerializeObject
* obj
) {
146 if (obj
->pickle
.ReadBool(&obj
->iter
, &tmp
))
151 inline void WriteGURL(const GURL
& url
, SerializeObject
* obj
) {
152 obj
->pickle
.WriteString(url
.possibly_invalid_spec());
155 inline GURL
ReadGURL(const SerializeObject
* obj
) {
157 if (obj
->pickle
.ReadString(&obj
->iter
, &spec
))
162 // Read/WriteString pickle the WebString as <int length><WebUChar* data>.
163 // If length == -1, then the WebString itself is NULL (WebString()).
164 // Otherwise the length is the number of WebUChars (not bytes) in the WebString.
165 inline void WriteString(const WebString
& str
, SerializeObject
* obj
) {
168 // Version 1 writes <length in bytes><string data>.
169 // It saves WebString() and "" as "".
170 obj
->pickle
.WriteInt(str
.length() * sizeof(WebUChar
));
171 obj
->pickle
.WriteBytes(str
.data(), str
.length() * sizeof(WebUChar
));
174 // Version 2 writes <length in WebUChar><string data>.
175 // It uses -1 in the length field to mean WebString().
177 obj
->pickle
.WriteInt(-1);
179 obj
->pickle
.WriteInt(str
.length());
180 obj
->pickle
.WriteBytes(str
.data(),
181 str
.length() * sizeof(WebUChar
));
185 // Version 3+ writes <length in bytes><string data>.
186 // It uses -1 in the length field to mean WebString().
188 obj
->pickle
.WriteInt(-1);
190 obj
->pickle
.WriteInt(str
.length() * sizeof(WebUChar
));
191 obj
->pickle
.WriteBytes(str
.data(),
192 str
.length() * sizeof(WebUChar
));
198 // This reads a serialized WebString from obj. If a string can't be read,
199 // WebString() is returned.
200 inline WebString
ReadString(const SerializeObject
* obj
) {
203 // Versions 1, 2, and 3 all start with an integer.
204 if (!obj
->pickle
.ReadInt(&obj
->iter
, &length
))
207 // Starting with version 2, -1 means WebString().
211 // In version 2, the length field was the length in WebUChars.
212 // In version 1 and 3 it is the length in bytes.
214 if (obj
->version
== 2)
215 bytes
*= sizeof(WebUChar
);
218 if (!ReadBytes(obj
, &data
, bytes
))
220 return WebString(static_cast<const WebUChar
*>(data
),
221 bytes
/ sizeof(WebUChar
));
224 // Writes a Vector of Strings into a SerializeObject for serialization.
225 void WriteStringVector(
226 const WebVector
<WebString
>& data
, SerializeObject
* obj
) {
227 WriteInteger(static_cast<int>(data
.size()), obj
);
228 for (size_t i
= 0, c
= data
.size(); i
< c
; ++i
) {
229 unsigned ui
= static_cast<unsigned>(i
); // sigh
230 WriteString(data
[ui
], obj
);
234 WebVector
<WebString
> ReadStringVector(const SerializeObject
* obj
) {
235 int num_elements
= ReadInteger(obj
);
236 WebVector
<WebString
> result(static_cast<size_t>(num_elements
));
237 for (int i
= 0; i
< num_elements
; ++i
)
238 result
[i
] = ReadString(obj
);
242 // Writes a FormData object into a SerializeObject for serialization.
243 void WriteFormData(const WebHTTPBody
& http_body
, SerializeObject
* obj
) {
244 WriteBoolean(!http_body
.isNull(), obj
);
246 if (http_body
.isNull())
249 WriteInteger(static_cast<int>(http_body
.elementCount()), obj
);
250 WebHTTPBody::Element element
;
251 for (size_t i
= 0; http_body
.elementAt(i
, element
); ++i
) {
252 WriteInteger(element
.type
, obj
);
253 if (element
.type
== WebHTTPBody::Element::TypeData
) {
254 WriteData(element
.data
.data(), static_cast<int>(element
.data
.size()),
256 } else if (element
.type
== WebHTTPBody::Element::TypeFile
) {
257 WriteString(element
.filePath
, obj
);
258 WriteInteger64(element
.fileStart
, obj
);
259 WriteInteger64(element
.fileLength
, obj
);
260 WriteReal(element
.modificationTime
, obj
);
262 WriteGURL(element
.blobURL
, obj
);
265 WriteInteger64(http_body
.identifier(), obj
);
266 WriteBoolean(http_body
.containsPasswordData(), obj
);
269 WebHTTPBody
ReadFormData(const SerializeObject
* obj
) {
270 // In newer versions, an initial boolean indicates if we have form data.
271 if (obj
->version
>= 5 && !ReadBoolean(obj
))
272 return WebHTTPBody();
274 // In older versions, 0 elements implied no form data.
275 int num_elements
= ReadInteger(obj
);
276 if (num_elements
== 0 && obj
->version
< 5)
277 return WebHTTPBody();
279 WebHTTPBody http_body
;
280 http_body
.initialize();
282 for (int i
= 0; i
< num_elements
; ++i
) {
283 int type
= ReadInteger(obj
);
284 if (type
== WebHTTPBody::Element::TypeData
) {
287 ReadData(obj
, &data
, &length
);
289 http_body
.appendData(WebData(static_cast<const char*>(data
), length
));
290 } else if (type
== WebHTTPBody::Element::TypeFile
) {
291 WebString file_path
= ReadString(obj
);
292 long long file_start
= 0;
293 long long file_length
= -1;
294 double modification_time
= 0.0;
295 if (obj
->version
>= 8) {
296 file_start
= ReadInteger64(obj
);
297 file_length
= ReadInteger64(obj
);
298 modification_time
= ReadReal(obj
);
300 http_body
.appendFileRange(file_path
, file_start
, file_length
,
302 } else if (obj
->version
>= 10) {
303 GURL blob_url
= ReadGURL(obj
);
304 http_body
.appendBlob(blob_url
);
307 if (obj
->version
>= 4)
308 http_body
.setIdentifier(ReadInteger64(obj
));
310 if (obj
->version
>= 12)
311 http_body
.setContainsPasswordData(ReadBoolean(obj
));
316 // Writes the HistoryItem data into the SerializeObject object for
318 void WriteHistoryItem(
319 const WebHistoryItem
& item
, SerializeObject
* obj
) {
320 // WARNING: This data may be persisted for later use. As such, care must be
321 // taken when changing the serialized format. If a new field needs to be
322 // written, only adding at the end will make it easier to deal with loading
323 // older versions. Similarly, this should NOT save fields with sensitive
324 // data, such as password fields.
325 WriteInteger(kVersion
, obj
);
326 WriteString(item
.urlString(), obj
);
327 WriteString(item
.originalURLString(), obj
);
328 WriteString(item
.target(), obj
);
329 WriteString(item
.parent(), obj
);
330 WriteString(item
.title(), obj
);
331 WriteString(item
.alternateTitle(), obj
);
332 WriteReal(item
.lastVisitedTime(), obj
);
333 WriteInteger(item
.scrollOffset().x
, obj
);
334 WriteInteger(item
.scrollOffset().y
, obj
);
335 WriteBoolean(item
.isTargetItem(), obj
);
336 WriteInteger(item
.visitCount(), obj
);
337 WriteString(item
.referrer(), obj
);
339 WriteStringVector(item
.documentState(), obj
);
342 WriteReal(item
.pageScaleFactor(), obj
);
344 WriteInteger64(item
.itemSequenceNumber(), obj
);
346 WriteInteger64(item
.documentSequenceNumber(), obj
);
348 bool has_state_object
= !item
.stateObject().isNull();
349 WriteBoolean(has_state_object
, obj
);
350 if (has_state_object
)
351 WriteString(item
.stateObject().toString(), obj
);
354 // Yes, the referrer is written twice. This is for backwards
355 // compatibility with the format.
356 WriteFormData(item
.httpBody(), obj
);
357 WriteString(item
.httpContentType(), obj
);
358 WriteString(item
.referrer(), obj
);
361 const WebVector
<WebHistoryItem
>& children
= item
.children();
362 WriteInteger(static_cast<int>(children
.size()), obj
);
363 for (size_t i
= 0, c
= children
.size(); i
< c
; ++i
)
364 WriteHistoryItem(children
[i
], obj
);
367 // Creates a new HistoryItem tree based on the serialized string.
368 // Assumes the data is in the format returned by WriteHistoryItem.
369 WebHistoryItem
ReadHistoryItem(
370 const SerializeObject
* obj
,
371 IncludeFormData include_form_data
,
372 bool include_scroll_offset
) {
373 // See note in WriteHistoryItem. on this.
374 obj
->version
= ReadInteger(obj
);
376 if (obj
->version
== -1) {
377 GURL url
= ReadGURL(obj
);
380 item
.setURLString(WebString::fromUTF8(url
.possibly_invalid_spec()));
384 if (obj
->version
> kVersion
|| obj
->version
< 1)
385 return WebHistoryItem();
390 item
.setURLString(ReadString(obj
));
391 item
.setOriginalURLString(ReadString(obj
));
392 item
.setTarget(ReadString(obj
));
393 item
.setParent(ReadString(obj
));
394 item
.setTitle(ReadString(obj
));
395 item
.setAlternateTitle(ReadString(obj
));
396 item
.setLastVisitedTime(ReadReal(obj
));
398 int x
= ReadInteger(obj
);
399 int y
= ReadInteger(obj
);
400 if (include_scroll_offset
)
401 item
.setScrollOffset(WebPoint(x
, y
));
403 item
.setIsTargetItem(ReadBoolean(obj
));
404 item
.setVisitCount(ReadInteger(obj
));
405 item
.setReferrer(ReadString(obj
));
407 item
.setDocumentState(ReadStringVector(obj
));
409 if (obj
->version
>= 11)
410 item
.setPageScaleFactor(ReadReal(obj
));
411 if (obj
->version
>= 9)
412 item
.setItemSequenceNumber(ReadInteger64(obj
));
413 if (obj
->version
>= 6)
414 item
.setDocumentSequenceNumber(ReadInteger64(obj
));
415 if (obj
->version
>= 7) {
416 bool has_state_object
= ReadBoolean(obj
);
417 if (has_state_object
) {
419 WebSerializedScriptValue::fromString(ReadString(obj
)));
423 // The extra referrer string is read for backwards compat.
424 const WebHTTPBody
& http_body
= ReadFormData(obj
);
425 const WebString
& http_content_type
= ReadString(obj
);
426 ALLOW_UNUSED
const WebString
& unused_referrer
= ReadString(obj
);
427 if (include_form_data
== ALWAYS_INCLUDE_FORM_DATA
||
428 (include_form_data
== INCLUDE_FORM_DATA_WITHOUT_PASSWORDS
&&
429 !http_body
.isNull() && !http_body
.containsPasswordData())) {
430 // Include the full HTTP body.
431 item
.setHTTPBody(http_body
);
432 item
.setHTTPContentType(http_content_type
);
433 } else if (!http_body
.isNull()) {
434 // Don't include the data in the HTTP body, but include its identifier. This
435 // enables fetching data from the cache.
436 WebHTTPBody empty_http_body
;
437 empty_http_body
.initialize();
438 empty_http_body
.setIdentifier(http_body
.identifier());
439 item
.setHTTPBody(empty_http_body
);
443 int num_children
= ReadInteger(obj
);
444 for (int i
= 0; i
< num_children
; ++i
)
445 item
.appendToChildren(ReadHistoryItem(obj
,
447 include_scroll_offset
));
452 // Reconstruct a HistoryItem from a string, using our JSON Value deserializer.
453 // This assumes that the given serialized string has all the required key,value
454 // pairs, and does minimal error checking. The form data of the post is restored
455 // if |include_form_data| is |ALWAYS_INCLUDE_FORM_DATA| or if the data doesn't
456 // contain passwords and |include_form_data| is
457 // |INCLUDE_FORM_DATA_WITHOUT_PASSWORDS|. Otherwise the form data is empty. If
458 // |include_scroll_offset| is true, the scroll offset is restored.
459 WebHistoryItem
HistoryItemFromString(
460 const std::string
& serialized_item
,
461 IncludeFormData include_form_data
,
462 bool include_scroll_offset
) {
463 if (serialized_item
.empty())
464 return WebHistoryItem();
466 SerializeObject
obj(serialized_item
.data(),
467 static_cast<int>(serialized_item
.length()));
468 return ReadHistoryItem(&obj
, include_form_data
, include_scroll_offset
);
472 std::string
RemoveFormDataFromHistoryState(const std::string
& content_state
) {
473 // TODO(darin): We should avoid using the WebKit API here, so that we do not
474 // need to have WebKit initialized before calling this method.
475 const WebHistoryItem
& item
=
476 HistoryItemFromString(content_state
, NEVER_INCLUDE_FORM_DATA
, true);
478 // Couldn't parse the string, return an empty string.
479 return std::string();
482 return HistoryItemToString(item
);
485 std::string
RemovePasswordDataFromHistoryState(
486 const std::string
& content_state
) {
487 // TODO(darin): We should avoid using the WebKit API here, so that we do not
488 // need to have WebKit initialized before calling this method.
489 const WebHistoryItem
& item
=
490 HistoryItemFromString(
491 content_state
, INCLUDE_FORM_DATA_WITHOUT_PASSWORDS
, true);
493 // Couldn't parse the string, return an empty string.
494 return std::string();
497 return HistoryItemToString(item
);
500 std::string
RemoveScrollOffsetFromHistoryState(
501 const std::string
& content_state
) {
502 // TODO(darin): We should avoid using the WebKit API here, so that we do not
503 // need to have WebKit initialized before calling this method.
504 const WebHistoryItem
& item
=
505 HistoryItemFromString(content_state
, ALWAYS_INCLUDE_FORM_DATA
, false);
507 // Couldn't parse the string, return an empty string.
508 return std::string();
511 return HistoryItemToString(item
);
514 std::string
CreateHistoryStateForURL(const GURL
& url
) {
515 // We avoid using the WebKit API here, so that we do not need to have WebKit
516 // initialized before calling this method. Instead, we write a simple
517 // serialization of the given URL with a dummy version number of -1. This
518 // will be interpreted by ReadHistoryItem as a request to create a default
521 WriteInteger(-1, &obj
);
522 WriteGURL(url
, &obj
);
523 return obj
.GetAsString();
526 // Serialize a HistoryItem to a string, using our JSON Value serializer.
527 std::string
HistoryItemToString(const WebHistoryItem
& item
) {
529 return std::string();
532 WriteHistoryItem(item
, &obj
);
533 return obj
.GetAsString();
536 WebHistoryItem
HistoryItemFromString(const std::string
& serialized_item
) {
537 return HistoryItemFromString(serialized_item
, ALWAYS_INCLUDE_FORM_DATA
, true);
540 std::vector
<FilePath
> FilePathsFromHistoryState(
541 const std::string
& content_state
) {
542 std::vector
<FilePath
> to_return
;
543 // TODO(darin): We should avoid using the WebKit API here, so that we do not
544 // need to have WebKit initialized before calling this method.
545 const WebHistoryItem
& item
=
546 HistoryItemFromString(content_state
, ALWAYS_INCLUDE_FORM_DATA
, true);
548 // Couldn't parse the string.
551 const WebHTTPBody
& http_body
= item
.httpBody();
552 if (!http_body
.isNull()) {
553 WebHTTPBody::Element element
;
554 for (size_t i
= 0; i
< http_body
.elementCount(); ++i
) {
555 http_body
.elementAt(i
, element
);
556 if (element
.type
== WebHTTPBody::Element::TypeFile
)
557 to_return
.push_back(WebStringToFilePath(element
.filePath
));
563 // For testing purposes only.
564 void HistoryItemToVersionedString(const WebHistoryItem
& item
, int version
,
565 std::string
* serialized_item
) {
567 serialized_item
->clear();
571 // Temporarily change the version.
572 int real_version
= kVersion
;
576 WriteHistoryItem(item
, &obj
);
577 *serialized_item
= obj
.GetAsString();
579 kVersion
= real_version
;
582 int HistoryItemCurrentVersion() {
586 } // namespace webkit_glue