| 1 | /* |
| 2 | * Copyright (C) 2016 Igalia S.L. |
| 3 | * |
| 4 | * This library is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU Library General Public |
| 6 | * License as published by the Free Software Foundation; either |
| 7 | * version 2 of the License, or (at your option) any later version. |
| 8 | * |
| 9 | * This library is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | * Library General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU Library General Public License |
| 15 | * along with this library; see the file COPYING.LIB. If not, write to |
| 16 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 17 | * Boston, MA 02110-1301, USA. |
| 18 | */ |
| 19 | |
| 20 | #include "config.h" |
| 21 | #include "WebKitWebViewSessionState.h" |
| 22 | |
| 23 | #include "WebKitWebViewSessionStatePrivate.h" |
| 24 | #include <WebCore/BackForwardItemIdentifier.h> |
| 25 | #include <wtf/glib/GRefPtr.h> |
| 26 | #include <wtf/glib/GUniquePtr.h> |
| 27 | |
| 28 | using namespace WebKit; |
| 29 | |
| 30 | struct _WebKitWebViewSessionState { |
| 31 | _WebKitWebViewSessionState(SessionState&& state) |
| 32 | : sessionState(WTFMove(state)) |
| 33 | , referenceCount(1) |
| 34 | { |
| 35 | } |
| 36 | |
| 37 | SessionState sessionState; |
| 38 | int referenceCount; |
| 39 | }; |
| 40 | |
| 41 | G_DEFINE_BOXED_TYPE(WebKitWebViewSessionState, webkit_web_view_session_state, webkit_web_view_session_state_ref, webkit_web_view_session_state_unref) |
| 42 | |
| 43 | // Version information: |
| 44 | // - Version 2: removed backforward list item identifier since it's always regenerated. |
| 45 | // - Version 1: initial version. |
| 46 | static const guint16 g_sessionStateVersion = 2; |
| 47 | #define HTTP_BODY_ELEMENT_TYPE_STRING_V1 "(uaysxmxmds)" |
| 48 | #define HTTP_BODY_ELEMENT_FORMAT_STRING_V1 "(uay&sxmxmd&s)" |
| 49 | #define HTTP_BODY_TYPE_STRING_V1 "m(sa" HTTP_BODY_ELEMENT_TYPE_STRING_V1 ")" |
| 50 | #define HTTP_BODY_FORMAT_STRING_V1 "m(&sa" HTTP_BODY_ELEMENT_TYPE_STRING_V1 ")" |
| 51 | #define FRAME_STATE_TYPE_STRING_V1 "(ssssasmayxx(ii)d" HTTP_BODY_TYPE_STRING_V1 "av)" |
| 52 | #define FRAME_STATE_FORMAT_STRING_V1 "(&s&s&s&sasmayxx(ii)d@" HTTP_BODY_TYPE_STRING_V1 "av)" |
| 53 | #define BACK_FORWARD_LIST_ITEM_TYPE_STRING_V1 "(ts" FRAME_STATE_TYPE_STRING_V1 "u)" |
| 54 | #define BACK_FORWARD_LIST_ITEM_TYPE_STRING_V2 "(s" FRAME_STATE_TYPE_STRING_V1 "u)" |
| 55 | #define BACK_FORWARD_LIST_ITEM_FORMAT_STRING_V1 "(t&s@" FRAME_STATE_TYPE_STRING_V1 "u)" |
| 56 | #define BACK_FORWARD_LIST_ITEM_FORMAT_STRING_V2 "(&s@" FRAME_STATE_TYPE_STRING_V1 "u)" |
| 57 | #define SESSION_STATE_TYPE_STRING_V1 "(qa" BACK_FORWARD_LIST_ITEM_TYPE_STRING_V1 "mu)" |
| 58 | #define SESSION_STATE_TYPE_STRING_V2 "(qa" BACK_FORWARD_LIST_ITEM_TYPE_STRING_V2 "mu)" |
| 59 | |
| 60 | // Use our own enum types to ensure the serialized format even if the core enums change. |
| 61 | enum ExternalURLsPolicy { |
| 62 | Allow, |
| 63 | AllowExternalSchemes, |
| 64 | NotAllow |
| 65 | }; |
| 66 | |
| 67 | static inline unsigned toExternalURLsPolicy(WebCore::ShouldOpenExternalURLsPolicy policy) |
| 68 | { |
| 69 | switch (policy) { |
| 70 | case WebCore::ShouldOpenExternalURLsPolicy::ShouldAllow: |
| 71 | return ExternalURLsPolicy::Allow; |
| 72 | case WebCore::ShouldOpenExternalURLsPolicy::ShouldAllowExternalSchemes: |
| 73 | return ExternalURLsPolicy::AllowExternalSchemes; |
| 74 | case WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow: |
| 75 | return ExternalURLsPolicy::NotAllow; |
| 76 | } |
| 77 | |
| 78 | return ExternalURLsPolicy::NotAllow; |
| 79 | } |
| 80 | |
| 81 | static inline WebCore::ShouldOpenExternalURLsPolicy toWebCoreExternalURLsPolicy(unsigned policy) |
| 82 | { |
| 83 | switch (policy) { |
| 84 | case ExternalURLsPolicy::Allow: |
| 85 | return WebCore::ShouldOpenExternalURLsPolicy::ShouldAllow; |
| 86 | case ExternalURLsPolicy::AllowExternalSchemes: |
| 87 | return WebCore::ShouldOpenExternalURLsPolicy::ShouldAllowExternalSchemes; |
| 88 | case ExternalURLsPolicy::NotAllow: |
| 89 | return WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow; |
| 90 | } |
| 91 | |
| 92 | return WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow; |
| 93 | } |
| 94 | |
| 95 | enum HTMLBodyElementType { |
| 96 | Data, |
| 97 | File, |
| 98 | Blob |
| 99 | }; |
| 100 | |
| 101 | static inline unsigned toHTMLBodyElementType(HTTPBody::Element::Type type) |
| 102 | { |
| 103 | switch (type) { |
| 104 | case HTTPBody::Element::Type::Data: |
| 105 | return HTMLBodyElementType::Data; |
| 106 | case HTTPBody::Element::Type::File: |
| 107 | return HTMLBodyElementType::File; |
| 108 | case HTTPBody::Element::Type::Blob: |
| 109 | return HTMLBodyElementType::Blob; |
| 110 | } |
| 111 | |
| 112 | return HTMLBodyElementType::Data; |
| 113 | } |
| 114 | |
| 115 | static inline HTTPBody::Element::Type toHTTPBodyElementType(unsigned type) |
| 116 | { |
| 117 | switch (type) { |
| 118 | case HTMLBodyElementType::Data: |
| 119 | return HTTPBody::Element::Type::Data; |
| 120 | case HTMLBodyElementType::File: |
| 121 | return HTTPBody::Element::Type::File; |
| 122 | case HTMLBodyElementType::Blob: |
| 123 | return HTTPBody::Element::Type::Blob; |
| 124 | } |
| 125 | |
| 126 | return HTTPBody::Element::Type::Data; |
| 127 | } |
| 128 | |
| 129 | static inline void encodeHTTPBody(GVariantBuilder* sessionBuilder, const HTTPBody& httpBody) |
| 130 | { |
| 131 | g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE("(sa" HTTP_BODY_ELEMENT_TYPE_STRING_V1 ")" )); |
| 132 | g_variant_builder_add(sessionBuilder, "s" , httpBody.contentType.utf8().data()); |
| 133 | g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE("a" HTTP_BODY_ELEMENT_TYPE_STRING_V1)); |
| 134 | g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE(HTTP_BODY_ELEMENT_TYPE_STRING_V1)); |
| 135 | for (const auto& element : httpBody.elements) { |
| 136 | g_variant_builder_add(sessionBuilder, "u" , toHTMLBodyElementType(element.type)); |
| 137 | g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE("ay" )); |
| 138 | for (auto item : element.data) |
| 139 | g_variant_builder_add(sessionBuilder, "y" , item); |
| 140 | g_variant_builder_close(sessionBuilder); |
| 141 | g_variant_builder_add(sessionBuilder, "s" , element.filePath.utf8().data()); |
| 142 | g_variant_builder_add(sessionBuilder, "x" , element.fileStart); |
| 143 | if (element.fileLength) |
| 144 | g_variant_builder_add(sessionBuilder, "mx" , TRUE, element.fileLength.value()); |
| 145 | else |
| 146 | g_variant_builder_add(sessionBuilder, "mx" , FALSE); |
| 147 | if (element.expectedFileModificationTime) |
| 148 | g_variant_builder_add(sessionBuilder, "md" , TRUE, element.expectedFileModificationTime.value()); |
| 149 | else |
| 150 | g_variant_builder_add(sessionBuilder, "md" , FALSE); |
| 151 | g_variant_builder_add(sessionBuilder, "s" , element.blobURLString.utf8().data()); |
| 152 | } |
| 153 | g_variant_builder_close(sessionBuilder); |
| 154 | g_variant_builder_close(sessionBuilder); |
| 155 | g_variant_builder_close(sessionBuilder); |
| 156 | } |
| 157 | |
| 158 | static inline void encodeFrameState(GVariantBuilder* sessionBuilder, const FrameState& frameState) |
| 159 | { |
| 160 | g_variant_builder_add(sessionBuilder, "s" , frameState.urlString.utf8().data()); |
| 161 | g_variant_builder_add(sessionBuilder, "s" , frameState.originalURLString.utf8().data()); |
| 162 | g_variant_builder_add(sessionBuilder, "s" , frameState.referrer.utf8().data()); |
| 163 | g_variant_builder_add(sessionBuilder, "s" , frameState.target.utf8().data()); |
| 164 | g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE("as" )); |
| 165 | for (const auto& state : frameState.documentState) |
| 166 | g_variant_builder_add(sessionBuilder, "s" , state.utf8().data()); |
| 167 | g_variant_builder_close(sessionBuilder); |
| 168 | if (!frameState.stateObjectData) |
| 169 | g_variant_builder_add(sessionBuilder, "may" , FALSE); |
| 170 | else { |
| 171 | g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE("may" )); |
| 172 | g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE("ay" )); |
| 173 | for (auto item : frameState.stateObjectData.value()) |
| 174 | g_variant_builder_add(sessionBuilder, "y" , item); |
| 175 | g_variant_builder_close(sessionBuilder); |
| 176 | g_variant_builder_close(sessionBuilder); |
| 177 | } |
| 178 | g_variant_builder_add(sessionBuilder, "x" , frameState.documentSequenceNumber); |
| 179 | g_variant_builder_add(sessionBuilder, "x" , frameState.itemSequenceNumber); |
| 180 | g_variant_builder_add(sessionBuilder, "(ii)" , frameState.scrollPosition.x(), frameState.scrollPosition.y()); |
| 181 | g_variant_builder_add(sessionBuilder, "d" , static_cast<gdouble>(frameState.pageScaleFactor)); |
| 182 | if (!frameState.httpBody) |
| 183 | g_variant_builder_add(sessionBuilder, HTTP_BODY_TYPE_STRING_V1, FALSE); |
| 184 | else { |
| 185 | g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE(HTTP_BODY_TYPE_STRING_V1)); |
| 186 | encodeHTTPBody(sessionBuilder, frameState.httpBody.value()); |
| 187 | g_variant_builder_close(sessionBuilder); |
| 188 | } |
| 189 | g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE("av" )); |
| 190 | for (const auto& child : frameState.children) { |
| 191 | GVariantBuilder frameStateBuilder; |
| 192 | g_variant_builder_init(&frameStateBuilder, G_VARIANT_TYPE(FRAME_STATE_TYPE_STRING_V1)); |
| 193 | encodeFrameState(&frameStateBuilder, child); |
| 194 | g_variant_builder_add(sessionBuilder, "v" , g_variant_builder_end(&frameStateBuilder)); |
| 195 | } |
| 196 | g_variant_builder_close(sessionBuilder); |
| 197 | } |
| 198 | |
| 199 | static inline void encodePageState(GVariantBuilder* sessionBuilder, const PageState& pageState) |
| 200 | { |
| 201 | g_variant_builder_add(sessionBuilder, "s" , pageState.title.utf8().data()); |
| 202 | g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE(FRAME_STATE_TYPE_STRING_V1)); |
| 203 | encodeFrameState(sessionBuilder, pageState.mainFrameState); |
| 204 | g_variant_builder_close(sessionBuilder); |
| 205 | g_variant_builder_add(sessionBuilder, "u" , toExternalURLsPolicy(pageState.shouldOpenExternalURLsPolicy)); |
| 206 | } |
| 207 | |
| 208 | static inline void encodeBackForwardListItemState(GVariantBuilder* sessionBuilder, const BackForwardListItemState& item) |
| 209 | { |
| 210 | g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE(BACK_FORWARD_LIST_ITEM_TYPE_STRING_V2)); |
| 211 | encodePageState(sessionBuilder, item.pageState); |
| 212 | g_variant_builder_close(sessionBuilder); |
| 213 | } |
| 214 | |
| 215 | static inline void encodeBackForwardListState(GVariantBuilder* sessionBuilder, const BackForwardListState& backForwardListState) |
| 216 | { |
| 217 | g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE("a" BACK_FORWARD_LIST_ITEM_TYPE_STRING_V2)); |
| 218 | for (const auto& item : backForwardListState.items) |
| 219 | encodeBackForwardListItemState(sessionBuilder, item); |
| 220 | g_variant_builder_close(sessionBuilder); |
| 221 | |
| 222 | if (backForwardListState.currentIndex) |
| 223 | g_variant_builder_add(sessionBuilder, "mu" , TRUE, backForwardListState.currentIndex.value()); |
| 224 | else |
| 225 | g_variant_builder_add(sessionBuilder, "mu" , FALSE); |
| 226 | } |
| 227 | |
| 228 | static GBytes* encodeSessionState(const SessionState& sessionState) |
| 229 | { |
| 230 | GVariantBuilder sessionBuilder; |
| 231 | g_variant_builder_init(&sessionBuilder, G_VARIANT_TYPE(SESSION_STATE_TYPE_STRING_V2)); |
| 232 | g_variant_builder_add(&sessionBuilder, "q" , g_sessionStateVersion); |
| 233 | encodeBackForwardListState(&sessionBuilder, sessionState.backForwardListState); |
| 234 | GRefPtr<GVariant> variant = g_variant_builder_end(&sessionBuilder); |
| 235 | return g_variant_get_data_as_bytes(variant.get()); |
| 236 | } |
| 237 | |
| 238 | static inline bool decodeHTTPBody(GVariant* httpBodyVariant, HTTPBody& httpBody) |
| 239 | { |
| 240 | gboolean hasHTTPBody; |
| 241 | const char* contentType; |
| 242 | GUniqueOutPtr<GVariantIter> elementsIter; |
| 243 | g_variant_get(httpBodyVariant, HTTP_BODY_FORMAT_STRING_V1, &hasHTTPBody, &contentType, &elementsIter.outPtr()); |
| 244 | if (!hasHTTPBody) |
| 245 | return false; |
| 246 | httpBody.contentType = String::fromUTF8(contentType); |
| 247 | gsize elementsLength = g_variant_iter_n_children(elementsIter.get()); |
| 248 | if (!elementsLength) |
| 249 | return true; |
| 250 | httpBody.elements.reserveInitialCapacity(elementsLength); |
| 251 | unsigned type; |
| 252 | GVariantIter* dataIter; |
| 253 | const char* filePath; |
| 254 | gint64 fileStart; |
| 255 | gboolean hasFileLength; |
| 256 | gint64 fileLength; |
| 257 | gboolean hasFileModificationTime; |
| 258 | gdouble fileModificationTime; |
| 259 | const char* blobURLString; |
| 260 | while (g_variant_iter_loop(elementsIter.get(), HTTP_BODY_ELEMENT_FORMAT_STRING_V1, &type, &dataIter, &filePath, &fileStart, &hasFileLength, &fileLength, &hasFileModificationTime, &fileModificationTime, &blobURLString)) { |
| 261 | HTTPBody::Element element; |
| 262 | element.type = toHTTPBodyElementType(type); |
| 263 | if (gsize dataLength = g_variant_iter_n_children(dataIter)) { |
| 264 | element.data.reserveInitialCapacity(dataLength); |
| 265 | guchar dataValue; |
| 266 | while (g_variant_iter_next(dataIter, "y" , &dataValue)) |
| 267 | element.data.uncheckedAppend(dataValue); |
| 268 | } |
| 269 | element.filePath = String::fromUTF8(filePath); |
| 270 | element.fileStart = fileStart; |
| 271 | if (hasFileLength) |
| 272 | element.fileLength = fileLength; |
| 273 | if (hasFileModificationTime) |
| 274 | element.expectedFileModificationTime = WallTime::fromRawSeconds(fileModificationTime); |
| 275 | element.blobURLString = String::fromUTF8(blobURLString); |
| 276 | |
| 277 | httpBody.elements.uncheckedAppend(WTFMove(element)); |
| 278 | } |
| 279 | |
| 280 | return true; |
| 281 | } |
| 282 | |
| 283 | static inline void decodeFrameState(GVariant* frameStateVariant, FrameState& frameState) |
| 284 | { |
| 285 | const char* urlString; |
| 286 | const char* originalURLString; |
| 287 | const char* referrer; |
| 288 | const char* target; |
| 289 | GUniqueOutPtr<GVariantIter> documentStateIter; |
| 290 | GUniqueOutPtr<GVariantIter> stateObjectDataIter; |
| 291 | gint64 documentSequenceNumber; |
| 292 | gint64 itemSequenceNumber; |
| 293 | gint32 scrollPositionX, scrollPositionY; |
| 294 | gdouble pageScaleFactor; |
| 295 | GVariant* httpBodyVariant; |
| 296 | GUniqueOutPtr<GVariantIter> childrenIter; |
| 297 | g_variant_get(frameStateVariant, FRAME_STATE_FORMAT_STRING_V1, &urlString, &originalURLString, &referrer, &target, |
| 298 | &documentStateIter.outPtr(), &stateObjectDataIter.outPtr(), &documentSequenceNumber, &itemSequenceNumber, |
| 299 | &scrollPositionX, &scrollPositionY, &pageScaleFactor, &httpBodyVariant, &childrenIter.outPtr()); |
| 300 | frameState.urlString = String::fromUTF8(urlString); |
| 301 | frameState.originalURLString = String::fromUTF8(originalURLString); |
| 302 | // frameState.referrer must not be an empty string since we never want to |
| 303 | // send an empty Referer header. Bug #159606. |
| 304 | if (strlen(referrer)) |
| 305 | frameState.referrer = String::fromUTF8(referrer); |
| 306 | frameState.target = String::fromUTF8(target); |
| 307 | if (gsize documentStateLength = g_variant_iter_n_children(documentStateIter.get())) { |
| 308 | frameState.documentState.reserveInitialCapacity(documentStateLength); |
| 309 | const char* documentStateString; |
| 310 | while (g_variant_iter_next(documentStateIter.get(), "&s" , &documentStateString)) |
| 311 | frameState.documentState.uncheckedAppend(String::fromUTF8(documentStateString)); |
| 312 | } |
| 313 | if (stateObjectDataIter) { |
| 314 | Vector<uint8_t> stateObjectVector; |
| 315 | if (gsize stateObjectDataLength = g_variant_iter_n_children(stateObjectDataIter.get())) { |
| 316 | stateObjectVector.reserveInitialCapacity(stateObjectDataLength); |
| 317 | guchar stateObjectDataValue; |
| 318 | while (g_variant_iter_next(stateObjectDataIter.get(), "y" , &stateObjectDataValue)) |
| 319 | stateObjectVector.uncheckedAppend(stateObjectDataValue); |
| 320 | } |
| 321 | frameState.stateObjectData = WTFMove(stateObjectVector); |
| 322 | } |
| 323 | frameState.documentSequenceNumber = documentSequenceNumber; |
| 324 | frameState.itemSequenceNumber = itemSequenceNumber; |
| 325 | frameState.scrollPosition.setX(scrollPositionX); |
| 326 | frameState.scrollPosition.setY(scrollPositionY); |
| 327 | frameState.pageScaleFactor = pageScaleFactor; |
| 328 | HTTPBody httpBody; |
| 329 | if (decodeHTTPBody(httpBodyVariant, httpBody)) |
| 330 | frameState.httpBody = WTFMove(httpBody); |
| 331 | g_variant_unref(httpBodyVariant); |
| 332 | while (GRefPtr<GVariant> child = adoptGRef(g_variant_iter_next_value(childrenIter.get()))) { |
| 333 | FrameState childFrameState; |
| 334 | GRefPtr<GVariant> childVariant = adoptGRef(g_variant_get_variant(child.get())); |
| 335 | decodeFrameState(childVariant.get(), childFrameState); |
| 336 | frameState.children.append(WTFMove(childFrameState)); |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | static inline void decodeBackForwardListItemStateV1(GVariantIter* backForwardListStateIter, BackForwardListState& backForwardListState) |
| 341 | { |
| 342 | guint64 identifier; |
| 343 | const char* title; |
| 344 | GVariant* frameStateVariant; |
| 345 | unsigned shouldOpenExternalURLsPolicy; |
| 346 | while (g_variant_iter_loop(backForwardListStateIter, BACK_FORWARD_LIST_ITEM_FORMAT_STRING_V1, &identifier, &title, &frameStateVariant, &shouldOpenExternalURLsPolicy)) { |
| 347 | BackForwardListItemState state; |
| 348 | state.pageState.title = String::fromUTF8(title); |
| 349 | decodeFrameState(frameStateVariant, state.pageState.mainFrameState); |
| 350 | state.pageState.shouldOpenExternalURLsPolicy = toWebCoreExternalURLsPolicy(shouldOpenExternalURLsPolicy); |
| 351 | backForwardListState.items.uncheckedAppend(WTFMove(state)); |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | static inline void decodeBackForwardListItemState(GVariantIter* backForwardListStateIter, BackForwardListState& backForwardListState, guint16 version) |
| 356 | { |
| 357 | gsize backForwardListStateLength = g_variant_iter_n_children(backForwardListStateIter); |
| 358 | if (!backForwardListStateLength) |
| 359 | return; |
| 360 | |
| 361 | backForwardListState.items.reserveInitialCapacity(backForwardListStateLength); |
| 362 | if (version == 1) { |
| 363 | decodeBackForwardListItemStateV1(backForwardListStateIter, backForwardListState); |
| 364 | return; |
| 365 | } |
| 366 | |
| 367 | const char* title; |
| 368 | GVariant* frameStateVariant; |
| 369 | unsigned shouldOpenExternalURLsPolicy; |
| 370 | while (g_variant_iter_loop(backForwardListStateIter, BACK_FORWARD_LIST_ITEM_FORMAT_STRING_V2, &title, &frameStateVariant, &shouldOpenExternalURLsPolicy)) { |
| 371 | BackForwardListItemState state; |
| 372 | state.pageState.title = String::fromUTF8(title); |
| 373 | decodeFrameState(frameStateVariant, state.pageState.mainFrameState); |
| 374 | state.pageState.shouldOpenExternalURLsPolicy = toWebCoreExternalURLsPolicy(shouldOpenExternalURLsPolicy); |
| 375 | backForwardListState.items.uncheckedAppend(WTFMove(state)); |
| 376 | } |
| 377 | } |
| 378 | |
| 379 | static bool decodeSessionState(GBytes* data, SessionState& sessionState) |
| 380 | { |
| 381 | static const char* sessionStateTypeStringVersions[] = { |
| 382 | SESSION_STATE_TYPE_STRING_V2, |
| 383 | SESSION_STATE_TYPE_STRING_V1, |
| 384 | nullptr |
| 385 | }; |
| 386 | const char* sessionStateTypeString = nullptr; |
| 387 | GRefPtr<GVariant> variant; |
| 388 | for (unsigned i = 0; sessionStateTypeStringVersions[i]; ++i) { |
| 389 | sessionStateTypeString = sessionStateTypeStringVersions[i]; |
| 390 | variant = g_variant_new_from_bytes(G_VARIANT_TYPE(sessionStateTypeString), data, FALSE); |
| 391 | if (g_variant_is_normal_form(variant.get())) |
| 392 | break; |
| 393 | variant = nullptr; |
| 394 | } |
| 395 | if (!variant) |
| 396 | return false; |
| 397 | |
| 398 | guint16 version; |
| 399 | GUniqueOutPtr<GVariantIter> backForwardListStateIter; |
| 400 | gboolean hasCurrentIndex; |
| 401 | guint32 currentIndex; |
| 402 | g_variant_get(variant.get(), sessionStateTypeString, &version, &backForwardListStateIter.outPtr(), &hasCurrentIndex, ¤tIndex); |
| 403 | if (!version || version > g_sessionStateVersion) |
| 404 | return false; |
| 405 | |
| 406 | decodeBackForwardListItemState(backForwardListStateIter.get(), sessionState.backForwardListState, version); |
| 407 | |
| 408 | if (hasCurrentIndex) |
| 409 | sessionState.backForwardListState.currentIndex = std::min<uint32_t>(currentIndex, sessionState.backForwardListState.items.size() - 1); |
| 410 | return true; |
| 411 | } |
| 412 | |
| 413 | WebKitWebViewSessionState* webkitWebViewSessionStateCreate(SessionState&& sessionState) |
| 414 | { |
| 415 | WebKitWebViewSessionState* state = static_cast<WebKitWebViewSessionState*>(fastMalloc(sizeof(WebKitWebViewSessionState))); |
| 416 | new (state) WebKitWebViewSessionState(WTFMove(sessionState)); |
| 417 | return state; |
| 418 | } |
| 419 | |
| 420 | const SessionState& webkitWebViewSessionStateGetSessionState(WebKitWebViewSessionState* state) |
| 421 | { |
| 422 | return state->sessionState; |
| 423 | } |
| 424 | |
| 425 | /** |
| 426 | * webkit_web_view_session_state_new: |
| 427 | * @data: a #GBytes |
| 428 | * |
| 429 | * Creates a new #WebKitWebViewSessionState from serialized data. |
| 430 | * |
| 431 | * Returns: (transfer full): a new #WebKitWebViewSessionState, or %NULL if @data doesn't contain a |
| 432 | * valid serialized #WebKitWebViewSessionState. |
| 433 | * |
| 434 | * Since: 2.12 |
| 435 | */ |
| 436 | WebKitWebViewSessionState* webkit_web_view_session_state_new(GBytes* data) |
| 437 | { |
| 438 | g_return_val_if_fail(data, nullptr); |
| 439 | |
| 440 | SessionState sessionState; |
| 441 | if (!decodeSessionState(data, sessionState)) |
| 442 | return nullptr; |
| 443 | return webkitWebViewSessionStateCreate(WTFMove(sessionState)); |
| 444 | } |
| 445 | |
| 446 | /** |
| 447 | * webkit_web_view_session_state_ref: |
| 448 | * @state: a #WebKitWebViewSessionState |
| 449 | * |
| 450 | * Atomically increments the reference count of @state by one. This |
| 451 | * function is MT-safe and may be called from any thread. |
| 452 | * |
| 453 | * Returns: The passed in #WebKitWebViewSessionState |
| 454 | * |
| 455 | * Since: 2.12 |
| 456 | */ |
| 457 | WebKitWebViewSessionState* webkit_web_view_session_state_ref(WebKitWebViewSessionState* state) |
| 458 | { |
| 459 | g_return_val_if_fail(state, nullptr); |
| 460 | g_atomic_int_inc(&state->referenceCount); |
| 461 | return state; |
| 462 | } |
| 463 | |
| 464 | /** |
| 465 | * webkit_web_view_session_state_unref: |
| 466 | * @state: a #WebKitWebViewSessionState |
| 467 | * |
| 468 | * Atomically decrements the reference count of @state by one. If the |
| 469 | * reference count drops to 0, all memory allocated by the #WebKitWebViewSessionState is |
| 470 | * released. This function is MT-safe and may be called from any thread. |
| 471 | * |
| 472 | * Since: 2.12 |
| 473 | */ |
| 474 | void webkit_web_view_session_state_unref(WebKitWebViewSessionState* state) |
| 475 | { |
| 476 | g_return_if_fail(state); |
| 477 | if (g_atomic_int_dec_and_test(&state->referenceCount)) { |
| 478 | state->~WebKitWebViewSessionState(); |
| 479 | fastFree(state); |
| 480 | } |
| 481 | } |
| 482 | |
| 483 | /** |
| 484 | * webkit_web_view_session_state_serialize: |
| 485 | * @state: a #WebKitWebViewSessionState |
| 486 | * |
| 487 | * Serializes a #WebKitWebViewSessionState. |
| 488 | * |
| 489 | * Returns: (transfer full): a #GBytes containing the @state serialized. |
| 490 | * |
| 491 | * Since: 2.12 |
| 492 | */ |
| 493 | GBytes* webkit_web_view_session_state_serialize(WebKitWebViewSessionState* state) |
| 494 | { |
| 495 | g_return_val_if_fail(state, nullptr); |
| 496 | |
| 497 | return encodeSessionState(state->sessionState); |
| 498 | } |
| 499 | |