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.
196 // 21: Add frame sequence number
198 // NOTE: If the version is -1, then the pickle contains only a URL string.
199 // See ReadPageState.
201 const int kMinVersion
= 11;
202 const int kCurrentVersion
= 21;
204 // A bunch of convenience functions to read/write to SerializeObjects. The
205 // de-serializers assume the input data will be in the correct format and fall
206 // back to returning safe defaults when not.
208 void WriteData(const void* data
, int length
, SerializeObject
* obj
) {
209 obj
->pickle
.WriteData(static_cast<const char*>(data
), length
);
212 void ReadData(SerializeObject
* obj
, const void** data
, int* length
) {
214 if (obj
->pickle
.ReadData(&obj
->iter
, &tmp
, length
)) {
217 obj
->parse_error
= true;
223 void WriteInteger(int data
, SerializeObject
* obj
) {
224 obj
->pickle
.WriteInt(data
);
227 int ReadInteger(SerializeObject
* obj
) {
229 if (obj
->pickle
.ReadInt(&obj
->iter
, &tmp
))
231 obj
->parse_error
= true;
235 void ConsumeInteger(SerializeObject
* obj
) {
236 int unused ALLOW_UNUSED
= ReadInteger(obj
);
239 void WriteInteger64(int64 data
, SerializeObject
* obj
) {
240 obj
->pickle
.WriteInt64(data
);
243 int64
ReadInteger64(SerializeObject
* obj
) {
245 if (obj
->pickle
.ReadInt64(&obj
->iter
, &tmp
))
247 obj
->parse_error
= true;
251 void ConsumeInteger64(SerializeObject
* obj
) {
252 int64 unused ALLOW_UNUSED
= ReadInteger64(obj
);
255 void WriteReal(double data
, SerializeObject
* obj
) {
256 WriteData(&data
, sizeof(double), obj
);
259 double ReadReal(SerializeObject
* obj
) {
260 const void* tmp
= NULL
;
263 ReadData(obj
, &tmp
, &length
);
264 if (length
== static_cast<int>(sizeof(double))) {
265 // Use memcpy, as tmp may not be correctly aligned.
266 memcpy(&value
, tmp
, sizeof(double));
268 obj
->parse_error
= true;
273 void ConsumeReal(SerializeObject
* obj
) {
274 double unused ALLOW_UNUSED
= ReadReal(obj
);
277 void WriteBoolean(bool data
, SerializeObject
* obj
) {
278 obj
->pickle
.WriteInt(data
? 1 : 0);
281 bool ReadBoolean(SerializeObject
* obj
) {
283 if (obj
->pickle
.ReadBool(&obj
->iter
, &tmp
))
285 obj
->parse_error
= true;
289 void ConsumeBoolean(SerializeObject
* obj
) {
290 bool unused ALLOW_UNUSED
= ReadBoolean(obj
);
293 void WriteGURL(const GURL
& url
, SerializeObject
* obj
) {
294 obj
->pickle
.WriteString(url
.possibly_invalid_spec());
297 GURL
ReadGURL(SerializeObject
* obj
) {
299 if (obj
->pickle
.ReadString(&obj
->iter
, &spec
))
301 obj
->parse_error
= true;
305 void WriteStdString(const std::string
& s
, SerializeObject
* obj
) {
306 obj
->pickle
.WriteString(s
);
309 std::string
ReadStdString(SerializeObject
* obj
) {
311 if (obj
->pickle
.ReadString(&obj
->iter
, &s
))
313 obj
->parse_error
= true;
314 return std::string();
317 // WriteString pickles the NullableString16 as <int length><char16* data>.
318 // If length == -1, then the NullableString16 itself is null. Otherwise the
319 // length is the number of char16 (not bytes) in the NullableString16.
320 void WriteString(const base::NullableString16
& str
, SerializeObject
* obj
) {
322 obj
->pickle
.WriteInt(-1);
324 const base::char16
* data
= str
.string().data();
325 size_t length_in_bytes
= str
.string().length() * sizeof(base::char16
);
327 CHECK_LT(length_in_bytes
,
328 static_cast<size_t>(std::numeric_limits
<int>::max()));
329 obj
->pickle
.WriteInt(length_in_bytes
);
330 obj
->pickle
.WriteBytes(data
, length_in_bytes
);
334 // This reads a serialized NullableString16 from obj. If a string can't be
335 // read, NULL is returned.
336 const base::char16
* ReadStringNoCopy(SerializeObject
* obj
, int* num_chars
) {
338 if (!obj
->pickle
.ReadInt(&obj
->iter
, &length_in_bytes
)) {
339 obj
->parse_error
= true;
343 if (length_in_bytes
< 0)
347 if (!obj
->pickle
.ReadBytes(&obj
->iter
, &data
, length_in_bytes
)) {
348 obj
->parse_error
= true;
353 *num_chars
= length_in_bytes
/ sizeof(base::char16
);
354 return reinterpret_cast<const base::char16
*>(data
);
357 base::NullableString16
ReadString(SerializeObject
* obj
) {
359 const base::char16
* chars
= ReadStringNoCopy(obj
, &num_chars
);
361 base::NullableString16(base::string16(chars
, num_chars
), false) :
362 base::NullableString16();
365 void ConsumeString(SerializeObject
* obj
) {
366 const base::char16
* unused ALLOW_UNUSED
= ReadStringNoCopy(obj
, NULL
);
369 template <typename T
>
370 void WriteAndValidateVectorSize(const std::vector
<T
>& v
, SerializeObject
* obj
) {
371 CHECK_LT(v
.size(), std::numeric_limits
<int>::max() / sizeof(T
));
372 WriteInteger(static_cast<int>(v
.size()), obj
);
375 size_t ReadAndValidateVectorSize(SerializeObject
* obj
, size_t element_size
) {
376 size_t num_elements
= static_cast<size_t>(ReadInteger(obj
));
378 // Ensure that resizing a vector to size num_elements makes sense.
379 if (std::numeric_limits
<int>::max() / element_size
<= num_elements
) {
380 obj
->parse_error
= true;
384 // Ensure that it is plausible for the pickle to contain num_elements worth
386 if (obj
->pickle
.payload_size() <= num_elements
) {
387 obj
->parse_error
= true;
394 // Writes a Vector of strings into a SerializeObject for serialization.
395 void WriteStringVector(
396 const std::vector
<base::NullableString16
>& data
, SerializeObject
* obj
) {
397 WriteAndValidateVectorSize(data
, obj
);
398 for (size_t i
= 0; i
< data
.size(); ++i
) {
399 WriteString(data
[i
], obj
);
403 void ReadStringVector(SerializeObject
* obj
,
404 std::vector
<base::NullableString16
>* result
) {
405 size_t num_elements
=
406 ReadAndValidateVectorSize(obj
, sizeof(base::NullableString16
));
408 result
->resize(num_elements
);
409 for (size_t i
= 0; i
< num_elements
; ++i
)
410 (*result
)[i
] = ReadString(obj
);
413 // Writes an ExplodedHttpBody object into a SerializeObject for serialization.
414 void WriteHttpBody(const ExplodedHttpBody
& http_body
, SerializeObject
* obj
) {
415 WriteBoolean(!http_body
.is_null
, obj
);
417 if (http_body
.is_null
)
420 WriteAndValidateVectorSize(http_body
.elements
, obj
);
421 for (size_t i
= 0; i
< http_body
.elements
.size(); ++i
) {
422 const ExplodedHttpBodyElement
& element
= http_body
.elements
[i
];
423 WriteInteger(element
.type
, obj
);
424 if (element
.type
== blink::WebHTTPBody::Element::TypeData
) {
425 WriteData(element
.data
.data(), static_cast<int>(element
.data
.size()),
427 } else if (element
.type
== blink::WebHTTPBody::Element::TypeFile
) {
428 WriteString(element
.file_path
, obj
);
429 WriteInteger64(element
.file_start
, obj
);
430 WriteInteger64(element
.file_length
, obj
);
431 WriteReal(element
.file_modification_time
, obj
);
432 } else if (element
.type
==
433 blink::WebHTTPBody::Element::TypeFileSystemURL
) {
434 WriteGURL(element
.filesystem_url
, obj
);
435 WriteInteger64(element
.file_start
, obj
);
436 WriteInteger64(element
.file_length
, obj
);
437 WriteReal(element
.file_modification_time
, obj
);
439 DCHECK(element
.type
== blink::WebHTTPBody::Element::TypeBlob
);
440 WriteStdString(element
.blob_uuid
, obj
);
443 WriteInteger64(http_body
.identifier
, obj
);
444 WriteBoolean(http_body
.contains_passwords
, obj
);
447 void ReadHttpBody(SerializeObject
* obj
, ExplodedHttpBody
* http_body
) {
448 // An initial boolean indicates if we have an HTTP body.
449 if (!ReadBoolean(obj
))
451 http_body
->is_null
= false;
453 int num_elements
= ReadInteger(obj
);
455 for (int i
= 0; i
< num_elements
; ++i
) {
456 int type
= ReadInteger(obj
);
457 if (type
== blink::WebHTTPBody::Element::TypeData
) {
460 ReadData(obj
, &data
, &length
);
462 AppendDataToHttpBody(http_body
, static_cast<const char*>(data
),
465 } else if (type
== blink::WebHTTPBody::Element::TypeFile
) {
466 base::NullableString16 file_path
= ReadString(obj
);
467 int64 file_start
= ReadInteger64(obj
);
468 int64 file_length
= ReadInteger64(obj
);
469 double file_modification_time
= ReadReal(obj
);
470 AppendFileRangeToHttpBody(http_body
, file_path
, file_start
, file_length
,
471 file_modification_time
);
472 } else if (type
== blink::WebHTTPBody::Element::TypeFileSystemURL
) {
473 GURL url
= ReadGURL(obj
);
474 int64 file_start
= ReadInteger64(obj
);
475 int64 file_length
= ReadInteger64(obj
);
476 double file_modification_time
= ReadReal(obj
);
477 AppendURLRangeToHttpBody(http_body
, url
, file_start
, file_length
,
478 file_modification_time
);
479 } else if (type
== blink::WebHTTPBody::Element::TypeBlob
) {
480 if (obj
->version
>= 16) {
481 std::string blob_uuid
= ReadStdString(obj
);
482 AppendBlobToHttpBody(http_body
, blob_uuid
);
484 ReadGURL(obj
); // Skip the obsolete blob url value.
488 http_body
->identifier
= ReadInteger64(obj
);
490 if (obj
->version
>= 12)
491 http_body
->contains_passwords
= ReadBoolean(obj
);
494 // Writes the ExplodedFrameState data into the SerializeObject object for
496 void WriteFrameState(
497 const ExplodedFrameState
& state
, SerializeObject
* obj
, bool is_top
) {
498 // WARNING: This data may be persisted for later use. As such, care must be
499 // taken when changing the serialized format. If a new field needs to be
500 // written, only adding at the end will make it easier to deal with loading
501 // older versions. Similarly, this should NOT save fields with sensitive
502 // data, such as password fields.
504 WriteString(state
.url_string
, obj
);
505 WriteString(state
.target
, obj
);
506 WriteInteger(state
.scroll_offset
.x(), obj
);
507 WriteInteger(state
.scroll_offset
.y(), obj
);
508 WriteString(state
.referrer
, obj
);
510 WriteStringVector(state
.document_state
, obj
);
512 WriteReal(state
.page_scale_factor
, obj
);
513 WriteInteger64(state
.item_sequence_number
, obj
);
514 WriteInteger64(state
.document_sequence_number
, obj
);
515 WriteInteger64(state
.frame_sequence_number
, obj
);
516 WriteInteger(state
.referrer_policy
, obj
);
517 WriteReal(state
.pinch_viewport_scroll_offset
.x(), obj
);
518 WriteReal(state
.pinch_viewport_scroll_offset
.y(), obj
);
520 bool has_state_object
= !state
.state_object
.is_null();
521 WriteBoolean(has_state_object
, obj
);
522 if (has_state_object
)
523 WriteString(state
.state_object
, obj
);
525 WriteHttpBody(state
.http_body
, obj
);
527 // NOTE: It is a quirk of the format that we still have to write the
528 // http_content_type field when the HTTP body is null. That's why this code
529 // is here instead of inside WriteHttpBody.
530 WriteString(state
.http_body
.http_content_type
, obj
);
533 const std::vector
<ExplodedFrameState
>& children
= state
.children
;
534 WriteAndValidateVectorSize(children
, obj
);
535 for (size_t i
= 0; i
< children
.size(); ++i
)
536 WriteFrameState(children
[i
], obj
, false);
539 void ReadFrameState(SerializeObject
* obj
, bool is_top
,
540 ExplodedFrameState
* state
) {
541 if (obj
->version
< 14 && !is_top
)
542 ConsumeInteger(obj
); // Skip over redundant version field.
544 state
->url_string
= ReadString(obj
);
546 if (obj
->version
< 19)
547 ConsumeString(obj
); // Skip obsolete original url string field.
549 state
->target
= ReadString(obj
);
550 if (obj
->version
< 15) {
551 ConsumeString(obj
); // Skip obsolete parent field.
552 ConsumeString(obj
); // Skip obsolete title field.
553 ConsumeString(obj
); // Skip obsolete alternate title field.
554 ConsumeReal(obj
); // Skip obsolete visited time field.
557 int x
= ReadInteger(obj
);
558 int y
= ReadInteger(obj
);
559 state
->scroll_offset
= gfx::Point(x
, y
);
561 if (obj
->version
< 15) {
562 ConsumeBoolean(obj
); // Skip obsolete target item flag.
563 ConsumeInteger(obj
); // Skip obsolete visit count field.
565 state
->referrer
= ReadString(obj
);
567 ReadStringVector(obj
, &state
->document_state
);
569 state
->page_scale_factor
= ReadReal(obj
);
570 state
->item_sequence_number
= ReadInteger64(obj
);
571 state
->document_sequence_number
= ReadInteger64(obj
);
572 if (obj
->version
>= 21)
573 state
->frame_sequence_number
= ReadInteger64(obj
);
575 if (obj
->version
>= 17 && obj
->version
< 19)
576 ConsumeInteger64(obj
); // Skip obsolete target frame id number.
578 if (obj
->version
>= 18) {
579 state
->referrer_policy
=
580 static_cast<blink::WebReferrerPolicy
>(ReadInteger(obj
));
583 if (obj
->version
>= 20) {
584 double x
= ReadReal(obj
);
585 double y
= ReadReal(obj
);
586 state
->pinch_viewport_scroll_offset
= gfx::PointF(x
, y
);
588 state
->pinch_viewport_scroll_offset
= gfx::PointF(-1, -1);
591 bool has_state_object
= ReadBoolean(obj
);
592 if (has_state_object
)
593 state
->state_object
= ReadString(obj
);
595 ReadHttpBody(obj
, &state
->http_body
);
597 // NOTE: It is a quirk of the format that we still have to read the
598 // http_content_type field when the HTTP body is null. That's why this code
599 // is here instead of inside ReadHttpBody.
600 state
->http_body
.http_content_type
= ReadString(obj
);
602 if (obj
->version
< 14)
603 ConsumeString(obj
); // Skip unused referrer string.
605 #if defined(OS_ANDROID)
606 if (obj
->version
== 11) {
607 // Now-unused values that shipped in this version of Chrome for Android when
608 // it was on a private branch.
612 // In this version, page_scale_factor included device_scale_factor and
613 // scroll offsets were premultiplied by pageScaleFactor.
614 if (state
->page_scale_factor
) {
615 float device_scale_factor
= g_device_scale_factor_for_testing
;
616 if (!device_scale_factor
) {
617 device_scale_factor
=
618 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().
619 device_scale_factor();
621 state
->scroll_offset
=
622 gfx::Point(state
->scroll_offset
.x() / state
->page_scale_factor
,
623 state
->scroll_offset
.y() / state
->page_scale_factor
);
624 state
->page_scale_factor
/= device_scale_factor
;
630 size_t num_children
=
631 ReadAndValidateVectorSize(obj
, sizeof(ExplodedFrameState
));
632 state
->children
.resize(num_children
);
633 for (size_t i
= 0; i
< num_children
; ++i
)
634 ReadFrameState(obj
, false, &state
->children
[i
]);
637 void WritePageState(const ExplodedPageState
& state
, SerializeObject
* obj
) {
638 WriteInteger(obj
->version
, obj
);
639 WriteStringVector(state
.referenced_files
, obj
);
640 WriteFrameState(state
.top
, obj
, true);
643 void ReadPageState(SerializeObject
* obj
, ExplodedPageState
* state
) {
644 obj
->version
= ReadInteger(obj
);
646 if (obj
->version
== -1) {
647 GURL url
= ReadGURL(obj
);
648 // NOTE: GURL::possibly_invalid_spec() always returns valid UTF-8.
649 state
->top
.url_string
=
650 base::NullableString16(
651 base::UTF8ToUTF16(url
.possibly_invalid_spec()), false);
655 if (obj
->version
> kCurrentVersion
|| obj
->version
< kMinVersion
) {
656 obj
->parse_error
= true;
660 if (obj
->version
>= 14)
661 ReadStringVector(obj
, &state
->referenced_files
);
663 ReadFrameState(obj
, true, &state
->top
);
665 if (obj
->version
< 14)
666 RecursivelyAppendReferencedFiles(state
->top
, &state
->referenced_files
);
669 state
->referenced_files
.erase(
670 std::unique(state
->referenced_files
.begin(),
671 state
->referenced_files
.end()),
672 state
->referenced_files
.end());
677 ExplodedHttpBodyElement::ExplodedHttpBodyElement()
678 : type(blink::WebHTTPBody::Element::TypeData
),
681 file_modification_time(std::numeric_limits
<double>::quiet_NaN()) {
684 ExplodedHttpBodyElement::~ExplodedHttpBodyElement() {
687 ExplodedHttpBody::ExplodedHttpBody()
689 contains_passwords(false),
693 ExplodedHttpBody::~ExplodedHttpBody() {
696 ExplodedFrameState::ExplodedFrameState()
697 : item_sequence_number(0),
698 document_sequence_number(0),
699 frame_sequence_number(0),
700 page_scale_factor(0.0),
701 referrer_policy(blink::WebReferrerPolicyDefault
) {
704 ExplodedFrameState::ExplodedFrameState(const ExplodedFrameState
& other
) {
708 ExplodedFrameState::~ExplodedFrameState() {
711 void ExplodedFrameState::operator=(const ExplodedFrameState
& other
) {
716 void ExplodedFrameState::assign(const ExplodedFrameState
& other
) {
717 url_string
= other
.url_string
;
718 referrer
= other
.referrer
;
719 target
= other
.target
;
720 state_object
= other
.state_object
;
721 document_state
= other
.document_state
;
722 pinch_viewport_scroll_offset
= other
.pinch_viewport_scroll_offset
;
723 scroll_offset
= other
.scroll_offset
;
724 item_sequence_number
= other
.item_sequence_number
;
725 document_sequence_number
= other
.document_sequence_number
;
726 frame_sequence_number
= other
.frame_sequence_number
;
727 page_scale_factor
= other
.page_scale_factor
;
728 referrer_policy
= other
.referrer_policy
;
729 http_body
= other
.http_body
;
730 children
= other
.children
;
733 ExplodedPageState::ExplodedPageState() {
736 ExplodedPageState::~ExplodedPageState() {
739 bool DecodePageState(const std::string
& encoded
, ExplodedPageState
* exploded
) {
740 *exploded
= ExplodedPageState();
745 SerializeObject
obj(encoded
.data(), static_cast<int>(encoded
.size()));
746 ReadPageState(&obj
, exploded
);
747 return !obj
.parse_error
;
750 bool EncodePageState(const ExplodedPageState
& exploded
, std::string
* encoded
) {
752 obj
.version
= kCurrentVersion
;
753 WritePageState(exploded
, &obj
);
754 *encoded
= obj
.GetAsString();
758 #if defined(OS_ANDROID)
759 bool DecodePageStateWithDeviceScaleFactorForTesting(
760 const std::string
& encoded
,
761 float device_scale_factor
,
762 ExplodedPageState
* exploded
) {
763 g_device_scale_factor_for_testing
= device_scale_factor
;
764 bool rv
= DecodePageState(encoded
, exploded
);
765 g_device_scale_factor_for_testing
= 0.0;
770 } // namespace content