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
16namespace content {
17namespace {
18
19#if defined(OS_ANDROID)
20float g_device_scale_factor_for_testing = 0.0;
21#endif
22
23//-----------------------------------------------------------------------------
24
25void 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
33void 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
47void 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
61void 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
71void 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
80bool 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
137bool 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
160struct 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// 21: Add frame sequence number
197//
198// NOTE: If the version is -1, then the pickle contains only a URL string.
199// See ReadPageState.
200//
201const int kMinVersion = 11;
202const int kCurrentVersion = 21;
203
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.
207
208void WriteData(const void* data, int length, SerializeObject* obj) {
209  obj->pickle.WriteData(static_cast<const char*>(data), length);
210}
211
212void ReadData(SerializeObject* obj, const void** data, int* length) {
213  const char* tmp;
214  if (obj->pickle.ReadData(&obj->iter, &tmp, length)) {
215    *data = tmp;
216  } else {
217    obj->parse_error = true;
218    *data = NULL;
219    *length = 0;
220  }
221}
222
223void WriteInteger(int data, SerializeObject* obj) {
224  obj->pickle.WriteInt(data);
225}
226
227int ReadInteger(SerializeObject* obj) {
228  int tmp;
229  if (obj->pickle.ReadInt(&obj->iter, &tmp))
230    return tmp;
231  obj->parse_error = true;
232  return 0;
233}
234
235void ConsumeInteger(SerializeObject* obj) {
236  int unused ALLOW_UNUSED = ReadInteger(obj);
237}
238
239void WriteInteger64(int64 data, SerializeObject* obj) {
240  obj->pickle.WriteInt64(data);
241}
242
243int64 ReadInteger64(SerializeObject* obj) {
244  int64 tmp = 0;
245  if (obj->pickle.ReadInt64(&obj->iter, &tmp))
246    return tmp;
247  obj->parse_error = true;
248  return 0;
249}
250
251void ConsumeInteger64(SerializeObject* obj) {
252  int64 unused ALLOW_UNUSED = ReadInteger64(obj);
253}
254
255void WriteReal(double data, SerializeObject* obj) {
256  WriteData(&data, sizeof(double), obj);
257}
258
259double ReadReal(SerializeObject* obj) {
260  const void* tmp = NULL;
261  int length = 0;
262  double value = 0.0;
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));
267  } else {
268    obj->parse_error = true;
269  }
270  return value;
271}
272
273void ConsumeReal(SerializeObject* obj) {
274  double unused ALLOW_UNUSED = ReadReal(obj);
275}
276
277void WriteBoolean(bool data, SerializeObject* obj) {
278  obj->pickle.WriteInt(data ? 1 : 0);
279}
280
281bool ReadBoolean(SerializeObject* obj) {
282  bool tmp;
283  if (obj->pickle.ReadBool(&obj->iter, &tmp))
284    return tmp;
285  obj->parse_error = true;
286  return false;
287}
288
289void ConsumeBoolean(SerializeObject* obj) {
290  bool unused ALLOW_UNUSED = ReadBoolean(obj);
291}
292
293void WriteGURL(const GURL& url, SerializeObject* obj) {
294  obj->pickle.WriteString(url.possibly_invalid_spec());
295}
296
297GURL ReadGURL(SerializeObject* obj) {
298  std::string spec;
299  if (obj->pickle.ReadString(&obj->iter, &spec))
300    return GURL(spec);
301  obj->parse_error = true;
302  return GURL();
303}
304
305void WriteStdString(const std::string& s, SerializeObject* obj) {
306  obj->pickle.WriteString(s);
307}
308
309std::string ReadStdString(SerializeObject* obj) {
310  std::string s;
311  if (obj->pickle.ReadString(&obj->iter, &s))
312    return s;
313  obj->parse_error = true;
314  return std::string();
315}
316
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.
320void WriteString(const base::NullableString16& str, SerializeObject* obj) {
321  if (str.is_null()) {
322    obj->pickle.WriteInt(-1);
323  } else {
324    const base::char16* data = str.string().data();
325    size_t length_in_bytes = str.string().length() * sizeof(base::char16);
326
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);
331  }
332}
333
334// This reads a serialized NullableString16 from obj. If a string can't be
335// read, NULL is returned.
336const base::char16* ReadStringNoCopy(SerializeObject* obj, int* num_chars) {
337  int length_in_bytes;
338  if (!obj->pickle.ReadInt(&obj->iter, &length_in_bytes)) {
339    obj->parse_error = true;
340    return NULL;
341  }
342
343  if (length_in_bytes < 0)
344    return NULL;
345
346  const char* data;
347  if (!obj->pickle.ReadBytes(&obj->iter, &data, length_in_bytes)) {
348    obj->parse_error = true;
349    return NULL;
350  }
351
352  if (num_chars)
353    *num_chars = length_in_bytes / sizeof(base::char16);
354  return reinterpret_cast<const base::char16*>(data);
355}
356
357base::NullableString16 ReadString(SerializeObject* obj) {
358  int num_chars;
359  const base::char16* chars = ReadStringNoCopy(obj, &num_chars);
360  return chars ?
361      base::NullableString16(base::string16(chars, num_chars), false) :
362      base::NullableString16();
363}
364
365void ConsumeString(SerializeObject* obj) {
366  const base::char16* unused ALLOW_UNUSED = ReadStringNoCopy(obj, NULL);
367}
368
369template <typename T>
370void 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);
373}
374
375size_t ReadAndValidateVectorSize(SerializeObject* obj, size_t element_size) {
376  size_t num_elements = static_cast<size_t>(ReadInteger(obj));
377
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;
381    return 0;
382  }
383
384  // Ensure that it is plausible for the pickle to contain num_elements worth
385  // of data.
386  if (obj->pickle.payload_size() <= num_elements) {
387    obj->parse_error = true;
388    return 0;
389  }
390
391  return num_elements;
392}
393
394// Writes a Vector of strings into a SerializeObject for serialization.
395void 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);
400  }
401}
402
403void ReadStringVector(SerializeObject* obj,
404                      std::vector<base::NullableString16>* result) {
405  size_t num_elements =
406      ReadAndValidateVectorSize(obj, sizeof(base::NullableString16));
407
408  result->resize(num_elements);
409  for (size_t i = 0; i < num_elements; ++i)
410    (*result)[i] = ReadString(obj);
411}
412
413// Writes an ExplodedHttpBody object into a SerializeObject for serialization.
414void WriteHttpBody(const ExplodedHttpBody& http_body, SerializeObject* obj) {
415  WriteBoolean(!http_body.is_null, obj);
416
417  if (http_body.is_null)
418    return;
419
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()),
426                obj);
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);
438    } else {
439      DCHECK(element.type == blink::WebHTTPBody::Element::TypeBlob);
440      WriteStdString(element.blob_uuid, obj);
441    }
442  }
443  WriteInteger64(http_body.identifier, obj);
444  WriteBoolean(http_body.contains_passwords, obj);
445}
446
447void ReadHttpBody(SerializeObject* obj, ExplodedHttpBody* http_body) {
448  // An initial boolean indicates if we have an HTTP body.
449  if (!ReadBoolean(obj))
450    return;
451  http_body->is_null = false;
452
453  int num_elements = ReadInteger(obj);
454
455  for (int i = 0; i < num_elements; ++i) {
456    int type = ReadInteger(obj);
457    if (type == blink::WebHTTPBody::Element::TypeData) {
458      const void* data;
459      int length = -1;
460      ReadData(obj, &data, &length);
461      if (length >= 0) {
462        AppendDataToHttpBody(http_body, static_cast<const char*>(data),
463                             length);
464      }
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);
483      } else {
484        ReadGURL(obj); // Skip the obsolete blob url value.
485      }
486    }
487  }
488  http_body->identifier = ReadInteger64(obj);
489
490  if (obj->version >= 12)
491    http_body->contains_passwords = ReadBoolean(obj);
492}
493
494// Writes the ExplodedFrameState data into the SerializeObject object for
495// serialization.
496void 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.
503
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);
509
510  WriteStringVector(state.document_state, obj);
511
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);
519
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);
524
525  WriteHttpBody(state.http_body, obj);
526
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);
531
532  // Subitems
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);
537}
538
539void ReadFrameState(SerializeObject* obj, bool is_top,
540                    ExplodedFrameState* state) {
541  if (obj->version < 14 && !is_top)
542    ConsumeInteger(obj);  // Skip over redundant version field.
543
544  state->url_string = ReadString(obj);
545
546  if (obj->version < 19)
547    ConsumeString(obj);  // Skip obsolete original url string field.
548
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.
555  }
556
557  int x = ReadInteger(obj);
558  int y = ReadInteger(obj);
559  state->scroll_offset = gfx::Point(x, y);
560
561  if (obj->version < 15) {
562    ConsumeBoolean(obj);  // Skip obsolete target item flag.
563    ConsumeInteger(obj);  // Skip obsolete visit count field.
564  }
565  state->referrer = ReadString(obj);
566
567  ReadStringVector(obj, &state->document_state);
568
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);
574
575  if (obj->version >= 17 && obj->version < 19)
576    ConsumeInteger64(obj); // Skip obsolete target frame id number.
577
578  if (obj->version >= 18) {
579    state->referrer_policy =
580        static_cast<blink::WebReferrerPolicy>(ReadInteger(obj));
581  }
582
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);
587  } else {
588    state->pinch_viewport_scroll_offset = gfx::PointF(-1, -1);
589  }
590
591  bool has_state_object = ReadBoolean(obj);
592  if (has_state_object)
593    state->state_object = ReadString(obj);
594
595  ReadHttpBody(obj, &state->http_body);
596
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);
601
602  if (obj->version < 14)
603    ConsumeString(obj);  // Skip unused referrer string.
604
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.
609    ReadReal(obj);
610    ReadBoolean(obj);
611
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();
620      }
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;
625    }
626  }
627#endif
628
629  // Subitems
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]);
635}
636
637void WritePageState(const ExplodedPageState& state, SerializeObject* obj) {
638  WriteInteger(obj->version, obj);
639  WriteStringVector(state.referenced_files, obj);
640  WriteFrameState(state.top, obj, true);
641}
642
643void ReadPageState(SerializeObject* obj, ExplodedPageState* state) {
644  obj->version = ReadInteger(obj);
645
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);
652    return;
653  }
654
655  if (obj->version > kCurrentVersion || obj->version < kMinVersion) {
656    obj->parse_error = true;
657    return;
658  }
659
660  if (obj->version >= 14)
661    ReadStringVector(obj, &state->referenced_files);
662
663  ReadFrameState(obj, true, &state->top);
664
665  if (obj->version < 14)
666    RecursivelyAppendReferencedFiles(state->top, &state->referenced_files);
667
668  // De-dupe
669  state->referenced_files.erase(
670      std::unique(state->referenced_files.begin(),
671                  state->referenced_files.end()),
672      state->referenced_files.end());
673}
674
675}  // namespace
676
677ExplodedHttpBodyElement::ExplodedHttpBodyElement()
678    : type(blink::WebHTTPBody::Element::TypeData),
679      file_start(0),
680      file_length(-1),
681      file_modification_time(std::numeric_limits<double>::quiet_NaN()) {
682}
683
684ExplodedHttpBodyElement::~ExplodedHttpBodyElement() {
685}
686
687ExplodedHttpBody::ExplodedHttpBody()
688    : identifier(0),
689      contains_passwords(false),
690      is_null(true) {
691}
692
693ExplodedHttpBody::~ExplodedHttpBody() {
694}
695
696ExplodedFrameState::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) {
702}
703
704ExplodedFrameState::ExplodedFrameState(const ExplodedFrameState& other) {
705  assign(other);
706}
707
708ExplodedFrameState::~ExplodedFrameState() {
709}
710
711void ExplodedFrameState::operator=(const ExplodedFrameState& other) {
712  if (&other != this)
713    assign(other);
714}
715
716void 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;
731}
732
733ExplodedPageState::ExplodedPageState() {
734}
735
736ExplodedPageState::~ExplodedPageState() {
737}
738
739bool DecodePageState(const std::string& encoded, ExplodedPageState* exploded) {
740  *exploded = ExplodedPageState();
741
742  if (encoded.empty())
743    return true;
744
745  SerializeObject obj(encoded.data(), static_cast<int>(encoded.size()));
746  ReadPageState(&obj, exploded);
747  return !obj.parse_error;
748}
749
750bool EncodePageState(const ExplodedPageState& exploded, std::string* encoded) {
751  SerializeObject obj;
752  obj.version = kCurrentVersion;
753  WritePageState(exploded, &obj);
754  *encoded = obj.GetAsString();
755  return true;
756}
757
758#if defined(OS_ANDROID)
759bool 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;
766  return rv;
767}
768#endif
769
770}  // namespace content
771