2 * Copyright (C) 2014 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #include "LegacySessionStateCoding.h"
30 #include "SessionState.h"
32 #include <wtf/MallocPtr.h>
33 #include <wtf/cf/TypeCasts.h>
34 #include <wtf/text/StringView.h>
38 // Session state keys.
39 static const uint32_t sessionStateDataVersion = 2;
41 static const CFStringRef sessionHistoryKey = CFSTR("SessionHistory");
42 static const CFStringRef provisionalURLKey = CFSTR("ProvisionalURL");
44 // Session history keys.
45 static const uint32_t sessionHistoryVersion = 1;
47 static const CFStringRef sessionHistoryVersionKey = CFSTR("SessionHistoryVersion");
48 static const CFStringRef sessionHistoryCurrentIndexKey = CFSTR("SessionHistoryCurrentIndex");
49 static const CFStringRef sessionHistoryEntriesKey = CFSTR("SessionHistoryEntries");
51 // Session history entry keys.
52 static const CFStringRef sessionHistoryEntryURLKey = CFSTR("SessionHistoryEntryURL");
53 static CFStringRef sessionHistoryEntryTitleKey = CFSTR("SessionHistoryEntryTitle");
54 static CFStringRef sessionHistoryEntryOriginalURLKey = CFSTR("SessionHistoryEntryOriginalURL");
55 static CFStringRef sessionHistoryEntryDataKey = CFSTR("SessionHistoryEntryData");
57 // Session history entry data.
58 const uint32_t sessionHistoryEntryDataVersion = 2;
60 template<typename T> void isValidEnum(T);
62 class HistoryEntryDataEncoder {
64 HistoryEntryDataEncoder()
66 , m_bufferCapacity(512)
67 , m_buffer(MallocPtr<uint8_t>::malloc(m_bufferCapacity))
68 , m_bufferPointer(m_buffer.get())
70 // Keep format compatibility by encoding an unused uint64_t here.
71 *this << static_cast<uint64_t>(0);
74 HistoryEntryDataEncoder& operator<<(uint32_t value)
76 return encodeArithmeticType(value);
79 HistoryEntryDataEncoder& operator<<(int32_t value)
81 return encodeArithmeticType(value);
84 HistoryEntryDataEncoder& operator<<(uint64_t value)
86 return encodeArithmeticType(value);
89 HistoryEntryDataEncoder& operator<<(int64_t value)
91 return encodeArithmeticType(value);
94 HistoryEntryDataEncoder& operator<<(float value)
96 return encodeArithmeticType(value);
99 HistoryEntryDataEncoder& operator<<(double value)
101 return encodeArithmeticType(value);
104 HistoryEntryDataEncoder& operator<<(bool value)
106 return encodeArithmeticType(value);
109 HistoryEntryDataEncoder& operator<<(const String& value)
111 // Special case the null string.
113 return *this << std::numeric_limits<uint32_t>::max();
115 uint32_t length = value.length();
118 *this << static_cast<uint64_t>(length * sizeof(UChar));
119 encodeFixedLengthData(reinterpret_cast<const uint8_t*>(StringView(value).upconvertedCharacters().get()), length * sizeof(UChar), alignof(UChar));
124 HistoryEntryDataEncoder& operator<<(const Vector<uint8_t>& value)
126 *this << static_cast<uint64_t>(value.size());
127 encodeFixedLengthData(value.data(), value.size(), 1);
132 HistoryEntryDataEncoder& operator<<(const Vector<char>& value)
134 *this << static_cast<uint64_t>(value.size());
135 encodeFixedLengthData(reinterpret_cast<const uint8_t*>(value.data()), value.size(), 1);
141 HistoryEntryDataEncoder& operator<<(WebCore::FloatRect value)
145 *this << value.width();
146 *this << value.height();
151 HistoryEntryDataEncoder& operator<<(WebCore::IntRect value)
155 *this << value.width();
156 *this << value.height();
161 HistoryEntryDataEncoder& operator<<(WebCore::FloatSize value)
163 *this << value.width();
164 *this << value.height();
169 HistoryEntryDataEncoder& operator<<(WebCore::IntSize value)
171 *this << value.width();
172 *this << value.height();
179 auto operator<<(T value) -> typename std::enable_if<std::is_enum<T>::value, HistoryEntryDataEncoder&>::type
181 return *this << static_cast<uint32_t>(value);
184 MallocPtr<uint8_t> finishEncoding(size_t& size)
187 return std::move(m_buffer);
191 template<typename Type>
192 HistoryEntryDataEncoder& encodeArithmeticType(Type value)
194 static_assert(std::is_arithmetic<Type>::value, "");
196 encodeFixedLengthData(reinterpret_cast<uint8_t*>(&value), sizeof(value), sizeof(value));
200 void encodeFixedLengthData(const uint8_t* data, size_t size, unsigned alignment)
202 ASSERT(!(reinterpret_cast<uintptr_t>(data) % alignment));
204 uint8_t* buffer = grow(alignment, size);
205 memcpy(buffer, data, size);
208 uint8_t* grow(unsigned alignment, size_t size)
210 size_t alignedSize = ((m_bufferSize + alignment - 1) / alignment) * alignment;
212 growCapacity(alignedSize + size);
214 m_bufferSize = alignedSize + size;
215 m_bufferPointer = m_buffer.get() + m_bufferSize;
217 return m_buffer.get() + alignedSize;
220 void growCapacity(size_t newSize)
222 if (newSize <= m_bufferCapacity)
225 size_t newCapacity = m_bufferCapacity * 2;
226 while (newCapacity < newSize)
229 m_buffer.realloc(newCapacity);
230 m_bufferCapacity = newCapacity;
234 size_t m_bufferCapacity;
235 MallocPtr<uint8_t> m_buffer;
236 uint8_t* m_bufferPointer;
239 enum class FormDataElementType {
245 static bool isValidEnum(FormDataElementType type)
248 case FormDataElementType::Data:
249 case FormDataElementType::EncodedFile:
250 case FormDataElementType::EncodedBlob:
257 static void encodeFormDataElement(HistoryEntryDataEncoder& encoder, const HTTPBody::Element& element)
259 switch (element.type) {
260 case HTTPBody::Element::Type::Data:
261 encoder << FormDataElementType::Data;
262 encoder << element.data;
265 case HTTPBody::Element::Type::File:
266 encoder << FormDataElementType::EncodedFile;
267 encoder << element.filePath;
269 // Used to be generatedFilename.
272 // Used to be shouldGenerateFile.
275 encoder << element.fileStart;
276 encoder << element.fileLength.valueOr(-1);
277 encoder << element.expectedFileModificationTime.valueOr(std::numeric_limits<double>::quiet_NaN());
280 case HTTPBody::Element::Type::Blob:
281 encoder << FormDataElementType::EncodedBlob;
282 encoder << element.blobURLString;
287 static void encodeFormData(HistoryEntryDataEncoder& encoder, const HTTPBody& formData)
289 // Used to be alwaysStream.
292 // Used to be boundary.
293 encoder << Vector<uint8_t>();
295 encoder << static_cast<uint64_t>(formData.elements.size());
296 for (const auto& element : formData.elements)
297 encodeFormDataElement(encoder, element);
299 // Used to be hasGeneratedFiles.
302 // Used to be identifier.
303 encoder << static_cast<int64_t>(0);
306 static void encodeFrameStateNode(HistoryEntryDataEncoder& encoder, const FrameState& frameState)
308 encoder << static_cast<uint64_t>(frameState.children.size());
310 for (const auto& childFrameState : frameState.children) {
311 encoder << childFrameState.originalURLString;
312 encoder << childFrameState.urlString;
314 encodeFrameStateNode(encoder, childFrameState);
317 encoder << frameState.documentSequenceNumber;
319 encoder << static_cast<uint64_t>(frameState.documentState.size());
320 for (const auto& documentState : frameState.documentState)
321 encoder << documentState;
323 if (frameState.httpBody) {
324 encoder << frameState.httpBody.value().contentType;
327 encodeFormData(encoder, frameState.httpBody.value());
333 encoder << frameState.itemSequenceNumber;
335 encoder << frameState.referrer;
337 encoder << frameState.scrollPoint.x();
338 encoder << frameState.scrollPoint.y();
340 encoder << frameState.pageScaleFactor;
342 encoder << !!frameState.stateObjectData;
343 if (frameState.stateObjectData)
344 encoder << frameState.stateObjectData.value();
346 encoder << frameState.target;
349 // FIXME: iOS should not use the legacy session state encoder.
350 encoder << frameState.exposedContentRect;
351 encoder << frameState.unobscuredContentRect;
352 encoder << frameState.minimumLayoutSizeInScrollViewCoordinates;
353 encoder << frameState.contentSize;
354 encoder << frameState.scaleIsInitial;
358 static MallocPtr<uint8_t> encodeSessionHistoryEntryData(const FrameState& frameState, size_t& bufferSize)
360 HistoryEntryDataEncoder encoder;
362 encoder << sessionHistoryEntryDataVersion;
363 encodeFrameStateNode(encoder, frameState);
365 return encoder.finishEncoding(bufferSize);
368 RefPtr<API::Data> encodeLegacySessionHistoryEntryData(const FrameState& frameState)
371 auto buffer = encodeSessionHistoryEntryData(frameState, bufferSize);
373 return API::Data::createWithoutCopying(buffer.leakPtr(), bufferSize, [] (unsigned char* buffer, const void* context) {
378 static RetainPtr<CFDataRef> encodeSessionHistoryEntryData(const FrameState& frameState)
380 static CFAllocatorRef fastMallocDeallocator;
382 static std::once_flag onceFlag;
383 std::call_once(onceFlag, [] {
384 CFAllocatorContext context = {
389 nullptr, // copyDescription
391 nullptr, // reallocate
392 [](void *ptr, void *info) {
395 nullptr, // preferredSize
397 fastMallocDeallocator = CFAllocatorCreate(kCFAllocatorDefault, &context);
401 auto buffer = encodeSessionHistoryEntryData(frameState, bufferSize);
403 return adoptCF(CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buffer.leakPtr(), bufferSize, fastMallocDeallocator));
406 static RetainPtr<CFDictionaryRef> createDictionary(std::initializer_list<std::pair<CFStringRef, CFTypeRef>> keyValuePairs)
408 Vector<CFTypeRef> keys;
409 Vector<CFTypeRef> values;
411 keys.reserveInitialCapacity(keyValuePairs.size());
412 values.reserveInitialCapacity(keyValuePairs.size());
414 for (const auto& keyValuePair : keyValuePairs) {
415 keys.uncheckedAppend(keyValuePair.first);
416 values.uncheckedAppend(keyValuePair.second);
419 return adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys.data(), values.data(), keyValuePairs.size(), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
422 static RetainPtr<CFDictionaryRef> encodeSessionHistory(const BackForwardListState& backForwardListState)
424 ASSERT(!backForwardListState.currentIndex || backForwardListState.currentIndex.value() < backForwardListState.items.size());
426 auto sessionHistoryVersionNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &sessionHistoryVersion));
428 if (!backForwardListState.currentIndex)
429 return createDictionary({ { sessionHistoryVersionKey, sessionHistoryVersionNumber.get() } });
431 auto entries = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, backForwardListState.items.size(), &kCFTypeArrayCallBacks));
433 for (const auto& item : backForwardListState.items) {
434 auto url = item.pageState.mainFrameState.urlString.createCFString();
435 auto title = item.pageState.title.createCFString();
436 auto originalURL = item.pageState.mainFrameState.originalURLString.createCFString();
437 auto data = encodeSessionHistoryEntryData(item.pageState.mainFrameState);
439 auto entryDictionary = createDictionary({ { sessionHistoryEntryURLKey, url.get() }, { sessionHistoryEntryTitleKey, title.get() }, { sessionHistoryEntryOriginalURLKey, originalURL.get() }, { sessionHistoryEntryDataKey, data.get() } });
441 CFArrayAppendValue(entries.get(), entryDictionary.get());
444 uint32_t currentIndex = backForwardListState.currentIndex.value();
445 auto currentIndexNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, ¤tIndex));
447 return createDictionary({ { sessionHistoryVersionKey, sessionHistoryVersionNumber.get() }, { sessionHistoryCurrentIndexKey, currentIndexNumber.get() }, { sessionHistoryEntriesKey, entries.get() } });
450 RefPtr<API::Data> encodeLegacySessionState(const SessionState& sessionState)
452 auto sessionHistoryDictionary = encodeSessionHistory(sessionState.backForwardListState);
453 auto provisionalURLString = sessionState.provisionalURL.isNull() ? nullptr : sessionState.provisionalURL.string().createCFString();
455 RetainPtr<CFDictionaryRef> stateDictionary;
456 if (provisionalURLString)
457 stateDictionary = createDictionary({ { sessionHistoryKey, sessionHistoryDictionary.get() }, { provisionalURLKey, provisionalURLString.get() } });
459 stateDictionary = createDictionary({ { sessionHistoryKey, sessionHistoryDictionary.get() } });
461 auto writeStream = adoptCF(CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorDefault, nullptr));
465 if (!CFWriteStreamOpen(writeStream.get()))
468 if (!CFPropertyListWrite(stateDictionary.get(), writeStream.get(), kCFPropertyListBinaryFormat_v1_0, 0, nullptr))
471 auto data = adoptCF(static_cast<CFDataRef>(CFWriteStreamCopyProperty(writeStream.get(), kCFStreamPropertyDataWritten)));
473 CFIndex length = CFDataGetLength(data.get());
475 size_t bufferSize = length + sizeof(uint32_t);
476 auto buffer = MallocPtr<uint8_t>::malloc(bufferSize);
478 // Put the session state version number at the start of the buffer
479 buffer.get()[0] = (sessionStateDataVersion & 0xff000000) >> 24;
480 buffer.get()[1] = (sessionStateDataVersion & 0x00ff0000) >> 16;
481 buffer.get()[2] = (sessionStateDataVersion & 0x0000ff00) >> 8;
482 buffer.get()[3] = (sessionStateDataVersion & 0x000000ff);
484 // Copy in the actual session state data
485 CFDataGetBytes(data.get(), CFRangeMake(0, length), buffer.get() + sizeof(uint32_t));
487 return API::Data::createWithoutCopying(buffer.leakPtr(), bufferSize, [] (unsigned char* buffer, const void* context) {
492 class HistoryEntryDataDecoder {
494 HistoryEntryDataDecoder(const uint8_t* buffer, size_t bufferSize)
496 , m_bufferEnd(buffer + bufferSize)
498 // Keep format compatibility by decoding an unused uint64_t here.
503 HistoryEntryDataDecoder& operator>>(bool& value)
505 return decodeArithmeticType(value);
508 HistoryEntryDataDecoder& operator>>(uint32_t& value)
510 return decodeArithmeticType(value);
513 HistoryEntryDataDecoder& operator>>(int32_t& value)
515 return *this >> reinterpret_cast<uint32_t&>(value);
518 HistoryEntryDataDecoder& operator>>(uint64_t& value)
520 return decodeArithmeticType(value);
523 HistoryEntryDataDecoder& operator>>(int64_t& value)
525 return *this >> reinterpret_cast<uint64_t&>(value);
528 HistoryEntryDataDecoder& operator>>(float& value)
530 return decodeArithmeticType(value);
533 HistoryEntryDataDecoder& operator>>(double& value)
535 return decodeArithmeticType(value);
538 HistoryEntryDataDecoder& operator>>(String& value)
545 if (length == std::numeric_limits<uint32_t>::max()) {
546 // This is the null string.
551 uint64_t lengthInBytes;
552 *this >> lengthInBytes;
554 if (lengthInBytes % sizeof(UChar) || lengthInBytes / sizeof(UChar) != length) {
559 if (!bufferIsLargeEnoughToContain<UChar>(length)) {
565 auto string = String::createUninitialized(length, buffer);
566 decodeFixedLengthData(reinterpret_cast<uint8_t*>(buffer), length * sizeof(UChar), alignof(UChar));
572 HistoryEntryDataDecoder& operator>>(Vector<uint8_t>& value)
579 if (!alignBufferPosition(1, size))
582 const uint8_t* data = m_buffer;
585 value.append(data, size);
589 HistoryEntryDataDecoder& operator>>(Vector<char>& value)
596 if (!alignBufferPosition(1, size))
599 const uint8_t* data = m_buffer;
602 value.append(data, size);
607 HistoryEntryDataDecoder& operator>>(WebCore::FloatRect& value)
609 value = WebCore::FloatRect();
623 value = WebCore::FloatRect(x, y, width, height);
627 HistoryEntryDataDecoder& operator>>(WebCore::IntRect& value)
629 value = WebCore::IntRect();
643 value = WebCore::IntRect(x, y, width, height);
647 HistoryEntryDataDecoder& operator>>(WebCore::FloatSize& value)
649 value = WebCore::FloatSize();
657 value = WebCore::FloatSize(width, height);
661 HistoryEntryDataDecoder& operator>>(WebCore::IntSize& value)
663 value = WebCore::IntSize();
671 value = WebCore::IntSize(width, height);
677 auto operator>>(Optional<T>& value) -> typename std::enable_if<std::is_enum<T>::value, HistoryEntryDataDecoder&>::type
679 uint32_t underlyingEnumValue;
680 *this >> underlyingEnumValue;
682 if (!isValid() || !isValidEnum(static_cast<T>(underlyingEnumValue)))
685 value = static_cast<T>(underlyingEnumValue);
690 bool isValid() const { return m_buffer <= m_bufferEnd; }
691 void markInvalid() { m_buffer = m_bufferEnd + 1; }
693 bool finishDecoding() { return m_buffer == m_bufferEnd; }
696 template<typename Type>
697 HistoryEntryDataDecoder& decodeArithmeticType(Type& value)
699 static_assert(std::is_arithmetic<Type>::value, "");
702 decodeFixedLengthData(reinterpret_cast<uint8_t*>(&value), sizeof(value), sizeof(value));
706 void decodeFixedLengthData(uint8_t* data, size_t size, unsigned alignment)
708 if (!alignBufferPosition(alignment, size))
711 memcpy(data, m_buffer, size);
715 bool alignBufferPosition(unsigned alignment, size_t size)
717 const uint8_t* alignedPosition = alignedBuffer(alignment);
718 if (!alignedBufferIsLargeEnoughToContain(alignedPosition, size)) {
719 // We've walked off the end of this buffer.
724 m_buffer = alignedPosition;
728 const uint8_t* alignedBuffer(unsigned alignment) const
730 ASSERT(alignment && !(alignment & (alignment - 1)));
732 uintptr_t alignmentMask = alignment - 1;
733 return reinterpret_cast<uint8_t*>((reinterpret_cast<uintptr_t>(m_buffer) + alignmentMask) & ~alignmentMask);
737 bool bufferIsLargeEnoughToContain(size_t numElements) const
739 static_assert(std::is_arithmetic<T>::value, "Type T must have a fixed, known encoded size!");
741 if (numElements > std::numeric_limits<size_t>::max() / sizeof(T))
744 return bufferIsLargeEnoughToContain(alignof(T), numElements * sizeof(T));
747 bool bufferIsLargeEnoughToContain(unsigned alignment, size_t size) const
749 return alignedBufferIsLargeEnoughToContain(alignedBuffer(alignment), size);
752 inline bool alignedBufferIsLargeEnoughToContain(const uint8_t* alignedPosition, size_t size) const
754 return m_bufferEnd >= alignedPosition && static_cast<size_t>(m_bufferEnd - alignedPosition) >= size;
757 const uint8_t* m_buffer;
758 const uint8_t* m_bufferEnd;
761 static void decodeFormDataElement(HistoryEntryDataDecoder& decoder, HTTPBody::Element& formDataElement)
763 Optional<FormDataElementType> elementType;
764 decoder >> elementType;
768 switch (elementType.value()) {
769 case FormDataElementType::Data:
770 formDataElement.type = HTTPBody::Element::Type::Data;
771 decoder >> formDataElement.data;
774 case FormDataElementType::EncodedFile: {
775 decoder >> formDataElement.filePath;
777 String generatedFilename;
778 decoder >> generatedFilename;
780 bool shouldGenerateFile;
781 decoder >> shouldGenerateFile;
783 decoder >> formDataElement.fileStart;
784 if (formDataElement.fileStart < 0) {
785 decoder.markInvalid();
790 decoder >> fileLength;
791 if (fileLength != -1) {
792 if (fileLength < formDataElement.fileStart)
795 formDataElement.fileLength = fileLength;
799 double expectedFileModificationTime;
800 decoder >> expectedFileModificationTime;
801 if (expectedFileModificationTime != std::numeric_limits<double>::quiet_NaN())
802 formDataElement.expectedFileModificationTime = expectedFileModificationTime;
807 case FormDataElementType::EncodedBlob:
808 decoder >> formDataElement.blobURLString;
813 static void decodeFormData(HistoryEntryDataDecoder& decoder, HTTPBody& formData)
816 decoder >> alwaysStream;
818 Vector<uint8_t> boundary;
821 uint64_t formDataElementCount;
822 decoder >> formDataElementCount;
824 for (uint64_t i = 0; i < formDataElementCount; ++i) {
825 HTTPBody::Element formDataElement;
826 decodeFormDataElement(decoder, formDataElement);
828 if (!decoder.isValid())
831 formData.elements.append(std::move(formDataElement));
834 bool hasGeneratedFiles;
835 decoder >> hasGeneratedFiles;
838 decoder >> identifier;
841 static void decodeBackForwardTreeNode(HistoryEntryDataDecoder& decoder, FrameState& frameState)
844 decoder >> childCount;
846 for (uint64_t i = 0; i < childCount; ++i) {
847 FrameState childFrameState;
848 decoder >> childFrameState.originalURLString;
849 decoder >> childFrameState.urlString;
851 decodeBackForwardTreeNode(decoder, childFrameState);
853 if (!decoder.isValid())
856 frameState.children.append(std::move(childFrameState));
859 decoder >> frameState.documentSequenceNumber;
861 uint64_t documentStateVectorSize;
862 decoder >> documentStateVectorSize;
864 for (uint64_t i = 0; i < documentStateVectorSize; ++i) {
868 if (!decoder.isValid())
871 frameState.documentState.append(std::move(state));
874 String formContentType;
875 decoder >> formContentType;
878 decoder >> hasFormData;
882 httpBody.contentType = std::move(formContentType);
884 decodeFormData(decoder, httpBody);
886 frameState.httpBody = std::move(httpBody);
889 decoder >> frameState.itemSequenceNumber;
891 decoder >> frameState.referrer;
893 int32_t scrollPointX;
894 decoder >> scrollPointX;
896 int32_t scrollPointY;
897 decoder >> scrollPointY;
899 frameState.scrollPoint = WebCore::IntPoint(scrollPointX, scrollPointY);
901 decoder >> frameState.pageScaleFactor;
904 decoder >> hasStateObject;
906 if (hasStateObject) {
907 Vector<uint8_t> stateObjectData;
908 decoder >> stateObjectData;
910 frameState.stateObjectData = std::move(stateObjectData);
913 decoder >> frameState.target;
915 // FIXME: iOS should not use the legacy session state decoder.
917 decoder >> frameState.exposedContentRect;
918 decoder >> frameState.unobscuredContentRect;
919 decoder >> frameState.minimumLayoutSizeInScrollViewCoordinates;
920 decoder >> frameState.contentSize;
921 decoder >> frameState.scaleIsInitial;
925 static bool decodeSessionHistoryEntryData(const uint8_t* buffer, size_t bufferSize, FrameState& mainFrameState)
927 HistoryEntryDataDecoder decoder { buffer, bufferSize };
932 if (version != sessionHistoryEntryDataVersion)
935 decodeBackForwardTreeNode(decoder, mainFrameState);
937 return decoder.finishDecoding();
940 static bool decodeSessionHistoryEntryData(CFDataRef historyEntryData, FrameState& mainFrameState)
942 return decodeSessionHistoryEntryData(CFDataGetBytePtr(historyEntryData), static_cast<size_t>(CFDataGetLength(historyEntryData)), mainFrameState);
945 static bool decodeSessionHistoryEntry(CFDictionaryRef entryDictionary, BackForwardListItemState& backForwardListItemState)
947 auto title = dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(entryDictionary, sessionHistoryEntryTitleKey));
951 auto urlString = dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(entryDictionary, sessionHistoryEntryURLKey));
955 auto originalURLString = dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(entryDictionary, sessionHistoryEntryOriginalURLKey));
956 if (!originalURLString)
959 auto historyEntryData = dynamic_cf_cast<CFDataRef>(CFDictionaryGetValue(entryDictionary, sessionHistoryEntryDataKey));
960 if (!historyEntryData)
963 if (!decodeSessionHistoryEntryData(historyEntryData, backForwardListItemState.pageState.mainFrameState))
966 backForwardListItemState.pageState.title = title;
967 backForwardListItemState.pageState.mainFrameState.urlString = urlString;
968 backForwardListItemState.pageState.mainFrameState.originalURLString = originalURLString;
973 static bool decodeSessionHistoryEntries(CFArrayRef entriesArray, Vector<BackForwardListItemState>& entries)
975 for (CFIndex i = 0, size = CFArrayGetCount(entriesArray); i < size; ++i) {
976 auto entryDictionary = dynamic_cf_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(entriesArray, i));
977 if (!entryDictionary)
980 BackForwardListItemState entry;
981 if (!decodeSessionHistoryEntry(entryDictionary, entry))
984 entries.append(std::move(entry));
990 static bool decodeV0SessionHistory(CFDictionaryRef sessionHistoryDictionary, BackForwardListState& backForwardListState)
992 auto currentIndexNumber = dynamic_cf_cast<CFNumberRef>(CFDictionaryGetValue(sessionHistoryDictionary, sessionHistoryCurrentIndexKey));
993 if (!currentIndexNumber)
996 CFIndex currentIndex;
997 if (!CFNumberGetValue(currentIndexNumber, kCFNumberCFIndexType, ¤tIndex))
1000 if (currentIndex < -1)
1003 auto historyEntries = dynamic_cf_cast<CFArrayRef>(CFDictionaryGetValue(sessionHistoryDictionary, sessionHistoryEntriesKey));
1004 if (!historyEntries)
1007 // Version 0 session history relied on currentIndex == -1 to represent the same thing as not having a current index.
1008 bool hasCurrentIndex = currentIndex != -1;
1010 if (!decodeSessionHistoryEntries(historyEntries, backForwardListState.items))
1013 if (!hasCurrentIndex && CFArrayGetCount(historyEntries))
1016 if (hasCurrentIndex) {
1017 if (static_cast<uint32_t>(currentIndex) >= backForwardListState.items.size())
1020 backForwardListState.currentIndex = static_cast<uint32_t>(currentIndex);
1026 static bool decodeV1SessionHistory(CFDictionaryRef sessionHistoryDictionary, BackForwardListState& backForwardListState)
1028 auto currentIndexNumber = dynamic_cf_cast<CFNumberRef>(CFDictionaryGetValue(sessionHistoryDictionary, sessionHistoryCurrentIndexKey));
1029 if (!currentIndexNumber) {
1030 // No current index means the dictionary represents an empty session.
1031 backForwardListState.currentIndex = Nullopt;
1032 backForwardListState.items = { };
1036 CFIndex currentIndex;
1037 if (!CFNumberGetValue(currentIndexNumber, kCFNumberCFIndexType, ¤tIndex))
1040 if (currentIndex < 0)
1043 auto historyEntries = dynamic_cf_cast<CFArrayRef>(CFDictionaryGetValue(sessionHistoryDictionary, sessionHistoryEntriesKey));
1044 if (!historyEntries)
1047 if (!decodeSessionHistoryEntries(historyEntries, backForwardListState.items))
1050 backForwardListState.currentIndex = static_cast<uint32_t>(currentIndex);
1051 if (static_cast<uint32_t>(currentIndex) >= backForwardListState.items.size())
1057 static bool decodeSessionHistory(CFDictionaryRef backForwardListDictionary, BackForwardListState& backForwardListState)
1059 auto sessionHistoryVersionNumber = dynamic_cf_cast<CFNumberRef>(CFDictionaryGetValue(backForwardListDictionary, sessionHistoryVersionKey));
1060 if (!sessionHistoryVersionNumber) {
1061 // Version 0 session history dictionaries did not contain a version number.
1062 return decodeV0SessionHistory(backForwardListDictionary, backForwardListState);
1065 CFIndex sessionHistoryVersion;
1066 if (!CFNumberGetValue(sessionHistoryVersionNumber, kCFNumberCFIndexType, &sessionHistoryVersion))
1069 if (sessionHistoryVersion == 1)
1070 return decodeV1SessionHistory(backForwardListDictionary, backForwardListState);
1075 bool decodeLegacySessionState(const API::Data& data, SessionState& sessionState)
1077 size_t size = data.size();
1078 const uint8_t* bytes = data.bytes();
1080 if (size < sizeof(uint32_t))
1083 uint32_t versionNumber = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
1085 if (versionNumber != sessionStateDataVersion)
1088 auto sessionStateDictionary = adoptCF(dynamic_cf_cast<CFDictionaryRef>(CFPropertyListCreateWithData(kCFAllocatorDefault, adoptCF(CFDataCreate(kCFAllocatorDefault, bytes + sizeof(uint32_t), size - sizeof(uint32_t))).get(), kCFPropertyListImmutable, nullptr, nullptr)));
1089 if (!sessionStateDictionary)
1092 if (auto backForwardListDictionary = dynamic_cf_cast<CFDictionaryRef>(CFDictionaryGetValue(sessionStateDictionary.get(), sessionHistoryKey))) {
1093 if (!decodeSessionHistory(backForwardListDictionary, sessionState.backForwardListState))
1097 if (auto provisionalURLString = dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(sessionStateDictionary.get(), provisionalURLKey))) {
1098 sessionState.provisionalURL = WebCore::URL(WebCore::URL(), provisionalURLString);
1099 if (!sessionState.provisionalURL.isValid())
1106 bool decodeLegacySessionHistoryEntryData(const uint8_t* data, size_t size, FrameState& mainFrameState)
1108 return decodeSessionHistoryEntryData(data, size, mainFrameState);
1111 } // namespace WebKit