1 // Copyright 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 "content/common/page_state_serialization.h"
10 #include "base/pickle.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "ui/gfx/screen.h"
19 #if defined(OS_ANDROID)
20 float g_device_scale_factor_for_testing
= 0.0;
23 //-----------------------------------------------------------------------------
25 void AppendDataToHttpBody(ExplodedHttpBody
* http_body
, const char* data
,
27 ExplodedHttpBodyElement element
;
28 element
.type
= blink::WebHTTPBody::Element::TypeData
;
29 element
.data
.assign(data
, data_length
);
30 http_body
->elements
.push_back(element
);
33 void AppendFileRangeToHttpBody(ExplodedHttpBody
* http_body
,
34 const base::NullableString16
& file_path
,
37 double file_modification_time
) {
38 ExplodedHttpBodyElement element
;
39 element
.type
= blink::WebHTTPBody::Element::TypeFile
;
40 element
.file_path
= file_path
;
41 element
.file_start
= file_start
;
42 element
.file_length
= file_length
;
43 element
.file_modification_time
= file_modification_time
;
44 http_body
->elements
.push_back(element
);
47 void AppendURLRangeToHttpBody(ExplodedHttpBody
* http_body
,
51 double file_modification_time
) {
52 ExplodedHttpBodyElement element
;
53 element
.type
= blink::WebHTTPBody::Element::TypeFileSystemURL
;
54 element
.filesystem_url
= url
;
55 element
.file_start
= file_start
;
56 element
.file_length
= file_length
;
57 element
.file_modification_time
= file_modification_time
;
58 http_body
->elements
.push_back(element
);
61 void AppendBlobToHttpBody(ExplodedHttpBody
* http_body
,
62 const std::string
& uuid
) {
63 ExplodedHttpBodyElement element
;
64 element
.type
= blink::WebHTTPBody::Element::TypeBlob
;
65 element
.blob_uuid
= uuid
;
66 http_body
->elements
.push_back(element
);
69 //----------------------------------------------------------------------------
71 void AppendReferencedFilesFromHttpBody(
72 const std::vector
<ExplodedHttpBodyElement
>& elements
,
73 std::vector
<base::NullableString16
>* referenced_files
) {
74 for (size_t i
= 0; i
< elements
.size(); ++i
) {
75 if (elements
[i
].type
== blink::WebHTTPBody::Element::TypeFile
)
76 referenced_files
->push_back(elements
[i
].file_path
);
80 bool AppendReferencedFilesFromDocumentState(
81 const std::vector
<base::NullableString16
>& document_state
,
82 std::vector
<base::NullableString16
>* referenced_files
) {
83 if (document_state
.empty())
86 // This algorithm is adapted from Blink's core/html/FormController.cpp code.
87 // We only care about how that code worked when this code snapshot was taken
88 // as this code is only needed for backwards compat.
90 // For reference, see FormController::formStatesFromStateVector at:
91 // http://src.chromium.org/viewvc/blink/trunk/Source/core/html/FormController.cpp?pathrev=152274
95 if (document_state
.size() < 3)
98 index
++; // Skip over magic signature.
99 index
++; // Skip over form key.
102 if (!base::StringToSizeT(document_state
[index
++].string(), &item_count
))
105 while (item_count
--) {
106 if (index
+ 1 >= document_state
.size())
109 index
++; // Skip over name.
110 const base::NullableString16
& type
= document_state
[index
++];
112 if (index
>= document_state
.size())
116 if (!base::StringToSizeT(document_state
[index
++].string(), &value_size
))
119 if (index
+ value_size
> document_state
.size() ||
120 index
+ value_size
< index
) // Check for overflow.
123 if (EqualsASCII(type
.string(), "file")) {
127 referenced_files
->push_back(document_state
[index
++]);
128 index
++; // Skip over display name.
137 bool RecursivelyAppendReferencedFiles(
138 const ExplodedFrameState
& frame_state
,
139 std::vector
<base::NullableString16
>* referenced_files
) {
140 if (!frame_state
.http_body
.is_null
) {
141 AppendReferencedFilesFromHttpBody(frame_state
.http_body
.elements
,
145 if (!AppendReferencedFilesFromDocumentState(frame_state
.document_state
,
149 for (size_t i
= 0; i
< frame_state
.children
.size(); ++i
) {
150 if (!RecursivelyAppendReferencedFiles(frame_state
.children
[i
],
158 //----------------------------------------------------------------------------
160 struct SerializeObject
{
166 SerializeObject(const char* data
, int len
)
170 iter
= PickleIterator(pickle
);
173 std::string
GetAsString() {
174 return std::string(static_cast<const char*>(pickle
.data()), pickle
.size());
183 // Version ID of serialized format.
185 // 12: Adds support for contains_passwords in HTTP body
186 // 13: Adds support for URL (FileSystem URL)
187 // 14: Adds list of referenced files, version written only for first item.
188 // 15: Removes a bunch of values we defined but never used.
189 // 16: Switched from blob urls to blob uuids.
190 // 17: Add a target frame id number.
191 // 18: Add referrer policy.
192 // 19: Remove target frame id, which was a bad idea, and original url string,
193 // which is no longer used.
194 // 20: Add pinch viewport scroll offset, the offset of the pinched zoomed
195 // viewport within the unzoomed main frame.
197 // NOTE: If the version is -1, then the pickle contains only a URL string.
198 // See ReadPageState.
200 const int kMinVersion
= 11;
201 const int kCurrentVersion
= 20;
203 // A bunch of convenience functions to read/write to SerializeObjects. The
204 // de-serializers assume the input data will be in the correct format and fall
205 // back to returning safe defaults when not.
207 void WriteData(const void* data
, int length
, SerializeObject
* obj
) {
208 obj
->pickle
.WriteData(static_cast<const char*>(data
), length
);
211 void ReadData(SerializeObject
* obj
, const void** data
, int* length
) {
213 if (obj
->pickle
.ReadData(&obj
->iter
, &tmp
, length
)) {
216 obj
->parse_error
= true;
222 void WriteInteger(int data
, SerializeObject
* obj
) {
223 obj
->pickle
.WriteInt(data
);
226 int ReadInteger(SerializeObject
* obj
) {
228 if (obj
->pickle
.ReadInt(&obj
->iter
, &tmp
))
230 obj
->parse_error
= true;
234 void ConsumeInteger(SerializeObject
* obj
) {
235 int unused ALLOW_UNUSED
= ReadInteger(obj
);
238 void WriteInteger64(int64 data
, SerializeObject
* obj
) {
239 obj
->pickle
.WriteInt64(data
);
242 int64
ReadInteger64(SerializeObject
* obj
) {
244 if (obj
->pickle
.ReadInt64(&obj
->iter
, &tmp
))
246 obj
->parse_error
= true;
250 void ConsumeInteger64(SerializeObject
* obj
) {
251 int64 unused ALLOW_UNUSED
= ReadInteger64(obj
);
254 void WriteReal(double data
, SerializeObject
* obj
) {
255 WriteData(&data
, sizeof(double), obj
);
258 double ReadReal(SerializeObject
* obj
) {
259 const void* tmp
= NULL
;
262 ReadData(obj
, &tmp
, &length
);
263 if (length
== static_cast<int>(sizeof(double))) {
264 // Use memcpy, as tmp may not be correctly aligned.
265 memcpy(&value
, tmp
, sizeof(double));
267 obj
->parse_error
= true;
272 void ConsumeReal(SerializeObject
* obj
) {
273 double unused ALLOW_UNUSED
= ReadReal(obj
);
276 void WriteBoolean(bool data
, SerializeObject
* obj
) {
277 obj
->pickle
.WriteInt(data
? 1 : 0);
280 bool ReadBoolean(SerializeObject
* obj
) {
282 if (obj
->pickle
.ReadBool(&obj
->iter
, &tmp
))
284 obj
->parse_error
= true;
288 void ConsumeBoolean(SerializeObject
* obj
) {
289 bool unused ALLOW_UNUSED
= ReadBoolean(obj
);
292 void WriteGURL(const GURL
& url
, SerializeObject
* obj
) {
293 obj
->pickle
.WriteString(url
.possibly_invalid_spec());
296 GURL
ReadGURL(SerializeObject
* obj
) {
298 if (obj
->pickle
.ReadString(&obj
->iter
, &spec
))
300 obj
->parse_error
= true;
304 void WriteStdString(const std::string
& s
, SerializeObject
* obj
) {
305 obj
->pickle
.WriteString(s
);
308 std::string
ReadStdString(SerializeObject
* obj
) {
310 if (obj
->pickle
.ReadString(&obj
->iter
, &s
))
312 obj
->parse_error
= true;
313 return std::string();
316 // WriteString pickles the NullableString16 as <int length><char16* data>.
317 // If length == -1, then the NullableString16 itself is null. Otherwise the
318 // length is the number of char16 (not bytes) in the NullableString16.
319 void WriteString(const base::NullableString16
& str
, SerializeObject
* obj
) {
321 obj
->pickle
.WriteInt(-1);
323 const base::char16
* data
= str
.string().data();
324 size_t length_in_bytes
= str
.string().length() * sizeof(base::char16
);
326 CHECK_LT(length_in_bytes
,
327 static_cast<size_t>(std::numeric_limits
<int>::max()));
328 obj
->pickle
.WriteInt(length_in_bytes
);
329 obj
->pickle
.WriteBytes(data
, length_in_bytes
);
333 // This reads a serialized NullableString16 from obj. If a string can't be
334 // read, NULL is returned.
335 const base::char16
* ReadStringNoCopy(SerializeObject
* obj
, int* num_chars
) {
337 if (!obj
->pickle
.ReadInt(&obj
->iter
, &length_in_bytes
)) {
338 obj
->parse_error
= true;
342 if (length_in_bytes
< 0)
346 if (!obj
->pickle
.ReadBytes(&obj
->iter
, &data
, length_in_bytes
)) {
347 obj
->parse_error
= true;
352 *num_chars
= length_in_bytes
/ sizeof(base::char16
);
353 return reinterpret_cast<const base::char16
*>(data
);
356 base::NullableString16
ReadString(SerializeObject
* obj
) {
358 const base::char16
* chars
= ReadStringNoCopy(obj
, &num_chars
);
360 base::NullableString16(base::string16(chars
, num_chars
), false) :
361 base::NullableString16();
364 void ConsumeString(SerializeObject
* obj
) {
365 const base::char16
* unused ALLOW_UNUSED
= ReadStringNoCopy(obj
, NULL
);
368 template <typename T
>
369 void WriteAndValidateVectorSize(const std::vector
<T
>& v
, SerializeObject
* obj
) {
370 CHECK_LT(v
.size(), std::numeric_limits
<int>::max() / sizeof(T
));
371 WriteInteger(static_cast<int>(v
.size()), obj
);
374 size_t ReadAndValidateVectorSize(SerializeObject
* obj
, size_t element_size
) {
375 size_t num_elements
= static_cast<size_t>(ReadInteger(obj
));
377 // Ensure that resizing a vector to size num_elements makes sense.
378 if (std::numeric_limits
<int>::max() / element_size
<= num_elements
) {
379 obj
->parse_error
= true;
383 // Ensure that it is plausible for the pickle to contain num_elements worth
385 if (obj
->pickle
.payload_size() <= num_elements
) {
386 obj
->parse_error
= true;
393 // Writes a Vector of strings into a SerializeObject for serialization.
394 void WriteStringVector(
395 const std::vector
<base::NullableString16
>& data
, SerializeObject
* obj
) {
396 WriteAndValidateVectorSize(data
, obj
);
397 for (size_t i
= 0; i
< data
.size(); ++i
) {
398 WriteString(data
[i
], obj
);
402 void ReadStringVector(SerializeObject
* obj
,
403 std::vector
<base::NullableString16
>* result
) {
404 size_t num_elements
=
405 ReadAndValidateVectorSize(obj
, sizeof(base::NullableString16
));
407 result
->resize(num_elements
);
408 for (size_t i
= 0; i
< num_elements
; ++i
)
409 (*result
)[i
] = ReadString(obj
);
412 // Writes an ExplodedHttpBody object into a SerializeObject for serialization.
413 void WriteHttpBody(const ExplodedHttpBody
& http_body
, SerializeObject
* obj
) {
414 WriteBoolean(!http_body
.is_null
, obj
);
416 if (http_body
.is_null
)
419 WriteAndValidateVectorSize(http_body
.elements
, obj
);
420 for (size_t i
= 0; i
< http_body
.elements
.size(); ++i
) {
421 const ExplodedHttpBodyElement
& element
= http_body
.elements
[i
];
422 WriteInteger(element
.type
, obj
);
423 if (element
.type
== blink::WebHTTPBody::Element::TypeData
) {
424 WriteData(element
.data
.data(), static_cast<int>(element
.data
.size()),
426 } else if (element
.type
== blink::WebHTTPBody::Element::TypeFile
) {
427 WriteString(element
.file_path
, obj
);
428 WriteInteger64(element
.file_start
, obj
);
429 WriteInteger64(element
.file_length
, obj
);
430 WriteReal(element
.file_modification_time
, obj
);
431 } else if (element
.type
==
432 blink::WebHTTPBody::Element::TypeFileSystemURL
) {
433 WriteGURL(element
.filesystem_url
, obj
);
434 WriteInteger64(element
.file_start
, obj
);
435 WriteInteger64(element
.file_length
, obj
);
436 WriteReal(element
.file_modification_time
, obj
);
438 DCHECK(element
.type
== blink::WebHTTPBody::Element::TypeBlob
);
439 WriteStdString(element
.blob_uuid
, obj
);
442 WriteInteger64(http_body
.identifier
, obj
);
443 WriteBoolean(http_body
.contains_passwords
, obj
);
446 void ReadHttpBody(SerializeObject
* obj
, ExplodedHttpBody
* http_body
) {
447 // An initial boolean indicates if we have an HTTP body.
448 if (!ReadBoolean(obj
))
450 http_body
->is_null
= false;
452 int num_elements
= ReadInteger(obj
);
454 for (int i
= 0; i
< num_elements
; ++i
) {
455 int type
= ReadInteger(obj
);
456 if (type
== blink::WebHTTPBody::Element::TypeData
) {
459 ReadData(obj
, &data
, &length
);
461 AppendDataToHttpBody(http_body
, static_cast<const char*>(data
),
464 } else if (type
== blink::WebHTTPBody::Element::TypeFile
) {
465 base::NullableString16 file_path
= ReadString(obj
);
466 int64 file_start
= ReadInteger64(obj
);
467 int64 file_length
= ReadInteger64(obj
);
468 double file_modification_time
= ReadReal(obj
);
469 AppendFileRangeToHttpBody(http_body
, file_path
, file_start
, file_length
,
470 file_modification_time
);
471 } else if (type
== blink::WebHTTPBody::Element::TypeFileSystemURL
) {
472 GURL url
= ReadGURL(obj
);
473 int64 file_start
= ReadInteger64(obj
);
474 int64 file_length
= ReadInteger64(obj
);
475 double file_modification_time
= ReadReal(obj
);
476 AppendURLRangeToHttpBody(http_body
, url
, file_start
, file_length
,
477 file_modification_time
);
478 } else if (type
== blink::WebHTTPBody::Element::TypeBlob
) {
479 if (obj
->version
>= 16) {
480 std::string blob_uuid
= ReadStdString(obj
);
481 AppendBlobToHttpBody(http_body
, blob_uuid
);
483 ReadGURL(obj
); // Skip the obsolete blob url value.
487 http_body
->identifier
= ReadInteger64(obj
);
489 if (obj
->version
>= 12)
490 http_body
->contains_passwords
= ReadBoolean(obj
);
493 // Writes the ExplodedFrameState data into the SerializeObject object for
495 void WriteFrameState(
496 const ExplodedFrameState
& state
, SerializeObject
* obj
, bool is_top
) {
497 // WARNING: This data may be persisted for later use. As such, care must be
498 // taken when changing the serialized format. If a new field needs to be
499 // written, only adding at the end will make it easier to deal with loading
500 // older versions. Similarly, this should NOT save fields with sensitive
501 // data, such as password fields.
503 WriteString(state
.url_string
, obj
);
504 WriteString(state
.target
, obj
);
505 WriteInteger(state
.scroll_offset
.x(), obj
);
506 WriteInteger(state
.scroll_offset
.y(), obj
);
507 WriteString(state
.referrer
, obj
);
509 WriteStringVector(state
.document_state
, obj
);
511 WriteReal(state
.page_scale_factor
, obj
);
512 WriteInteger64(state
.item_sequence_number
, obj
);
513 WriteInteger64(state
.document_sequence_number
, obj
);
514 WriteInteger(state
.referrer_policy
, obj
);
515 WriteReal(state
.pinch_viewport_scroll_offset
.x(), obj
);
516 WriteReal(state
.pinch_viewport_scroll_offset
.y(), obj
);
518 bool has_state_object
= !state
.state_object
.is_null();
519 WriteBoolean(has_state_object
, obj
);
520 if (has_state_object
)
521 WriteString(state
.state_object
, obj
);
523 WriteHttpBody(state
.http_body
, obj
);
525 // NOTE: It is a quirk of the format that we still have to write the
526 // http_content_type field when the HTTP body is null. That's why this code
527 // is here instead of inside WriteHttpBody.
528 WriteString(state
.http_body
.http_content_type
, obj
);
531 const std::vector
<ExplodedFrameState
>& children
= state
.children
;
532 WriteAndValidateVectorSize(children
, obj
);
533 for (size_t i
= 0; i
< children
.size(); ++i
)
534 WriteFrameState(children
[i
], obj
, false);
537 void ReadFrameState(SerializeObject
* obj
, bool is_top
,
538 ExplodedFrameState
* state
) {
539 if (obj
->version
< 14 && !is_top
)
540 ConsumeInteger(obj
); // Skip over redundant version field.
542 state
->url_string
= ReadString(obj
);
544 if (obj
->version
< 19)
545 ConsumeString(obj
); // Skip obsolete original url string field.
547 state
->target
= ReadString(obj
);
548 if (obj
->version
< 15) {
549 ConsumeString(obj
); // Skip obsolete parent field.
550 ConsumeString(obj
); // Skip obsolete title field.
551 ConsumeString(obj
); // Skip obsolete alternate title field.
552 ConsumeReal(obj
); // Skip obsolete visited time field.
555 int x
= ReadInteger(obj
);
556 int y
= ReadInteger(obj
);
557 state
->scroll_offset
= gfx::Point(x
, y
);
559 if (obj
->version
< 15) {
560 ConsumeBoolean(obj
); // Skip obsolete target item flag.
561 ConsumeInteger(obj
); // Skip obsolete visit count field.
563 state
->referrer
= ReadString(obj
);
565 ReadStringVector(obj
, &state
->document_state
);
567 state
->page_scale_factor
= ReadReal(obj
);
568 state
->item_sequence_number
= ReadInteger64(obj
);
569 state
->document_sequence_number
= ReadInteger64(obj
);
571 if (obj
->version
>= 17 && obj
->version
< 19)
572 ConsumeInteger64(obj
); // Skip obsolete target frame id number.
574 if (obj
->version
>= 18) {
575 state
->referrer_policy
=
576 static_cast<blink::WebReferrerPolicy
>(ReadInteger(obj
));
579 if (obj
->version
>= 20) {
580 double x
= ReadReal(obj
);
581 double y
= ReadReal(obj
);
582 state
->pinch_viewport_scroll_offset
= gfx::PointF(x
, y
);
584 state
->pinch_viewport_scroll_offset
= gfx::PointF(-1, -1);
587 bool has_state_object
= ReadBoolean(obj
);
588 if (has_state_object
)
589 state
->state_object
= ReadString(obj
);
591 ReadHttpBody(obj
, &state
->http_body
);
593 // NOTE: It is a quirk of the format that we still have to read the
594 // http_content_type field when the HTTP body is null. That's why this code
595 // is here instead of inside ReadHttpBody.
596 state
->http_body
.http_content_type
= ReadString(obj
);
598 if (obj
->version
< 14)
599 ConsumeString(obj
); // Skip unused referrer string.
601 #if defined(OS_ANDROID)
602 if (obj
->version
== 11) {
603 // Now-unused values that shipped in this version of Chrome for Android when
604 // it was on a private branch.
608 // In this version, page_scale_factor included device_scale_factor and
609 // scroll offsets were premultiplied by pageScaleFactor.
610 if (state
->page_scale_factor
) {
611 float device_scale_factor
= g_device_scale_factor_for_testing
;
612 if (!device_scale_factor
) {
613 device_scale_factor
=
614 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().
615 device_scale_factor();
617 state
->scroll_offset
=
618 gfx::Point(state
->scroll_offset
.x() / state
->page_scale_factor
,
619 state
->scroll_offset
.y() / state
->page_scale_factor
);
620 state
->page_scale_factor
/= device_scale_factor
;
626 size_t num_children
=
627 ReadAndValidateVectorSize(obj
, sizeof(ExplodedFrameState
));
628 state
->children
.resize(num_children
);
629 for (size_t i
= 0; i
< num_children
; ++i
)
630 ReadFrameState(obj
, false, &state
->children
[i
]);
633 void WritePageState(const ExplodedPageState
& state
, SerializeObject
* obj
) {
634 WriteInteger(obj
->version
, obj
);
635 WriteStringVector(state
.referenced_files
, obj
);
636 WriteFrameState(state
.top
, obj
, true);
639 void ReadPageState(SerializeObject
* obj
, ExplodedPageState
* state
) {
640 obj
->version
= ReadInteger(obj
);
642 if (obj
->version
== -1) {
643 GURL url
= ReadGURL(obj
);
644 // NOTE: GURL::possibly_invalid_spec() always returns valid UTF-8.
645 state
->top
.url_string
=
646 base::NullableString16(
647 base::UTF8ToUTF16(url
.possibly_invalid_spec()), false);
651 if (obj
->version
> kCurrentVersion
|| obj
->version
< kMinVersion
) {
652 obj
->parse_error
= true;
656 if (obj
->version
>= 14)
657 ReadStringVector(obj
, &state
->referenced_files
);
659 ReadFrameState(obj
, true, &state
->top
);
661 if (obj
->version
< 14)
662 RecursivelyAppendReferencedFiles(state
->top
, &state
->referenced_files
);
665 state
->referenced_files
.erase(
666 std::unique(state
->referenced_files
.begin(),
667 state
->referenced_files
.end()),
668 state
->referenced_files
.end());
673 ExplodedHttpBodyElement::ExplodedHttpBodyElement()
674 : type(blink::WebHTTPBody::Element::TypeData
),
677 file_modification_time(std::numeric_limits
<double>::quiet_NaN()) {
680 ExplodedHttpBodyElement::~ExplodedHttpBodyElement() {
683 ExplodedHttpBody::ExplodedHttpBody()
685 contains_passwords(false),
689 ExplodedHttpBody::~ExplodedHttpBody() {
692 ExplodedFrameState::ExplodedFrameState()
693 : item_sequence_number(0),
694 document_sequence_number(0),
695 page_scale_factor(0.0),
696 referrer_policy(blink::WebReferrerPolicyDefault
) {
699 ExplodedFrameState::~ExplodedFrameState() {
702 ExplodedPageState::ExplodedPageState() {
705 ExplodedPageState::~ExplodedPageState() {
708 bool DecodePageState(const std::string
& encoded
, ExplodedPageState
* exploded
) {
709 *exploded
= ExplodedPageState();
714 SerializeObject
obj(encoded
.data(), static_cast<int>(encoded
.size()));
715 ReadPageState(&obj
, exploded
);
716 return !obj
.parse_error
;
719 bool EncodePageState(const ExplodedPageState
& exploded
, std::string
* encoded
) {
721 obj
.version
= kCurrentVersion
;
722 WritePageState(exploded
, &obj
);
723 *encoded
= obj
.GetAsString();
727 #if defined(OS_ANDROID)
728 bool DecodePageStateWithDeviceScaleFactorForTesting(
729 const std::string
& encoded
,
730 float device_scale_factor
,
731 ExplodedPageState
* exploded
) {
732 g_device_scale_factor_for_testing
= device_scale_factor
;
733 bool rv
= DecodePageState(encoded
, exploded
);
734 g_device_scale_factor_for_testing
= 0.0;
739 } // namespace content