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 "SessionStateConversion.h"
28
29#include "SessionState.h"
30#include <WebCore/BlobData.h>
31#include <WebCore/FormData.h>
32#include <WebCore/HistoryItem.h>
33#include <wtf/FileSystem.h>
34
35namespace WebKit {
36using namespace WebCore;
37
38static HTTPBody toHTTPBody(const FormData& formData)
39{
40 HTTPBody httpBody;
41
42 for (const auto& formDataElement : formData.elements()) {
43 HTTPBody::Element element;
44
45 switchOn(formDataElement.data,
46 [&] (const Vector<char>& bytes) {
47 element.type = HTTPBody::Element::Type::Data;
48 element.data = bytes;
49 }, [&] (const FormDataElement::EncodedFileData& fileData) {
50 element.filePath = fileData.filename;
51 element.fileStart = fileData.fileStart;
52 if (fileData.fileLength != BlobDataItem::toEndOfFile)
53 element.fileLength = fileData.fileLength;
54 element.expectedFileModificationTime = fileData.expectedFileModificationTime;
55 }, [&] (const FormDataElement::EncodedBlobData& blobData) {
56 element.blobURLString = blobData.url.string();
57 }
58 );
59
60 httpBody.elements.append(WTFMove(element));
61 }
62
63 return httpBody;
64}
65
66static FrameState toFrameState(const HistoryItem& historyItem)
67{
68 FrameState frameState;
69
70 frameState.urlString = historyItem.urlString();
71 frameState.originalURLString = historyItem.originalURLString();
72 frameState.referrer = historyItem.referrer();
73 frameState.target = historyItem.target();
74
75 frameState.documentState = historyItem.documentState();
76 if (RefPtr<SerializedScriptValue> stateObject = historyItem.stateObject())
77 frameState.stateObjectData = stateObject->data();
78
79 frameState.documentSequenceNumber = historyItem.documentSequenceNumber();
80 frameState.itemSequenceNumber = historyItem.itemSequenceNumber();
81
82 frameState.scrollPosition = historyItem.scrollPosition();
83 frameState.shouldRestoreScrollPosition = historyItem.shouldRestoreScrollPosition();
84 frameState.pageScaleFactor = historyItem.pageScaleFactor();
85
86 if (FormData* formData = const_cast<HistoryItem&>(historyItem).formData()) {
87 HTTPBody httpBody = toHTTPBody(*formData);
88 httpBody.contentType = historyItem.formContentType();
89
90 frameState.httpBody = WTFMove(httpBody);
91 }
92
93#if PLATFORM(IOS_FAMILY)
94 frameState.exposedContentRect = historyItem.exposedContentRect();
95 frameState.unobscuredContentRect = historyItem.unobscuredContentRect();
96 frameState.minimumLayoutSizeInScrollViewCoordinates = historyItem.minimumLayoutSizeInScrollViewCoordinates();
97 frameState.contentSize = historyItem.contentSize();
98 frameState.scaleIsInitial = historyItem.scaleIsInitial();
99 frameState.obscuredInsets = historyItem.obscuredInsets();
100#endif
101
102 for (auto& childHistoryItem : historyItem.children()) {
103 FrameState childFrameState = toFrameState(childHistoryItem);
104 frameState.children.append(WTFMove(childFrameState));
105 }
106
107 return frameState;
108}
109
110BackForwardListItemState toBackForwardListItemState(const WebCore::HistoryItem& historyItem)
111{
112 BackForwardListItemState state;
113 state.identifier = historyItem.identifier();
114 state.pageState.title = historyItem.title();
115 state.pageState.mainFrameState = toFrameState(historyItem);
116 state.pageState.shouldOpenExternalURLsPolicy = historyItem.shouldOpenExternalURLsPolicy();
117 state.pageState.sessionStateObject = historyItem.stateObject();
118 return state;
119}
120
121static Ref<FormData> toFormData(const HTTPBody& httpBody)
122{
123 auto formData = FormData::create();
124
125 for (const auto& element : httpBody.elements) {
126 switch (element.type) {
127 case HTTPBody::Element::Type::Data:
128 formData->appendData(element.data.data(), element.data.size());
129 break;
130
131 case HTTPBody::Element::Type::File:
132 formData->appendFileRange(element.filePath, element.fileStart, element.fileLength.valueOr(BlobDataItem::toEndOfFile), element.expectedFileModificationTime);
133 break;
134
135 case HTTPBody::Element::Type::Blob:
136 formData->appendBlob(URL(URL(), element.blobURLString));
137 break;
138 }
139 }
140
141 return formData;
142}
143
144static void applyFrameState(HistoryItem& historyItem, const FrameState& frameState)
145{
146 historyItem.setOriginalURLString(frameState.originalURLString);
147 historyItem.setReferrer(frameState.referrer);
148 historyItem.setTarget(frameState.target);
149
150 historyItem.setDocumentState(frameState.documentState);
151
152 if (frameState.stateObjectData) {
153 Vector<uint8_t> stateObjectData = frameState.stateObjectData.value();
154 historyItem.setStateObject(SerializedScriptValue::adopt(WTFMove(stateObjectData)));
155 }
156
157 historyItem.setDocumentSequenceNumber(frameState.documentSequenceNumber);
158 historyItem.setItemSequenceNumber(frameState.itemSequenceNumber);
159
160 historyItem.setScrollPosition(frameState.scrollPosition);
161 historyItem.setShouldRestoreScrollPosition(frameState.shouldRestoreScrollPosition);
162 historyItem.setPageScaleFactor(frameState.pageScaleFactor);
163
164 if (frameState.httpBody) {
165 const auto& httpBody = frameState.httpBody.value();
166 historyItem.setFormContentType(httpBody.contentType);
167
168 historyItem.setFormData(toFormData(httpBody));
169 }
170
171#if PLATFORM(IOS_FAMILY)
172 historyItem.setExposedContentRect(frameState.exposedContentRect);
173 historyItem.setUnobscuredContentRect(frameState.unobscuredContentRect);
174 historyItem.setMinimumLayoutSizeInScrollViewCoordinates(frameState.minimumLayoutSizeInScrollViewCoordinates);
175 historyItem.setContentSize(frameState.contentSize);
176 historyItem.setScaleIsInitial(frameState.scaleIsInitial);
177 historyItem.setObscuredInsets(frameState.obscuredInsets);
178#endif
179
180 for (const auto& childFrameState : frameState.children) {
181 Ref<HistoryItem> childHistoryItem = HistoryItem::create(childFrameState.urlString, { }, { });
182 applyFrameState(childHistoryItem, childFrameState);
183
184 historyItem.addChildItem(WTFMove(childHistoryItem));
185 }
186}
187
188Ref<HistoryItem> toHistoryItem(const BackForwardListItemState& itemState)
189{
190 Ref<HistoryItem> historyItem = HistoryItem::create(itemState.pageState.mainFrameState.urlString, itemState.pageState.title, { }, itemState.identifier);
191 historyItem->setShouldOpenExternalURLsPolicy(itemState.pageState.shouldOpenExternalURLsPolicy);
192 historyItem->setStateObject(itemState.pageState.sessionStateObject.get());
193 applyFrameState(historyItem, itemState.pageState.mainFrameState);
194
195 return historyItem;
196}
197
198} // namespace WebKit
199