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. 4 5 #include "content/common/page_state_serialization.h" 6 7 #include <algorithm> 8 #include <limits> 9 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" 15 16 namespace content { 17 namespace { 18 19 #if defined(OS_ANDROID) 20 float g_device_scale_factor_for_testing = 0.0; 21 #endif 22 23 //----------------------------------------------------------------------------- 24 25 void AppendDataToHttpBody(ExplodedHttpBody* http_body, const char* data, 26 int data_length) { 27 ExplodedHttpBodyElement element; 28 element.type = blink::WebHTTPBody::Element::TypeData; 29 element.data.assign(data, data_length); 30 http_body->elements.push_back(element); 31 } 32 33 void AppendFileRangeToHttpBody(ExplodedHttpBody* http_body, 34 const base::NullableString16& file_path, 35 int file_start, 36 int file_length, 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); 45 } 46 47 void AppendURLRangeToHttpBody(ExplodedHttpBody* http_body, 48 const GURL& url, 49 int file_start, 50 int file_length, 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); 59 } 60 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); 67 } 68 69 //---------------------------------------------------------------------------- 70 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); 77 } 78 } 79 80 bool AppendReferencedFilesFromDocumentState( 81 const std::vector<base::NullableString16>& document_state, 82 std::vector<base::NullableString16>* referenced_files) { 83 if (document_state.empty()) 84 return true; 85 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. 89 // 90 // For reference, see FormController::formStatesFromStateVector at: 91 // http://src.chromium.org/viewvc/blink/trunk/Source/core/html/FormController.cpp?pathrev=152274 92 93 size_t index = 0; 94 95 if (document_state.size() < 3) 96 return false; 97 98 index++; // Skip over magic signature. 99 index++; // Skip over form key. 100 101 size_t item_count; 102 if (!base::StringToSizeT(document_state[index++].string(), &item_count)) 103 return false; 104 105 while (item_count--) { 106 if (index + 1 >= document_state.size()) 107 return false; 108 109 index++; // Skip over name. 110 const base::NullableString16& type = document_state[index++]; 111 112 if (index >= document_state.size()) 113 return false; 114 115 size_t value_size; 116 if (!base::StringToSizeT(document_state[index++].string(), &value_size)) 117 return false; 118 119 if (index + value_size > document_state.size() || 120 index + value_size < index) // Check for overflow. 121 return false; 122 123 if (EqualsASCII(type.string(), "file")) { 124 if (value_size != 2) 125 return false; 126 127 referenced_files->push_back(document_state[index++]); 128 index++; // Skip over display name. 129 } else { 130 index += value_size; 131 } 132 } 133 134 return true; 135 } 136 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, 142 referenced_files); 143 } 144 145 if (!AppendReferencedFilesFromDocumentState(frame_state.document_state, 146 referenced_files)) 147 return false; 148 149 for (size_t i = 0; i < frame_state.children.size(); ++i) { 150 if (!RecursivelyAppendReferencedFiles(frame_state.children[i], 151 referenced_files)) 152 return false; 153 } 154 155 return true; 156 } 157 158 //---------------------------------------------------------------------------- 159 160 struct SerializeObject { 161 SerializeObject() 162 : version(0), 163 parse_error(false) { 164 } 165 166 SerializeObject(const char* data, int len) 167 : pickle(data, len), 168 version(0), 169 parse_error(false) { 170 iter = PickleIterator(pickle); 171 } 172 173 std::string GetAsString() { 174 return std::string(static_cast<const char*>(pickle.data()), pickle.size()); 175 } 176 177 Pickle pickle; 178 PickleIterator iter; 179 int version; 180 bool parse_error; 181 }; 182 183 // Version ID of serialized format. 184 // 11: Min version 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 // 197 // NOTE: If the version is -1, then the pickle contains only a URL string. 198 // See ReadPageState. 199 // 200 const int kMinVersion = 11; 201 const int kCurrentVersion = 20; 202 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. 206 207 void WriteData(const void* data, int length, SerializeObject* obj) { 208 obj->pickle.WriteData(static_cast<const char*>(data), length); 209 } 210 211 void ReadData(SerializeObject* obj, const void** data, int* length) { 212 const char* tmp; 213 if (obj->pickle.ReadData(&obj->iter, &tmp, length)) { 214 *data = tmp; 215 } else { 216 obj->parse_error = true; 217 *data = NULL; 218 *length = 0; 219 } 220 } 221 222 void WriteInteger(int data, SerializeObject* obj) { 223 obj->pickle.WriteInt(data); 224 } 225 226 int ReadInteger(SerializeObject* obj) { 227 int tmp; 228 if (obj->pickle.ReadInt(&obj->iter, &tmp)) 229 return tmp; 230 obj->parse_error = true; 231 return 0; 232 } 233 234 void ConsumeInteger(SerializeObject* obj) { 235 int unused ALLOW_UNUSED = ReadInteger(obj); 236 } 237 238 void WriteInteger64(int64 data, SerializeObject* obj) { 239 obj->pickle.WriteInt64(data); 240 } 241 242 int64 ReadInteger64(SerializeObject* obj) { 243 int64 tmp = 0; 244 if (obj->pickle.ReadInt64(&obj->iter, &tmp)) 245 return tmp; 246 obj->parse_error = true; 247 return 0; 248 } 249 250 void ConsumeInteger64(SerializeObject* obj) { 251 int64 unused ALLOW_UNUSED = ReadInteger64(obj); 252 } 253 254 void WriteReal(double data, SerializeObject* obj) { 255 WriteData(&data, sizeof(double), obj); 256 } 257 258 double ReadReal(SerializeObject* obj) { 259 const void* tmp = NULL; 260 int length = 0; 261 double value = 0.0; 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)); 266 } else { 267 obj->parse_error = true; 268 } 269 return value; 270 } 271 272 void ConsumeReal(SerializeObject* obj) { 273 double unused ALLOW_UNUSED = ReadReal(obj); 274 } 275 276 void WriteBoolean(bool data, SerializeObject* obj) { 277 obj->pickle.WriteInt(data ? 1 : 0); 278 } 279 280 bool ReadBoolean(SerializeObject* obj) { 281 bool tmp; 282 if (obj->pickle.ReadBool(&obj->iter, &tmp)) 283 return tmp; 284 obj->parse_error = true; 285 return false; 286 } 287 288 void ConsumeBoolean(SerializeObject* obj) { 289 bool unused ALLOW_UNUSED = ReadBoolean(obj); 290 } 291 292 void WriteGURL(const GURL& url, SerializeObject* obj) { 293 obj->pickle.WriteString(url.possibly_invalid_spec()); 294 } 295 296 GURL ReadGURL(SerializeObject* obj) { 297 std::string spec; 298 if (obj->pickle.ReadString(&obj->iter, &spec)) 299 return GURL(spec); 300 obj->parse_error = true; 301 return GURL(); 302 } 303 304 void WriteStdString(const std::string& s, SerializeObject* obj) { 305 obj->pickle.WriteString(s); 306 } 307 308 std::string ReadStdString(SerializeObject* obj) { 309 std::string s; 310 if (obj->pickle.ReadString(&obj->iter, &s)) 311 return s; 312 obj->parse_error = true; 313 return std::string(); 314 } 315 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) { 320 if (str.is_null()) { 321 obj->pickle.WriteInt(-1); 322 } else { 323 const base::char16* data = str.string().data(); 324 size_t length_in_bytes = str.string().length() * sizeof(base::char16); 325 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); 330 } 331 } 332 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) { 336 int length_in_bytes; 337 if (!obj->pickle.ReadInt(&obj->iter, &length_in_bytes)) { 338 obj->parse_error = true; 339 return NULL; 340 } 341 342 if (length_in_bytes < 0) 343 return NULL; 344 345 const char* data; 346 if (!obj->pickle.ReadBytes(&obj->iter, &data, length_in_bytes)) { 347 obj->parse_error = true; 348 return NULL; 349 } 350 351 if (num_chars) 352 *num_chars = length_in_bytes / sizeof(base::char16); 353 return reinterpret_cast<const base::char16*>(data); 354 } 355 356 base::NullableString16 ReadString(SerializeObject* obj) { 357 int num_chars; 358 const base::char16* chars = ReadStringNoCopy(obj, &num_chars); 359 return chars ? 360 base::NullableString16(base::string16(chars, num_chars), false) : 361 base::NullableString16(); 362 } 363 364 void ConsumeString(SerializeObject* obj) { 365 const base::char16* unused ALLOW_UNUSED = ReadStringNoCopy(obj, NULL); 366 } 367 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); 372 } 373 374 size_t ReadAndValidateVectorSize(SerializeObject* obj, size_t element_size) { 375 size_t num_elements = static_cast<size_t>(ReadInteger(obj)); 376 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; 380 return 0; 381 } 382 383 // Ensure that it is plausible for the pickle to contain num_elements worth 384 // of data. 385 if (obj->pickle.payload_size() <= num_elements) { 386 obj->parse_error = true; 387 return 0; 388 } 389 390 return num_elements; 391 } 392 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); 399 } 400 } 401 402 void ReadStringVector(SerializeObject* obj, 403 std::vector<base::NullableString16>* result) { 404 size_t num_elements = 405 ReadAndValidateVectorSize(obj, sizeof(base::NullableString16)); 406 407 result->resize(num_elements); 408 for (size_t i = 0; i < num_elements; ++i) 409 (*result)[i] = ReadString(obj); 410 } 411 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); 415 416 if (http_body.is_null) 417 return; 418 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()), 425 obj); 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); 437 } else { 438 DCHECK(element.type == blink::WebHTTPBody::Element::TypeBlob); 439 WriteStdString(element.blob_uuid, obj); 440 } 441 } 442 WriteInteger64(http_body.identifier, obj); 443 WriteBoolean(http_body.contains_passwords, obj); 444 } 445 446 void ReadHttpBody(SerializeObject* obj, ExplodedHttpBody* http_body) { 447 // An initial boolean indicates if we have an HTTP body. 448 if (!ReadBoolean(obj)) 449 return; 450 http_body->is_null = false; 451 452 int num_elements = ReadInteger(obj); 453 454 for (int i = 0; i < num_elements; ++i) { 455 int type = ReadInteger(obj); 456 if (type == blink::WebHTTPBody::Element::TypeData) { 457 const void* data; 458 int length = -1; 459 ReadData(obj, &data, &length); 460 if (length >= 0) { 461 AppendDataToHttpBody(http_body, static_cast<const char*>(data), 462 length); 463 } 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); 482 } else { 483 ReadGURL(obj); // Skip the obsolete blob url value. 484 } 485 } 486 } 487 http_body->identifier = ReadInteger64(obj); 488 489 if (obj->version >= 12) 490 http_body->contains_passwords = ReadBoolean(obj); 491 } 492 493 // Writes the ExplodedFrameState data into the SerializeObject object for 494 // serialization. 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. 502 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); 508 509 WriteStringVector(state.document_state, obj); 510 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); 517 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); 522 523 WriteHttpBody(state.http_body, obj); 524 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); 529 530 // Subitems 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); 535 } 536 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. 541 542 state->url_string = ReadString(obj); 543 544 if (obj->version < 19) 545 ConsumeString(obj); // Skip obsolete original url string field. 546 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. 553 } 554 555 int x = ReadInteger(obj); 556 int y = ReadInteger(obj); 557 state->scroll_offset = gfx::Point(x, y); 558 559 if (obj->version < 15) { 560 ConsumeBoolean(obj); // Skip obsolete target item flag. 561 ConsumeInteger(obj); // Skip obsolete visit count field. 562 } 563 state->referrer = ReadString(obj); 564 565 ReadStringVector(obj, &state->document_state); 566 567 state->page_scale_factor = ReadReal(obj); 568 state->item_sequence_number = ReadInteger64(obj); 569 state->document_sequence_number = ReadInteger64(obj); 570 571 if (obj->version >= 17 && obj->version < 19) 572 ConsumeInteger64(obj); // Skip obsolete target frame id number. 573 574 if (obj->version >= 18) { 575 state->referrer_policy = 576 static_cast<blink::WebReferrerPolicy>(ReadInteger(obj)); 577 } 578 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); 583 } else { 584 state->pinch_viewport_scroll_offset = gfx::PointF(-1, -1); 585 } 586 587 bool has_state_object = ReadBoolean(obj); 588 if (has_state_object) 589 state->state_object = ReadString(obj); 590 591 ReadHttpBody(obj, &state->http_body); 592 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); 597 598 if (obj->version < 14) 599 ConsumeString(obj); // Skip unused referrer string. 600 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. 605 ReadReal(obj); 606 ReadBoolean(obj); 607 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(); 616 } 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; 621 } 622 } 623 #endif 624 625 // Subitems 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]); 631 } 632 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); 637 } 638 639 void ReadPageState(SerializeObject* obj, ExplodedPageState* state) { 640 obj->version = ReadInteger(obj); 641 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); 648 return; 649 } 650 651 if (obj->version > kCurrentVersion || obj->version < kMinVersion) { 652 obj->parse_error = true; 653 return; 654 } 655 656 if (obj->version >= 14) 657 ReadStringVector(obj, &state->referenced_files); 658 659 ReadFrameState(obj, true, &state->top); 660 661 if (obj->version < 14) 662 RecursivelyAppendReferencedFiles(state->top, &state->referenced_files); 663 664 // De-dupe 665 state->referenced_files.erase( 666 std::unique(state->referenced_files.begin(), 667 state->referenced_files.end()), 668 state->referenced_files.end()); 669 } 670 671 } // namespace 672 673 ExplodedHttpBodyElement::ExplodedHttpBodyElement() 674 : type(blink::WebHTTPBody::Element::TypeData), 675 file_start(0), 676 file_length(-1), 677 file_modification_time(std::numeric_limits<double>::quiet_NaN()) { 678 } 679 680 ExplodedHttpBodyElement::~ExplodedHttpBodyElement() { 681 } 682 683 ExplodedHttpBody::ExplodedHttpBody() 684 : identifier(0), 685 contains_passwords(false), 686 is_null(true) { 687 } 688 689 ExplodedHttpBody::~ExplodedHttpBody() { 690 } 691 692 ExplodedFrameState::ExplodedFrameState() 693 : item_sequence_number(0), 694 document_sequence_number(0), 695 page_scale_factor(0.0), 696 referrer_policy(blink::WebReferrerPolicyDefault) { 697 } 698 699 ExplodedFrameState::~ExplodedFrameState() { 700 } 701 702 ExplodedPageState::ExplodedPageState() { 703 } 704 705 ExplodedPageState::~ExplodedPageState() { 706 } 707 708 bool DecodePageState(const std::string& encoded, ExplodedPageState* exploded) { 709 *exploded = ExplodedPageState(); 710 711 if (encoded.empty()) 712 return true; 713 714 SerializeObject obj(encoded.data(), static_cast<int>(encoded.size())); 715 ReadPageState(&obj, exploded); 716 return !obj.parse_error; 717 } 718 719 bool EncodePageState(const ExplodedPageState& exploded, std::string* encoded) { 720 SerializeObject obj; 721 obj.version = kCurrentVersion; 722 WritePageState(exploded, &obj); 723 *encoded = obj.GetAsString(); 724 return true; 725 } 726 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; 735 return rv; 736 } 737 #endif 738 739 } // namespace content 740