summaryrefslogtreecommitdiff
path: root/Source/WebKit2/UIProcess/API/gtk/WebKitWebViewSessionState.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit2/UIProcess/API/gtk/WebKitWebViewSessionState.cpp')
-rw-r--r--Source/WebKit2/UIProcess/API/gtk/WebKitWebViewSessionState.cpp461
1 files changed, 461 insertions, 0 deletions
diff --git a/Source/WebKit2/UIProcess/API/gtk/WebKitWebViewSessionState.cpp b/Source/WebKit2/UIProcess/API/gtk/WebKitWebViewSessionState.cpp
new file mode 100644
index 000000000..325d0141c
--- /dev/null
+++ b/Source/WebKit2/UIProcess/API/gtk/WebKitWebViewSessionState.cpp
@@ -0,0 +1,461 @@
+/*
+ * Copyright (C) 2016 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WebKitWebViewSessionState.h"
+
+#include "WebKitWebViewSessionStatePrivate.h"
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+
+using namespace WebKit;
+
+struct _WebKitWebViewSessionState {
+ _WebKitWebViewSessionState(SessionState&& state)
+ : sessionState(WTFMove(state))
+ , referenceCount(1)
+ {
+ }
+
+ SessionState sessionState;
+ int referenceCount;
+};
+
+G_DEFINE_BOXED_TYPE(WebKitWebViewSessionState, webkit_web_view_session_state, webkit_web_view_session_state_ref, webkit_web_view_session_state_unref)
+
+static const guint16 g_sessionStateVersion = 1;
+#define HTTP_BODY_ELEMENT_TYPE_STRING_V1 "(uaysxmxmds)"
+#define HTTP_BODY_ELEMENT_FORMAT_STRING_V1 "(uay&sxmxmd&s)"
+#define HTTP_BODY_TYPE_STRING_V1 "m(sa" HTTP_BODY_ELEMENT_TYPE_STRING_V1 ")"
+#define HTTP_BODY_FORMAT_STRING_V1 "m(&sa" HTTP_BODY_ELEMENT_TYPE_STRING_V1 ")"
+#define FRAME_STATE_TYPE_STRING_V1 "(ssssasmayxx(ii)d" HTTP_BODY_TYPE_STRING_V1 "av)"
+#define FRAME_STATE_FORMAT_STRING_V1 "(&s&s&s&sasmayxx(ii)d@" HTTP_BODY_TYPE_STRING_V1 "av)"
+#define BACK_FORWARD_LIST_ITEM_TYPE_STRING_V1 "(ts" FRAME_STATE_TYPE_STRING_V1 "u)"
+#define BACK_FORWARD_LIST_ITEM_FORMAT_STRING_V1 "(t&s@" FRAME_STATE_TYPE_STRING_V1 "u)"
+#define SESSION_STATE_TYPE_STRING_V1 "(qa" BACK_FORWARD_LIST_ITEM_TYPE_STRING_V1 "mu)"
+
+// Use our own enum types to ensure the serialized format even if the core enums change.
+enum ExternalURLsPolicy {
+ Allow,
+ AllowExternalSchemes,
+ NotAllow
+};
+
+static inline unsigned toExternalURLsPolicy(WebCore::ShouldOpenExternalURLsPolicy policy)
+{
+ switch (policy) {
+ case WebCore::ShouldOpenExternalURLsPolicy::ShouldAllow:
+ return ExternalURLsPolicy::Allow;
+ case WebCore::ShouldOpenExternalURLsPolicy::ShouldAllowExternalSchemes:
+ return ExternalURLsPolicy::AllowExternalSchemes;
+ case WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow:
+ return ExternalURLsPolicy::NotAllow;
+ }
+
+ return ExternalURLsPolicy::NotAllow;
+}
+
+static inline WebCore::ShouldOpenExternalURLsPolicy toWebCoreExternalURLsPolicy(unsigned policy)
+{
+ switch (policy) {
+ case ExternalURLsPolicy::Allow:
+ return WebCore::ShouldOpenExternalURLsPolicy::ShouldAllow;
+ case ExternalURLsPolicy::AllowExternalSchemes:
+ return WebCore::ShouldOpenExternalURLsPolicy::ShouldAllowExternalSchemes;
+ case ExternalURLsPolicy::NotAllow:
+ return WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow;
+ }
+
+ return WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow;
+}
+
+enum HTMLBodyElementType {
+ Data,
+ File,
+ Blob
+};
+
+static inline unsigned toHTMLBodyElementType(HTTPBody::Element::Type type)
+{
+ switch (type) {
+ case HTTPBody::Element::Type::Data:
+ return HTMLBodyElementType::Data;
+ case HTTPBody::Element::Type::File:
+ return HTMLBodyElementType::File;
+ case HTTPBody::Element::Type::Blob:
+ return HTMLBodyElementType::Blob;
+ }
+
+ return HTMLBodyElementType::Data;
+}
+
+static inline HTTPBody::Element::Type toHTTPBodyElementType(unsigned type)
+{
+ switch (type) {
+ case HTMLBodyElementType::Data:
+ return HTTPBody::Element::Type::Data;
+ case HTMLBodyElementType::File:
+ return HTTPBody::Element::Type::File;
+ case HTMLBodyElementType::Blob:
+ return HTTPBody::Element::Type::Blob;
+ }
+
+ return HTTPBody::Element::Type::Data;
+}
+
+static inline void encodeHTTPBody(GVariantBuilder* sessionBuilder, const HTTPBody& httpBody)
+{
+ g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE("(sa" HTTP_BODY_ELEMENT_TYPE_STRING_V1 ")"));
+ g_variant_builder_add(sessionBuilder, "s", httpBody.contentType.utf8().data());
+ g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE("a" HTTP_BODY_ELEMENT_TYPE_STRING_V1));
+ g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE(HTTP_BODY_ELEMENT_TYPE_STRING_V1));
+ for (const auto& element : httpBody.elements) {
+ g_variant_builder_add(sessionBuilder, "u", toHTMLBodyElementType(element.type));
+ g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE("ay"));
+ for (auto item : element.data)
+ g_variant_builder_add(sessionBuilder, "y", item);
+ g_variant_builder_close(sessionBuilder);
+ g_variant_builder_add(sessionBuilder, "s", element.filePath.utf8().data());
+ g_variant_builder_add(sessionBuilder, "x", element.fileStart);
+ if (element.fileLength)
+ g_variant_builder_add(sessionBuilder, "mx", TRUE, element.fileLength.value());
+ else
+ g_variant_builder_add(sessionBuilder, "mx", FALSE);
+ if (element.expectedFileModificationTime)
+ g_variant_builder_add(sessionBuilder, "md", TRUE, element.expectedFileModificationTime.value());
+ else
+ g_variant_builder_add(sessionBuilder, "md", FALSE);
+ g_variant_builder_add(sessionBuilder, "s", element.blobURLString.utf8().data());
+ }
+ g_variant_builder_close(sessionBuilder);
+ g_variant_builder_close(sessionBuilder);
+ g_variant_builder_close(sessionBuilder);
+}
+
+static inline void encodeFrameState(GVariantBuilder* sessionBuilder, const FrameState& frameState)
+{
+ g_variant_builder_add(sessionBuilder, "s", frameState.urlString.utf8().data());
+ g_variant_builder_add(sessionBuilder, "s", frameState.originalURLString.utf8().data());
+ g_variant_builder_add(sessionBuilder, "s", frameState.referrer.utf8().data());
+ g_variant_builder_add(sessionBuilder, "s", frameState.target.utf8().data());
+ g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE("as"));
+ for (const auto& state : frameState.documentState)
+ g_variant_builder_add(sessionBuilder, "s", state.utf8().data());
+ g_variant_builder_close(sessionBuilder);
+ if (!frameState.stateObjectData)
+ g_variant_builder_add(sessionBuilder, "may", FALSE);
+ else {
+ g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE("may"));
+ g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE("ay"));
+ for (auto item : frameState.stateObjectData.value())
+ g_variant_builder_add(sessionBuilder, "y", item);
+ g_variant_builder_close(sessionBuilder);
+ g_variant_builder_close(sessionBuilder);
+ }
+ g_variant_builder_add(sessionBuilder, "x", frameState.documentSequenceNumber);
+ g_variant_builder_add(sessionBuilder, "x", frameState.itemSequenceNumber);
+ g_variant_builder_add(sessionBuilder, "(ii)", frameState.scrollPosition.x(), frameState.scrollPosition.y());
+ g_variant_builder_add(sessionBuilder, "d", frameState.pageScaleFactor);
+ if (!frameState.httpBody)
+ g_variant_builder_add(sessionBuilder, HTTP_BODY_TYPE_STRING_V1, FALSE);
+ else {
+ g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE(HTTP_BODY_TYPE_STRING_V1));
+ encodeHTTPBody(sessionBuilder, frameState.httpBody.value());
+ g_variant_builder_close(sessionBuilder);
+ }
+ g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE("av"));
+ for (const auto& child : frameState.children) {
+ GVariantBuilder frameStateBuilder;
+ g_variant_builder_init(&frameStateBuilder, G_VARIANT_TYPE(FRAME_STATE_TYPE_STRING_V1));
+ encodeFrameState(&frameStateBuilder, child);
+ g_variant_builder_add(sessionBuilder, "v", g_variant_builder_end(&frameStateBuilder));
+ }
+ g_variant_builder_close(sessionBuilder);
+}
+
+static inline void encodePageState(GVariantBuilder* sessionBuilder, const PageState& pageState)
+{
+ g_variant_builder_add(sessionBuilder, "s", pageState.title.utf8().data());
+ g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE(FRAME_STATE_TYPE_STRING_V1));
+ encodeFrameState(sessionBuilder, pageState.mainFrameState);
+ g_variant_builder_close(sessionBuilder);
+ g_variant_builder_add(sessionBuilder, "u", toExternalURLsPolicy(pageState.shouldOpenExternalURLsPolicy));
+}
+
+static inline void encodeBackForwardListItemState(GVariantBuilder* sessionBuilder, const BackForwardListItemState& item)
+{
+ g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE(BACK_FORWARD_LIST_ITEM_TYPE_STRING_V1));
+ g_variant_builder_add(sessionBuilder, "t", item.identifier);
+ encodePageState(sessionBuilder, item.pageState);
+ g_variant_builder_close(sessionBuilder);
+}
+
+static inline void encodeBackForwardListState(GVariantBuilder* sessionBuilder, const BackForwardListState& backForwardListState)
+{
+ g_variant_builder_open(sessionBuilder, G_VARIANT_TYPE("a" BACK_FORWARD_LIST_ITEM_TYPE_STRING_V1));
+ for (const auto& item : backForwardListState.items)
+ encodeBackForwardListItemState(sessionBuilder, item);
+ g_variant_builder_close(sessionBuilder);
+
+ if (backForwardListState.currentIndex)
+ g_variant_builder_add(sessionBuilder, "mu", TRUE, backForwardListState.currentIndex.value());
+ else
+ g_variant_builder_add(sessionBuilder, "mu", FALSE);
+}
+
+static GBytes* encodeSessionState(const SessionState& sessionState)
+{
+ GVariantBuilder sessionBuilder;
+ g_variant_builder_init(&sessionBuilder, G_VARIANT_TYPE(SESSION_STATE_TYPE_STRING_V1));
+ g_variant_builder_add(&sessionBuilder, "q", g_sessionStateVersion);
+ encodeBackForwardListState(&sessionBuilder, sessionState.backForwardListState);
+ GRefPtr<GVariant> variant = g_variant_builder_end(&sessionBuilder);
+ return g_variant_get_data_as_bytes(variant.get());
+}
+
+static inline bool decodeHTTPBody(GVariant* httpBodyVariant, HTTPBody& httpBody)
+{
+ gboolean hasHTTPBody;
+ const char* contentType;
+ GUniqueOutPtr<GVariantIter> elementsIter;
+ g_variant_get(httpBodyVariant, HTTP_BODY_FORMAT_STRING_V1, &hasHTTPBody, &contentType, &elementsIter.outPtr());
+ if (!hasHTTPBody)
+ return false;
+ httpBody.contentType = String::fromUTF8(contentType);
+ gsize elementsLength = g_variant_iter_n_children(elementsIter.get());
+ if (!elementsLength)
+ return true;
+ httpBody.elements.reserveInitialCapacity(elementsLength);
+ unsigned type;
+ GVariantIter* dataIter;
+ const char* filePath;
+ gint64 fileStart;
+ gboolean hasFileLength;
+ gint64 fileLength;
+ gboolean hasFileModificationTime;
+ gdouble fileModificationTime;
+ const char* blobURLString;
+ while (g_variant_iter_loop(elementsIter.get(), HTTP_BODY_ELEMENT_FORMAT_STRING_V1, &type, &dataIter, &filePath, &fileStart, &hasFileLength, &fileLength, &hasFileModificationTime, &fileModificationTime, &blobURLString)) {
+ HTTPBody::Element element;
+ element.type = toHTTPBodyElementType(type);
+ if (gsize dataLength = g_variant_iter_n_children(dataIter)) {
+ element.data.reserveInitialCapacity(dataLength);
+ guchar dataValue;
+ while (g_variant_iter_next(dataIter, "y", &dataValue))
+ element.data.uncheckedAppend(dataValue);
+ }
+ element.filePath = String::fromUTF8(filePath);
+ element.fileStart = fileStart;
+ if (hasFileLength)
+ element.fileLength = fileLength;
+ if (hasFileModificationTime)
+ element.expectedFileModificationTime = fileModificationTime;
+ element.blobURLString = String::fromUTF8(blobURLString);
+
+ httpBody.elements.uncheckedAppend(WTFMove(element));
+ }
+
+ return true;
+}
+
+static inline void decodeFrameState(GVariant* frameStateVariant, FrameState& frameState)
+{
+ const char* urlString;
+ const char* originalURLString;
+ const char* referrer;
+ const char* target;
+ GUniqueOutPtr<GVariantIter> documentStateIter;
+ GUniqueOutPtr<GVariantIter> stateObjectDataIter;
+ gint64 documentSequenceNumber;
+ gint64 itemSequenceNumber;
+ gint32 scrollPositionX, scrollPositionY;
+ gdouble pageScaleFactor;
+ GVariant* httpBodyVariant;
+ GUniqueOutPtr<GVariantIter> childrenIter;
+ g_variant_get(frameStateVariant, FRAME_STATE_FORMAT_STRING_V1, &urlString, &originalURLString, &referrer, &target,
+ &documentStateIter.outPtr(), &stateObjectDataIter.outPtr(), &documentSequenceNumber, &itemSequenceNumber,
+ &scrollPositionX, &scrollPositionY, &pageScaleFactor, &httpBodyVariant, &childrenIter.outPtr());
+ frameState.urlString = String::fromUTF8(urlString);
+ frameState.originalURLString = String::fromUTF8(originalURLString);
+ // frameState.referrer must not be an empty string since we never want to
+ // send an empty Referer header. Bug #159606.
+ if (strlen(referrer))
+ frameState.referrer = String::fromUTF8(referrer);
+ frameState.target = String::fromUTF8(target);
+ if (gsize documentStateLength = g_variant_iter_n_children(documentStateIter.get())) {
+ frameState.documentState.reserveInitialCapacity(documentStateLength);
+ const char* documentStateString;
+ while (g_variant_iter_next(documentStateIter.get(), "&s", &documentStateString))
+ frameState.documentState.uncheckedAppend(String::fromUTF8(documentStateString));
+ }
+ if (stateObjectDataIter) {
+ Vector<uint8_t> stateObjectVector;
+ if (gsize stateObjectDataLength = g_variant_iter_n_children(stateObjectDataIter.get())) {
+ stateObjectVector.reserveInitialCapacity(stateObjectDataLength);
+ guchar stateObjectDataValue;
+ while (g_variant_iter_next(stateObjectDataIter.get(), "y", &stateObjectDataValue))
+ stateObjectVector.uncheckedAppend(stateObjectDataValue);
+ }
+ frameState.stateObjectData = WTFMove(stateObjectVector);
+ }
+ frameState.documentSequenceNumber = documentSequenceNumber;
+ frameState.itemSequenceNumber = itemSequenceNumber;
+ frameState.scrollPosition.setX(scrollPositionX);
+ frameState.scrollPosition.setY(scrollPositionY);
+ frameState.pageScaleFactor = pageScaleFactor;
+ HTTPBody httpBody;
+ if (decodeHTTPBody(httpBodyVariant, httpBody))
+ frameState.httpBody = WTFMove(httpBody);
+ g_variant_unref(httpBodyVariant);
+ while (GRefPtr<GVariant> child = adoptGRef(g_variant_iter_next_value(childrenIter.get()))) {
+ FrameState childFrameState;
+ GRefPtr<GVariant> childVariant = adoptGRef(g_variant_get_variant(child.get()));
+ decodeFrameState(childVariant.get(), childFrameState);
+ frameState.children.append(WTFMove(childFrameState));
+ }
+}
+
+static inline void decodeBackForwardListItemState(GVariantIter* backForwardListStateIter, BackForwardListState& backForwardListState)
+{
+ gsize backForwardListStateLength = g_variant_iter_n_children(backForwardListStateIter);
+ if (!backForwardListStateLength)
+ return;
+
+ backForwardListState.items.reserveInitialCapacity(backForwardListStateLength);
+ guint64 identifier;
+ const char* title;
+ GVariant* frameStateVariant;
+ unsigned shouldOpenExternalURLsPolicy;
+ while (g_variant_iter_loop(backForwardListStateIter, BACK_FORWARD_LIST_ITEM_FORMAT_STRING_V1, &identifier, &title, &frameStateVariant, &shouldOpenExternalURLsPolicy)) {
+ BackForwardListItemState state;
+ state.identifier = identifier;
+ state.pageState.title = String::fromUTF8(title);
+ decodeFrameState(frameStateVariant, state.pageState.mainFrameState);
+ state.pageState.shouldOpenExternalURLsPolicy = toWebCoreExternalURLsPolicy(shouldOpenExternalURLsPolicy);
+ backForwardListState.items.uncheckedAppend(WTFMove(state));
+ }
+}
+
+static bool decodeSessionState(GBytes* data, SessionState& sessionState)
+{
+ GRefPtr<GVariant> variant = g_variant_new_from_bytes(G_VARIANT_TYPE(SESSION_STATE_TYPE_STRING_V1), data, FALSE);
+ if (!g_variant_is_normal_form(variant.get()))
+ return false;
+
+ guint16 version;
+ GUniqueOutPtr<GVariantIter> backForwardListStateIter;
+ gboolean hasCurrentIndex;
+ guint32 currentIndex;
+ g_variant_get(variant.get(), SESSION_STATE_TYPE_STRING_V1, &version, &backForwardListStateIter.outPtr(), &hasCurrentIndex, &currentIndex);
+ if (!version || version > g_sessionStateVersion)
+ return false;
+
+ decodeBackForwardListItemState(backForwardListStateIter.get(), sessionState.backForwardListState);
+
+ if (hasCurrentIndex)
+ sessionState.backForwardListState.currentIndex = currentIndex;
+ return true;
+}
+
+WebKitWebViewSessionState* webkitWebViewSessionStateCreate(SessionState&& sessionState)
+{
+ WebKitWebViewSessionState* state = static_cast<WebKitWebViewSessionState*>(fastMalloc(sizeof(WebKitWebViewSessionState)));
+ new (state) WebKitWebViewSessionState(WTFMove(sessionState));
+ return state;
+}
+
+const SessionState& webkitWebViewSessionStateGetSessionState(WebKitWebViewSessionState* state)
+{
+ return state->sessionState;
+}
+
+/**
+ * webkit_web_view_session_state_new:
+ * @data: a #GBytes
+ *
+ * Creates a new #WebKitWebViewSessionState from serialized data.
+ *
+ * Returns: (transfer full): a new #WebKitWebViewSessionState, or %NULL if @data doesn't contain a
+ * valid serialized #WebKitWebViewSessionState.
+ *
+ * Since: 2.12
+ */
+WebKitWebViewSessionState* webkit_web_view_session_state_new(GBytes* data)
+{
+ g_return_val_if_fail(data, nullptr);
+
+ SessionState sessionState;
+ if (!decodeSessionState(data, sessionState))
+ return nullptr;
+ return webkitWebViewSessionStateCreate(WTFMove(sessionState));
+}
+
+/**
+ * webkit_web_view_session_state_ref:
+ * @state: a #WebKitWebViewSessionState
+ *
+ * Atomically increments the reference count of @state by one. This
+ * function is MT-safe and may be called from any thread.
+ *
+ * Returns: The passed in #WebKitWebViewSessionState
+ *
+ * Since: 2.12
+ */
+WebKitWebViewSessionState* webkit_web_view_session_state_ref(WebKitWebViewSessionState* state)
+{
+ g_return_val_if_fail(state, nullptr);
+ g_atomic_int_inc(&state->referenceCount);
+ return state;
+}
+
+/**
+ * webkit_web_view_session_state_unref:
+ * @state: a #WebKitWebViewSessionState
+ *
+ * Atomically decrements the reference count of @state by one. If the
+ * reference count drops to 0, all memory allocated by the #WebKitWebViewSessionState is
+ * released. This function is MT-safe and may be called from any thread.
+ *
+ * Since: 2.12
+ */
+void webkit_web_view_session_state_unref(WebKitWebViewSessionState* state)
+{
+ g_return_if_fail(state);
+ if (g_atomic_int_dec_and_test(&state->referenceCount)) {
+ state->~WebKitWebViewSessionState();
+ fastFree(state);
+ }
+}
+
+/**
+ * webkit_web_view_session_state_serialize:
+ * @state: a #WebKitWebViewSessionState
+ *
+ * Serializes a #WebKitWebViewSessionState.
+ *
+ * Returns: (transfer full): a #GBytes containing the @state serialized.
+ *
+ * Since: 2.12
+ */
+GBytes* webkit_web_view_session_state_serialize(WebKitWebViewSessionState* state)
+{
+ g_return_val_if_fail(state, nullptr);
+
+ return encodeSessionState(state->sessionState);
+}