Handle invalid data more gracefully.
[WebKit-https.git] / Source / WebKit2 / UIProcess / mac / LegacySessionStateCoding.cpp
1 /*
2  * Copyright (C) 2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 #include "config.h"
27 #include "LegacySessionStateCoding.h"
28
29 #include "APIData.h"
30 #include "SessionState.h"
31 #include <mutex>
32 #include <wtf/MallocPtr.h>
33 #include <wtf/cf/TypeCasts.h>
34 #include <wtf/text/StringView.h>
35
36 namespace WebKit {
37
38 // Session state keys.
39 static const uint32_t sessionStateDataVersion = 2;
40
41 static const CFStringRef sessionHistoryKey = CFSTR("SessionHistory");
42 static const CFStringRef provisionalURLKey = CFSTR("ProvisionalURL");
43
44 // Session history keys.
45 static const uint32_t sessionHistoryVersion = 1;
46
47 static const CFStringRef sessionHistoryVersionKey = CFSTR("SessionHistoryVersion");
48 static const CFStringRef sessionHistoryCurrentIndexKey = CFSTR("SessionHistoryCurrentIndex");
49 static const CFStringRef sessionHistoryEntriesKey = CFSTR("SessionHistoryEntries");
50
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");
56
57 // Session history entry data.
58 const uint32_t sessionHistoryEntryDataVersion = 2;
59
60 template<typename T> void isValidEnum(T);
61
62 class HistoryEntryDataEncoder {
63 public:
64     HistoryEntryDataEncoder()
65         : m_bufferSize(0)
66         , m_bufferCapacity(512)
67         , m_buffer(MallocPtr<uint8_t>::malloc(m_bufferCapacity))
68         , m_bufferPointer(m_buffer.get())
69     {
70         // Keep format compatibility by encoding an unused uint64_t here.
71         *this << static_cast<uint64_t>(0);
72     }
73
74     HistoryEntryDataEncoder& operator<<(uint32_t value)
75     {
76         return encodeArithmeticType(value);
77     }
78
79     HistoryEntryDataEncoder& operator<<(int32_t value)
80     {
81         return encodeArithmeticType(value);
82     }
83
84     HistoryEntryDataEncoder& operator<<(uint64_t value)
85     {
86         return encodeArithmeticType(value);
87     }
88
89     HistoryEntryDataEncoder& operator<<(int64_t value)
90     {
91         return encodeArithmeticType(value);
92     }
93
94     HistoryEntryDataEncoder& operator<<(float value)
95     {
96         return encodeArithmeticType(value);
97     }
98
99     HistoryEntryDataEncoder& operator<<(double value)
100     {
101         return encodeArithmeticType(value);
102     }
103
104     HistoryEntryDataEncoder& operator<<(bool value)
105     {
106         return encodeArithmeticType(value);
107     }
108
109     HistoryEntryDataEncoder& operator<<(const String& value)
110     {
111         // Special case the null string.
112         if (value.isNull())
113             return *this << std::numeric_limits<uint32_t>::max();
114
115         uint32_t length = value.length();
116         *this << length;
117
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));
120
121         return *this;
122     }
123
124     HistoryEntryDataEncoder& operator<<(const Vector<uint8_t>& value)
125     {
126         *this << static_cast<uint64_t>(value.size());
127         encodeFixedLengthData(value.data(), value.size(), 1);
128
129         return *this;
130     }
131
132     HistoryEntryDataEncoder& operator<<(const Vector<char>& value)
133     {
134         *this << static_cast<uint64_t>(value.size());
135         encodeFixedLengthData(reinterpret_cast<const uint8_t*>(value.data()), value.size(), 1);
136
137         return *this;
138     }
139
140 #if PLATFORM(IOS)
141     HistoryEntryDataEncoder& operator<<(WebCore::FloatRect value)
142     {
143         *this << value.x();
144         *this << value.y();
145         *this << value.width();
146         *this << value.height();
147
148         return *this;
149     }
150
151     HistoryEntryDataEncoder& operator<<(WebCore::IntRect value)
152     {
153         *this << value.x();
154         *this << value.y();
155         *this << value.width();
156         *this << value.height();
157
158         return *this;
159     }
160
161     HistoryEntryDataEncoder& operator<<(WebCore::FloatSize value)
162     {
163         *this << value.width();
164         *this << value.height();
165
166         return *this;
167     }
168
169     HistoryEntryDataEncoder& operator<<(WebCore::IntSize value)
170     {
171         *this << value.width();
172         *this << value.height();
173
174         return *this;
175     }
176 #endif
177
178     template<typename T>
179     auto operator<<(T value) -> typename std::enable_if<std::is_enum<T>::value, HistoryEntryDataEncoder&>::type
180     {
181         return *this << static_cast<uint32_t>(value);
182     }
183
184     MallocPtr<uint8_t> finishEncoding(size_t& size)
185     {
186         size = m_bufferSize;
187         return std::move(m_buffer);
188     }
189
190 private:
191     template<typename Type>
192     HistoryEntryDataEncoder& encodeArithmeticType(Type value)
193     {
194         static_assert(std::is_arithmetic<Type>::value, "");
195
196         encodeFixedLengthData(reinterpret_cast<uint8_t*>(&value), sizeof(value), sizeof(value));
197         return *this;
198     }
199
200     void encodeFixedLengthData(const uint8_t* data, size_t size, unsigned alignment)
201     {
202         ASSERT(!(reinterpret_cast<uintptr_t>(data) % alignment));
203
204         uint8_t* buffer = grow(alignment, size);
205         memcpy(buffer, data, size);
206     }
207
208     uint8_t* grow(unsigned alignment, size_t size)
209     {
210         size_t alignedSize = ((m_bufferSize + alignment - 1) / alignment) * alignment;
211
212         growCapacity(alignedSize + size);
213
214         m_bufferSize = alignedSize + size;
215         m_bufferPointer = m_buffer.get() + m_bufferSize;
216
217         return m_buffer.get() + alignedSize;
218     }
219
220     void growCapacity(size_t newSize)
221     {
222         if (newSize <= m_bufferCapacity)
223             return;
224
225         size_t newCapacity = m_bufferCapacity * 2;
226         while (newCapacity < newSize)
227             newCapacity *= 2;
228
229         m_buffer.realloc(newCapacity);
230         m_bufferCapacity = newCapacity;
231     }
232
233     size_t m_bufferSize;
234     size_t m_bufferCapacity;
235     MallocPtr<uint8_t> m_buffer;
236     uint8_t* m_bufferPointer;
237 };
238
239 enum class FormDataElementType {
240     Data = 0,
241     EncodedFile = 1,
242     EncodedBlob = 2,
243 };
244
245 static bool isValidEnum(FormDataElementType type)
246 {
247     switch (type) {
248     case FormDataElementType::Data:
249     case FormDataElementType::EncodedFile:
250     case FormDataElementType::EncodedBlob:
251         return true;
252     }
253
254     return false;
255 }
256
257 static void encodeFormDataElement(HistoryEntryDataEncoder& encoder, const HTTPBody::Element& element)
258 {
259     switch (element.type) {
260     case HTTPBody::Element::Type::Data:
261         encoder << FormDataElementType::Data;
262         encoder << element.data;
263         break;
264
265     case HTTPBody::Element::Type::File:
266         encoder << FormDataElementType::EncodedFile;
267         encoder << element.filePath;
268
269         // Used to be generatedFilename.
270         encoder << String();
271
272         // Used to be shouldGenerateFile.
273         encoder << false;
274
275         encoder << element.fileStart;
276         encoder << element.fileLength.valueOr(-1);
277         encoder << element.expectedFileModificationTime.valueOr(std::numeric_limits<double>::quiet_NaN());
278         break;
279
280     case HTTPBody::Element::Type::Blob:
281         encoder << FormDataElementType::EncodedBlob;
282         encoder << element.blobURLString;
283         break;
284     }
285 }
286
287 static void encodeFormData(HistoryEntryDataEncoder& encoder, const HTTPBody& formData)
288 {
289     // Used to be alwaysStream.
290     encoder << false;
291
292     // Used to be boundary.
293     encoder << Vector<uint8_t>();
294
295     encoder << static_cast<uint64_t>(formData.elements.size());
296     for (const auto& element : formData.elements)
297         encodeFormDataElement(encoder, element);
298
299     // Used to be hasGeneratedFiles.
300     encoder << false;
301
302     // Used to be identifier.
303     encoder << static_cast<int64_t>(0);
304 }
305
306 static void encodeFrameStateNode(HistoryEntryDataEncoder& encoder, const FrameState& frameState)
307 {
308     encoder << static_cast<uint64_t>(frameState.children.size());
309
310     for (const auto& childFrameState : frameState.children) {
311         encoder << childFrameState.originalURLString;
312         encoder << childFrameState.urlString;
313
314         encodeFrameStateNode(encoder, childFrameState);
315     }
316
317     encoder << frameState.documentSequenceNumber;
318
319     encoder << static_cast<uint64_t>(frameState.documentState.size());
320     for (const auto& documentState : frameState.documentState)
321         encoder << documentState;
322
323     if (frameState.httpBody) {
324         encoder << frameState.httpBody.value().contentType;
325         encoder << true;
326
327         encodeFormData(encoder, frameState.httpBody.value());
328     } else {
329         encoder << String();
330         encoder << false;
331     }
332
333     encoder << frameState.itemSequenceNumber;
334
335     encoder << frameState.referrer;
336
337     encoder << frameState.scrollPoint.x();
338     encoder << frameState.scrollPoint.y();
339
340     encoder << frameState.pageScaleFactor;
341
342     encoder << !!frameState.stateObjectData;
343     if (frameState.stateObjectData)
344         encoder << frameState.stateObjectData.value();
345
346     encoder << frameState.target;
347
348 #if PLATFORM(IOS)
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;
355 #endif
356 }
357
358 static MallocPtr<uint8_t> encodeSessionHistoryEntryData(const FrameState& frameState, size_t& bufferSize)
359 {
360     HistoryEntryDataEncoder encoder;
361
362     encoder << sessionHistoryEntryDataVersion;
363     encodeFrameStateNode(encoder, frameState);
364
365     return encoder.finishEncoding(bufferSize);
366 }
367
368 RefPtr<API::Data> encodeLegacySessionHistoryEntryData(const FrameState& frameState)
369 {
370     size_t bufferSize;
371     auto buffer = encodeSessionHistoryEntryData(frameState, bufferSize);
372
373     return API::Data::createWithoutCopying(buffer.leakPtr(), bufferSize, [] (unsigned char* buffer, const void* context) {
374         fastFree(buffer);
375     }, nullptr);
376 }
377
378 static RetainPtr<CFDataRef> encodeSessionHistoryEntryData(const FrameState& frameState)
379 {
380     static CFAllocatorRef fastMallocDeallocator;
381
382     static std::once_flag onceFlag;
383     std::call_once(onceFlag, [] {
384         CFAllocatorContext context = {
385             0, // version
386             nullptr, // info
387             nullptr, // retain
388             nullptr, // release
389             nullptr, // copyDescription
390             nullptr, // allocate
391             nullptr, // reallocate
392             [](void *ptr, void *info) {
393                 WTF::fastFree(ptr);
394             },
395             nullptr, // preferredSize
396         };
397         fastMallocDeallocator = CFAllocatorCreate(kCFAllocatorDefault, &context);
398     });
399
400     size_t bufferSize;
401     auto buffer = encodeSessionHistoryEntryData(frameState, bufferSize);
402
403     return adoptCF(CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buffer.leakPtr(), bufferSize, fastMallocDeallocator));
404 }
405
406 static RetainPtr<CFDictionaryRef> createDictionary(std::initializer_list<std::pair<CFStringRef, CFTypeRef>> keyValuePairs)
407 {
408     Vector<CFTypeRef> keys;
409     Vector<CFTypeRef> values;
410
411     keys.reserveInitialCapacity(keyValuePairs.size());
412     values.reserveInitialCapacity(keyValuePairs.size());
413
414     for (const auto& keyValuePair : keyValuePairs) {
415         keys.uncheckedAppend(keyValuePair.first);
416         values.uncheckedAppend(keyValuePair.second);
417     }
418
419     return adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys.data(), values.data(), keyValuePairs.size(), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
420 }
421
422 static RetainPtr<CFDictionaryRef> encodeSessionHistory(const BackForwardListState& backForwardListState)
423 {
424     ASSERT(!backForwardListState.currentIndex || backForwardListState.currentIndex.value() < backForwardListState.items.size());
425
426     auto sessionHistoryVersionNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &sessionHistoryVersion));
427
428     if (!backForwardListState.currentIndex)
429         return createDictionary({ { sessionHistoryVersionKey, sessionHistoryVersionNumber.get() } });
430
431     auto entries = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, backForwardListState.items.size(), &kCFTypeArrayCallBacks));
432
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);
438
439         auto entryDictionary = createDictionary({ { sessionHistoryEntryURLKey, url.get() }, { sessionHistoryEntryTitleKey, title.get() }, { sessionHistoryEntryOriginalURLKey, originalURL.get() }, { sessionHistoryEntryDataKey, data.get() } });
440
441         CFArrayAppendValue(entries.get(), entryDictionary.get());
442     }
443
444     uint32_t currentIndex = backForwardListState.currentIndex.value();
445     auto currentIndexNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &currentIndex));
446
447     return createDictionary({ { sessionHistoryVersionKey, sessionHistoryVersionNumber.get() }, { sessionHistoryCurrentIndexKey, currentIndexNumber.get() }, { sessionHistoryEntriesKey, entries.get() } });
448 }
449
450 RefPtr<API::Data> encodeLegacySessionState(const SessionState& sessionState)
451 {
452     auto sessionHistoryDictionary = encodeSessionHistory(sessionState.backForwardListState);
453     auto provisionalURLString = sessionState.provisionalURL.isNull() ? nullptr : sessionState.provisionalURL.string().createCFString();
454
455     RetainPtr<CFDictionaryRef> stateDictionary;
456     if (provisionalURLString)
457         stateDictionary = createDictionary({ { sessionHistoryKey, sessionHistoryDictionary.get() }, { provisionalURLKey, provisionalURLString.get() } });
458     else
459         stateDictionary = createDictionary({ { sessionHistoryKey, sessionHistoryDictionary.get() } });
460
461     auto writeStream = adoptCF(CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorDefault, nullptr));
462     if (!writeStream)
463         return nullptr;
464
465     if (!CFWriteStreamOpen(writeStream.get()))
466         return nullptr;
467
468     if (!CFPropertyListWrite(stateDictionary.get(), writeStream.get(), kCFPropertyListBinaryFormat_v1_0, 0, nullptr))
469         return nullptr;
470
471     auto data = adoptCF(static_cast<CFDataRef>(CFWriteStreamCopyProperty(writeStream.get(), kCFStreamPropertyDataWritten)));
472
473     CFIndex length = CFDataGetLength(data.get());
474
475     size_t bufferSize = length + sizeof(uint32_t);
476     auto buffer = MallocPtr<uint8_t>::malloc(bufferSize);
477
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);
483
484     // Copy in the actual session state data
485     CFDataGetBytes(data.get(), CFRangeMake(0, length), buffer.get() + sizeof(uint32_t));
486
487     return API::Data::createWithoutCopying(buffer.leakPtr(), bufferSize, [] (unsigned char* buffer, const void* context) {
488         fastFree(buffer);
489     }, nullptr);
490 }
491
492 class HistoryEntryDataDecoder {
493 public:
494     HistoryEntryDataDecoder(const uint8_t* buffer, size_t bufferSize)
495         : m_buffer(buffer)
496         , m_bufferEnd(buffer + bufferSize)
497     {
498         // Keep format compatibility by decoding an unused uint64_t here.
499         uint64_t value;
500         *this >> value;
501     }
502
503     HistoryEntryDataDecoder& operator>>(bool& value)
504     {
505         return decodeArithmeticType(value);
506     }
507
508     HistoryEntryDataDecoder& operator>>(uint32_t& value)
509     {
510         return decodeArithmeticType(value);
511     }
512
513     HistoryEntryDataDecoder& operator>>(int32_t& value)
514     {
515         return *this >> reinterpret_cast<uint32_t&>(value);
516     }
517
518     HistoryEntryDataDecoder& operator>>(uint64_t& value)
519     {
520         return decodeArithmeticType(value);
521     }
522
523     HistoryEntryDataDecoder& operator>>(int64_t& value)
524     {
525         return *this >> reinterpret_cast<uint64_t&>(value);
526     }
527
528     HistoryEntryDataDecoder& operator>>(float& value)
529     {
530         return decodeArithmeticType(value);
531     }
532
533     HistoryEntryDataDecoder& operator>>(double& value)
534     {
535         return decodeArithmeticType(value);
536     }
537
538     HistoryEntryDataDecoder& operator>>(String& value)
539     {
540         value = String();
541
542         uint32_t length;
543         *this >> length;
544
545         if (length == std::numeric_limits<uint32_t>::max()) {
546             // This is the null string.
547             value = String();
548             return *this;
549         }
550
551         uint64_t lengthInBytes;
552         *this >> lengthInBytes;
553
554         if (lengthInBytes % sizeof(UChar) || lengthInBytes / sizeof(UChar) != length) {
555             markInvalid();
556             return *this;
557         }
558
559         if (!bufferIsLargeEnoughToContain<UChar>(length)) {
560             markInvalid();
561             return *this;
562         }
563
564         UChar* buffer;
565         auto string = String::createUninitialized(length, buffer);
566         decodeFixedLengthData(reinterpret_cast<uint8_t*>(buffer), length * sizeof(UChar), alignof(UChar));
567
568         value = string;
569         return *this;
570     }
571
572     HistoryEntryDataDecoder& operator>>(Vector<uint8_t>& value)
573     {
574         value = { };
575
576         uint64_t size;
577         *this >> size;
578
579         if (!alignBufferPosition(1, size))
580             return *this;
581
582         const uint8_t* data = m_buffer;
583         m_buffer += size;
584
585         value.append(data, size);
586         return *this;
587     }
588
589     HistoryEntryDataDecoder& operator>>(Vector<char>& value)
590     {
591         value = { };
592
593         uint64_t size;
594         *this >> size;
595
596         if (!alignBufferPosition(1, size))
597             return *this;
598
599         const uint8_t* data = m_buffer;
600         m_buffer += size;
601
602         value.append(data, size);
603         return *this;
604     }
605
606 #if PLATFORM(IOS)
607     HistoryEntryDataDecoder& operator>>(WebCore::FloatRect& value)
608     {
609         value = WebCore::FloatRect();
610
611         float x;
612         *this >> x;
613
614         float y;
615         *this >> y;
616
617         float width;
618         *this >> width;
619
620         float height;
621         *this >> height;
622
623         value = WebCore::FloatRect(x, y, width, height);
624         return *this;
625     }
626
627     HistoryEntryDataDecoder& operator>>(WebCore::IntRect& value)
628     {
629         value = WebCore::IntRect();
630
631         int32_t x;
632         *this >> x;
633
634         int32_t y;
635         *this >> y;
636
637         int32_t width;
638         *this >> width;
639
640         int32_t height;
641         *this >> height;
642
643         value = WebCore::IntRect(x, y, width, height);
644         return *this;
645     }
646
647     HistoryEntryDataDecoder& operator>>(WebCore::FloatSize& value)
648     {
649         value = WebCore::FloatSize();
650
651         float width;
652         *this >> width;
653
654         float height;
655         *this >> height;
656
657         value = WebCore::FloatSize(width, height);
658         return *this;
659     }
660
661     HistoryEntryDataDecoder& operator>>(WebCore::IntSize& value)
662     {
663         value = WebCore::IntSize();
664
665         int32_t width;
666         *this >> width;
667
668         int32_t height;
669         *this >> height;
670
671         value = WebCore::IntSize(width, height);
672         return *this;
673     }
674 #endif
675
676     template<typename T>
677     auto operator>>(Optional<T>& value) -> typename std::enable_if<std::is_enum<T>::value, HistoryEntryDataDecoder&>::type
678     {
679         uint32_t underlyingEnumValue;
680         *this >> underlyingEnumValue;
681
682         if (!isValid() || !isValidEnum(static_cast<T>(underlyingEnumValue)))
683             value = Nullopt;
684         else
685             value = static_cast<T>(underlyingEnumValue);
686
687         return *this;
688     }
689
690     bool isValid() const { return m_buffer <= m_bufferEnd; }
691     void markInvalid() { m_buffer = m_bufferEnd + 1; }
692
693     bool finishDecoding() { return m_buffer == m_bufferEnd; }
694
695 private:
696     template<typename Type>
697     HistoryEntryDataDecoder& decodeArithmeticType(Type& value)
698     {
699         static_assert(std::is_arithmetic<Type>::value, "");
700         value = Type();
701
702         decodeFixedLengthData(reinterpret_cast<uint8_t*>(&value), sizeof(value), sizeof(value));
703         return *this;
704     }
705
706     void decodeFixedLengthData(uint8_t* data, size_t size, unsigned alignment)
707     {
708         if (!alignBufferPosition(alignment, size))
709             return;
710
711         memcpy(data, m_buffer, size);
712         m_buffer += size;
713     }
714
715     bool alignBufferPosition(unsigned alignment, size_t size)
716     {
717         const uint8_t* alignedPosition = alignedBuffer(alignment);
718         if (!alignedBufferIsLargeEnoughToContain(alignedPosition, size)) {
719             // We've walked off the end of this buffer.
720             markInvalid();
721             return false;
722         }
723
724         m_buffer = alignedPosition;
725         return true;
726     }
727
728     const uint8_t* alignedBuffer(unsigned alignment) const
729     {
730         ASSERT(alignment && !(alignment & (alignment - 1)));
731
732         uintptr_t alignmentMask = alignment - 1;
733         return reinterpret_cast<uint8_t*>((reinterpret_cast<uintptr_t>(m_buffer) + alignmentMask) & ~alignmentMask);
734     }
735
736     template<typename T>
737     bool bufferIsLargeEnoughToContain(size_t numElements) const
738     {
739         static_assert(std::is_arithmetic<T>::value, "Type T must have a fixed, known encoded size!");
740
741         if (numElements > std::numeric_limits<size_t>::max() / sizeof(T))
742             return false;
743
744         return bufferIsLargeEnoughToContain(alignof(T), numElements * sizeof(T));
745     }
746
747     bool bufferIsLargeEnoughToContain(unsigned alignment, size_t size) const
748     {
749         return alignedBufferIsLargeEnoughToContain(alignedBuffer(alignment), size);
750     }
751
752     inline bool alignedBufferIsLargeEnoughToContain(const uint8_t* alignedPosition, size_t size) const
753     {
754         return m_bufferEnd >= alignedPosition && static_cast<size_t>(m_bufferEnd - alignedPosition) >= size;
755     }
756
757     const uint8_t* m_buffer;
758     const uint8_t* m_bufferEnd;
759 };
760
761 static void decodeFormDataElement(HistoryEntryDataDecoder& decoder, HTTPBody::Element& formDataElement)
762 {
763     Optional<FormDataElementType> elementType;
764     decoder >> elementType;
765     if (!elementType)
766         return;
767
768     switch (elementType.value()) {
769     case FormDataElementType::Data:
770         formDataElement.type = HTTPBody::Element::Type::Data;
771         decoder >> formDataElement.data;
772         break;
773
774     case FormDataElementType::EncodedFile: {
775         decoder >> formDataElement.filePath;
776
777         String generatedFilename;
778         decoder >> generatedFilename;
779
780         bool shouldGenerateFile;
781         decoder >> shouldGenerateFile;
782
783         decoder >> formDataElement.fileStart;
784         if (formDataElement.fileStart < 0) {
785             decoder.markInvalid();
786             return;
787         }
788
789         int64_t fileLength;
790         decoder >> fileLength;
791         if (fileLength != -1) {
792             if (fileLength < formDataElement.fileStart)
793                 return;
794
795             formDataElement.fileLength = fileLength;
796         }
797
798
799         double expectedFileModificationTime;
800         decoder >> expectedFileModificationTime;
801         if (expectedFileModificationTime != std::numeric_limits<double>::quiet_NaN())
802             formDataElement.expectedFileModificationTime = expectedFileModificationTime;
803
804         break;
805     }
806
807     case FormDataElementType::EncodedBlob:
808         decoder >> formDataElement.blobURLString;
809         break;
810     }
811 }
812
813 static void decodeFormData(HistoryEntryDataDecoder& decoder, HTTPBody& formData)
814 {
815     bool alwaysStream;
816     decoder >> alwaysStream;
817
818     Vector<uint8_t> boundary;
819     decoder >> boundary;
820
821     uint64_t formDataElementCount;
822     decoder >> formDataElementCount;
823
824     for (uint64_t i = 0; i < formDataElementCount; ++i) {
825         HTTPBody::Element formDataElement;
826         decodeFormDataElement(decoder, formDataElement);
827
828         if (!decoder.isValid())
829             return;
830
831         formData.elements.append(std::move(formDataElement));
832     }
833
834     bool hasGeneratedFiles;
835     decoder >> hasGeneratedFiles;
836
837     int64_t identifier;
838     decoder >> identifier;
839 }
840
841 static void decodeBackForwardTreeNode(HistoryEntryDataDecoder& decoder, FrameState& frameState)
842 {
843     uint64_t childCount;
844     decoder >> childCount;
845
846     for (uint64_t i = 0; i < childCount; ++i) {
847         FrameState childFrameState;
848         decoder >> childFrameState.originalURLString;
849         decoder >> childFrameState.urlString;
850
851         decodeBackForwardTreeNode(decoder, childFrameState);
852
853         if (!decoder.isValid())
854             return;
855
856         frameState.children.append(std::move(childFrameState));
857     }
858
859     decoder >> frameState.documentSequenceNumber;
860
861     uint64_t documentStateVectorSize;
862     decoder >> documentStateVectorSize;
863
864     for (uint64_t i = 0; i < documentStateVectorSize; ++i) {
865         String state;
866         decoder >> state;
867
868         if (!decoder.isValid())
869             return;
870
871         frameState.documentState.append(std::move(state));
872     }
873
874     String formContentType;
875     decoder >> formContentType;
876
877     bool hasFormData;
878     decoder >> hasFormData;
879
880     if (hasFormData) {
881         HTTPBody httpBody;
882         httpBody.contentType = std::move(formContentType);
883
884         decodeFormData(decoder, httpBody);
885
886         frameState.httpBody = std::move(httpBody);
887     }
888
889     decoder >> frameState.itemSequenceNumber;
890
891     decoder >> frameState.referrer;
892
893     int32_t scrollPointX;
894     decoder >> scrollPointX;
895
896     int32_t scrollPointY;
897     decoder >> scrollPointY;
898
899     frameState.scrollPoint = WebCore::IntPoint(scrollPointX, scrollPointY);
900
901     decoder >> frameState.pageScaleFactor;
902
903     bool hasStateObject;
904     decoder >> hasStateObject;
905
906     if (hasStateObject) {
907         Vector<uint8_t> stateObjectData;
908         decoder >> stateObjectData;
909
910         frameState.stateObjectData = std::move(stateObjectData);
911     }
912
913     decoder >> frameState.target;
914
915     // FIXME: iOS should not use the legacy session state decoder.
916 #if PLATFORM(IOS)
917     decoder >> frameState.exposedContentRect;
918     decoder >> frameState.unobscuredContentRect;
919     decoder >> frameState.minimumLayoutSizeInScrollViewCoordinates;
920     decoder >> frameState.contentSize;
921     decoder >> frameState.scaleIsInitial;
922 #endif
923 }
924
925 static bool decodeSessionHistoryEntryData(const uint8_t* buffer, size_t bufferSize, FrameState& mainFrameState)
926 {
927     HistoryEntryDataDecoder decoder { buffer, bufferSize };
928
929     uint32_t version;
930     decoder >> version;
931
932     if (version != sessionHistoryEntryDataVersion)
933         return false;
934
935     decodeBackForwardTreeNode(decoder, mainFrameState);
936
937     return decoder.finishDecoding();
938 }
939
940 static bool decodeSessionHistoryEntryData(CFDataRef historyEntryData, FrameState& mainFrameState)
941 {
942     return decodeSessionHistoryEntryData(CFDataGetBytePtr(historyEntryData), static_cast<size_t>(CFDataGetLength(historyEntryData)), mainFrameState);
943 }
944
945 static bool decodeSessionHistoryEntry(CFDictionaryRef entryDictionary, BackForwardListItemState& backForwardListItemState)
946 {
947     auto title = dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(entryDictionary, sessionHistoryEntryTitleKey));
948     if (!title)
949         return false;
950
951     auto urlString = dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(entryDictionary, sessionHistoryEntryURLKey));
952     if (!urlString)
953         return false;
954
955     auto originalURLString = dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(entryDictionary, sessionHistoryEntryOriginalURLKey));
956     if (!originalURLString)
957         return false;
958
959     auto historyEntryData = dynamic_cf_cast<CFDataRef>(CFDictionaryGetValue(entryDictionary, sessionHistoryEntryDataKey));
960     if (!historyEntryData)
961         return false;
962
963     if (!decodeSessionHistoryEntryData(historyEntryData, backForwardListItemState.pageState.mainFrameState))
964         return false;
965
966     backForwardListItemState.pageState.title = title;
967     backForwardListItemState.pageState.mainFrameState.urlString = urlString;
968     backForwardListItemState.pageState.mainFrameState.originalURLString = originalURLString;
969
970     return true;
971 }
972
973 static bool decodeSessionHistoryEntries(CFArrayRef entriesArray, Vector<BackForwardListItemState>& entries)
974 {
975     for (CFIndex i = 0, size = CFArrayGetCount(entriesArray); i < size; ++i) {
976         auto entryDictionary = dynamic_cf_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(entriesArray, i));
977         if (!entryDictionary)
978             return false;
979
980         BackForwardListItemState entry;
981         if (!decodeSessionHistoryEntry(entryDictionary, entry))
982             return false;
983
984         entries.append(std::move(entry));
985     }
986
987     return true;
988 }
989
990 static bool decodeV0SessionHistory(CFDictionaryRef sessionHistoryDictionary, BackForwardListState& backForwardListState)
991 {
992     auto currentIndexNumber = dynamic_cf_cast<CFNumberRef>(CFDictionaryGetValue(sessionHistoryDictionary, sessionHistoryCurrentIndexKey));
993     if (!currentIndexNumber)
994         return false;
995
996     CFIndex currentIndex;
997     if (!CFNumberGetValue(currentIndexNumber, kCFNumberCFIndexType, &currentIndex))
998         return false;
999
1000     if (currentIndex < -1)
1001         return false;
1002
1003     auto historyEntries = dynamic_cf_cast<CFArrayRef>(CFDictionaryGetValue(sessionHistoryDictionary, sessionHistoryEntriesKey));
1004     if (!historyEntries)
1005         return false;
1006
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;
1009
1010     if (!decodeSessionHistoryEntries(historyEntries, backForwardListState.items))
1011         return false;
1012
1013     if (!hasCurrentIndex && CFArrayGetCount(historyEntries))
1014         return false;
1015
1016     if (hasCurrentIndex) {
1017         if (static_cast<uint32_t>(currentIndex) >= backForwardListState.items.size())
1018             return false;
1019
1020         backForwardListState.currentIndex = static_cast<uint32_t>(currentIndex);
1021     }
1022
1023     return true;
1024 }
1025
1026 static bool decodeV1SessionHistory(CFDictionaryRef sessionHistoryDictionary, BackForwardListState& backForwardListState)
1027 {
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 = { };
1033         return true;
1034     }
1035
1036     CFIndex currentIndex;
1037     if (!CFNumberGetValue(currentIndexNumber, kCFNumberCFIndexType, &currentIndex))
1038         return false;
1039
1040     if (currentIndex < 0)
1041         return false;
1042
1043     auto historyEntries = dynamic_cf_cast<CFArrayRef>(CFDictionaryGetValue(sessionHistoryDictionary, sessionHistoryEntriesKey));
1044     if (!historyEntries)
1045         return false;
1046
1047     if (!decodeSessionHistoryEntries(historyEntries, backForwardListState.items))
1048         return false;
1049
1050     backForwardListState.currentIndex = static_cast<uint32_t>(currentIndex);
1051     if (static_cast<uint32_t>(currentIndex) >= backForwardListState.items.size())
1052         return false;
1053
1054     return true;
1055 }
1056
1057 static bool decodeSessionHistory(CFDictionaryRef backForwardListDictionary, BackForwardListState& backForwardListState)
1058 {
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);
1063     }
1064
1065     CFIndex sessionHistoryVersion;
1066     if (!CFNumberGetValue(sessionHistoryVersionNumber, kCFNumberCFIndexType, &sessionHistoryVersion))
1067         return false;
1068
1069     if (sessionHistoryVersion == 1)
1070         return decodeV1SessionHistory(backForwardListDictionary, backForwardListState);
1071
1072     return false;
1073 }
1074
1075 bool decodeLegacySessionState(const API::Data& data, SessionState& sessionState)
1076 {
1077     size_t size = data.size();
1078     const uint8_t* bytes = data.bytes();
1079
1080     if (size < sizeof(uint32_t))
1081         return false;
1082
1083     uint32_t versionNumber = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
1084
1085     if (versionNumber != sessionStateDataVersion)
1086         return false;
1087
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)
1090         return false;
1091
1092     if (auto backForwardListDictionary = dynamic_cf_cast<CFDictionaryRef>(CFDictionaryGetValue(sessionStateDictionary.get(), sessionHistoryKey))) {
1093         if (!decodeSessionHistory(backForwardListDictionary, sessionState.backForwardListState))
1094             return false;
1095     }
1096
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())
1100             return false;
1101     }
1102
1103     return true;
1104 }
1105
1106 bool decodeLegacySessionHistoryEntryData(const uint8_t* data, size_t size, FrameState& mainFrameState)
1107 {
1108     return decodeSessionHistoryEntryData(data, size, mainFrameState);
1109 }
1110
1111 } // namespace WebKit