• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
SerializeObjectwebkit_glue::SerializeObject34   SerializeObject() : iter(NULL) {}
SerializeObjectwebkit_glue::SerializeObject35   SerializeObject(const char* data, int len) : pickle(data, len), iter(NULL) {}
36 
GetAsStringwebkit_glue::SerializeObject37   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.
WriteData(const void * data,int length,SerializeObject * obj)70 inline void WriteData(const void* data, int length, SerializeObject* obj) {
71   obj->pickle.WriteData(static_cast<const char*>(data), length);
72 }
73 
ReadData(const SerializeObject * obj,const void ** data,int * length)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 
ReadBytes(const SerializeObject * obj,const void ** data,int length)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 
WriteInteger(int data,SerializeObject * obj)90 inline void WriteInteger(int data, SerializeObject* obj) {
91   obj->pickle.WriteInt(data);
92 }
93 
ReadInteger(const SerializeObject * obj)94 inline int ReadInteger(const SerializeObject* obj) {
95   int tmp = 0;
96   obj->pickle.ReadInt(&obj->iter, &tmp);
97   return tmp;
98 }
99 
WriteInteger64(int64 data,SerializeObject * obj)100 inline void WriteInteger64(int64 data, SerializeObject* obj) {
101   obj->pickle.WriteInt64(data);
102 }
103 
ReadInteger64(const SerializeObject * obj)104 inline int64 ReadInteger64(const SerializeObject* obj) {
105   int64 tmp = 0;
106   obj->pickle.ReadInt64(&obj->iter, &tmp);
107   return tmp;
108 }
109 
WriteReal(double data,SerializeObject * obj)110 inline void WriteReal(double data, SerializeObject* obj) {
111   WriteData(&data, sizeof(double), obj);
112 }
113 
ReadReal(const SerializeObject * obj)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 
WriteBoolean(bool data,SerializeObject * obj)124 inline void WriteBoolean(bool data, SerializeObject* obj) {
125   obj->pickle.WriteInt(data ? 1 : 0);
126 }
127 
ReadBoolean(const SerializeObject * obj)128 inline bool ReadBoolean(const SerializeObject* obj) {
129   bool tmp = false;
130   obj->pickle.ReadBool(&obj->iter, &tmp);
131   return tmp;
132 }
133 
WriteGURL(const GURL & url,SerializeObject * obj)134 inline void WriteGURL(const GURL& url, SerializeObject* obj) {
135   obj->pickle.WriteString(url.possibly_invalid_spec());
136 }
137 
ReadGURL(const SerializeObject * obj)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.
WriteString(const WebString & str,SerializeObject * obj)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.
ReadString(const SerializeObject * obj)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.
WriteStringVector(const WebVector<WebString> & data,SerializeObject * obj)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 
ReadStringVector(const SerializeObject * obj)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.
WriteFormData(const WebHTTPBody & http_body,SerializeObject * obj)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 
ReadFormData(const SerializeObject * obj)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.
WriteHistoryItem(const WebHistoryItem & item,SerializeObject * obj)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.
ReadHistoryItem(const SerializeObject * obj,bool include_form_data,bool include_scroll_offset)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.
HistoryItemToString(const WebHistoryItem & item)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.
HistoryItemFromString(const std::string & serialized_item,bool include_form_data,bool include_scroll_offset)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 
HistoryItemFromString(const std::string & serialized_item)443 WebHistoryItem HistoryItemFromString(
444     const std::string& serialized_item) {
445   return HistoryItemFromString(serialized_item, true, true);
446 }
447 
448 // For testing purposes only.
HistoryItemToVersionedString(const WebHistoryItem & item,int version,std::string * serialized_item)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 
CreateHistoryStateForURL(const GURL & url)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 
RemoveFormDataFromHistoryState(const std::string & content_state)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 
RemoveScrollOffsetFromHistoryState(const std::string & content_state)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