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
36namespace WebKit {
37
38// Session state keys.
39static const uint32_t sessionStateDataVersion = 2;
40
41static const CFStringRef sessionHistoryKey = CFSTR("SessionHistory");
42static const CFStringRef provisionalURLKey = CFSTR("ProvisionalURL");
43
44// Session history keys.
45static const uint32_t sessionHistoryVersion = 1;
46
47static const CFStringRef sessionHistoryVersionKey = CFSTR("SessionHistoryVersion");
48static const CFStringRef sessionHistoryCurrentIndexKey = CFSTR("SessionHistoryCurrentIndex");
49static const CFStringRef sessionHistoryEntriesKey = CFSTR("SessionHistoryEntries");
50
51// Session history entry keys.
52static const CFStringRef sessionHistoryEntryURLKey = CFSTR("SessionHistoryEntryURL");
53static CFStringRef sessionHistoryEntryTitleKey = CFSTR("SessionHistoryEntryTitle");
54static CFStringRef sessionHistoryEntryOriginalURLKey = CFSTR("SessionHistoryEntryOriginalURL");
55static CFStringRef sessionHistoryEntryDataKey = CFSTR("SessionHistoryEntryData");
56
57// Session history entry data.
58const uint32_t sessionHistoryEntryDataVersion = 2;
59
60template<typename T> void isValidEnum(T);
61
62class HistoryEntryDataEncoder {
63public:
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 WTF::move(m_buffer);
188    }
189
190private:
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
239enum class FormDataElementType {
240    Data = 0,
241    EncodedFile = 1,
242    EncodedBlob = 2,
243};
244
245static 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
257static 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
287static 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
306static 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
358static 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
368static RetainPtr<CFDataRef> encodeSessionHistoryEntryData(const FrameState& frameState)
369{
370    static CFAllocatorRef fastMallocDeallocator;
371
372    static std::once_flag onceFlag;
373    std::call_once(onceFlag, [] {
374        CFAllocatorContext context = {
375            0, // version
376            nullptr, // info
377            nullptr, // retain
378            nullptr, // release
379            nullptr, // copyDescription
380            nullptr, // allocate
381            nullptr, // reallocate
382            [](void *ptr, void *info) {
383                WTF::fastFree(ptr);
384            },
385            nullptr, // preferredSize
386        };
387        fastMallocDeallocator = CFAllocatorCreate(kCFAllocatorDefault, &context);
388    });
389
390    size_t bufferSize;
391    auto buffer = encodeSessionHistoryEntryData(frameState, bufferSize);
392
393    return adoptCF(CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, buffer.leakPtr(), bufferSize, fastMallocDeallocator));
394}
395
396static RetainPtr<CFDictionaryRef> createDictionary(std::initializer_list<std::pair<CFStringRef, CFTypeRef>> keyValuePairs)
397{
398    Vector<CFTypeRef> keys;
399    Vector<CFTypeRef> values;
400
401    keys.reserveInitialCapacity(keyValuePairs.size());
402    values.reserveInitialCapacity(keyValuePairs.size());
403
404    for (const auto& keyValuePair : keyValuePairs) {
405        keys.uncheckedAppend(keyValuePair.first);
406        values.uncheckedAppend(keyValuePair.second);
407    }
408
409    return adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys.data(), values.data(), keyValuePairs.size(), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
410}
411
412static RetainPtr<CFDictionaryRef> encodeSessionHistory(const BackForwardListState& backForwardListState)
413{
414    ASSERT(!backForwardListState.currentIndex || backForwardListState.currentIndex.value() < backForwardListState.items.size());
415
416    auto sessionHistoryVersionNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &sessionHistoryVersion));
417
418    if (!backForwardListState.currentIndex)
419        return createDictionary({ { sessionHistoryVersionKey, sessionHistoryVersionNumber.get() } });
420
421    auto entries = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, backForwardListState.items.size(), &kCFTypeArrayCallBacks));
422
423    for (const auto& item : backForwardListState.items) {
424        auto url = item.pageState.mainFrameState.urlString.createCFString();
425        auto title = item.pageState.title.createCFString();
426        auto originalURL = item.pageState.mainFrameState.originalURLString.createCFString();
427        auto data = encodeSessionHistoryEntryData(item.pageState.mainFrameState);
428
429        auto entryDictionary = createDictionary({ { sessionHistoryEntryURLKey, url.get() }, { sessionHistoryEntryTitleKey, title.get() }, { sessionHistoryEntryOriginalURLKey, originalURL.get() }, { sessionHistoryEntryDataKey, data.get() } });
430
431        CFArrayAppendValue(entries.get(), entryDictionary.get());
432    }
433
434    uint32_t currentIndex = backForwardListState.currentIndex.value();
435    auto currentIndexNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &currentIndex));
436
437    return createDictionary({ { sessionHistoryVersionKey, sessionHistoryVersionNumber.get() }, { sessionHistoryCurrentIndexKey, currentIndexNumber.get() }, { sessionHistoryEntriesKey, entries.get() } });
438}
439
440RefPtr<API::Data> encodeLegacySessionState(const SessionState& sessionState)
441{
442    auto sessionHistoryDictionary = encodeSessionHistory(sessionState.backForwardListState);
443    auto provisionalURLString = sessionState.provisionalURL.isNull() ? nullptr : sessionState.provisionalURL.string().createCFString();
444
445    RetainPtr<CFDictionaryRef> stateDictionary;
446    if (provisionalURLString)
447        stateDictionary = createDictionary({ { sessionHistoryKey, sessionHistoryDictionary.get() }, { provisionalURLKey, provisionalURLString.get() } });
448    else
449        stateDictionary = createDictionary({ { sessionHistoryKey, sessionHistoryDictionary.get() } });
450
451    auto writeStream = adoptCF(CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorDefault, nullptr));
452    if (!writeStream)
453        return nullptr;
454
455    if (!CFWriteStreamOpen(writeStream.get()))
456        return nullptr;
457
458    if (!CFPropertyListWrite(stateDictionary.get(), writeStream.get(), kCFPropertyListBinaryFormat_v1_0, 0, nullptr))
459        return nullptr;
460
461    auto data = adoptCF(static_cast<CFDataRef>(CFWriteStreamCopyProperty(writeStream.get(), kCFStreamPropertyDataWritten)));
462
463    CFIndex length = CFDataGetLength(data.get());
464
465    size_t bufferSize = length + sizeof(uint32_t);
466    auto buffer = MallocPtr<uint8_t>::malloc(bufferSize);
467
468    // Put the session state version number at the start of the buffer
469    buffer.get()[0] = (sessionStateDataVersion & 0xff000000) >> 24;
470    buffer.get()[1] = (sessionStateDataVersion & 0x00ff0000) >> 16;
471    buffer.get()[2] = (sessionStateDataVersion & 0x0000ff00) >> 8;
472    buffer.get()[3] = (sessionStateDataVersion & 0x000000ff);
473
474    // Copy in the actual session state data
475    CFDataGetBytes(data.get(), CFRangeMake(0, length), buffer.get() + sizeof(uint32_t));
476
477    return API::Data::createWithoutCopying(buffer.leakPtr(), bufferSize, [] (unsigned char* buffer, const void* context) {
478        fastFree(buffer);
479    }, nullptr);
480}
481
482class HistoryEntryDataDecoder {
483public:
484    HistoryEntryDataDecoder(const uint8_t* buffer, size_t bufferSize)
485        : m_buffer(buffer)
486        , m_bufferEnd(buffer + bufferSize)
487    {
488        // Keep format compatibility by decoding an unused uint64_t here.
489        uint64_t value;
490        *this >> value;
491    }
492
493    HistoryEntryDataDecoder& operator>>(bool& value)
494    {
495        return decodeArithmeticType(value);
496    }
497
498    HistoryEntryDataDecoder& operator>>(uint32_t& value)
499    {
500        return decodeArithmeticType(value);
501    }
502
503    HistoryEntryDataDecoder& operator>>(int32_t& value)
504    {
505        return *this >> reinterpret_cast<uint32_t&>(value);
506    }
507
508    HistoryEntryDataDecoder& operator>>(uint64_t& value)
509    {
510        return decodeArithmeticType(value);
511    }
512
513    HistoryEntryDataDecoder& operator>>(int64_t& value)
514    {
515        return *this >> reinterpret_cast<uint64_t&>(value);
516    }
517
518    HistoryEntryDataDecoder& operator>>(float& value)
519    {
520        return decodeArithmeticType(value);
521    }
522
523    HistoryEntryDataDecoder& operator>>(double& value)
524    {
525        return decodeArithmeticType(value);
526    }
527
528    HistoryEntryDataDecoder& operator>>(String& value)
529    {
530        value = String();
531
532        uint32_t length;
533        *this >> length;
534
535        if (length == std::numeric_limits<uint32_t>::max()) {
536            // This is the null string.
537            value = String();
538            return *this;
539        }
540
541        uint64_t lengthInBytes;
542        *this >> lengthInBytes;
543
544        if (lengthInBytes % sizeof(UChar) || lengthInBytes / sizeof(UChar) != length) {
545            markInvalid();
546            return *this;
547        }
548
549        if (!bufferIsLargeEnoughToContain<UChar>(length)) {
550            markInvalid();
551            return *this;
552        }
553
554        UChar* buffer;
555        auto string = String::createUninitialized(length, buffer);
556        decodeFixedLengthData(reinterpret_cast<uint8_t*>(buffer), length * sizeof(UChar), alignof(UChar));
557
558        value = string;
559        return *this;
560    }
561
562    HistoryEntryDataDecoder& operator>>(Vector<uint8_t>& value)
563    {
564        value = { };
565
566        uint64_t size;
567        *this >> size;
568
569        if (!alignBufferPosition(1, size))
570            return *this;
571
572        const uint8_t* data = m_buffer;
573        m_buffer += size;
574
575        value.append(data, size);
576        return *this;
577    }
578
579    HistoryEntryDataDecoder& operator>>(Vector<char>& value)
580    {
581        value = { };
582
583        uint64_t size;
584        *this >> size;
585
586        if (!alignBufferPosition(1, size))
587            return *this;
588
589        const uint8_t* data = m_buffer;
590        m_buffer += size;
591
592        value.append(data, size);
593        return *this;
594    }
595
596#if PLATFORM(IOS)
597    HistoryEntryDataDecoder& operator>>(WebCore::FloatRect& value)
598    {
599        value = WebCore::FloatRect();
600
601        float x;
602        *this >> x;
603
604        float y;
605        *this >> y;
606
607        float width;
608        *this >> width;
609
610        float height;
611        *this >> height;
612
613        value = WebCore::FloatRect(x, y, width, height);
614        return *this;
615    }
616
617    HistoryEntryDataDecoder& operator>>(WebCore::IntRect& value)
618    {
619        value = WebCore::IntRect();
620
621        int32_t x;
622        *this >> x;
623
624        int32_t y;
625        *this >> y;
626
627        int32_t width;
628        *this >> width;
629
630        int32_t height;
631        *this >> height;
632
633        value = WebCore::IntRect(x, y, width, height);
634        return *this;
635    }
636
637    HistoryEntryDataDecoder& operator>>(WebCore::FloatSize& value)
638    {
639        value = WebCore::FloatSize();
640
641        float width;
642        *this >> width;
643
644        float height;
645        *this >> height;
646
647        value = WebCore::FloatSize(width, height);
648        return *this;
649    }
650
651    HistoryEntryDataDecoder& operator>>(WebCore::IntSize& value)
652    {
653        value = WebCore::IntSize();
654
655        int32_t width;
656        *this >> width;
657
658        int32_t height;
659        *this >> height;
660
661        value = WebCore::IntSize(width, height);
662        return *this;
663    }
664#endif
665
666    template<typename T>
667    auto operator>>(Optional<T>& value) -> typename std::enable_if<std::is_enum<T>::value, HistoryEntryDataDecoder&>::type
668    {
669        uint32_t underlyingEnumValue;
670        *this >> underlyingEnumValue;
671
672        if (!isValid() || !isValidEnum(static_cast<T>(underlyingEnumValue)))
673            value = Nullopt;
674        else
675            value = static_cast<T>(underlyingEnumValue);
676
677        return *this;
678    }
679
680    bool isValid() const { return m_buffer <= m_bufferEnd; }
681    void markInvalid() { m_buffer = m_bufferEnd + 1; }
682
683    bool finishDecoding() { return m_buffer == m_bufferEnd; }
684
685private:
686    template<typename Type>
687    HistoryEntryDataDecoder& decodeArithmeticType(Type& value)
688    {
689        static_assert(std::is_arithmetic<Type>::value, "");
690        value = Type();
691
692        decodeFixedLengthData(reinterpret_cast<uint8_t*>(&value), sizeof(value), sizeof(value));
693        return *this;
694    }
695
696    void decodeFixedLengthData(uint8_t* data, size_t size, unsigned alignment)
697    {
698        if (!alignBufferPosition(alignment, size))
699            return;
700
701        memcpy(data, m_buffer, size);
702        m_buffer += size;
703    }
704
705    bool alignBufferPosition(unsigned alignment, size_t size)
706    {
707        const uint8_t* alignedPosition = alignedBuffer(alignment);
708        if (!alignedBufferIsLargeEnoughToContain(alignedPosition, size)) {
709            // We've walked off the end of this buffer.
710            markInvalid();
711            return false;
712        }
713
714        m_buffer = alignedPosition;
715        return true;
716    }
717
718    const uint8_t* alignedBuffer(unsigned alignment) const
719    {
720        ASSERT(alignment && !(alignment & (alignment - 1)));
721
722        uintptr_t alignmentMask = alignment - 1;
723        return reinterpret_cast<uint8_t*>((reinterpret_cast<uintptr_t>(m_buffer) + alignmentMask) & ~alignmentMask);
724    }
725
726    template<typename T>
727    bool bufferIsLargeEnoughToContain(size_t numElements) const
728    {
729        static_assert(std::is_arithmetic<T>::value, "Type T must have a fixed, known encoded size!");
730
731        if (numElements > std::numeric_limits<size_t>::max() / sizeof(T))
732            return false;
733
734        return bufferIsLargeEnoughToContain(alignof(T), numElements * sizeof(T));
735    }
736
737    bool bufferIsLargeEnoughToContain(unsigned alignment, size_t size) const
738    {
739        return alignedBufferIsLargeEnoughToContain(alignedBuffer(alignment), size);
740    }
741
742    inline bool alignedBufferIsLargeEnoughToContain(const uint8_t* alignedPosition, size_t size) const
743    {
744        return m_bufferEnd >= alignedPosition && static_cast<size_t>(m_bufferEnd - alignedPosition) >= size;
745    }
746
747    const uint8_t* m_buffer;
748    const uint8_t* m_bufferEnd;
749};
750
751static void decodeFormDataElement(HistoryEntryDataDecoder& decoder, HTTPBody::Element& formDataElement)
752{
753    Optional<FormDataElementType> elementType;
754    decoder >> elementType;
755    if (!elementType)
756        return;
757
758    switch (elementType.value()) {
759    case FormDataElementType::Data:
760        formDataElement.type = HTTPBody::Element::Type::Data;
761        decoder >> formDataElement.data;
762        break;
763
764    case FormDataElementType::EncodedFile: {
765        decoder >> formDataElement.filePath;
766
767        String generatedFilename;
768        decoder >> generatedFilename;
769
770        bool shouldGenerateFile;
771        decoder >> shouldGenerateFile;
772
773        decoder >> formDataElement.fileStart;
774        if (formDataElement.fileStart < 0) {
775            decoder.markInvalid();
776            return;
777        }
778
779        int64_t fileLength;
780        decoder >> fileLength;
781        if (fileLength != -1) {
782            if (fileLength < formDataElement.fileStart)
783                return;
784
785            formDataElement.fileLength = fileLength;
786        }
787
788
789        double expectedFileModificationTime;
790        decoder >> expectedFileModificationTime;
791        if (expectedFileModificationTime != std::numeric_limits<double>::quiet_NaN())
792            formDataElement.expectedFileModificationTime = expectedFileModificationTime;
793
794        break;
795    }
796
797    case FormDataElementType::EncodedBlob:
798        decoder >> formDataElement.blobURLString;
799        break;
800    }
801}
802
803static void decodeFormData(HistoryEntryDataDecoder& decoder, HTTPBody& formData)
804{
805    bool alwaysStream;
806    decoder >> alwaysStream;
807
808    Vector<uint8_t> boundary;
809    decoder >> boundary;
810
811    uint64_t formDataElementCount;
812    decoder >> formDataElementCount;
813
814    for (uint64_t i = 0; i < formDataElementCount; ++i) {
815        HTTPBody::Element formDataElement;
816        decodeFormDataElement(decoder, formDataElement);
817
818        if (!decoder.isValid())
819            return;
820
821        formData.elements.append(WTF::move(formDataElement));
822    }
823
824    bool hasGeneratedFiles;
825    decoder >> hasGeneratedFiles;
826
827    int64_t identifier;
828    decoder >> identifier;
829}
830
831static void decodeBackForwardTreeNode(HistoryEntryDataDecoder& decoder, FrameState& frameState)
832{
833    uint64_t childCount;
834    decoder >> childCount;
835
836    for (uint64_t i = 0; i < childCount; ++i) {
837        FrameState childFrameState;
838        decoder >> childFrameState.originalURLString;
839        decoder >> childFrameState.urlString;
840
841        decodeBackForwardTreeNode(decoder, childFrameState);
842
843        if (!decoder.isValid())
844            return;
845
846        frameState.children.append(WTF::move(childFrameState));
847    }
848
849    decoder >> frameState.documentSequenceNumber;
850
851    uint64_t documentStateVectorSize;
852    decoder >> documentStateVectorSize;
853
854    for (uint64_t i = 0; i < documentStateVectorSize; ++i) {
855        String state;
856        decoder >> state;
857
858        if (!decoder.isValid())
859            return;
860
861        frameState.documentState.append(WTF::move(state));
862    }
863
864    String formContentType;
865    decoder >> formContentType;
866
867    bool hasFormData;
868    decoder >> hasFormData;
869
870    if (hasFormData) {
871        HTTPBody httpBody;
872        httpBody.contentType = WTF::move(formContentType);
873
874        decodeFormData(decoder, httpBody);
875
876        frameState.httpBody = WTF::move(httpBody);
877    }
878
879    decoder >> frameState.itemSequenceNumber;
880
881    decoder >> frameState.referrer;
882
883    int32_t scrollPointX;
884    decoder >> scrollPointX;
885
886    int32_t scrollPointY;
887    decoder >> scrollPointY;
888
889    frameState.scrollPoint = WebCore::IntPoint(scrollPointX, scrollPointY);
890
891    decoder >> frameState.pageScaleFactor;
892
893    bool hasStateObject;
894    decoder >> hasStateObject;
895
896    if (hasStateObject) {
897        Vector<uint8_t> stateObjectData;
898        decoder >> stateObjectData;
899
900        frameState.stateObjectData = WTF::move(stateObjectData);
901    }
902
903    decoder >> frameState.target;
904
905    // FIXME: iOS should not use the legacy session state decoder.
906#if PLATFORM(IOS)
907    decoder >> frameState.exposedContentRect;
908    decoder >> frameState.unobscuredContentRect;
909    decoder >> frameState.minimumLayoutSizeInScrollViewCoordinates;
910    decoder >> frameState.contentSize;
911    decoder >> frameState.scaleIsInitial;
912#endif
913}
914
915static bool decodeSessionHistoryEntryData(const uint8_t* buffer, size_t bufferSize, FrameState& mainFrameState)
916{
917    HistoryEntryDataDecoder decoder { buffer, bufferSize };
918
919    uint32_t version;
920    decoder >> version;
921
922    if (version != sessionHistoryEntryDataVersion)
923        return false;
924
925    decodeBackForwardTreeNode(decoder, mainFrameState);
926
927    return decoder.finishDecoding();
928}
929
930static bool decodeSessionHistoryEntryData(CFDataRef historyEntryData, FrameState& mainFrameState)
931{
932    return decodeSessionHistoryEntryData(CFDataGetBytePtr(historyEntryData), static_cast<size_t>(CFDataGetLength(historyEntryData)), mainFrameState);
933}
934
935static bool decodeSessionHistoryEntry(CFDictionaryRef entryDictionary, BackForwardListItemState& backForwardListItemState)
936{
937    auto title = dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(entryDictionary, sessionHistoryEntryTitleKey));
938    if (!title)
939        return false;
940
941    auto urlString = dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(entryDictionary, sessionHistoryEntryURLKey));
942    if (!urlString)
943        return false;
944
945    auto originalURLString = dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(entryDictionary, sessionHistoryEntryOriginalURLKey));
946    if (!originalURLString)
947        return false;
948
949    auto historyEntryData = dynamic_cf_cast<CFDataRef>(CFDictionaryGetValue(entryDictionary, sessionHistoryEntryDataKey));
950    if (!historyEntryData)
951        return false;
952
953    if (!decodeSessionHistoryEntryData(historyEntryData, backForwardListItemState.pageState.mainFrameState))
954        return false;
955
956    backForwardListItemState.pageState.title = title;
957    backForwardListItemState.pageState.mainFrameState.urlString = urlString;
958    backForwardListItemState.pageState.mainFrameState.originalURLString = originalURLString;
959
960    return true;
961}
962
963static bool decodeSessionHistoryEntries(CFArrayRef entriesArray, Vector<BackForwardListItemState>& entries)
964{
965    for (CFIndex i = 0, size = CFArrayGetCount(entriesArray); i < size; ++i) {
966        auto entryDictionary = dynamic_cf_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(entriesArray, i));
967        if (!entryDictionary)
968            return false;
969
970        BackForwardListItemState entry;
971        if (!decodeSessionHistoryEntry(entryDictionary, entry))
972            return false;
973
974        entries.append(WTF::move(entry));
975    }
976
977    return true;
978}
979
980static bool decodeV0SessionHistory(CFDictionaryRef sessionHistoryDictionary, BackForwardListState& backForwardListState)
981{
982    auto currentIndexNumber = dynamic_cf_cast<CFNumberRef>(CFDictionaryGetValue(sessionHistoryDictionary, sessionHistoryCurrentIndexKey));
983    if (!currentIndexNumber)
984        return false;
985
986    CFIndex currentIndex;
987    if (!CFNumberGetValue(currentIndexNumber, kCFNumberCFIndexType, &currentIndex))
988        return false;
989
990    if (currentIndex < -1)
991        return false;
992
993    auto historyEntries = dynamic_cf_cast<CFArrayRef>(CFDictionaryGetValue(sessionHistoryDictionary, sessionHistoryEntriesKey));
994    if (!historyEntries)
995        return false;
996
997    // Version 0 session history relied on currentIndex == -1 to represent the same thing as not having a current index.
998    bool hasCurrentIndex = currentIndex != -1;
999
1000    if (!decodeSessionHistoryEntries(historyEntries, backForwardListState.items))
1001        return false;
1002
1003    if (!hasCurrentIndex && CFArrayGetCount(historyEntries))
1004        return false;
1005
1006    if (hasCurrentIndex) {
1007        if (static_cast<uint32_t>(currentIndex) >= backForwardListState.items.size())
1008            return false;
1009
1010        backForwardListState.currentIndex = static_cast<uint32_t>(currentIndex);
1011    }
1012
1013    return true;
1014}
1015
1016static bool decodeV1SessionHistory(CFDictionaryRef sessionHistoryDictionary, BackForwardListState& backForwardListState)
1017{
1018    auto currentIndexNumber = dynamic_cf_cast<CFNumberRef>(CFDictionaryGetValue(sessionHistoryDictionary, sessionHistoryCurrentIndexKey));
1019    if (!currentIndexNumber) {
1020        // No current index means the dictionary represents an empty session.
1021        backForwardListState.currentIndex = Nullopt;
1022        backForwardListState.items = { };
1023        return true;
1024    }
1025
1026    CFIndex currentIndex;
1027    if (!CFNumberGetValue(currentIndexNumber, kCFNumberCFIndexType, &currentIndex))
1028        return false;
1029
1030    if (currentIndex < 0)
1031        return false;
1032
1033    auto historyEntries = dynamic_cf_cast<CFArrayRef>(CFDictionaryGetValue(sessionHistoryDictionary, sessionHistoryEntriesKey));
1034    if (!historyEntries)
1035        return false;
1036
1037    if (!decodeSessionHistoryEntries(historyEntries, backForwardListState.items))
1038        return false;
1039
1040    backForwardListState.currentIndex = static_cast<uint32_t>(currentIndex);
1041    if (static_cast<uint32_t>(currentIndex) >= backForwardListState.items.size())
1042        return false;
1043
1044    return true;
1045}
1046
1047static bool decodeSessionHistory(CFDictionaryRef backForwardListDictionary, BackForwardListState& backForwardListState)
1048{
1049    auto sessionHistoryVersionNumber = dynamic_cf_cast<CFNumberRef>(CFDictionaryGetValue(backForwardListDictionary, sessionHistoryVersionKey));
1050    if (!sessionHistoryVersionNumber) {
1051        // Version 0 session history dictionaries did not contain a version number.
1052        return decodeV0SessionHistory(backForwardListDictionary, backForwardListState);
1053    }
1054
1055    CFIndex sessionHistoryVersion;
1056    if (!CFNumberGetValue(sessionHistoryVersionNumber, kCFNumberCFIndexType, &sessionHistoryVersion))
1057        return false;
1058
1059    if (sessionHistoryVersion == 1)
1060        return decodeV1SessionHistory(backForwardListDictionary, backForwardListState);
1061
1062    return false;
1063}
1064
1065bool decodeLegacySessionState(const uint8_t* bytes, size_t size, SessionState& sessionState)
1066{
1067    if (size < sizeof(uint32_t))
1068        return false;
1069
1070    uint32_t versionNumber = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
1071
1072    if (versionNumber != sessionStateDataVersion)
1073        return false;
1074
1075    auto sessionStateDictionary = adoptCF(dynamic_cf_cast<CFDictionaryRef>(CFPropertyListCreateWithData(kCFAllocatorDefault, adoptCF(CFDataCreate(kCFAllocatorDefault, bytes + sizeof(uint32_t), size - sizeof(uint32_t))).get(), kCFPropertyListImmutable, nullptr, nullptr)));
1076    if (!sessionStateDictionary)
1077        return false;
1078
1079    if (auto backForwardListDictionary = dynamic_cf_cast<CFDictionaryRef>(CFDictionaryGetValue(sessionStateDictionary.get(), sessionHistoryKey))) {
1080        if (!decodeSessionHistory(backForwardListDictionary, sessionState.backForwardListState))
1081            return false;
1082    }
1083
1084    if (auto provisionalURLString = dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(sessionStateDictionary.get(), provisionalURLKey))) {
1085        sessionState.provisionalURL = WebCore::URL(WebCore::URL(), provisionalURLString);
1086        if (!sessionState.provisionalURL.isValid())
1087            return false;
1088    }
1089
1090    return true;
1091}
1092
1093} // namespace WebKit
1094