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