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 (base::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
= base::PickleIterator(pickle
);
173 std::string
GetAsString() {
174 return std::string(static_cast<const char*>(pickle
.data()), pickle
.size());
178 base::PickleIterator iter
;
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.
197 // 22: Add scroll restoration type.
198 // 23: Remove frame sequence number, there are easier ways.
200 // NOTE: If the version is -1, then the pickle contains only a URL string.
201 // See ReadPageState.
203 const int kMinVersion
= 11;
204 const int kCurrentVersion
= 23;
206 // A bunch of convenience functions to read/write to SerializeObjects. The
207 // de-serializers assume the input data will be in the correct format and fall
208 // back to returning safe defaults when not.
210 void WriteData(const void* data
, int length
, SerializeObject
* obj
) {
211 obj
->pickle
.WriteData(static_cast<const char*>(data
), length
);
214 void ReadData(SerializeObject
* obj
, const void** data
, int* length
) {
216 if (obj
->iter
.ReadData(&tmp
, length
)) {
219 obj
->parse_error
= true;
225 void WriteInteger(int data
, SerializeObject
* obj
) {
226 obj
->pickle
.WriteInt(data
);
229 int ReadInteger(SerializeObject
* obj
) {
231 if (obj
->iter
.ReadInt(&tmp
))
233 obj
->parse_error
= true;
237 void WriteInteger64(int64 data
, SerializeObject
* obj
) {
238 obj
->pickle
.WriteInt64(data
);
241 int64
ReadInteger64(SerializeObject
* obj
) {
243 if (obj
->iter
.ReadInt64(&tmp
))
245 obj
->parse_error
= true;
249 void WriteReal(double data
, SerializeObject
* obj
) {
250 WriteData(&data
, sizeof(double), obj
);
253 double ReadReal(SerializeObject
* obj
) {
254 const void* tmp
= NULL
;
257 ReadData(obj
, &tmp
, &length
);
258 if (length
== static_cast<int>(sizeof(double))) {
259 // Use memcpy, as tmp may not be correctly aligned.
260 memcpy(&value
, tmp
, sizeof(double));
262 obj
->parse_error
= true;
267 void WriteBoolean(bool data
, SerializeObject
* obj
) {
268 obj
->pickle
.WriteInt(data
? 1 : 0);
271 bool ReadBoolean(SerializeObject
* obj
) {
273 if (obj
->iter
.ReadBool(&tmp
))
275 obj
->parse_error
= true;
279 void WriteGURL(const GURL
& url
, SerializeObject
* obj
) {
280 obj
->pickle
.WriteString(url
.possibly_invalid_spec());
283 GURL
ReadGURL(SerializeObject
* obj
) {
285 if (obj
->iter
.ReadString(&spec
))
287 obj
->parse_error
= true;
291 void WriteStdString(const std::string
& s
, SerializeObject
* obj
) {
292 obj
->pickle
.WriteString(s
);
295 std::string
ReadStdString(SerializeObject
* obj
) {
297 if (obj
->iter
.ReadString(&s
))
299 obj
->parse_error
= true;
300 return std::string();
303 // WriteString pickles the NullableString16 as <int length><char16* data>.
304 // If length == -1, then the NullableString16 itself is null. Otherwise the
305 // length is the number of char16 (not bytes) in the NullableString16.
306 void WriteString(const base::NullableString16
& str
, SerializeObject
* obj
) {
308 obj
->pickle
.WriteInt(-1);
310 const base::char16
* data
= str
.string().data();
311 size_t length_in_bytes
= str
.string().length() * sizeof(base::char16
);
313 CHECK_LT(length_in_bytes
,
314 static_cast<size_t>(std::numeric_limits
<int>::max()));
315 obj
->pickle
.WriteInt(length_in_bytes
);
316 obj
->pickle
.WriteBytes(data
, length_in_bytes
);
320 // This reads a serialized NullableString16 from obj. If a string can't be
321 // read, NULL is returned.
322 const base::char16
* ReadStringNoCopy(SerializeObject
* obj
, int* num_chars
) {
324 if (!obj
->iter
.ReadInt(&length_in_bytes
)) {
325 obj
->parse_error
= true;
329 if (length_in_bytes
< 0)
333 if (!obj
->iter
.ReadBytes(&data
, length_in_bytes
)) {
334 obj
->parse_error
= true;
339 *num_chars
= length_in_bytes
/ sizeof(base::char16
);
340 return reinterpret_cast<const base::char16
*>(data
);
343 base::NullableString16
ReadString(SerializeObject
* obj
) {
345 const base::char16
* chars
= ReadStringNoCopy(obj
, &num_chars
);
347 base::NullableString16(base::string16(chars
, num_chars
), false) :
348 base::NullableString16();
351 template <typename T
>
352 void WriteAndValidateVectorSize(const std::vector
<T
>& v
, SerializeObject
* obj
) {
353 CHECK_LT(v
.size(), std::numeric_limits
<int>::max() / sizeof(T
));
354 WriteInteger(static_cast<int>(v
.size()), obj
);
357 size_t ReadAndValidateVectorSize(SerializeObject
* obj
, size_t element_size
) {
358 size_t num_elements
= static_cast<size_t>(ReadInteger(obj
));
360 // Ensure that resizing a vector to size num_elements makes sense.
361 if (std::numeric_limits
<int>::max() / element_size
<= num_elements
) {
362 obj
->parse_error
= true;
366 // Ensure that it is plausible for the pickle to contain num_elements worth
368 if (obj
->pickle
.payload_size() <= num_elements
) {
369 obj
->parse_error
= true;
376 // Writes a Vector of strings into a SerializeObject for serialization.
377 void WriteStringVector(
378 const std::vector
<base::NullableString16
>& data
, SerializeObject
* obj
) {
379 WriteAndValidateVectorSize(data
, obj
);
380 for (size_t i
= 0; i
< data
.size(); ++i
) {
381 WriteString(data
[i
], obj
);
385 void ReadStringVector(SerializeObject
* obj
,
386 std::vector
<base::NullableString16
>* result
) {
387 size_t num_elements
=
388 ReadAndValidateVectorSize(obj
, sizeof(base::NullableString16
));
390 result
->resize(num_elements
);
391 for (size_t i
= 0; i
< num_elements
; ++i
)
392 (*result
)[i
] = ReadString(obj
);
395 // Writes an ExplodedHttpBody object into a SerializeObject for serialization.
396 void WriteHttpBody(const ExplodedHttpBody
& http_body
, SerializeObject
* obj
) {
397 WriteBoolean(!http_body
.is_null
, obj
);
399 if (http_body
.is_null
)
402 WriteAndValidateVectorSize(http_body
.elements
, obj
);
403 for (size_t i
= 0; i
< http_body
.elements
.size(); ++i
) {
404 const ExplodedHttpBodyElement
& element
= http_body
.elements
[i
];
405 WriteInteger(element
.type
, obj
);
406 if (element
.type
== blink::WebHTTPBody::Element::TypeData
) {
407 WriteData(element
.data
.data(), static_cast<int>(element
.data
.size()),
409 } else if (element
.type
== blink::WebHTTPBody::Element::TypeFile
) {
410 WriteString(element
.file_path
, obj
);
411 WriteInteger64(element
.file_start
, obj
);
412 WriteInteger64(element
.file_length
, obj
);
413 WriteReal(element
.file_modification_time
, obj
);
414 } else if (element
.type
==
415 blink::WebHTTPBody::Element::TypeFileSystemURL
) {
416 WriteGURL(element
.filesystem_url
, obj
);
417 WriteInteger64(element
.file_start
, obj
);
418 WriteInteger64(element
.file_length
, obj
);
419 WriteReal(element
.file_modification_time
, obj
);
421 DCHECK(element
.type
== blink::WebHTTPBody::Element::TypeBlob
);
422 WriteStdString(element
.blob_uuid
, obj
);
425 WriteInteger64(http_body
.identifier
, obj
);
426 WriteBoolean(http_body
.contains_passwords
, obj
);
429 void ReadHttpBody(SerializeObject
* obj
, ExplodedHttpBody
* http_body
) {
430 // An initial boolean indicates if we have an HTTP body.
431 if (!ReadBoolean(obj
))
433 http_body
->is_null
= false;
435 int num_elements
= ReadInteger(obj
);
437 for (int i
= 0; i
< num_elements
; ++i
) {
438 int type
= ReadInteger(obj
);
439 if (type
== blink::WebHTTPBody::Element::TypeData
) {
442 ReadData(obj
, &data
, &length
);
444 AppendDataToHttpBody(http_body
, static_cast<const char*>(data
),
447 } else if (type
== blink::WebHTTPBody::Element::TypeFile
) {
448 base::NullableString16 file_path
= ReadString(obj
);
449 int64 file_start
= ReadInteger64(obj
);
450 int64 file_length
= ReadInteger64(obj
);
451 double file_modification_time
= ReadReal(obj
);
452 AppendFileRangeToHttpBody(http_body
, file_path
, file_start
, file_length
,
453 file_modification_time
);
454 } else if (type
== blink::WebHTTPBody::Element::TypeFileSystemURL
) {
455 GURL url
= ReadGURL(obj
);
456 int64 file_start
= ReadInteger64(obj
);
457 int64 file_length
= ReadInteger64(obj
);
458 double file_modification_time
= ReadReal(obj
);
459 AppendURLRangeToHttpBody(http_body
, url
, file_start
, file_length
,
460 file_modification_time
);
461 } else if (type
== blink::WebHTTPBody::Element::TypeBlob
) {
462 if (obj
->version
>= 16) {
463 std::string blob_uuid
= ReadStdString(obj
);
464 AppendBlobToHttpBody(http_body
, blob_uuid
);
466 ReadGURL(obj
); // Skip the obsolete blob url value.
470 http_body
->identifier
= ReadInteger64(obj
);
472 if (obj
->version
>= 12)
473 http_body
->contains_passwords
= ReadBoolean(obj
);
476 // Writes the ExplodedFrameState data into the SerializeObject object for
478 void WriteFrameState(
479 const ExplodedFrameState
& state
, SerializeObject
* obj
, bool is_top
) {
480 // WARNING: This data may be persisted for later use. As such, care must be
481 // taken when changing the serialized format. If a new field needs to be
482 // written, only adding at the end will make it easier to deal with loading
483 // older versions. Similarly, this should NOT save fields with sensitive
484 // data, such as password fields.
486 WriteString(state
.url_string
, obj
);
487 WriteString(state
.target
, obj
);
488 WriteInteger(state
.scroll_offset
.x(), obj
);
489 WriteInteger(state
.scroll_offset
.y(), obj
);
490 WriteString(state
.referrer
, obj
);
492 WriteStringVector(state
.document_state
, obj
);
494 WriteReal(state
.page_scale_factor
, obj
);
495 WriteInteger64(state
.item_sequence_number
, obj
);
496 WriteInteger64(state
.document_sequence_number
, obj
);
497 WriteInteger(state
.referrer_policy
, obj
);
498 WriteReal(state
.pinch_viewport_scroll_offset
.x(), obj
);
499 WriteReal(state
.pinch_viewport_scroll_offset
.y(), obj
);
501 WriteInteger(state
.scroll_restoration_type
, obj
);
503 bool has_state_object
= !state
.state_object
.is_null();
504 WriteBoolean(has_state_object
, obj
);
505 if (has_state_object
)
506 WriteString(state
.state_object
, obj
);
508 WriteHttpBody(state
.http_body
, obj
);
510 // NOTE: It is a quirk of the format that we still have to write the
511 // http_content_type field when the HTTP body is null. That's why this code
512 // is here instead of inside WriteHttpBody.
513 WriteString(state
.http_body
.http_content_type
, obj
);
516 const std::vector
<ExplodedFrameState
>& children
= state
.children
;
517 WriteAndValidateVectorSize(children
, obj
);
518 for (size_t i
= 0; i
< children
.size(); ++i
)
519 WriteFrameState(children
[i
], obj
, false);
522 void ReadFrameState(SerializeObject
* obj
, bool is_top
,
523 ExplodedFrameState
* state
) {
524 if (obj
->version
< 14 && !is_top
)
525 ReadInteger(obj
); // Skip over redundant version field.
527 state
->url_string
= ReadString(obj
);
529 if (obj
->version
< 19)
530 ReadString(obj
); // Skip obsolete original url string field.
532 state
->target
= ReadString(obj
);
533 if (obj
->version
< 15) {
534 ReadString(obj
); // Skip obsolete parent field.
535 ReadString(obj
); // Skip obsolete title field.
536 ReadString(obj
); // Skip obsolete alternate title field.
537 ReadReal(obj
); // Skip obsolete visited time field.
540 int x
= ReadInteger(obj
);
541 int y
= ReadInteger(obj
);
542 state
->scroll_offset
= gfx::Point(x
, y
);
544 if (obj
->version
< 15) {
545 ReadBoolean(obj
); // Skip obsolete target item flag.
546 ReadInteger(obj
); // Skip obsolete visit count field.
548 state
->referrer
= ReadString(obj
);
550 ReadStringVector(obj
, &state
->document_state
);
552 state
->page_scale_factor
= ReadReal(obj
);
553 state
->item_sequence_number
= ReadInteger64(obj
);
554 state
->document_sequence_number
= ReadInteger64(obj
);
555 if (obj
->version
>= 21 && obj
->version
< 23)
556 ReadInteger64(obj
); // Skip obsolete frame sequence number.
558 if (obj
->version
>= 17 && obj
->version
< 19)
559 ReadInteger64(obj
); // Skip obsolete target frame id number.
561 if (obj
->version
>= 18) {
562 state
->referrer_policy
=
563 static_cast<blink::WebReferrerPolicy
>(ReadInteger(obj
));
566 if (obj
->version
>= 20) {
567 double x
= ReadReal(obj
);
568 double y
= ReadReal(obj
);
569 state
->pinch_viewport_scroll_offset
= gfx::PointF(x
, y
);
571 state
->pinch_viewport_scroll_offset
= gfx::PointF(-1, -1);
574 if (obj
->version
>= 22) {
575 state
->scroll_restoration_type
=
576 static_cast<blink::WebHistoryScrollRestorationType
>(ReadInteger(obj
));
579 bool has_state_object
= ReadBoolean(obj
);
580 if (has_state_object
)
581 state
->state_object
= ReadString(obj
);
583 ReadHttpBody(obj
, &state
->http_body
);
585 // NOTE: It is a quirk of the format that we still have to read the
586 // http_content_type field when the HTTP body is null. That's why this code
587 // is here instead of inside ReadHttpBody.
588 state
->http_body
.http_content_type
= ReadString(obj
);
590 if (obj
->version
< 14)
591 ReadString(obj
); // Skip unused referrer string.
593 #if defined(OS_ANDROID)
594 if (obj
->version
== 11) {
595 // Now-unused values that shipped in this version of Chrome for Android when
596 // it was on a private branch.
600 // In this version, page_scale_factor included device_scale_factor and
601 // scroll offsets were premultiplied by pageScaleFactor.
602 if (state
->page_scale_factor
) {
603 float device_scale_factor
= g_device_scale_factor_for_testing
;
604 if (!device_scale_factor
) {
605 device_scale_factor
=
606 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().
607 device_scale_factor();
609 state
->scroll_offset
=
610 gfx::Point(state
->scroll_offset
.x() / state
->page_scale_factor
,
611 state
->scroll_offset
.y() / state
->page_scale_factor
);
612 state
->page_scale_factor
/= device_scale_factor
;
618 size_t num_children
=
619 ReadAndValidateVectorSize(obj
, sizeof(ExplodedFrameState
));
620 state
->children
.resize(num_children
);
621 for (size_t i
= 0; i
< num_children
; ++i
)
622 ReadFrameState(obj
, false, &state
->children
[i
]);
625 void WritePageState(const ExplodedPageState
& state
, SerializeObject
* obj
) {
626 WriteInteger(obj
->version
, obj
);
627 WriteStringVector(state
.referenced_files
, obj
);
628 WriteFrameState(state
.top
, obj
, true);
631 void ReadPageState(SerializeObject
* obj
, ExplodedPageState
* state
) {
632 obj
->version
= ReadInteger(obj
);
634 if (obj
->version
== -1) {
635 GURL url
= ReadGURL(obj
);
636 // NOTE: GURL::possibly_invalid_spec() always returns valid UTF-8.
637 state
->top
.url_string
=
638 base::NullableString16(
639 base::UTF8ToUTF16(url
.possibly_invalid_spec()), false);
643 if (obj
->version
> kCurrentVersion
|| obj
->version
< kMinVersion
) {
644 obj
->parse_error
= true;
648 if (obj
->version
>= 14)
649 ReadStringVector(obj
, &state
->referenced_files
);
651 ReadFrameState(obj
, true, &state
->top
);
653 if (obj
->version
< 14)
654 RecursivelyAppendReferencedFiles(state
->top
, &state
->referenced_files
);
657 state
->referenced_files
.erase(
658 std::unique(state
->referenced_files
.begin(),
659 state
->referenced_files
.end()),
660 state
->referenced_files
.end());
665 ExplodedHttpBodyElement::ExplodedHttpBodyElement()
666 : type(blink::WebHTTPBody::Element::TypeData
),
669 file_modification_time(std::numeric_limits
<double>::quiet_NaN()) {
672 ExplodedHttpBodyElement::~ExplodedHttpBodyElement() {
675 ExplodedHttpBody::ExplodedHttpBody()
677 contains_passwords(false),
681 ExplodedHttpBody::~ExplodedHttpBody() {
684 ExplodedFrameState::ExplodedFrameState()
685 : scroll_restoration_type(blink::WebHistoryScrollRestorationAuto
),
686 item_sequence_number(0),
687 document_sequence_number(0),
688 page_scale_factor(0.0),
689 referrer_policy(blink::WebReferrerPolicyDefault
) {
692 ExplodedFrameState::ExplodedFrameState(const ExplodedFrameState
& other
) {
696 ExplodedFrameState::~ExplodedFrameState() {
699 void ExplodedFrameState::operator=(const ExplodedFrameState
& other
) {
704 void ExplodedFrameState::assign(const ExplodedFrameState
& other
) {
705 url_string
= other
.url_string
;
706 referrer
= other
.referrer
;
707 target
= other
.target
;
708 state_object
= other
.state_object
;
709 document_state
= other
.document_state
;
710 scroll_restoration_type
= other
.scroll_restoration_type
;
711 pinch_viewport_scroll_offset
= other
.pinch_viewport_scroll_offset
;
712 scroll_offset
= other
.scroll_offset
;
713 item_sequence_number
= other
.item_sequence_number
;
714 document_sequence_number
= other
.document_sequence_number
;
715 page_scale_factor
= other
.page_scale_factor
;
716 referrer_policy
= other
.referrer_policy
;
717 http_body
= other
.http_body
;
718 children
= other
.children
;
721 ExplodedPageState::ExplodedPageState() {
724 ExplodedPageState::~ExplodedPageState() {
727 bool DecodePageState(const std::string
& encoded
, ExplodedPageState
* exploded
) {
728 *exploded
= ExplodedPageState();
733 SerializeObject
obj(encoded
.data(), static_cast<int>(encoded
.size()));
734 ReadPageState(&obj
, exploded
);
735 return !obj
.parse_error
;
738 bool EncodePageState(const ExplodedPageState
& exploded
, std::string
* encoded
) {
740 obj
.version
= kCurrentVersion
;
741 WritePageState(exploded
, &obj
);
742 *encoded
= obj
.GetAsString();
746 #if defined(OS_ANDROID)
747 bool DecodePageStateWithDeviceScaleFactorForTesting(
748 const std::string
& encoded
,
749 float device_scale_factor
,
750 ExplodedPageState
* exploded
) {
751 g_device_scale_factor_for_testing
= device_scale_factor
;
752 bool rv
= DecodePageState(encoded
, exploded
);
753 g_device_scale_factor_for_testing
= 0.0;
758 } // namespace content