// Copyright 2008, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "config.h" #include #pragma warning(push, 0) #include "HistoryItem.h" #include "PlatformString.h" #include "ResourceRequest.h" #pragma warning(pop) #undef LOG #include "webkit/glue/glue_serialize.h" #include "base/pickle.h" #include "base/scoped_ptr.h" #include "base/string_util.h" #include "webkit/glue/glue_util.h" #include "webkit/glue/webkit_glue.h" using namespace WebCore; 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(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 UChars, not number of bytes. // This version checks and reads v1 and v2 correctly. // Should be const, but unit tests may modify it. int kVersion = 3; // 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(data), length); } inline void ReadData(const SerializeObject* obj, const void** data, int* length) { const char* tmp; 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; obj->pickle.ReadInt(&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; ReadData(obj, &tmp, &length); return *static_cast(tmp); } inline void WriteBoolean(bool data, SerializeObject* obj) { obj->pickle.WriteInt(data ? 1 : 0); } inline bool ReadBoolean(const SerializeObject* obj) { bool tmp; obj->pickle.ReadBool(&obj->iter, &tmp); return tmp; } // Read/WriteString pickle the String as . // If length == -1, then the String itself is NULL (String()). // Otherwise the length is the number of UChars (not bytes) in the String. inline void WriteString(const String& data, SerializeObject* obj) { switch (kVersion) { case 1: // Version 1 writes . // It saves String() and "" as "". obj->pickle.WriteInt(data.length() * sizeof(UChar)); obj->pickle.WriteBytes(data.characters(), data.length() * sizeof(UChar)); break; case 2: // Version 2 writes . // It uses -1 in the length field to mean String(). if (data.isNull()) { obj->pickle.WriteInt(-1); } else { obj->pickle.WriteInt(data.length()); obj->pickle.WriteBytes(data.characters(), data.length() * sizeof(UChar)); } break; case 3: // Version 3 writes . // It uses -1 in the length field to mean String(). if (data.isNull()) { obj->pickle.WriteInt(-1); } else { obj->pickle.WriteInt(data.length() * sizeof(UChar)); obj->pickle.WriteBytes(data.characters(), data.length() * sizeof(UChar)); } break; default: NOTREACHED(); break; } } // This reads a serialized String from obj. If a string can't be read, // String() is returned. inline String ReadString(const SerializeObject* obj) { int length; // Versions 1, 2, and 3 all start with an integer. if (!obj->pickle.ReadInt(&obj->iter, &length)) return String(); // Starting with version 2, -1 means String(). if (length == -1) return String(); // In version 2, the length field was the length in UChars. // In version 1 and 3 it is the length in bytes. int bytes = ((obj->version == 2) ? length * sizeof(UChar) : length); const void* data; if (!ReadBytes(obj, &data, bytes)) return String(); return String(static_cast(data), bytes / sizeof(UChar)); } // Writes a Vector of Strings into a SerializeObject for serialization. static void WriteStringVector(const Vector& data, SerializeObject* obj) { WriteInteger(static_cast(data.size()), obj); for (size_t i = 0, c = data.size(); i < c; ++i) { unsigned ui = static_cast(i); // sigh WriteString(data[ui], obj); } } static void ReadStringVector(const SerializeObject* obj, Vector* data) { int num_elements = ReadInteger(obj); data->reserveCapacity(num_elements); for (int i = 0; i < num_elements; ++i) { data->append(ReadString(obj)); } } // Writes a FormData object into a SerializeObject for serialization. static void WriteFormData(const FormData* form_data, SerializeObject* obj) { if (form_data == NULL) { WriteInteger(0, obj); return; } WriteInteger(static_cast(form_data->elements().size()), obj); for (size_t i = 0, c = form_data->elements().size(); i < c; ++i) { const FormDataElement& e = form_data->elements().at(i); WriteInteger(e.m_type, obj); if (e.m_type == FormDataElement::data) { WriteData(e.m_data.data(), static_cast(e.m_data.size()), obj); } else { WriteString(e.m_filename, obj); } } } static FormData* ReadFormData(const SerializeObject* obj) { int num_elements = ReadInteger(obj); if (num_elements == 0) return NULL; FormData* form_data = new FormData(); for (int i = 0; i < num_elements; ++i) { int type = ReadInteger(obj); if (type == FormDataElement::data) { const void* data; int length; ReadData(obj, &data, &length); form_data->appendData(static_cast(data), length); } else { form_data->appendFile(ReadString(obj)); } } return form_data; } // Writes the HistoryItem data into the SerializeObject object for // serialization. static void WriteHistoryItem(const HistoryItem* 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->scrollPoint().x(), obj); WriteInteger(item->scrollPoint().y(), obj); WriteBoolean(item->isTargetItem(), obj); WriteInteger(item->visitCount(), obj); WriteString(item->rssFeedReferrer(), obj); WriteStringVector(item->documentState(), obj); // No access to formData through a const HistoryItem = lame. WriteFormData(const_cast(item)->formData(), obj); WriteString(item->formContentType(), obj); WriteString(item->formReferrer(), obj); // Subitems WriteInteger(static_cast(item->children().size()), obj); for (size_t i = 0, c = item->children().size(); i < c; ++i) WriteHistoryItem(item->children().at(i).get(), obj); } // Creates a new HistoryItem tree based on the serialized string. // Assumes the data is in the format returned by WriteHistoryItem. static HistoryItem* ReadHistoryItem(const SerializeObject* obj) { // See note in WriteHistoryItem. on this. obj->version = ReadInteger(obj); if (obj->version > kVersion) return NULL; HistoryItem* item = new HistoryItem(); 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->setScrollPoint(IntPoint(x, y)); item->setIsTargetItem(ReadBoolean(obj)); item->setVisitCount(ReadInteger(obj)); item->setRSSFeedReferrer(ReadString(obj)); Vector document_state; ReadStringVector(obj, &document_state); item->setDocumentState(document_state); // Form data. If there is any form data, we assume POST, otherwise GET. // ResourceRequest takes ownership of the new FormData, then gives it to // HistoryItem. ResourceRequest dummy_request; // only way to initialize HistoryItem dummy_request.setHTTPBody(ReadFormData(obj)); dummy_request.setHTTPContentType(ReadString(obj)); dummy_request.setHTTPReferrer(ReadString(obj)); if (dummy_request.httpBody()) dummy_request.setHTTPMethod("POST"); item->setFormInfoFromRequest(dummy_request); // Subitems int num_children = ReadInteger(obj); for (int i = 0; i < num_children; ++i) item->addChildItem(ReadHistoryItem(obj)); return item; } // Serialize a HistoryItem to a string, using our JSON Value serializer. void HistoryItemToString(PassRefPtr item, std::string* serialized_item) { if (!item) { serialized_item->clear(); return; } SerializeObject obj; WriteHistoryItem(item.get(), &obj); *serialized_item = obj.GetAsString(); } // Reconstruct a HistoryItem from a string, using our JSON Value deserializer. // This assumes that the given serialized string has all the required key,value // pairs, and does minimal error checking. PassRefPtr HistoryItemFromString( const std::string& serialized_item) { if (serialized_item.empty()) return NULL; SerializeObject obj(serialized_item.data(), static_cast(serialized_item.length())); return ReadHistoryItem(&obj); } // For testing purposes only. void HistoryItemToVersionedString(PassRefPtr item, int version, std::string* serialized_item) { if (!item) { serialized_item->clear(); return; } // Temporarily change the version. int real_version = kVersion; kVersion = version; SerializeObject obj; WriteHistoryItem(item.get(), &obj); *serialized_item = obj.GetAsString(); kVersion = real_version; } std::string CreateHistoryStateForURL(const GURL& url) { RefPtr item(new HistoryItem(GURLToKURL(url), String())); std::string data; HistoryItemToString(item, &data); return data; } } // namespace webkit_glue