Home | History | Annotate | Download | only in common
      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 // 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 //
    201 const int kMinVersion = 11;
    202 const 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 
    208 void WriteData(const void* data, int length, SerializeObject* obj) {
    209   obj->pickle.WriteData(static_cast<const char*>(data), length);
    210 }
    211 
    212 void 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 
    223 void WriteInteger(int data, SerializeObject* obj) {
    224   obj->pickle.WriteInt(data);
    225 }
    226 
    227 int 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 
    235 void ConsumeInteger(SerializeObject* obj) {
    236   int unused ALLOW_UNUSED = ReadInteger(obj);
    237 }
    238 
    239 void WriteInteger64(int64 data, SerializeObject* obj) {
    240   obj->pickle.WriteInt64(data);
    241 }
    242 
    243 int64 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 
    251 void ConsumeInteger64(SerializeObject* obj) {
    252   int64 unused ALLOW_UNUSED = ReadInteger64(obj);
    253 }
    254 
    255 void WriteReal(double data, SerializeObject* obj) {
    256   WriteData(&data, sizeof(double), obj);
    257 }
    258 
    259 double 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 
    273 void ConsumeReal(SerializeObject* obj) {
    274   double unused ALLOW_UNUSED = ReadReal(obj);
    275 }
    276 
    277 void WriteBoolean(bool data, SerializeObject* obj) {
    278   obj->pickle.WriteInt(data ? 1 : 0);
    279 }
    280 
    281 bool 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 
    289 void ConsumeBoolean(SerializeObject* obj) {
    290   bool unused ALLOW_UNUSED = ReadBoolean(obj);
    291 }
    292 
    293 void WriteGURL(const GURL& url, SerializeObject* obj) {
    294   obj->pickle.WriteString(url.possibly_invalid_spec());
    295 }
    296 
    297 GURL 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 
    305 void WriteStdString(const std::string& s, SerializeObject* obj) {
    306   obj->pickle.WriteString(s);
    307 }
    308 
    309 std::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.
    320 void 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.
    336 const 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 
    357 base::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 
    365 void ConsumeString(SerializeObject* obj) {
    366   const base::char16* unused ALLOW_UNUSED = ReadStringNoCopy(obj, NULL);
    367 }
    368 
    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);
    373 }
    374 
    375 size_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.
    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);
    400   }
    401 }
    402 
    403 void 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.
    414 void 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 
    447 void 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.
    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.
    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 
    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.
    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 
    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);
    641 }
    642 
    643 void 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 
    677 ExplodedHttpBodyElement::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 
    684 ExplodedHttpBodyElement::~ExplodedHttpBodyElement() {
    685 }
    686 
    687 ExplodedHttpBody::ExplodedHttpBody()
    688     : identifier(0),
    689       contains_passwords(false),
    690       is_null(true) {
    691 }
    692 
    693 ExplodedHttpBody::~ExplodedHttpBody() {
    694 }
    695 
    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) {
    702 }
    703 
    704 ExplodedFrameState::ExplodedFrameState(const ExplodedFrameState& other) {
    705   assign(other);
    706 }
    707 
    708 ExplodedFrameState::~ExplodedFrameState() {
    709 }
    710 
    711 void ExplodedFrameState::operator=(const ExplodedFrameState& other) {
    712   if (&other != this)
    713     assign(other);
    714 }
    715 
    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;
    731 }
    732 
    733 ExplodedPageState::ExplodedPageState() {
    734 }
    735 
    736 ExplodedPageState::~ExplodedPageState() {
    737 }
    738 
    739 bool 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 
    750 bool 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)
    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;
    766   return rv;
    767 }
    768 #endif
    769 
    770 }  // namespace content
    771