Home | History | Annotate | Download | only in glue
      1 // Copyright (c) 2011 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 "webkit/glue/glue_serialize.h"
      6 
      7 #include <string>
      8 
      9 #include "base/pickle.h"
     10 #include "base/utf_string_conversions.h"
     11 #include "googleurl/src/gurl.h"
     12 #include "third_party/WebKit/Source/WebKit/chromium/public/WebData.h"
     13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebHistoryItem.h"
     14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebHTTPBody.h"
     15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPoint.h"
     16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSerializedScriptValue.h"
     17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h"
     18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h"
     19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebVector.h"
     20 #include "webkit/glue/webkit_glue.h"
     21 
     22 using WebKit::WebData;
     23 using WebKit::WebHistoryItem;
     24 using WebKit::WebHTTPBody;
     25 using WebKit::WebPoint;
     26 using WebKit::WebSerializedScriptValue;
     27 using WebKit::WebString;
     28 using WebKit::WebUChar;
     29 using WebKit::WebVector;
     30 
     31 namespace webkit_glue {
     32 
     33 struct SerializeObject {
     34   SerializeObject() : iter(NULL) {}
     35   SerializeObject(const char* data, int len) : pickle(data, len), iter(NULL) {}
     36 
     37   std::string GetAsString() {
     38     return std::string(static_cast<const char*>(pickle.data()), pickle.size());
     39   }
     40 
     41   Pickle pickle;
     42   mutable void* iter;
     43   mutable int version;
     44 };
     45 
     46 // TODO(mpcomplete): obsolete versions 1 and 2 after 1/1/2008.
     47 // Version ID used in reading/writing history items.
     48 // 1: Initial revision.
     49 // 2: Added case for NULL string versus "". Version 2 code can read Version 1
     50 //    data, but not vice versa.
     51 // 3: Version 2 was broken, it stored number of WebUChars, not number of bytes.
     52 //    This version checks and reads v1 and v2 correctly.
     53 // 4: Adds support for storing FormData::identifier().
     54 // 5: Adds support for empty FormData
     55 // 6: Adds support for documentSequenceNumbers
     56 // 7: Adds support for stateObject
     57 // 8: Adds support for file range and modification time
     58 // 9: Adds support for itemSequenceNumbers
     59 // 10: Adds support for blob
     60 // Should be const, but unit tests may modify it.
     61 //
     62 // NOTE: If the version is -1, then the pickle contains only a URL string.
     63 // See CreateHistoryStateForURL.
     64 //
     65 int kVersion = 10;
     66 
     67 // A bunch of convenience functions to read/write to SerializeObjects.
     68 // The serializers assume the input data is in the correct format and so does
     69 // no error checking.
     70 inline void WriteData(const void* data, int length, SerializeObject* obj) {
     71   obj->pickle.WriteData(static_cast<const char*>(data), length);
     72 }
     73 
     74 inline void ReadData(const SerializeObject* obj, const void** data,
     75                      int* length) {
     76   const char* tmp = NULL;
     77   obj->pickle.ReadData(&obj->iter, &tmp, length);
     78   *data = tmp;
     79 }
     80 
     81 inline bool ReadBytes(const SerializeObject* obj, const void** data,
     82                      int length) {
     83   const char *tmp;
     84   if (!obj->pickle.ReadBytes(&obj->iter, &tmp, length))
     85     return false;
     86   *data = tmp;
     87   return true;
     88 }
     89 
     90 inline void WriteInteger(int data, SerializeObject* obj) {
     91   obj->pickle.WriteInt(data);
     92 }
     93 
     94 inline int ReadInteger(const SerializeObject* obj) {
     95   int tmp = 0;
     96   obj->pickle.ReadInt(&obj->iter, &tmp);
     97   return tmp;
     98 }
     99 
    100 inline void WriteInteger64(int64 data, SerializeObject* obj) {
    101   obj->pickle.WriteInt64(data);
    102 }
    103 
    104 inline int64 ReadInteger64(const SerializeObject* obj) {
    105   int64 tmp = 0;
    106   obj->pickle.ReadInt64(&obj->iter, &tmp);
    107   return tmp;
    108 }
    109 
    110 inline void WriteReal(double data, SerializeObject* obj) {
    111   WriteData(&data, sizeof(double), obj);
    112 }
    113 
    114 inline double ReadReal(const SerializeObject* obj) {
    115   const void* tmp = NULL;
    116   int length = 0;
    117   ReadData(obj, &tmp, &length);
    118   if (tmp && length > 0 && length >= static_cast<int>(sizeof(0.0)))
    119     return *static_cast<const double*>(tmp);
    120   else
    121     return 0.0;
    122 }
    123 
    124 inline void WriteBoolean(bool data, SerializeObject* obj) {
    125   obj->pickle.WriteInt(data ? 1 : 0);
    126 }
    127 
    128 inline bool ReadBoolean(const SerializeObject* obj) {
    129   bool tmp = false;
    130   obj->pickle.ReadBool(&obj->iter, &tmp);
    131   return tmp;
    132 }
    133 
    134 inline void WriteGURL(const GURL& url, SerializeObject* obj) {
    135   obj->pickle.WriteString(url.possibly_invalid_spec());
    136 }
    137 
    138 inline GURL ReadGURL(const SerializeObject* obj) {
    139   std::string spec;
    140   obj->pickle.ReadString(&obj->iter, &spec);
    141   return GURL(spec);
    142 }
    143 
    144 // Read/WriteString pickle the WebString as <int length><WebUChar* data>.
    145 // If length == -1, then the WebString itself is NULL (WebString()).
    146 // Otherwise the length is the number of WebUChars (not bytes) in the WebString.
    147 inline void WriteString(const WebString& str, SerializeObject* obj) {
    148   switch (kVersion) {
    149     case 1:
    150       // Version 1 writes <length in bytes><string data>.
    151       // It saves WebString() and "" as "".
    152       obj->pickle.WriteInt(str.length() * sizeof(WebUChar));
    153       obj->pickle.WriteBytes(str.data(), str.length() * sizeof(WebUChar));
    154       break;
    155     case 2:
    156       // Version 2 writes <length in WebUChar><string data>.
    157       // It uses -1 in the length field to mean WebString().
    158       if (str.isNull()) {
    159         obj->pickle.WriteInt(-1);
    160       } else {
    161         obj->pickle.WriteInt(str.length());
    162         obj->pickle.WriteBytes(str.data(),
    163                                str.length() * sizeof(WebUChar));
    164       }
    165       break;
    166     default:
    167       // Version 3+ writes <length in bytes><string data>.
    168       // It uses -1 in the length field to mean WebString().
    169       if (str.isNull()) {
    170         obj->pickle.WriteInt(-1);
    171       } else {
    172         obj->pickle.WriteInt(str.length() * sizeof(WebUChar));
    173         obj->pickle.WriteBytes(str.data(),
    174                                str.length() * sizeof(WebUChar));
    175       }
    176       break;
    177   }
    178 }
    179 
    180 // This reads a serialized WebString from obj. If a string can't be read,
    181 // WebString() is returned.
    182 inline WebString ReadString(const SerializeObject* obj) {
    183   int length;
    184 
    185   // Versions 1, 2, and 3 all start with an integer.
    186   if (!obj->pickle.ReadInt(&obj->iter, &length))
    187     return WebString();
    188 
    189   // Starting with version 2, -1 means WebString().
    190   if (length == -1)
    191     return WebString();
    192 
    193   // In version 2, the length field was the length in WebUChars.
    194   // In version 1 and 3 it is the length in bytes.
    195   int bytes = length;
    196   if (obj->version == 2)
    197     bytes *= sizeof(WebUChar);
    198 
    199   const void* data;
    200   if (!ReadBytes(obj, &data, bytes))
    201     return WebString();
    202   return WebString(static_cast<const WebUChar*>(data),
    203                    bytes / sizeof(WebUChar));
    204 }
    205 
    206 // Writes a Vector of Strings into a SerializeObject for serialization.
    207 static void WriteStringVector(
    208     const WebVector<WebString>& data, SerializeObject* obj) {
    209   WriteInteger(static_cast<int>(data.size()), obj);
    210   for (size_t i = 0, c = data.size(); i < c; ++i) {
    211     unsigned ui = static_cast<unsigned>(i);  // sigh
    212     WriteString(data[ui], obj);
    213   }
    214 }
    215 
    216 static WebVector<WebString> ReadStringVector(const SerializeObject* obj) {
    217   int num_elements = ReadInteger(obj);
    218   WebVector<WebString> result(static_cast<size_t>(num_elements));
    219   for (int i = 0; i < num_elements; ++i)
    220     result[i] = ReadString(obj);
    221   return result;
    222 }
    223 
    224 // Writes a FormData object into a SerializeObject for serialization.
    225 static void WriteFormData(const WebHTTPBody& http_body, SerializeObject* obj) {
    226   WriteBoolean(!http_body.isNull(), obj);
    227 
    228   if (http_body.isNull())
    229     return;
    230 
    231   WriteInteger(static_cast<int>(http_body.elementCount()), obj);
    232   WebHTTPBody::Element element;
    233   for (size_t i = 0; http_body.elementAt(i, element); ++i) {
    234     WriteInteger(element.type, obj);
    235     if (element.type == WebHTTPBody::Element::TypeData) {
    236       WriteData(element.data.data(), static_cast<int>(element.data.size()),
    237                 obj);
    238     } else if (element.type == WebHTTPBody::Element::TypeFile) {
    239       WriteString(element.filePath, obj);
    240       WriteInteger64(element.fileStart, obj);
    241       WriteInteger64(element.fileLength, obj);
    242       WriteReal(element.modificationTime, obj);
    243     } else {
    244       WriteGURL(element.blobURL, obj);
    245     }
    246   }
    247   WriteInteger64(http_body.identifier(), obj);
    248 }
    249 
    250 static WebHTTPBody ReadFormData(const SerializeObject* obj) {
    251   // In newer versions, an initial boolean indicates if we have form data.
    252   if (obj->version >= 5 && !ReadBoolean(obj))
    253     return WebHTTPBody();
    254 
    255   // In older versions, 0 elements implied no form data.
    256   int num_elements = ReadInteger(obj);
    257   if (num_elements == 0 && obj->version < 5)
    258     return WebHTTPBody();
    259 
    260   WebHTTPBody http_body;
    261   http_body.initialize();
    262 
    263   for (int i = 0; i < num_elements; ++i) {
    264     int type = ReadInteger(obj);
    265     if (type == WebHTTPBody::Element::TypeData) {
    266       const void* data;
    267       int length = -1;
    268       ReadData(obj, &data, &length);
    269       if (length >= 0)
    270         http_body.appendData(WebData(static_cast<const char*>(data), length));
    271     } else if (type == WebHTTPBody::Element::TypeFile) {
    272       WebString file_path = ReadString(obj);
    273       long long file_start = 0;
    274       long long file_length = -1;
    275       double modification_time = 0.0;
    276       if (obj->version >= 8) {
    277         file_start = ReadInteger64(obj);
    278         file_length = ReadInteger64(obj);
    279         modification_time = ReadReal(obj);
    280       }
    281       http_body.appendFileRange(file_path, file_start, file_length,
    282                                 modification_time);
    283     } else if (obj->version >= 10) {
    284       GURL blob_url = ReadGURL(obj);
    285       http_body.appendBlob(blob_url);
    286     }
    287   }
    288   if (obj->version >= 4)
    289     http_body.setIdentifier(ReadInteger64(obj));
    290 
    291   return http_body;
    292 }
    293 
    294 // Writes the HistoryItem data into the SerializeObject object for
    295 // serialization.
    296 static void WriteHistoryItem(
    297     const WebHistoryItem& item, SerializeObject* obj) {
    298   // WARNING: This data may be persisted for later use. As such, care must be
    299   // taken when changing the serialized format. If a new field needs to be
    300   // written, only adding at the end will make it easier to deal with loading
    301   // older versions. Similarly, this should NOT save fields with sensitive
    302   // data, such as password fields.
    303   WriteInteger(kVersion, obj);
    304   WriteString(item.urlString(), obj);
    305   WriteString(item.originalURLString(), obj);
    306   WriteString(item.target(), obj);
    307   WriteString(item.parent(), obj);
    308   WriteString(item.title(), obj);
    309   WriteString(item.alternateTitle(), obj);
    310   WriteReal(item.lastVisitedTime(), obj);
    311   WriteInteger(item.scrollOffset().x, obj);
    312   WriteInteger(item.scrollOffset().y, obj);
    313   WriteBoolean(item.isTargetItem(), obj);
    314   WriteInteger(item.visitCount(), obj);
    315   WriteString(item.referrer(), obj);
    316 
    317   WriteStringVector(item.documentState(), obj);
    318 
    319   if (kVersion >= 9)
    320     WriteInteger64(item.itemSequenceNumber(), obj);
    321   if (kVersion >= 6)
    322     WriteInteger64(item.documentSequenceNumber(), obj);
    323   if (kVersion >= 7) {
    324     bool has_state_object = !item.stateObject().isNull();
    325     WriteBoolean(has_state_object, obj);
    326     if (has_state_object)
    327       WriteString(item.stateObject().toString(), obj);
    328   }
    329 
    330   // Yes, the referrer is written twice.  This is for backwards
    331   // compatibility with the format.
    332   WriteFormData(item.httpBody(), obj);
    333   WriteString(item.httpContentType(), obj);
    334   WriteString(item.referrer(), obj);
    335 
    336   // Subitems
    337   const WebVector<WebHistoryItem>& children = item.children();
    338   WriteInteger(static_cast<int>(children.size()), obj);
    339   for (size_t i = 0, c = children.size(); i < c; ++i)
    340     WriteHistoryItem(children[i], obj);
    341 }
    342 
    343 // Creates a new HistoryItem tree based on the serialized string.
    344 // Assumes the data is in the format returned by WriteHistoryItem.
    345 static WebHistoryItem ReadHistoryItem(
    346     const SerializeObject* obj,
    347     bool include_form_data,
    348     bool include_scroll_offset) {
    349   // See note in WriteHistoryItem. on this.
    350   obj->version = ReadInteger(obj);
    351 
    352   if (obj->version == -1) {
    353     GURL url = ReadGURL(obj);
    354     WebHistoryItem item;
    355     item.initialize();
    356     item.setURLString(WebString::fromUTF8(url.possibly_invalid_spec()));
    357     return item;
    358   }
    359 
    360   if (obj->version > kVersion || obj->version < 1)
    361     return WebHistoryItem();
    362 
    363   WebHistoryItem item;
    364   item.initialize();
    365 
    366   item.setURLString(ReadString(obj));
    367   item.setOriginalURLString(ReadString(obj));
    368   item.setTarget(ReadString(obj));
    369   item.setParent(ReadString(obj));
    370   item.setTitle(ReadString(obj));
    371   item.setAlternateTitle(ReadString(obj));
    372   item.setLastVisitedTime(ReadReal(obj));
    373 
    374   int x = ReadInteger(obj);
    375   int y = ReadInteger(obj);
    376   if (include_scroll_offset)
    377     item.setScrollOffset(WebPoint(x, y));
    378 
    379   item.setIsTargetItem(ReadBoolean(obj));
    380   item.setVisitCount(ReadInteger(obj));
    381   item.setReferrer(ReadString(obj));
    382 
    383   item.setDocumentState(ReadStringVector(obj));
    384 
    385   if (obj->version >= 9)
    386     item.setItemSequenceNumber(ReadInteger64(obj));
    387   if (obj->version >= 6)
    388     item.setDocumentSequenceNumber(ReadInteger64(obj));
    389   if (obj->version >= 7) {
    390     bool has_state_object = ReadBoolean(obj);
    391     if (has_state_object) {
    392       item.setStateObject(
    393           WebSerializedScriptValue::fromString(ReadString(obj)));
    394     }
    395   }
    396 
    397   // The extra referrer string is read for backwards compat.
    398   const WebHTTPBody& http_body = ReadFormData(obj);
    399   const WebString& http_content_type = ReadString(obj);
    400   ALLOW_UNUSED const WebString& unused_referrer = ReadString(obj);
    401   if (include_form_data) {
    402     item.setHTTPBody(http_body);
    403     item.setHTTPContentType(http_content_type);
    404   }
    405 
    406   // Subitems
    407   int num_children = ReadInteger(obj);
    408   for (int i = 0; i < num_children; ++i)
    409     item.appendToChildren(ReadHistoryItem(obj,
    410                                           include_form_data,
    411                                           include_scroll_offset));
    412 
    413   return item;
    414 }
    415 
    416 // Serialize a HistoryItem to a string, using our JSON Value serializer.
    417 std::string HistoryItemToString(const WebHistoryItem& item) {
    418   if (item.isNull())
    419     return std::string();
    420 
    421   SerializeObject obj;
    422   WriteHistoryItem(item, &obj);
    423   return obj.GetAsString();
    424 }
    425 
    426 // Reconstruct a HistoryItem from a string, using our JSON Value deserializer.
    427 // This assumes that the given serialized string has all the required key,value
    428 // pairs, and does minimal error checking. If |include_form_data| is true,
    429 // the form data from a post is restored, otherwise the form data is empty.
    430 // If |include_scroll_offset| is true, the scroll offset is restored.
    431 static WebHistoryItem HistoryItemFromString(
    432     const std::string& serialized_item,
    433     bool include_form_data,
    434     bool include_scroll_offset) {
    435   if (serialized_item.empty())
    436     return WebHistoryItem();
    437 
    438   SerializeObject obj(serialized_item.data(),
    439                       static_cast<int>(serialized_item.length()));
    440   return ReadHistoryItem(&obj, include_form_data, include_scroll_offset);
    441 }
    442 
    443 WebHistoryItem HistoryItemFromString(
    444     const std::string& serialized_item) {
    445   return HistoryItemFromString(serialized_item, true, true);
    446 }
    447 
    448 // For testing purposes only.
    449 void HistoryItemToVersionedString(const WebHistoryItem& item, int version,
    450                                   std::string* serialized_item) {
    451   if (item.isNull()) {
    452     serialized_item->clear();
    453     return;
    454   }
    455 
    456   // Temporarily change the version.
    457   int real_version = kVersion;
    458   kVersion = version;
    459 
    460   SerializeObject obj;
    461   WriteHistoryItem(item, &obj);
    462   *serialized_item = obj.GetAsString();
    463 
    464   kVersion = real_version;
    465 }
    466 
    467 std::string CreateHistoryStateForURL(const GURL& url) {
    468   // We avoid using the WebKit API here, so that we do not need to have WebKit
    469   // initialized before calling this method.  Instead, we write a simple
    470   // serialization of the given URL with a dummy version number of -1.  This
    471   // will be interpreted by ReadHistoryItem as a request to create a default
    472   // WebHistoryItem.
    473   SerializeObject obj;
    474   WriteInteger(-1, &obj);
    475   WriteGURL(url, &obj);
    476   return obj.GetAsString();
    477 }
    478 
    479 std::string RemoveFormDataFromHistoryState(const std::string& content_state) {
    480   // TODO(darin): We should avoid using the WebKit API here, so that we do not
    481   // need to have WebKit initialized before calling this method.
    482   const WebHistoryItem& item =
    483       HistoryItemFromString(content_state, false, true);
    484   if (item.isNull()) {
    485     // Couldn't parse the string, return an empty string.
    486     return std::string();
    487   }
    488 
    489   return HistoryItemToString(item);
    490 }
    491 
    492 std::string RemoveScrollOffsetFromHistoryState(
    493     const std::string& content_state) {
    494   // TODO(darin): We should avoid using the WebKit API here, so that we do not
    495   // need to have WebKit initialized before calling this method.
    496   const WebHistoryItem& item =
    497       HistoryItemFromString(content_state, true, false);
    498   if (item.isNull()) {
    499     // Couldn't parse the string, return an empty string.
    500     return std::string();
    501   }
    502 
    503   return HistoryItemToString(item);
    504 }
    505 
    506 }  // namespace webkit_glue
    507