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.
193 // NOTE: If the version is -1, then the pickle contains only a URL string.
194 // See ReadPageState.
196 const int kMinVersion = 11;
197 const int kCurrentVersion = 18;
199 // A bunch of convenience functions to read/write to SerializeObjects. The
200 // de-serializers assume the input data will be in the correct format and fall
201 // back to returning safe defaults when not.
203 void WriteData(const void* data, int length, SerializeObject* obj) {
204 obj->pickle.WriteData(static_cast<const char*>(data), length);
207 void ReadData(SerializeObject* obj, const void** data, int* length) {
209 if (obj->pickle.ReadData(&obj->iter, &tmp, length)) {
212 obj->parse_error = true;
218 void WriteInteger(int data, SerializeObject* obj) {
219 obj->pickle.WriteInt(data);
222 int ReadInteger(SerializeObject* obj) {
224 if (obj->pickle.ReadInt(&obj->iter, &tmp))
226 obj->parse_error = true;
230 void ConsumeInteger(SerializeObject* obj) {
231 int unused ALLOW_UNUSED = ReadInteger(obj);
234 void WriteInteger64(int64 data, SerializeObject* obj) {
235 obj->pickle.WriteInt64(data);
238 int64 ReadInteger64(SerializeObject* obj) {
240 if (obj->pickle.ReadInt64(&obj->iter, &tmp))
242 obj->parse_error = true;
246 void WriteReal(double data, SerializeObject* obj) {
247 WriteData(&data, sizeof(double), obj);
250 double ReadReal(SerializeObject* obj) {
251 const void* tmp = NULL;
254 ReadData(obj, &tmp, &length);
255 if (length == static_cast<int>(sizeof(double))) {
256 // Use memcpy, as tmp may not be correctly aligned.
257 memcpy(&value, tmp, sizeof(double));
259 obj->parse_error = true;
264 void ConsumeReal(SerializeObject* obj) {
265 double unused ALLOW_UNUSED = ReadReal(obj);
268 void WriteBoolean(bool data, SerializeObject* obj) {
269 obj->pickle.WriteInt(data ? 1 : 0);
272 bool ReadBoolean(SerializeObject* obj) {
274 if (obj->pickle.ReadBool(&obj->iter, &tmp))
276 obj->parse_error = true;
280 void ConsumeBoolean(SerializeObject* obj) {
281 bool unused ALLOW_UNUSED = ReadBoolean(obj);
284 void WriteGURL(const GURL& url, SerializeObject* obj) {
285 obj->pickle.WriteString(url.possibly_invalid_spec());
288 GURL ReadGURL(SerializeObject* obj) {
290 if (obj->pickle.ReadString(&obj->iter, &spec))
292 obj->parse_error = true;
296 void WriteStdString(const std::string& s, SerializeObject* obj) {
297 obj->pickle.WriteString(s);
300 std::string ReadStdString(SerializeObject* obj) {
302 if (obj->pickle.ReadString(&obj->iter, &s))
304 obj->parse_error = true;
305 return std::string();
308 // WriteString pickles the NullableString16 as <int length><char16* data>.
309 // If length == -1, then the NullableString16 itself is null. Otherwise the
310 // length is the number of char16 (not bytes) in the NullableString16.
311 void WriteString(const base::NullableString16& str, SerializeObject* obj) {
313 obj->pickle.WriteInt(-1);
315 const base::char16* data = str.string().data();
316 size_t length_in_bytes = str.string().length() * sizeof(base::char16);
318 CHECK_LT(length_in_bytes,
319 static_cast<size_t>(std::numeric_limits<int>::max()));
320 obj->pickle.WriteInt(length_in_bytes);
321 obj->pickle.WriteBytes(data, length_in_bytes);
325 // This reads a serialized NullableString16 from obj. If a string can't be
326 // read, NULL is returned.
327 const base::char16* ReadStringNoCopy(SerializeObject* obj, int* num_chars) {
329 if (!obj->pickle.ReadInt(&obj->iter, &length_in_bytes)) {
330 obj->parse_error = true;
334 if (length_in_bytes < 0)
338 if (!obj->pickle.ReadBytes(&obj->iter, &data, length_in_bytes)) {
339 obj->parse_error = true;
344 *num_chars = length_in_bytes / sizeof(base::char16);
345 return reinterpret_cast<const base::char16*>(data);
348 base::NullableString16 ReadString(SerializeObject* obj) {
350 const base::char16* chars = ReadStringNoCopy(obj, &num_chars);
352 base::NullableString16(base::string16(chars, num_chars), false) :
353 base::NullableString16();
356 void ConsumeString(SerializeObject* obj) {
357 const base::char16* unused ALLOW_UNUSED = ReadStringNoCopy(obj, NULL);
360 template <typename T>
361 void WriteAndValidateVectorSize(const std::vector<T>& v, SerializeObject* obj) {
362 CHECK_LT(v.size(), std::numeric_limits<int>::max() / sizeof(T));
363 WriteInteger(static_cast<int>(v.size()), obj);
366 size_t ReadAndValidateVectorSize(SerializeObject* obj, size_t element_size) {
367 size_t num_elements = static_cast<size_t>(ReadInteger(obj));
369 // Ensure that resizing a vector to size num_elements makes sense.
370 if (std::numeric_limits<int>::max() / element_size <= num_elements) {
371 obj->parse_error = true;
375 // Ensure that it is plausible for the pickle to contain num_elements worth
377 if (obj->pickle.payload_size() <= num_elements) {
378 obj->parse_error = true;
385 // Writes a Vector of strings into a SerializeObject for serialization.
386 void WriteStringVector(
387 const std::vector<base::NullableString16>& data, SerializeObject* obj) {
388 WriteAndValidateVectorSize(data, obj);
389 for (size_t i = 0; i < data.size(); ++i) {
390 WriteString(data[i], obj);
394 void ReadStringVector(SerializeObject* obj,
395 std::vector<base::NullableString16>* result) {
396 size_t num_elements =
397 ReadAndValidateVectorSize(obj, sizeof(base::NullableString16));
399 result->resize(num_elements);
400 for (size_t i = 0; i < num_elements; ++i)
401 (*result)[i] = ReadString(obj);
404 // Writes an ExplodedHttpBody object into a SerializeObject for serialization.
405 void WriteHttpBody(const ExplodedHttpBody& http_body, SerializeObject* obj) {
406 WriteBoolean(!http_body.is_null, obj);
408 if (http_body.is_null)
411 WriteAndValidateVectorSize(http_body.elements, obj);
412 for (size_t i = 0; i < http_body.elements.size(); ++i) {
413 const ExplodedHttpBodyElement& element = http_body.elements[i];
414 WriteInteger(element.type, obj);
415 if (element.type == blink::WebHTTPBody::Element::TypeData) {
416 WriteData(element.data.data(), static_cast<int>(element.data.size()),
418 } else if (element.type == blink::WebHTTPBody::Element::TypeFile) {
419 WriteString(element.file_path, obj);
420 WriteInteger64(element.file_start, obj);
421 WriteInteger64(element.file_length, obj);
422 WriteReal(element.file_modification_time, obj);
423 } else if (element.type ==
424 blink::WebHTTPBody::Element::TypeFileSystemURL) {
425 WriteGURL(element.filesystem_url, obj);
426 WriteInteger64(element.file_start, obj);
427 WriteInteger64(element.file_length, obj);
428 WriteReal(element.file_modification_time, obj);
430 DCHECK(element.type == blink::WebHTTPBody::Element::TypeBlob);
431 WriteStdString(element.blob_uuid, obj);
434 WriteInteger64(http_body.identifier, obj);
435 WriteBoolean(http_body.contains_passwords, obj);
438 void ReadHttpBody(SerializeObject* obj, ExplodedHttpBody* http_body) {
439 // An initial boolean indicates if we have an HTTP body.
440 if (!ReadBoolean(obj))
442 http_body->is_null = false;
444 int num_elements = ReadInteger(obj);
446 for (int i = 0; i < num_elements; ++i) {
447 int type = ReadInteger(obj);
448 if (type == blink::WebHTTPBody::Element::TypeData) {
451 ReadData(obj, &data, &length);
453 AppendDataToHttpBody(http_body, static_cast<const char*>(data),
456 } else if (type == blink::WebHTTPBody::Element::TypeFile) {
457 base::NullableString16 file_path = ReadString(obj);
458 int64 file_start = ReadInteger64(obj);
459 int64 file_length = ReadInteger64(obj);
460 double file_modification_time = ReadReal(obj);
461 AppendFileRangeToHttpBody(http_body, file_path, file_start, file_length,
462 file_modification_time);
463 } else if (type == blink::WebHTTPBody::Element::TypeFileSystemURL) {
464 GURL url = ReadGURL(obj);
465 int64 file_start = ReadInteger64(obj);
466 int64 file_length = ReadInteger64(obj);
467 double file_modification_time = ReadReal(obj);
468 AppendURLRangeToHttpBody(http_body, url, file_start, file_length,
469 file_modification_time);
470 } else if (type == blink::WebHTTPBody::Element::TypeBlob) {
471 if (obj->version >= 16) {
472 std::string blob_uuid = ReadStdString(obj);
473 AppendBlobToHttpBody(http_body, blob_uuid);
475 ReadGURL(obj); // Skip the obsolete blob url value.
479 http_body->identifier = ReadInteger64(obj);
481 if (obj->version >= 12)
482 http_body->contains_passwords = ReadBoolean(obj);
485 // Writes the ExplodedFrameState data into the SerializeObject object for
487 void WriteFrameState(
488 const ExplodedFrameState& state, SerializeObject* obj, bool is_top) {
489 // WARNING: This data may be persisted for later use. As such, care must be
490 // taken when changing the serialized format. If a new field needs to be
491 // written, only adding at the end will make it easier to deal with loading
492 // older versions. Similarly, this should NOT save fields with sensitive
493 // data, such as password fields.
495 WriteString(state.url_string, obj);
496 WriteString(state.original_url_string, obj);
497 WriteString(state.target, obj);
498 WriteInteger(state.scroll_offset.x(), obj);
499 WriteInteger(state.scroll_offset.y(), obj);
500 WriteString(state.referrer, obj);
502 WriteStringVector(state.document_state, obj);
504 WriteReal(state.page_scale_factor, obj);
505 WriteInteger64(state.item_sequence_number, obj);
506 WriteInteger64(state.document_sequence_number, obj);
507 WriteInteger64(state.target_frame_id, obj);
508 WriteInteger(state.referrer_policy, obj);
510 bool has_state_object = !state.state_object.is_null();
511 WriteBoolean(has_state_object, obj);
512 if (has_state_object)
513 WriteString(state.state_object, obj);
515 WriteHttpBody(state.http_body, obj);
517 // NOTE: It is a quirk of the format that we still have to write the
518 // http_content_type field when the HTTP body is null. That's why this code
519 // is here instead of inside WriteHttpBody.
520 WriteString(state.http_body.http_content_type, obj);
523 const std::vector<ExplodedFrameState>& children = state.children;
524 WriteAndValidateVectorSize(children, obj);
525 for (size_t i = 0; i < children.size(); ++i)
526 WriteFrameState(children[i], obj, false);
529 void ReadFrameState(SerializeObject* obj, bool is_top,
530 ExplodedFrameState* state) {
531 if (obj->version < 14 && !is_top)
532 ConsumeInteger(obj); // Skip over redundant version field.
534 state->url_string = ReadString(obj);
535 state->original_url_string = ReadString(obj);
536 state->target = ReadString(obj);
537 if (obj->version < 15) {
538 ConsumeString(obj); // Skip obsolete parent field.
539 ConsumeString(obj); // Skip obsolete title field.
540 ConsumeString(obj); // Skip obsolete alternate title field.
541 ConsumeReal(obj); // Skip obsolete visited time field.
544 int x = ReadInteger(obj);
545 int y = ReadInteger(obj);
546 state->scroll_offset = gfx::Point(x, y);
548 if (obj->version < 15) {
549 ConsumeBoolean(obj); // Skip obsolete target item flag.
550 ConsumeInteger(obj); // Skip obsolete visit count field.
552 state->referrer = ReadString(obj);
554 ReadStringVector(obj, &state->document_state);
556 state->page_scale_factor = ReadReal(obj);
557 state->item_sequence_number = ReadInteger64(obj);
558 state->document_sequence_number = ReadInteger64(obj);
559 if (obj->version >= 17)
560 state->target_frame_id = ReadInteger64(obj);
561 if (obj->version >= 18) {
562 state->referrer_policy =
563 static_cast<blink::WebReferrerPolicy>(ReadInteger(obj));
566 bool has_state_object = ReadBoolean(obj);
567 if (has_state_object)
568 state->state_object = ReadString(obj);
570 ReadHttpBody(obj, &state->http_body);
572 // NOTE: It is a quirk of the format that we still have to read the
573 // http_content_type field when the HTTP body is null. That's why this code
574 // is here instead of inside ReadHttpBody.
575 state->http_body.http_content_type = ReadString(obj);
577 if (obj->version < 14)
578 ConsumeString(obj); // Skip unused referrer string.
580 #if defined(OS_ANDROID)
581 if (obj->version == 11) {
582 // Now-unused values that shipped in this version of Chrome for Android when
583 // it was on a private branch.
587 // In this version, page_scale_factor included device_scale_factor and
588 // scroll offsets were premultiplied by pageScaleFactor.
589 if (state->page_scale_factor) {
590 float device_scale_factor = g_device_scale_factor_for_testing;
591 if (!device_scale_factor) {
592 device_scale_factor =
593 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().
594 device_scale_factor();
596 state->scroll_offset =
597 gfx::Point(state->scroll_offset.x() / state->page_scale_factor,
598 state->scroll_offset.y() / state->page_scale_factor);
599 state->page_scale_factor /= device_scale_factor;
605 size_t num_children =
606 ReadAndValidateVectorSize(obj, sizeof(ExplodedFrameState));
607 state->children.resize(num_children);
608 for (size_t i = 0; i < num_children; ++i)
609 ReadFrameState(obj, false, &state->children[i]);
612 void WritePageState(const ExplodedPageState& state, SerializeObject* obj) {
613 WriteInteger(obj->version, obj);
614 WriteStringVector(state.referenced_files, obj);
615 WriteFrameState(state.top, obj, true);
618 void ReadPageState(SerializeObject* obj, ExplodedPageState* state) {
619 obj->version = ReadInteger(obj);
621 if (obj->version == -1) {
622 GURL url = ReadGURL(obj);
623 // NOTE: GURL::possibly_invalid_spec() always returns valid UTF-8.
624 state->top.url_string = state->top.original_url_string =
625 base::NullableString16(
626 base::UTF8ToUTF16(url.possibly_invalid_spec()), false);
630 if (obj->version > kCurrentVersion || obj->version < kMinVersion) {
631 obj->parse_error = true;
635 if (obj->version >= 14)
636 ReadStringVector(obj, &state->referenced_files);
638 ReadFrameState(obj, true, &state->top);
640 if (obj->version < 14)
641 RecursivelyAppendReferencedFiles(state->top, &state->referenced_files);
644 state->referenced_files.erase(
645 std::unique(state->referenced_files.begin(),
646 state->referenced_files.end()),
647 state->referenced_files.end());
652 ExplodedHttpBodyElement::ExplodedHttpBodyElement()
653 : type(blink::WebHTTPBody::Element::TypeData),
656 file_modification_time(std::numeric_limits<double>::quiet_NaN()) {
659 ExplodedHttpBodyElement::~ExplodedHttpBodyElement() {
662 ExplodedHttpBody::ExplodedHttpBody()
664 contains_passwords(false),
668 ExplodedHttpBody::~ExplodedHttpBody() {
671 ExplodedFrameState::ExplodedFrameState()
672 : item_sequence_number(0),
673 document_sequence_number(0),
675 page_scale_factor(0.0),
676 referrer_policy(blink::WebReferrerPolicyDefault) {
679 ExplodedFrameState::~ExplodedFrameState() {
682 ExplodedPageState::ExplodedPageState() {
685 ExplodedPageState::~ExplodedPageState() {
688 bool DecodePageState(const std::string& encoded, ExplodedPageState* exploded) {
689 *exploded = ExplodedPageState();
694 SerializeObject obj(encoded.data(), static_cast<int>(encoded.size()));
695 ReadPageState(&obj, exploded);
696 return !obj.parse_error;
699 bool EncodePageState(const ExplodedPageState& exploded, std::string* encoded) {
701 obj.version = kCurrentVersion;
702 WritePageState(exploded, &obj);
703 *encoded = obj.GetAsString();
707 #if defined(OS_ANDROID)
708 bool DecodePageStateWithDeviceScaleFactorForTesting(
709 const std::string& encoded,
710 float device_scale_factor,
711 ExplodedPageState* exploded) {
712 g_device_scale_factor_for_testing = device_scale_factor;
713 bool rv = DecodePageState(encoded, exploded);
714 g_device_scale_factor_for_testing = 0.0;
719 } // namespace content