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 //
    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