<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "webkit/glue/glue_serialize.h"

#include <string>

#include "base/pickle.h"
#include "base/utf_string_conversions.h"
#include "googleurl/src/gurl.h"
#include "third_party/WebKit/WebKit/chromium/public/WebData.h"
#include "third_party/WebKit/WebKit/chromium/public/WebHistoryItem.h"
#include "third_party/WebKit/WebKit/chromium/public/WebHTTPBody.h"
#include "third_party/WebKit/WebKit/chromium/public/WebPoint.h"
#include "third_party/WebKit/WebKit/chromium/public/WebSerializedScriptValue.h"
#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
#include "third_party/WebKit/WebKit/chromium/public/WebVector.h"
#include "webkit/glue/webkit_glue.h"

using WebKit::WebData;
using WebKit::WebFileInfo;
using WebKit::WebHistoryItem;
using WebKit::WebHTTPBody;
using WebKit::WebPoint;
using WebKit::WebSerializedScriptValue;
using WebKit::WebString;
using WebKit::WebUChar;
using WebKit::WebVector;

namespace webkit_glue {

struct SerializeObject {
  SerializeObject() : iter(NULL) {}
  SerializeObject(const char* data, int len) : pickle(data, len), iter(NULL) {}

  std::string GetAsString() {
    return std::string(static_cast<const char*>(pickle.data()), pickle.size());
  }

  Pickle pickle;
  mutable void* iter;
  mutable int version;
};

// TODO(mpcomplete): obsolete versions 1 and 2 after 1/1/2008.
// Version ID used in reading/writing history items.
// 1: Initial revision.
// 2: Added case for NULL string versus "". Version 2 code can read Version 1
//    data, but not vice versa.
// 3: Version 2 was broken, it stored number of WebUChars, not number of bytes.
//    This version checks and reads v1 and v2 correctly.
// 4: Adds support for storing FormData::identifier().
// 5: Adds support for empty FormData
// 6: Adds support for documentSequenceNumbers
// 7: Adds support for stateObject
// 8: Adds support for file range and modification time
// 9: Adds support for itemSequenceNumbers
// Should be const, but unit tests may modify it.
//
// NOTE: If the version is -1, then the pickle contains only a URL string.
// See CreateHistoryStateForURL.
//
#if defined(WEBKIT_BUG_40451_IS_FIXED)
int kVersion = 9;
#else
int kVersion = 8;
#endif

// A bunch of convenience functions to read/write to SerializeObjects.
// The serializers assume the input data is in the correct format and so does
// no error checking.
inline void WriteData(const void* data, int length, SerializeObject* obj) {
  obj->pickle.WriteData(static_cast<const char*>(data), length);
}

inline void ReadData(const SerializeObject* obj, const void** data,
                     int* length) {
  const char* tmp = NULL;
  obj->pickle.ReadData(&obj->iter, &tmp, length);
  *data = tmp;
}

inline bool ReadBytes(const SerializeObject* obj, const void** data,
                     int length) {
  const char *tmp;
  if (!obj->pickle.ReadBytes(&obj->iter, &tmp, length))
    return false;
  *data = tmp;
  return true;
}

inline void WriteInteger(int data, SerializeObject* obj) {
  obj->pickle.WriteInt(data);
}

inline int ReadInteger(const SerializeObject* obj) {
  int tmp = 0;
  obj->pickle.ReadInt(&obj->iter, &tmp);
  return tmp;
}

inline void WriteInteger64(int64 data, SerializeObject* obj) {
  obj->pickle.WriteInt64(data);
}

inline int64 ReadInteger64(const SerializeObject* obj) {
  int64 tmp = 0;
  obj->pickle.ReadInt64(&obj->iter, &tmp);
  return tmp;
}

inline void WriteReal(double data, SerializeObject* obj) {
  WriteData(&data, sizeof(double), obj);
}

inline double ReadReal(const SerializeObject* obj) {
  const void* tmp;
  int length = 0;
  ReadData(obj, &tmp, &length);
  if (length > 0 && length >= static_cast<int>(sizeof(0.0)))
    return *static_cast<const double*>(tmp);
  else
    return 0.0;
}

inline void WriteBoolean(bool data, SerializeObject* obj) {
  obj->pickle.WriteInt(data ? 1 : 0);
}

inline bool ReadBoolean(const SerializeObject* obj) {
  bool tmp = false;
  obj->pickle.ReadBool(&obj->iter, &tmp);
  return tmp;
}

inline void WriteGURL(const GURL& url, SerializeObject* obj) {
  obj->pickle.WriteString(url.possibly_invalid_spec());
}

inline GURL ReadGURL(const SerializeObject* obj) {
  std::string spec;
  obj->pickle.ReadString(&obj->iter, &spec);
  return GURL(spec);
}

// Read/WriteString pickle the WebString as <int length><WebUChar* data>.
// If length == -1, then the WebString itself is NULL (WebString()).
// Otherwise the length is the number of WebUChars (not bytes) in the WebString.
inline void WriteString(const WebString& str, SerializeObject* obj) {
  switch (kVersion) {
    case 1:
      // Version 1 writes <length in bytes><string data>.
      // It saves WebString() and "" as "".
      obj->pickle.WriteInt(str.length() * sizeof(WebUChar));
      obj->pickle.WriteBytes(str.data(), str.length() * sizeof(WebUChar));
      break;
    case 2:
      // Version 2 writes <length in WebUChar><string data>.
      // It uses -1 in the length field to mean WebString().
      if (str.isNull()) {
        obj->pickle.WriteInt(-1);
      } else {
        obj->pickle.WriteInt(str.length());
        obj->pickle.WriteBytes(str.data(),
                               str.length() * sizeof(WebUChar));
      }
      break;
    default:
      // Version 3+ writes <length in bytes><string data>.
      // It uses -1 in the length field to mean WebString().
      if (str.isNull()) {
        obj->pickle.WriteInt(-1);
      } else {
        obj->pickle.WriteInt(str.length() * sizeof(WebUChar));
        obj->pickle.WriteBytes(str.data(),
                               str.length() * sizeof(WebUChar));
      }
      break;
  }
}

// This reads a serialized WebString from obj. If a string can't be read,
// WebString() is returned.
inline WebString ReadString(const SerializeObject* obj) {
  int length;

  // Versions 1, 2, and 3 all start with an integer.
  if (!obj->pickle.ReadInt(&obj->iter, &length))
    return WebString();

  // Starting with version 2, -1 means WebString().
  if (length == -1)
    return WebString();

  // In version 2, the length field was the length in WebUChars.
  // In version 1 and 3 it is the length in bytes.
  int bytes = ((obj->version == 2) ? length * sizeof(WebUChar) : length);

  const void* data;
  if (!ReadBytes(obj, &data, bytes))
    return WebString();
  return WebString(static_cast<const WebUChar*>(data),
                   bytes / sizeof(WebUChar));
}

// Writes a Vector of Strings into a SerializeObject for serialization.
static void WriteStringVector(
    const WebVector<WebString>& data, SerializeObject* obj) {
  WriteInteger(static_cast<int>(data.size()), obj);
  for (size_t i = 0, c = data.size(); i < c; ++i) {
    unsigned ui = static_cast<unsigned>(i);  // sigh
    WriteString(data[ui], obj);
  }
}

static WebVector<WebString> ReadStringVector(const SerializeObject* obj) {
  int num_elements = ReadInteger(obj);
  WebVector<WebString> result(static_cast<size_t>(num_elements));
  for (int i = 0; i < num_elements; ++i)
    result[i] = ReadString(obj);
  return result;
}

// Writes a FormData object into a SerializeObject for serialization.
static void WriteFormData(const WebHTTPBody& http_body, SerializeObject* obj) {
  WriteBoolean(!http_body.isNull(), obj);

  if (http_body.isNull())
    return;

  WriteInteger(static_cast<int>(http_body.elementCount()), obj);
  WebHTTPBody::Element element;
  for (size_t i = 0; http_body.elementAt(i, element); ++i) {
    WriteInteger(element.type, obj);
    if (element.type == WebHTTPBody::Element::TypeData) {
      WriteData(element.data.data(), static_cast<int>(element.data.size()),
                obj);
    } else {
      WriteString(element.filePath, obj);
      WriteInteger64(element.fileStart, obj);
      WriteInteger64(element.fileLength, obj);
      WriteReal(element.fileInfo.modificationTime, obj);
    }
  }
  WriteInteger64(http_body.identifier(), obj);
}

static WebHTTPBody ReadFormData(const SerializeObject* obj) {
  // In newer versions, an initial boolean indicates if we have form data.
  if (obj->version >= 5 && !ReadBoolean(obj))
    return WebHTTPBody();

  // In older versions, 0 elements implied no form data.
  int num_elements = ReadInteger(obj);
  if (num_elements == 0 && obj->version < 5)
    return WebHTTPBody();

  WebHTTPBody http_body;
  http_body.initialize();

  for (int i = 0; i < num_elements; ++i) {
    int type = ReadInteger(obj);
    if (type == WebHTTPBody::Element::TypeData) {
      const void* data;
      int length = -1;
      ReadData(obj, &data, &length);
      if (length >= 0)
        http_body.appendData(WebData(static_cast<const char*>(data), length));
    } else {
      WebString file_path = ReadString(obj);
      long long file_start = 0;
      long long file_length = -1;
      WebFileInfo file_info;
      if (obj->version >= 8) {
        file_start = ReadInteger64(obj);
        file_length = ReadInteger64(obj);
        file_info.modificationTime = ReadReal(obj);
      }
      http_body.appendFileRange(file_path, file_start, file_length, file_info);
    }
  }
  if (obj->version >= 4)
    http_body.setIdentifier(ReadInteger64(obj));

  return http_body;
}

// Writes the HistoryItem data into the SerializeObject object for
// serialization.
static void WriteHistoryItem(
    const WebHistoryItem& item, SerializeObject* obj) {
  // WARNING: This data may be persisted for later use. As such, care must be
  // taken when changing the serialized format. If a new field needs to be
  // written, only adding at the end will make it easier to deal with loading
  // older versions. Similarly, this should NOT save fields with sensitive
  // data, such as password fields.
  WriteInteger(kVersion, obj);
  WriteString(item.urlString(), obj);
  WriteString(item.originalURLString(), obj);
  WriteString(item.target(), obj);
  WriteString(item.parent(), obj);
  WriteString(item.title(), obj);
  WriteString(item.alternateTitle(), obj);
  WriteReal(item.lastVisitedTime(), obj);
  WriteInteger(item.scrollOffset().x, obj);
  WriteInteger(item.scrollOffset().y, obj);
  WriteBoolean(item.isTargetItem(), obj);
  WriteInteger(item.visitCount(), obj);
  WriteString(item.referrer(), obj);

  WriteStringVector(item.documentState(), obj);

#if defined(WEBKIT_BUG_40451_IS_FIXED)
  if (kVersion >= 9)
    WriteInteger64(item.itemSequenceNumber(), obj);
#endif
  if (kVersion >= 6)
    WriteInteger64(item.documentSequenceNumber(), obj);
  if (kVersion >= 7) {
    bool has_state_object = !item.stateObject().isNull();
    WriteBoolean(has_state_object, obj);
    if (has_state_object)
      WriteString(item.stateObject().toString(), obj);
  }

  // Yes, the referrer is written twice.  This is for backwards
  // compatibility with the format.
  WriteFormData(item.httpBody(), obj);
  WriteString(item.httpContentType(), obj);
  WriteString(item.referrer(), obj);

  // Subitems
  const WebVector<WebHistoryItem>& children = item.children();
  WriteInteger(static_cast<int>(children.size()), obj);
  for (size_t i = 0, c = children.size(); i < c; ++i)
    WriteHistoryItem(children[i], obj);
}

// Creates a new HistoryItem tree based on the serialized string.
// Assumes the data is in the format returned by WriteHistoryItem.
static WebHistoryItem ReadHistoryItem(
    const SerializeObject* obj, bool include_form_data) {
  // See note in WriteHistoryItem. on this.
  obj->version = ReadInteger(obj);

  if (obj->version == -1) {
    GURL url = ReadGURL(obj);
    WebHistoryItem item;
    item.initialize();
    item.setURLString(WebString::fromUTF8(url.possibly_invalid_spec()));
    return item;
  }

  if (obj->version > kVersion || obj->version < 1)
    return WebHistoryItem();

  WebHistoryItem item;
  item.initialize();

  item.setURLString(ReadString(obj));
  item.setOriginalURLString(ReadString(obj));
  item.setTarget(ReadString(obj));
  item.setParent(ReadString(obj));
  item.setTitle(ReadString(obj));
  item.setAlternateTitle(ReadString(obj));
  item.setLastVisitedTime(ReadReal(obj));
  int x = ReadInteger(obj);
  int y = ReadInteger(obj);
  item.setScrollOffset(WebPoint(x, y));
  item.setIsTargetItem(ReadBoolean(obj));
  item.setVisitCount(ReadInteger(obj));
  item.setReferrer(ReadString(obj));

  item.setDocumentState(ReadStringVector(obj));

#if defined(WEBKIT_BUG_40451_IS_FIXED)
  if (obj->version >= 9)
    item.setItemSequenceNumber(ReadInteger64(obj));
#endif
  if (obj->version >= 6)
    item.setDocumentSequenceNumber(ReadInteger64(obj));
  if (obj->version >= 7) {
    bool has_state_object = ReadBoolean(obj);
    if (has_state_object) {
      item.setStateObject(
          WebSerializedScriptValue::fromString(ReadString(obj)));
    }
  }

  // The extra referrer string is read for backwards compat.
  const WebHTTPBody& http_body = ReadFormData(obj);
  const WebString& http_content_type = ReadString(obj);
  ALLOW_UNUSED const WebString& unused_referrer = ReadString(obj);
  if (include_form_data) {
    item.setHTTPBody(http_body);
    item.setHTTPContentType(http_content_type);
  }

  // Subitems
  int num_children = ReadInteger(obj);
  for (int i = 0; i < num_children; ++i)
    item.appendToChildren(ReadHistoryItem(obj, include_form_data));

  return item;<commit_msg>Remove preprocess hack now that WebKit has been updated.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "webkit/glue/glue_serialize.h"

#include <string>

#include "base/pickle.h"
#include "base/utf_string_conversions.h"
#include "googleurl/src/gurl.h"
#include "third_party/WebKit/WebKit/chromium/public/WebData.h"
#include "third_party/WebKit/WebKit/chromium/public/WebHistoryItem.h"
#include "third_party/WebKit/WebKit/chromium/public/WebHTTPBody.h"
#include "third_party/WebKit/WebKit/chromium/public/WebPoint.h"
#include "third_party/WebKit/WebKit/chromium/public/WebSerializedScriptValue.h"
#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
#include "third_party/WebKit/WebKit/chromium/public/WebVector.h"
#include "webkit/glue/webkit_glue.h"

using WebKit::WebData;
using WebKit::WebFileInfo;
using WebKit::WebHistoryItem;
using WebKit::WebHTTPBody;
using WebKit::WebPoint;
using WebKit::WebSerializedScriptValue;
using WebKit::WebString;
using WebKit::WebUChar;
using WebKit::WebVector;

namespace webkit_glue {

struct SerializeObject {
  SerializeObject() : iter(NULL) {}
  SerializeObject(const char* data, int len) : pickle(data, len), iter(NULL) {}

  std::string GetAsString() {
    return std::string(static_cast<const char*>(pickle.data()), pickle.size());
  }

  Pickle pickle;
  mutable void* iter;
  mutable int version;
};

// TODO(mpcomplete): obsolete versions 1 and 2 after 1/1/2008.
// Version ID used in reading/writing history items.
// 1: Initial revision.
// 2: Added case for NULL string versus "". Version 2 code can read Version 1
//    data, but not vice versa.
// 3: Version 2 was broken, it stored number of WebUChars, not number of bytes.
//    This version checks and reads v1 and v2 correctly.
// 4: Adds support for storing FormData::identifier().
// 5: Adds support for empty FormData
// 6: Adds support for documentSequenceNumbers
// 7: Adds support for stateObject
// 8: Adds support for file range and modification time
// 9: Adds support for itemSequenceNumbers
// Should be const, but unit tests may modify it.
//
// NOTE: If the version is -1, then the pickle contains only a URL string.
// See CreateHistoryStateForURL.
//
int kVersion = 9;

// A bunch of convenience functions to read/write to SerializeObjects.
// The serializers assume the input data is in the correct format and so does
// no error checking.
inline void WriteData(const void* data, int length, SerializeObject* obj) {
  obj->pickle.WriteData(static_cast<const char*>(data), length);
}

inline void ReadData(const SerializeObject* obj, const void** data,
                     int* length) {
  const char* tmp = NULL;
  obj->pickle.ReadData(&obj->iter, &tmp, length);
  *data = tmp;
}

inline bool ReadBytes(const SerializeObject* obj, const void** data,
                     int length) {
  const char *tmp;
  if (!obj->pickle.ReadBytes(&obj->iter, &tmp, length))
    return false;
  *data = tmp;
  return true;
}

inline void WriteInteger(int data, SerializeObject* obj) {
  obj->pickle.WriteInt(data);
}

inline int ReadInteger(const SerializeObject* obj) {
  int tmp = 0;
  obj->pickle.ReadInt(&obj->iter, &tmp);
  return tmp;
}

inline void WriteInteger64(int64 data, SerializeObject* obj) {
  obj->pickle.WriteInt64(data);
}

inline int64 ReadInteger64(const SerializeObject* obj) {
  int64 tmp = 0;
  obj->pickle.ReadInt64(&obj->iter, &tmp);
  return tmp;
}

inline void WriteReal(double data, SerializeObject* obj) {
  WriteData(&data, sizeof(double), obj);
}

inline double ReadReal(const SerializeObject* obj) {
  const void* tmp;
  int length = 0;
  ReadData(obj, &tmp, &length);
  if (length > 0 && length >= static_cast<int>(sizeof(0.0)))
    return *static_cast<const double*>(tmp);
  else
    return 0.0;
}

inline void WriteBoolean(bool data, SerializeObject* obj) {
  obj->pickle.WriteInt(data ? 1 : 0);
}

inline bool ReadBoolean(const SerializeObject* obj) {
  bool tmp = false;
  obj->pickle.ReadBool(&obj->iter, &tmp);
  return tmp;
}

inline void WriteGURL(const GURL& url, SerializeObject* obj) {
  obj->pickle.WriteString(url.possibly_invalid_spec());
}

inline GURL ReadGURL(const SerializeObject* obj) {
  std::string spec;
  obj->pickle.ReadString(&obj->iter, &spec);
  return GURL(spec);
}

// Read/WriteString pickle the WebString as <int length><WebUChar* data>.
// If length == -1, then the WebString itself is NULL (WebString()).
// Otherwise the length is the number of WebUChars (not bytes) in the WebString.
inline void WriteString(const WebString& str, SerializeObject* obj) {
  switch (kVersion) {
    case 1:
      // Version 1 writes <length in bytes><string data>.
      // It saves WebString() and "" as "".
      obj->pickle.WriteInt(str.length() * sizeof(WebUChar));
      obj->pickle.WriteBytes(str.data(), str.length() * sizeof(WebUChar));
      break;
    case 2:
      // Version 2 writes <length in WebUChar><string data>.
      // It uses -1 in the length field to mean WebString().
      if (str.isNull()) {
        obj->pickle.WriteInt(-1);
      } else {
        obj->pickle.WriteInt(str.length());
        obj->pickle.WriteBytes(str.data(),
                               str.length() * sizeof(WebUChar));
      }
      break;
    default:
      // Version 3+ writes <length in bytes><string data>.
      // It uses -1 in the length field to mean WebString().
      if (str.isNull()) {
        obj->pickle.WriteInt(-1);
      } else {
        obj->pickle.WriteInt(str.length() * sizeof(WebUChar));
        obj->pickle.WriteBytes(str.data(),
                               str.length() * sizeof(WebUChar));
      }
      break;
  }
}

// This reads a serialized WebString from obj. If a string can't be read,
// WebString() is returned.
inline WebString ReadString(const SerializeObject* obj) {
  int length;

  // Versions 1, 2, and 3 all start with an integer.
  if (!obj->pickle.ReadInt(&obj->iter, &length))
    return WebString();

  // Starting with version 2, -1 means WebString().
  if (length == -1)
    return WebString();

  // In version 2, the length field was the length in WebUChars.
  // In version 1 and 3 it is the length in bytes.
  int bytes = ((obj->version == 2) ? length * sizeof(WebUChar) : length);

  const void* data;
  if (!ReadBytes(obj, &data, bytes))
    return WebString();
  return WebString(static_cast<const WebUChar*>(data),
                   bytes / sizeof(WebUChar));
}

// Writes a Vector of Strings into a SerializeObject for serialization.
static void WriteStringVector(
    const WebVector<WebString>& data, SerializeObject* obj) {
  WriteInteger(static_cast<int>(data.size()), obj);
  for (size_t i = 0, c = data.size(); i < c; ++i) {
    unsigned ui = static_cast<unsigned>(i);  // sigh
    WriteString(data[ui], obj);
  }
}

static WebVector<WebString> ReadStringVector(const SerializeObject* obj) {
  int num_elements = ReadInteger(obj);
  WebVector<WebString> result(static_cast<size_t>(num_elements));
  for (int i = 0; i < num_elements; ++i)
    result[i] = ReadString(obj);
  return result;
}

// Writes a FormData object into a SerializeObject for serialization.
static void WriteFormData(const WebHTTPBody& http_body, SerializeObject* obj) {
  WriteBoolean(!http_body.isNull(), obj);

  if (http_body.isNull())
    return;

  WriteInteger(static_cast<int>(http_body.elementCount()), obj);
  WebHTTPBody::Element element;
  for (size_t i = 0; http_body.elementAt(i, element); ++i) {
    WriteInteger(element.type, obj);
    if (element.type == WebHTTPBody::Element::TypeData) {
      WriteData(element.data.data(), static_cast<int>(element.data.size()),
                obj);
    } else {
      WriteString(element.filePath, obj);
      WriteInteger64(element.fileStart, obj);
      WriteInteger64(element.fileLength, obj);
      WriteReal(element.fileInfo.modificationTime, obj);
    }
  }
  WriteInteger64(http_body.identifier(), obj);
}

static WebHTTPBody ReadFormData(const SerializeObject* obj) {
  // In newer versions, an initial boolean indicates if we have form data.
  if (obj->version >= 5 && !ReadBoolean(obj))
    return WebHTTPBody();

  // In older versions, 0 elements implied no form data.
  int num_elements = ReadInteger(obj);
  if (num_elements == 0 && obj->version < 5)
    return WebHTTPBody();

  WebHTTPBody http_body;
  http_body.initialize();

  for (int i = 0; i < num_elements; ++i) {
    int type = ReadInteger(obj);
    if (type == WebHTTPBody::Element::TypeData) {
      const void* data;
      int length = -1;
      ReadData(obj, &data, &length);
      if (length >= 0)
        http_body.appendData(WebData(static_cast<const char*>(data), length));
    } else {
      WebString file_path = ReadString(obj);
      long long file_start = 0;
      long long file_length = -1;
      WebFileInfo file_info;
      if (obj->version >= 8) {
        file_start = ReadInteger64(obj);
        file_length = ReadInteger64(obj);
        file_info.modificationTime = ReadReal(obj);
      }
      http_body.appendFileRange(file_path, file_start, file_length, file_info);
    }
  }
  if (obj->version >= 4)
    http_body.setIdentifier(ReadInteger64(obj));

  return http_body;
}

// Writes the HistoryItem data into the SerializeObject object for
// serialization.
static void WriteHistoryItem(
    const WebHistoryItem& item, SerializeObject* obj) {
  // WARNING: This data may be persisted for later use. As such, care must be
  // taken when changing the serialized format. If a new field needs to be
  // written, only adding at the end will make it easier to deal with loading
  // older versions. Similarly, this should NOT save fields with sensitive
  // data, such as password fields.
  WriteInteger(kVersion, obj);
  WriteString(item.urlString(), obj);
  WriteString(item.originalURLString(), obj);
  WriteString(item.target(), obj);
  WriteString(item.parent(), obj);
  WriteString(item.title(), obj);
  WriteString(item.alternateTitle(), obj);
  WriteReal(item.lastVisitedTime(), obj);
  WriteInteger(item.scrollOffset().x, obj);
  WriteInteger(item.scrollOffset().y, obj);
  WriteBoolean(item.isTargetItem(), obj);
  WriteInteger(item.visitCount(), obj);
  WriteString(item.referrer(), obj);

  WriteStringVector(item.documentState(), obj);

  if (kVersion >= 9)
    WriteInteger64(item.itemSequenceNumber(), obj);
  if (kVersion >= 6)
    WriteInteger64(item.documentSequenceNumber(), obj);
  if (kVersion >= 7) {
    bool has_state_object = !item.stateObject().isNull();
    WriteBoolean(has_state_object, obj);
    if (has_state_object)
      WriteString(item.stateObject().toString(), obj);
  }

  // Yes, the referrer is written twice.  This is for backwards
  // compatibility with the format.
  WriteFormData(item.httpBody(), obj);
  WriteString(item.httpContentType(), obj);
  WriteString(item.referrer(), obj);

  // Subitems
  const WebVector<WebHistoryItem>& children = item.children();
  WriteInteger(static_cast<int>(children.size()), obj);
  for (size_t i = 0, c = children.size(); i < c; ++i)
    WriteHistoryItem(children[i], obj);
}

// Creates a new HistoryItem tree based on the serialized string.
// Assumes the data is in the format returned by WriteHistoryItem.
static WebHistoryItem ReadHistoryItem(
    const SerializeObject* obj, bool include_form_data) {
  // See note in WriteHistoryItem. on this.
  obj->version = ReadInteger(obj);

  if (obj->version == -1) {
    GURL url = ReadGURL(obj);
    WebHistoryItem item;
    item.initialize();
    item.setURLString(WebString::fromUTF8(url.possibly_invalid_spec()));
    return item;
  }

  if (obj->version > kVersion || obj->version < 1)
    return WebHistoryItem();

  WebHistoryItem item;
  item.initialize();

  item.setURLString(ReadString(obj));
  item.setOriginalURLString(ReadString(obj));
  item.setTarget(ReadString(obj));
  item.setParent(ReadString(obj));
  item.setTitle(ReadString(obj));
  item.setAlternateTitle(ReadString(obj));
  item.setLastVisitedTime(ReadReal(obj));
  int x = ReadInteger(obj);
  int y = ReadInteger(obj);
  item.setScrollOffset(WebPoint(x, y));
  item.setIsTargetItem(ReadBoolean(obj));
  item.setVisitCount(ReadInteger(obj));
  item.setReferrer(ReadString(obj));

  item.setDocumentState(ReadStringVector(obj));

  if (obj->version >= 9)
    item.setItemSequenceNumber(ReadInteger64(obj));
  if (obj->version >= 6)
    item.setDocumentSequenceNumber(ReadInteger64(obj));
  if (obj->version >= 7) {
    bool has_state_object = ReadBoolean(obj);
    if (has_state_object) {
      item.setStateObject(
          WebSerializedScriptValue::fromString(ReadString(obj)));
    }
  }

  // The extra referrer string is read for backwards compat.
  const WebHTTPBody& http_body = ReadFormData(obj);
  const WebString& http_content_type = ReadString(obj);
  ALLOW_UNUSED const WebString& unused_referrer = ReadString(obj);
  if (include_form_data) {
    item.setHTTPBody(http_body);
    item.setHTTPContentType(http_content_type);
  }

  // Subitems
  int num_children = ReadInteger(obj);
  for (int i = 0; i < num_children; ++i)
    item.appendToChildren(ReadHistoryItem(obj, include_form_data));

  return item;
{'fraction_non_alphanumeric': 0.12775247487484473, 'fraction_numerical': 0.009410170512289682, 'mean_word_length': 3.8517165814463112, 'pattern_counts': {'":': 0, '<': 67, '<?xml version=': 0, '>': 157, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4905246', 'n_tokens_mistral': 8596, 'n_tokens_neox': 8321, 'n_words': 2294}