c67dba1e5bddca5e311abe427ecfd939e182878b
[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 <wtf/cf/TypeCasts.h>
32
33 namespace WebKit {
34
35 // Session state keys.
36 static const uint32_t sessionStateDataVersion = 2;
37
38 static const CFStringRef sessionHistoryKey = CFSTR("SessionHistory");
39 static const CFStringRef provisionalURLKey = CFSTR("ProvisionalURL");
40
41 // Session history keys.
42 static const CFStringRef sessionHistoryVersionKey = CFSTR("SessionHistoryVersion");
43 static const CFStringRef sessionHistoryCurrentIndexKey = CFSTR("SessionHistoryCurrentIndex");
44 static const CFStringRef sessionHistoryEntriesKey = CFSTR("SessionHistoryEntries");
45
46 // Session history entry keys.
47 static const CFStringRef sessionHistoryEntryURLKey = CFSTR("SessionHistoryEntryURL");
48 static CFStringRef sessionHistoryEntryTitleKey = CFSTR("SessionHistoryEntryTitle");
49 static CFStringRef sessionHistoryEntryOriginalURLKey = CFSTR("SessionHistoryEntryOriginalURL");
50 static CFStringRef sessionHistoryEntrySnapshotUUIDKey = CFSTR("SessionHistoryEntrySnapshotUUID");
51 static CFStringRef sessionHistoryEntryDataKey = CFSTR("SessionHistoryEntryData");
52
53 // Session history entry data.
54 const uint32_t sessionHistoryEntryDataVersion = 2;
55
56 LegacySessionStateDecoder::LegacySessionStateDecoder(API::Data* data)
57     : m_data(data)
58 {
59 }
60
61 LegacySessionStateDecoder::~LegacySessionStateDecoder()
62 {
63 }
64
65 bool LegacySessionStateDecoder::decodeSessionState(SessionState& sessionState) const
66 {
67     if (!m_data)
68         return false;
69
70     size_t size = m_data->size();
71     const uint8_t* bytes = m_data->bytes();
72
73     if (size < sizeof(uint32_t))
74         return false;
75
76     uint32_t versionNumber = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
77
78     if (versionNumber != sessionStateDataVersion)
79         return false;
80
81     auto data = adoptCF(CFDataCreate(kCFAllocatorDefault, bytes + sizeof(uint32_t), size - sizeof(uint32_t)));
82
83     auto sessionStateDictionary = adoptCF(dynamic_cf_cast<CFDictionaryRef>(CFPropertyListCreateWithData(kCFAllocatorDefault, data.get(), kCFPropertyListImmutable, nullptr, nullptr)));
84     if (!sessionStateDictionary)
85         return false;
86
87     if (auto backForwardListDictionary = dynamic_cf_cast<CFDictionaryRef>(CFDictionaryGetValue(sessionStateDictionary.get(), sessionHistoryKey))) {
88         if (!decodeSessionHistory(backForwardListDictionary, sessionState.backForwardListState))
89             return false;
90     }
91
92     if (auto provisionalURLString = dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(sessionStateDictionary.get(), provisionalURLKey))) {
93         sessionState.provisionalURL = WebCore::URL(WebCore::URL(), provisionalURLString);
94         if (!sessionState.provisionalURL.isValid())
95             return false;
96     }
97
98     return true;
99 }
100
101 bool LegacySessionStateDecoder::decodeSessionHistory(CFDictionaryRef backForwardListDictionary, BackForwardListState& backForwardListState) const
102 {
103     auto sessionHistoryVersionNumber = dynamic_cf_cast<CFNumberRef>(CFDictionaryGetValue(backForwardListDictionary, sessionHistoryVersionKey));
104     if (!sessionHistoryVersionNumber) {
105         // Version 0 session history dictionaries did not contain a version number.
106         return decodeV0SessionHistory(backForwardListDictionary, backForwardListState);
107     }
108
109     CFIndex sessionHistoryVersion;
110     if (!CFNumberGetValue(sessionHistoryVersionNumber, kCFNumberCFIndexType, &sessionHistoryVersion))
111         return false;
112
113     if (sessionHistoryVersion == 1)
114         return decodeV1SessionHistory(backForwardListDictionary, backForwardListState);
115
116     return false;
117 }
118
119 bool LegacySessionStateDecoder::decodeV0SessionHistory(CFDictionaryRef sessionHistoryDictionary, BackForwardListState& backForwardListState) const
120 {
121     // FIXME: Implement.
122     return false;
123 }
124
125 bool LegacySessionStateDecoder::decodeV1SessionHistory(CFDictionaryRef sessionHistoryDictionary, BackForwardListState& backForwardListState) const
126 {
127     auto currentIndexNumber = dynamic_cf_cast<CFNumberRef>(CFDictionaryGetValue(sessionHistoryDictionary, sessionHistoryCurrentIndexKey));
128     if (!currentIndexNumber) {
129         // No current index means the dictionary represents an empty session.
130         backForwardListState.currentIndex = 0;
131         backForwardListState.items = { };
132         return true;
133     }
134
135     CFIndex currentIndex;
136     if (!CFNumberGetValue(currentIndexNumber, kCFNumberCFIndexType, &currentIndex))
137         return false;
138
139     if (currentIndex < 0)
140         return false;
141
142     auto historyEntries = dynamic_cf_cast<CFArrayRef>(CFDictionaryGetValue(sessionHistoryDictionary, sessionHistoryEntriesKey));
143     if (!historyEntries)
144         return false;
145
146     if (!decodeSessionHistoryEntries(historyEntries, backForwardListState.items))
147         return false;
148
149     backForwardListState.currentIndex = static_cast<uint32_t>(currentIndex);
150     if (backForwardListState.currentIndex >= backForwardListState.items.size())
151         return false;
152
153     return true;
154 }
155
156 bool LegacySessionStateDecoder::decodeSessionHistoryEntries(CFArrayRef entriesArray, Vector<PageState>& entries) const
157 {
158     for (CFIndex i = 0, size = CFArrayGetCount(entriesArray); i < size; ++i) {
159         auto entryDictionary = dynamic_cf_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(entriesArray, i));
160         if (!entryDictionary)
161             return false;
162
163         PageState entry;
164         if (!decodeSessionHistoryEntry(entryDictionary, entry))
165             return false;
166
167         entries.append(std::move(entry));
168     }
169
170     return true;
171 }
172
173 bool LegacySessionStateDecoder::decodeSessionHistoryEntry(CFDictionaryRef entryDictionary, PageState& pageState) const
174 {
175     auto title = dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(entryDictionary, sessionHistoryEntryTitleKey));
176     if (!title)
177         return false;
178
179     auto urlString = dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(entryDictionary, sessionHistoryEntryURLKey));
180     if (!urlString)
181         return false;
182
183     auto originalURLString = dynamic_cf_cast<CFStringRef>(CFDictionaryGetValue(entryDictionary, sessionHistoryEntryOriginalURLKey));
184     if (!originalURLString)
185         return false;
186
187     auto historyEntryData = dynamic_cf_cast<CFDataRef>(CFDictionaryGetValue(entryDictionary, sessionHistoryEntryDataKey));
188     if (!historyEntryData)
189         return false;
190
191     if (!decodeSessionHistoryEntryData(historyEntryData, pageState.mainFrameState))
192         return false;
193
194     pageState.title = title;
195     pageState.mainFrameState.urlString = urlString;
196     pageState.mainFrameState.originalURLString = originalURLString;
197
198     return true;
199 }
200
201 class HistoryEntryDataDecoder {
202 public:
203     HistoryEntryDataDecoder(const uint8_t* buffer, size_t bufferSize)
204         : m_buffer(buffer)
205         , m_bufferEnd(buffer + bufferSize)
206     {
207         // Keep format compatibility by decoding an unused uint64_t here.
208         uint64_t value;
209         *this >> value;
210     }
211
212     HistoryEntryDataDecoder& operator>>(bool& value)
213     {
214         return decodeArithmeticType(value);
215     }
216
217     HistoryEntryDataDecoder& operator>>(uint32_t& value)
218     {
219         return decodeArithmeticType(value);
220     }
221
222     HistoryEntryDataDecoder& operator>>(int32_t& value)
223     {
224         return *this >> reinterpret_cast<uint32_t&>(value);
225     }
226
227     HistoryEntryDataDecoder& operator>>(uint64_t& value)
228     {
229         return decodeArithmeticType(value);
230     }
231
232     HistoryEntryDataDecoder& operator>>(int64_t& value)
233     {
234         return *this >> reinterpret_cast<uint64_t&>(value);
235     }
236
237     HistoryEntryDataDecoder& operator>>(float& value)
238     {
239         return decodeArithmeticType(value);
240     }
241
242     HistoryEntryDataDecoder& operator>>(String& value)
243     {
244         value = String();
245
246         uint32_t length;
247         *this >> length;
248
249         if (length == std::numeric_limits<uint32_t>::max()) {
250             // This is the null string.
251             value = String();
252             return *this;
253         }
254
255         uint64_t lengthInBytes;
256         *this >> lengthInBytes;
257
258         if (lengthInBytes % sizeof(UChar) || lengthInBytes / sizeof(UChar) != length) {
259             markInvalid();
260             return *this;
261         }
262
263         if (!bufferIsLargeEnoughToContain<UChar>(length)) {
264             markInvalid();
265             return *this;
266         }
267
268         UChar* buffer;
269         auto string = String::createUninitialized(length, buffer);
270         decodeFixedLengthData(reinterpret_cast<uint8_t*>(buffer), length * sizeof(UChar), alignof(UChar));
271
272         value = string;
273         return *this;
274     }
275
276     bool finishDecoding() { return m_buffer == m_bufferEnd; }
277
278 private:
279     template<typename Type>
280     HistoryEntryDataDecoder& decodeArithmeticType(Type& value)
281     {
282         static_assert(std::is_arithmetic<Type>::value, "");
283         value = Type();
284
285         decodeFixedLengthData(reinterpret_cast<uint8_t*>(&value), sizeof(value), sizeof(value));
286         return *this;
287     }
288
289     void decodeFixedLengthData(uint8_t* data, size_t size, unsigned alignment)
290     {
291         if (!alignBufferPosition(alignment, size))
292             return;
293
294         memcpy(data, m_buffer, size);
295         m_buffer += size;
296     }
297
298     bool alignBufferPosition(unsigned alignment, size_t size)
299     {
300         const uint8_t* alignedPosition = alignedBuffer(alignment);
301         if (!alignedBufferIsLargeEnoughToContain(alignedPosition, size)) {
302             // We've walked off the end of this buffer.
303             markInvalid();
304             return false;
305         }
306
307         m_buffer = alignedPosition;
308         return true;
309     }
310
311     const uint8_t* alignedBuffer(unsigned alignment) const
312     {
313         ASSERT(alignment && !(alignment & (alignment - 1)));
314
315         uintptr_t alignmentMask = alignment - 1;
316         return reinterpret_cast<uint8_t*>((reinterpret_cast<uintptr_t>(m_buffer) + alignmentMask) & ~alignmentMask);
317     }
318
319     template<typename T>
320     bool bufferIsLargeEnoughToContain(size_t numElements) const
321     {
322         static_assert(std::is_arithmetic<T>::value, "Type T must have a fixed, known encoded size!");
323
324         if (numElements > std::numeric_limits<size_t>::max() / sizeof(T))
325             return false;
326
327         return bufferIsLargeEnoughToContain(alignof(T), numElements * sizeof(T));
328     }
329
330     bool bufferIsLargeEnoughToContain(unsigned alignment, size_t size) const
331     {
332         return alignedBufferIsLargeEnoughToContain(alignedBuffer(alignment), size);
333     }
334
335     inline bool alignedBufferIsLargeEnoughToContain(const uint8_t* alignedPosition, size_t size) const
336     {
337         return m_bufferEnd >= alignedPosition && static_cast<size_t>(m_bufferEnd - alignedPosition) >= size;
338     }
339
340     void markInvalid() { m_buffer = m_bufferEnd + 1; }
341
342     const uint8_t* m_buffer;
343     const uint8_t* m_bufferEnd;
344 };
345
346 static void decodeBackForwardTreeNode(HistoryEntryDataDecoder& decoder, FrameState& frameState)
347 {
348     uint64_t childCount;
349     decoder >> childCount;
350
351     for (uint64_t i = 0; i < childCount; ++i) {
352         FrameState childFrameState;
353         decoder >> childFrameState.originalURLString;
354         decoder >> childFrameState.urlString;
355
356         decodeBackForwardTreeNode(decoder, childFrameState);
357         frameState.children.append(std::move(childFrameState));
358     }
359
360     decoder >> frameState.documentSequenceNumber;
361
362     uint64_t documentStateVectorSize;
363     decoder >> documentStateVectorSize;
364
365     for (uint64_t i = 0; i < documentStateVectorSize; ++i) {
366         // FIXME: Implement.
367         ASSERT_NOT_REACHED();
368     }
369
370     String formContentType;
371     decoder >> formContentType;
372
373     bool hasFormData;
374     decoder >> hasFormData;
375
376     if (hasFormData) {
377         // FIXME: Implement.
378         ASSERT_NOT_REACHED();
379     }
380
381     decoder >> frameState.itemSequenceNumber;
382
383     decoder >> frameState.referrer;
384
385     int32_t scrollPointX;
386     decoder >> scrollPointX;
387
388     int32_t scrollPointY;
389     decoder >> scrollPointY;
390
391     frameState.scrollPoint = WebCore::IntPoint(scrollPointX, scrollPointY);
392
393     decoder >> frameState.pageScaleFactor;
394
395     bool hasStateObject;
396     decoder >> hasStateObject;
397
398     if (hasStateObject) {
399         // FIXME: Implement.
400         ASSERT_NOT_REACHED();
401     }
402
403     decoder >> frameState.target;
404 }
405
406 bool LegacySessionStateDecoder::decodeSessionHistoryEntryData(CFDataRef historyEntryData, FrameState& mainFrameState) const
407 {
408     HistoryEntryDataDecoder decoder { CFDataGetBytePtr(historyEntryData), static_cast<size_t>(CFDataGetLength(historyEntryData)) };
409
410     uint32_t version;
411     decoder >> version;
412
413     if (version != sessionHistoryEntryDataVersion)
414         return false;
415
416     decodeBackForwardTreeNode(decoder, mainFrameState);
417
418     return decoder.finishDecoding();
419 }
420
421
422 } // namespace WebKit