[GTK][WPE] Add user messages API
https://bugs.webkit.org/show_bug.cgi?id=202847

Reviewed by Adrian Perez de Castro.

Source/WebCore/platform/gtk/po:

* POTFILES.in: Add new files containing translatable strings.

Source/WebKit:

We have never exposed an API to send/receive messages to/from Web extensions, to allow applications use their own
IPC. Now, with PSON enabled, it's a lot more difficult to implement the custom IPC on the application side,
because applications need to keep track of all the web processes launched, and the changes of web process in the
web view to send the messages to the right extension. That's already done internally by WebKit, so it would be
easier to provide a simple API so that apps don't need to worry about the web process being used. This patch adds
WebKitUserMessage, a simple message API consisting on a message name, parameters and file descriptors. Messages
can be sent from a WebKitWebContext to all the WebKitWebExtensions, or from a WebKitWebExtension to its
WebKitWebContext, or from a WebKitWebView to its WebKitWebPage (and vice versa).

* PlatformGTK.cmake:
* PlatformWPE.cmake:
* Shared/API/glib/WebKitUserMessage.cpp: Added.
(webkitUserMessageDispose):
(webkitUserMessageGetProperty):
(webkitUserMessageSetProperty):
(webkit_user_message_class_init):
(webkitUserMessageCreate):
(webkitUserMessageGetMessage):
(webkit_user_message_new):
(webkit_user_message_new_with_fd_list):
(webkit_user_message_get_name):
(webkit_user_message_get_parameters):
(webkit_user_message_get_fd_list):
(webkit_user_message_send_reply):
* Shared/API/glib/WebKitUserMessagePrivate.h: Added.
* Shared/glib/ArgumentCodersGLib.cpp: Added.
(IPC::encode):
(IPC::decode):
* Shared/glib/ArgumentCodersGLib.h: Added.
* Shared/glib/UserMessage.cpp: Added.
(WebKit::UserMessage::encode const):
(WebKit::UserMessage::decode):
* Shared/glib/UserMessage.h: Added.
(WebKit::UserMessage::UserMessage):
* SourcesGTK.txt:
* SourcesWPE.txt:
* UIProcess/API/glib/WebKitWebContext.cpp:
(webkitWebContextConstructed):
(webkit_web_context_class_init):
(webkit_web_context_send_message_to_all_extensions):
* UIProcess/API/glib/WebKitWebView.cpp:
(webkit_web_view_class_init):
(webkitWebViewDidReceiveUserMessage):
(webkit_web_view_send_message_to_page):
(webkit_web_view_send_message_to_page_finish):
* UIProcess/API/glib/WebKitWebViewPrivate.h:
* UIProcess/API/gtk/WebKitUserMessage.h: Added.
* UIProcess/API/gtk/WebKitWebContext.h:
* UIProcess/API/gtk/WebKitWebView.h:
* UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt:
* UIProcess/API/gtk/docs/webkit2gtk-docs.sgml:
* UIProcess/API/gtk/webkit2.h:
* UIProcess/API/wpe/APIViewClient.h:
(API::ViewClient::didReceiveUserMessage):
* UIProcess/API/wpe/PageClientImpl.cpp:
(WebKit::PageClientImpl::sendMessageToWebView):
* UIProcess/API/wpe/PageClientImpl.h:
* UIProcess/API/wpe/WPEView.cpp:
(WKWPE::View::didReceiveUserMessage):
* UIProcess/API/wpe/WPEView.h:
* UIProcess/API/wpe/WebKitUserMessage.h: Added.
* UIProcess/API/wpe/WebKitWebContext.h:
* UIProcess/API/wpe/WebKitWebView.h:
* UIProcess/API/wpe/docs/wpe-1.0-sections.txt:
* UIProcess/API/wpe/docs/wpe-docs.sgml:
* UIProcess/API/wpe/webkit.h:
* UIProcess/WebPageProxy.h:
* UIProcess/WebPageProxy.messages.in:
* UIProcess/WebProcessPool.h:
* UIProcess/WebProcessProxy.h:
* UIProcess/WebProcessProxy.messages.in:
* UIProcess/glib/WebProcessProxyGLib.cpp:
(WebKit::WebProcessProxy::sendMessageToWebContextWithReply):
(WebKit::WebProcessProxy::sendMessageToWebContext):
* UIProcess/gtk/WebPageProxyGtk.cpp:
(WebKit::WebPageProxy::sendMessageToWebViewWithReply):
(WebKit::WebPageProxy::sendMessageToWebView):
* UIProcess/wpe/WebPageProxyWPE.cpp:
(WebKit::WebPageProxy::sendMessageToWebViewWithReply):
(WebKit::WebPageProxy::sendMessageToWebView):
* WebProcess/InjectedBundle/API/glib/WebKitExtensionManager.h:
(WebKit::WebKitExtensionManager::extension const):
* WebProcess/InjectedBundle/API/glib/WebKitWebExtension.cpp:
(webkitWebExtensionDidReceiveUserMessage):
(webkit_web_extension_send_message_to_context):
(webkit_web_extension_send_message_to_context_finish):
* WebProcess/InjectedBundle/API/glib/WebKitWebExtensionPrivate.h:
* WebProcess/InjectedBundle/API/glib/WebKitWebPage.cpp:
(webkit_web_page_class_init):
(webkitWebPageDidReceiveUserMessage):
(webkit_web_page_send_message_to_view):
(webkit_web_page_send_message_to_view_finish):
* WebProcess/InjectedBundle/API/glib/WebKitWebPagePrivate.h:
* WebProcess/InjectedBundle/API/gtk/WebKitWebExtension.h:
* WebProcess/InjectedBundle/API/gtk/WebKitWebPage.h:
* WebProcess/InjectedBundle/API/gtk/webkit-web-extension.h:
* WebProcess/InjectedBundle/API/wpe/WebKitWebExtension.h:
* WebProcess/InjectedBundle/API/wpe/WebKitWebPage.h:
* WebProcess/InjectedBundle/API/wpe/docs/wpe-webextensions-1.0-sections.txt:
* WebProcess/InjectedBundle/API/wpe/webkit-web-extension.h:
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/WebPage.messages.in:
* WebProcess/WebPage/glib/WebPageGLib.cpp: Added.
(WebKit::WebPage::sendMessageToWebExtensionWithReply):
(WebKit::WebPage::sendMessageToWebExtension):
* WebProcess/WebProcess.h:
* WebProcess/WebProcess.messages.in:
* WebProcess/glib/WebProcessGLib.cpp:
(WebKit::WebProcess::sendMessageToWebExtension):

Tools:

Add a test to check the new API.

* TestWebKitAPI/Tests/WebKitGLib/TestWebExtensions.cpp:
(UserMessageTest::webViewUserMessageReceivedCallback):
(UserMessageTest::webContextUserMessageReceivedCallback):
(UserMessageTest::UserMessageTest):
(UserMessageTest::~UserMessageTest):
(UserMessageTest::sendMessage):
(UserMessageTest::sendMedssageToAllExtensions):
(UserMessageTest::viewUserMessageReceived):
(UserMessageTest::contextUserMessageReceived):
(UserMessageTest::waitUntilViewMessageReceived):
(UserMessageTest::waitUntilContextMessageReceived):
(readFileDescritpor):
(testWebExtensionUserMessages):
(beforeAll):
* TestWebKitAPI/Tests/WebKitGLib/WebExtensionTest.cpp:
(documentLoadedCallback):
(pageMessageReceivedCallback):
(pageCreatedCallback):
(extensionMessageReceivedCallback):
(webkit_web_extension_initialize_with_user_data):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251181 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/platform/gtk/po/ChangeLog b/Source/WebCore/platform/gtk/po/ChangeLog
index 2c3de5d..d262ed1 100644
--- a/Source/WebCore/platform/gtk/po/ChangeLog
+++ b/Source/WebCore/platform/gtk/po/ChangeLog
@@ -1,3 +1,12 @@
+2019-10-15  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GTK][WPE] Add user messages API
+        https://bugs.webkit.org/show_bug.cgi?id=202847
+
+        Reviewed by Adrian Perez de Castro.
+
+        * POTFILES.in: Add new files containing translatable strings.
+
 2019-09-07  Christian Kirbach  <Christian.Kirbach@googlemail.com>
 
         Updated German translation
diff --git a/Source/WebCore/platform/gtk/po/POTFILES.in b/Source/WebCore/platform/gtk/po/POTFILES.in
index b54d21d..4cebd95 100644
--- a/Source/WebCore/platform/gtk/po/POTFILES.in
+++ b/Source/WebCore/platform/gtk/po/POTFILES.in
@@ -7,6 +7,7 @@
 ../../../WebKit/Shared/API/glib/WebKitHitTestResult.cpp
 ../../../WebKit/Shared/API/glib/WebKitURIRequest.cpp
 ../../../WebKit/Shared/API/glib/WebKitURIResponse.cpp
+../../../WebKit/Shared/API/glib/WebKitUserMessage.cpp
 ../../../WebKit/Shared/WebErrors.cpp
 ../../../WebKit/Shared/gtk/WebErrorsGtk.cpp
 ../../../WebKit/Shared/soup/WebErrorsSoup.cpp
@@ -43,4 +44,5 @@
 ../../../WebKit/UIProcess/gtk/WebKitInspectorWindow.cpp
 ../../../WebKit/UIProcess/WebsiteData/WebsiteDataRecord.cpp
 ../../../WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebHitTestResult.cpp
+../../../WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebExtension.cpp
 ../../../WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebPage.cpp
diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog
index 57dfa2f..9afb8ee 100644
--- a/Source/WebKit/ChangeLog
+++ b/Source/WebKit/ChangeLog
@@ -1,3 +1,120 @@
+2019-10-15  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GTK][WPE] Add user messages API
+        https://bugs.webkit.org/show_bug.cgi?id=202847
+
+        Reviewed by Adrian Perez de Castro.
+
+        We have never exposed an API to send/receive messages to/from Web extensions, to allow applications use their own
+        IPC. Now, with PSON enabled, it's a lot more difficult to implement the custom IPC on the application side,
+        because applications need to keep track of all the web processes launched, and the changes of web process in the
+        web view to send the messages to the right extension. That's already done internally by WebKit, so it would be
+        easier to provide a simple API so that apps don't need to worry about the web process being used. This patch adds
+        WebKitUserMessage, a simple message API consisting on a message name, parameters and file descriptors. Messages
+        can be sent from a WebKitWebContext to all the WebKitWebExtensions, or from a WebKitWebExtension to its
+        WebKitWebContext, or from a WebKitWebView to its WebKitWebPage (and vice versa).
+
+        * PlatformGTK.cmake:
+        * PlatformWPE.cmake:
+        * Shared/API/glib/WebKitUserMessage.cpp: Added.
+        (webkitUserMessageDispose):
+        (webkitUserMessageGetProperty):
+        (webkitUserMessageSetProperty):
+        (webkit_user_message_class_init):
+        (webkitUserMessageCreate):
+        (webkitUserMessageGetMessage):
+        (webkit_user_message_new):
+        (webkit_user_message_new_with_fd_list):
+        (webkit_user_message_get_name):
+        (webkit_user_message_get_parameters):
+        (webkit_user_message_get_fd_list):
+        (webkit_user_message_send_reply):
+        * Shared/API/glib/WebKitUserMessagePrivate.h: Added.
+        * Shared/glib/ArgumentCodersGLib.cpp: Added.
+        (IPC::encode):
+        (IPC::decode):
+        * Shared/glib/ArgumentCodersGLib.h: Added.
+        * Shared/glib/UserMessage.cpp: Added.
+        (WebKit::UserMessage::encode const):
+        (WebKit::UserMessage::decode):
+        * Shared/glib/UserMessage.h: Added.
+        (WebKit::UserMessage::UserMessage):
+        * SourcesGTK.txt:
+        * SourcesWPE.txt:
+        * UIProcess/API/glib/WebKitWebContext.cpp:
+        (webkitWebContextConstructed):
+        (webkit_web_context_class_init):
+        (webkit_web_context_send_message_to_all_extensions):
+        * UIProcess/API/glib/WebKitWebView.cpp:
+        (webkit_web_view_class_init):
+        (webkitWebViewDidReceiveUserMessage):
+        (webkit_web_view_send_message_to_page):
+        (webkit_web_view_send_message_to_page_finish):
+        * UIProcess/API/glib/WebKitWebViewPrivate.h:
+        * UIProcess/API/gtk/WebKitUserMessage.h: Added.
+        * UIProcess/API/gtk/WebKitWebContext.h:
+        * UIProcess/API/gtk/WebKitWebView.h:
+        * UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt:
+        * UIProcess/API/gtk/docs/webkit2gtk-docs.sgml:
+        * UIProcess/API/gtk/webkit2.h:
+        * UIProcess/API/wpe/APIViewClient.h:
+        (API::ViewClient::didReceiveUserMessage):
+        * UIProcess/API/wpe/PageClientImpl.cpp:
+        (WebKit::PageClientImpl::sendMessageToWebView):
+        * UIProcess/API/wpe/PageClientImpl.h:
+        * UIProcess/API/wpe/WPEView.cpp:
+        (WKWPE::View::didReceiveUserMessage):
+        * UIProcess/API/wpe/WPEView.h:
+        * UIProcess/API/wpe/WebKitUserMessage.h: Added.
+        * UIProcess/API/wpe/WebKitWebContext.h:
+        * UIProcess/API/wpe/WebKitWebView.h:
+        * UIProcess/API/wpe/docs/wpe-1.0-sections.txt:
+        * UIProcess/API/wpe/docs/wpe-docs.sgml:
+        * UIProcess/API/wpe/webkit.h:
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebPageProxy.messages.in:
+        * UIProcess/WebProcessPool.h:
+        * UIProcess/WebProcessProxy.h:
+        * UIProcess/WebProcessProxy.messages.in:
+        * UIProcess/glib/WebProcessProxyGLib.cpp:
+        (WebKit::WebProcessProxy::sendMessageToWebContextWithReply):
+        (WebKit::WebProcessProxy::sendMessageToWebContext):
+        * UIProcess/gtk/WebPageProxyGtk.cpp:
+        (WebKit::WebPageProxy::sendMessageToWebViewWithReply):
+        (WebKit::WebPageProxy::sendMessageToWebView):
+        * UIProcess/wpe/WebPageProxyWPE.cpp:
+        (WebKit::WebPageProxy::sendMessageToWebViewWithReply):
+        (WebKit::WebPageProxy::sendMessageToWebView):
+        * WebProcess/InjectedBundle/API/glib/WebKitExtensionManager.h:
+        (WebKit::WebKitExtensionManager::extension const):
+        * WebProcess/InjectedBundle/API/glib/WebKitWebExtension.cpp:
+        (webkitWebExtensionDidReceiveUserMessage):
+        (webkit_web_extension_send_message_to_context):
+        (webkit_web_extension_send_message_to_context_finish):
+        * WebProcess/InjectedBundle/API/glib/WebKitWebExtensionPrivate.h:
+        * WebProcess/InjectedBundle/API/glib/WebKitWebPage.cpp:
+        (webkit_web_page_class_init):
+        (webkitWebPageDidReceiveUserMessage):
+        (webkit_web_page_send_message_to_view):
+        (webkit_web_page_send_message_to_view_finish):
+        * WebProcess/InjectedBundle/API/glib/WebKitWebPagePrivate.h:
+        * WebProcess/InjectedBundle/API/gtk/WebKitWebExtension.h:
+        * WebProcess/InjectedBundle/API/gtk/WebKitWebPage.h:
+        * WebProcess/InjectedBundle/API/gtk/webkit-web-extension.h:
+        * WebProcess/InjectedBundle/API/wpe/WebKitWebExtension.h:
+        * WebProcess/InjectedBundle/API/wpe/WebKitWebPage.h:
+        * WebProcess/InjectedBundle/API/wpe/docs/wpe-webextensions-1.0-sections.txt:
+        * WebProcess/InjectedBundle/API/wpe/webkit-web-extension.h:
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/WebPage.messages.in:
+        * WebProcess/WebPage/glib/WebPageGLib.cpp: Added.
+        (WebKit::WebPage::sendMessageToWebExtensionWithReply):
+        (WebKit::WebPage::sendMessageToWebExtension):
+        * WebProcess/WebProcess.h:
+        * WebProcess/WebProcess.messages.in:
+        * WebProcess/glib/WebProcessGLib.cpp:
+        (WebKit::WebProcess::sendMessageToWebExtension):
+
 2019-10-15  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Fix the internal macOS build after r251171
diff --git a/Source/WebKit/PlatformGTK.cmake b/Source/WebKit/PlatformGTK.cmake
index aa7655b..dfe1c52 100644
--- a/Source/WebKit/PlatformGTK.cmake
+++ b/Source/WebKit/PlatformGTK.cmake
@@ -109,6 +109,7 @@
     ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitUserContentFilterStore.h
     ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitUserContentManager.h
     ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitUserMediaPermissionRequest.h
+    ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitUserMessage.h
     ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitWebContext.h
     ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitWebInspector.h
     ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitWebResource.h
@@ -694,12 +695,14 @@
             ${WEBKIT_DIR}/Shared/API/glib/WebKitContextMenu.cpp
             ${WEBKIT_DIR}/Shared/API/glib/WebKitContextMenuItem.cpp
             ${WEBKIT_DIR}/Shared/API/glib/WebKitHitTestResult.cpp
+            ${WEBKIT_DIR}/Shared/API/glib/WebKitUserMessage.cpp
             ${WEBKIT_DIR}/Shared/API/glib/WebKitURIRequest.cpp
             ${WEBKIT_DIR}/Shared/API/glib/WebKitURIResponse.cpp
             ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitContextMenu.h
             ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitContextMenuActions.h
             ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitContextMenuItem.h
             ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitHitTestResult.h
+            ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitUserMessage.h
             ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitURIRequest.h
             ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitURIResponse.h
             ${WEBKIT_DIR}/WebProcess/InjectedBundle/API/glib/*.cpp
diff --git a/Source/WebKit/PlatformWPE.cmake b/Source/WebKit/PlatformWPE.cmake
index 8977421..73fb6a1 100644
--- a/Source/WebKit/PlatformWPE.cmake
+++ b/Source/WebKit/PlatformWPE.cmake
@@ -134,6 +134,7 @@
     ${WEBKIT_DIR}/UIProcess/API/wpe/WebKitUserContentFilterStore.h
     ${WEBKIT_DIR}/UIProcess/API/wpe/WebKitUserContentManager.h
     ${WEBKIT_DIR}/UIProcess/API/wpe/WebKitUserMediaPermissionRequest.h
+    ${WEBKIT_DIR}/UIProcess/API/wpe/WebKitUserMessage.h
     ${WEBKIT_DIR}/UIProcess/API/wpe/WebKitWebContext.h
     ${WEBKIT_DIR}/UIProcess/API/wpe/WebKitWebResource.h
     ${WEBKIT_DIR}/UIProcess/API/wpe/WebKitWebView.h
diff --git a/Source/WebKit/Shared/API/glib/WebKitUserMessage.cpp b/Source/WebKit/Shared/API/glib/WebKitUserMessage.cpp
new file mode 100644
index 0000000..760279f
--- /dev/null
+++ b/Source/WebKit/Shared/API/glib/WebKitUserMessage.cpp
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2019 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 "WebKitUserMessage.h"
+
+#include "WebKitUserMessagePrivate.h"
+#include <gio/gunixfdlist.h>
+#include <glib/gi18n-lib.h>
+#include <wtf/CompletionHandler.h>
+#include <wtf/glib/WTFGType.h>
+
+using namespace WebKit;
+
+enum {
+    PROP_0,
+
+    PROP_NAME,
+    PROP_PARAMETERS,
+    PROP_FD_LIST
+};
+
+/**
+ * SECTION: WebKitUserMessage
+ * @Short_description: A user message
+ * @Title: WebKitUserMessage
+ * @See_also: #WebKitWebContext, #WebKitWebView, #WebKitWebExtension, #WebKitWebPage
+ *
+ * A WebKitUserMessage is a message that can be used for the communication between the UI process
+ * and web extensions. A WebKitUserMessage always has a name, and it can also include parameters and
+ * UNIX file descriptors. Messages can be sent from a #WebKitWebContext to all #WebKitWebExtension<!-- -->s,
+ * from a #WebKitWebExtension to its corresponding #WebKitWebContext, and from a #WebKitWebView to its
+ * corresponding #WebKitWebPage (and vice versa). One to one messages can be replied to directly with
+ * webkit_user_message_send_reply().
+ *
+ * Since: 2.28
+ */
+struct _WebKitUserMessagePrivate {
+    UserMessage message;
+    CompletionHandler<void(UserMessage&&)> replyHandler;
+};
+
+WEBKIT_DEFINE_TYPE(WebKitUserMessage, webkit_user_message, G_TYPE_INITIALLY_UNOWNED)
+
+G_DEFINE_QUARK(WebKitUserMessageError, webkit_user_message_error)
+
+static void webkitUserMessageDispose(GObject* object)
+{
+    WebKitUserMessage* message = WEBKIT_USER_MESSAGE(object);
+
+    if (auto replyHandler = std::exchange(message->priv->replyHandler, nullptr))
+        replyHandler(UserMessage(message->priv->message.name, WEBKIT_USER_MESSAGE_UNHANDLED_MESSAGE));
+
+    G_OBJECT_CLASS(webkit_user_message_parent_class)->dispose(object);
+}
+
+static void webkitUserMessageGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* paramSpec)
+{
+    WebKitUserMessage* message = WEBKIT_USER_MESSAGE(object);
+
+    switch (propId) {
+    case PROP_NAME:
+        g_value_set_string(value, webkit_user_message_get_name(message));
+        break;
+    case PROP_PARAMETERS:
+        g_value_set_variant(value, webkit_user_message_get_parameters(message));
+        break;
+    case PROP_FD_LIST:
+        g_value_set_object(value, webkit_user_message_get_fd_list(message));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec);
+    }
+}
+
+static void webkitUserMessageSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* paramSpec)
+{
+    WebKitUserMessage* message = WEBKIT_USER_MESSAGE(object);
+
+    switch (propId) {
+    case PROP_NAME:
+        message->priv->message.name = g_value_get_string(value);
+        message->priv->message.type = UserMessage::Type::Message;
+        break;
+    case PROP_PARAMETERS:
+        message->priv->message.parameters = g_value_get_variant(value);
+        break;
+    case PROP_FD_LIST:
+        message->priv->message.fileDescriptors = G_UNIX_FD_LIST(g_value_get_object(value));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec);
+    }
+}
+
+static void webkit_user_message_class_init(WebKitUserMessageClass* klass)
+{
+    GObjectClass* gObjectClass = G_OBJECT_CLASS(klass);
+
+    gObjectClass->dispose = webkitUserMessageDispose;
+    gObjectClass->get_property = webkitUserMessageGetProperty;
+    gObjectClass->set_property = webkitUserMessageSetProperty;
+
+    /**
+     * WebKitUserMessage:name:
+     *
+     * The name of the user message.
+     *
+     * Since: 2.28
+     */
+    g_object_class_install_property(
+        gObjectClass,
+        PROP_NAME,
+        g_param_spec_string(
+            "name",
+            _("Name"),
+            _("The user message name"),
+            nullptr,
+            static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
+
+    /**
+     * WebKitUserMessage:parameters:
+     *
+     * The parameters of the user message as a #GVariant, or %NULL
+     * if the message doesn't include parameters. Note that only complete types are
+     * allowed.
+     *
+     * Since: 2.28
+     */
+    g_object_class_install_property(
+        gObjectClass,
+        PROP_PARAMETERS,
+        g_param_spec_variant(
+            "parameters",
+            _("Parameters"),
+            _("The user message parameters"),
+            G_VARIANT_TYPE_ANY,
+            nullptr,
+            static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
+
+    /**
+     * WebKitUserMessage:fd-list:
+     *
+     * The UNIX file descriptors of the user message.
+     *
+     * Since: 2.28
+     */
+    g_object_class_install_property(
+        gObjectClass,
+        PROP_FD_LIST,
+        g_param_spec_object(
+            "fd-list",
+            _("File Descriptor List"),
+            _("The user message list of file descriptors"),
+            G_TYPE_UNIX_FD_LIST,
+            static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
+}
+
+WebKitUserMessage* webkitUserMessageCreate(UserMessage&& message)
+{
+    WebKitUserMessage* returnValue = WEBKIT_USER_MESSAGE(g_object_new(WEBKIT_TYPE_USER_MESSAGE, nullptr));
+    returnValue->priv->message = WTFMove(message);
+    return returnValue;
+}
+
+WebKitUserMessage* webkitUserMessageCreate(UserMessage&& message, CompletionHandler<void(UserMessage&&)>&& replyHandler)
+{
+    WebKitUserMessage* returnValue = WEBKIT_USER_MESSAGE(g_object_new(WEBKIT_TYPE_USER_MESSAGE, nullptr));
+    returnValue->priv->message = WTFMove(message);
+    returnValue->priv->replyHandler = WTFMove(replyHandler);
+    return returnValue;
+}
+
+UserMessage& webkitUserMessageGetMessage(WebKitUserMessage* message)
+{
+    return message->priv->message;
+}
+
+/**
+ * webkit_user_message_new:
+ * @name: the message name
+ * @parameters: (nullable): the message parameters as a #GVariant, or %NULL
+ *
+ * Create a new #WebKitUserMessage with @name.
+ *
+ * Returns: the newly created #WebKitUserMessage object.
+ *
+ * Since: 2.28
+ */
+WebKitUserMessage* webkit_user_message_new(const char* name, GVariant* parameters)
+{
+    return webkit_user_message_new_with_fd_list(name, parameters, nullptr);
+}
+
+/**
+ * webkit_user_message_new_with_fd_list:
+ * @name: the message name
+ * @parameters: (nullable): the message parameters as a #GVariant
+ * @fd_list: (nullable): the message file descriptors
+ *
+ * Create a new #WebKitUserMessage including also a list of UNIX file descriptors to be sent.
+ *
+ * Returns: the newly created #WebKitUserMessage object.
+ *
+ * Since: 2.28
+ */
+WebKitUserMessage* webkit_user_message_new_with_fd_list(const char* name, GVariant* parameters, GUnixFDList* fdList)
+{
+    g_return_val_if_fail(name, nullptr);
+    g_return_val_if_fail(!fdList || G_IS_UNIX_FD_LIST(fdList), nullptr);
+
+    return WEBKIT_USER_MESSAGE(g_object_new(WEBKIT_TYPE_USER_MESSAGE, "name", name, "parameters", parameters, "fd-list", fdList, nullptr));
+}
+
+/**
+ * webkit_user_message_get_name:
+ * @message: a #WebKitUserMessage
+ *
+ * Get the @message name
+ *
+ * Returns: the message name
+ *
+ * Since: 2.28
+ */
+const char* webkit_user_message_get_name(WebKitUserMessage* message)
+{
+    g_return_val_if_fail(WEBKIT_IS_USER_MESSAGE(message), nullptr);
+
+    return message->priv->message.name.data();
+}
+
+/**
+ * webkit_user_message_get_parameters:
+ * @message: a #WebKitUserMessage
+ *
+ * Get the @message parameters
+ *
+ * Returns: (transfer none): the message parameters
+ *
+ * Since: 2.28
+ */
+GVariant* webkit_user_message_get_parameters(WebKitUserMessage* message)
+{
+    g_return_val_if_fail(WEBKIT_IS_USER_MESSAGE(message), nullptr);
+
+    return message->priv->message.parameters.get();
+}
+
+/**
+ * webkit_user_message_get_fd_list:
+ * @message: a #WebKitUserMessage
+ *
+ * Get the @message list of file descritpor
+ *
+ * Returns: (transfer none): the message list of file descriptors
+ *
+ * Since: 2.28
+ */
+GUnixFDList* webkit_user_message_get_fd_list(WebKitUserMessage* message)
+{
+    g_return_val_if_fail(WEBKIT_IS_USER_MESSAGE(message), nullptr);
+
+    return message->priv->message.fileDescriptors.get();
+}
+
+/**
+ * webkit_user_message_send_reply:
+ * @message: a #WebKitUserMessage
+ * @reply: a #WebKitUserMessage to send as reply
+ *
+ * Send a reply to @message. If @reply is floating, it's consumed.
+ * You can only send a reply to a #WebKitUserMessage that has been
+ * received.
+ *
+ * Since: 2.28
+ */
+void webkit_user_message_send_reply(WebKitUserMessage* message, WebKitUserMessage* reply)
+{
+    g_return_if_fail(WEBKIT_IS_USER_MESSAGE(message));
+    g_return_if_fail(WEBKIT_IS_USER_MESSAGE(reply));
+    g_return_if_fail(message->priv->replyHandler);
+
+    // We sink the reference in case of being floating.
+    GRefPtr<WebKitUserMessage> adoptedReply = reply;
+    if (auto replyHandler = std::exchange(message->priv->replyHandler, nullptr))
+        replyHandler(reply ? reply->priv->message : UserMessage());
+}
diff --git a/Source/WebKit/Shared/API/glib/WebKitUserMessagePrivate.h b/Source/WebKit/Shared/API/glib/WebKitUserMessagePrivate.h
new file mode 100644
index 0000000..bceaa01
--- /dev/null
+++ b/Source/WebKit/Shared/API/glib/WebKitUserMessagePrivate.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include "UserMessage.h"
+#include "WebKitUserMessage.h"
+
+WebKitUserMessage* webkitUserMessageCreate(WebKit::UserMessage&&);
+WebKitUserMessage* webkitUserMessageCreate(WebKit::UserMessage&&, CompletionHandler<void(WebKit::UserMessage&&)>&&);
+WebKit::UserMessage& webkitUserMessageGetMessage(WebKitUserMessage*);
diff --git a/Source/WebKit/Shared/glib/ArgumentCodersGLib.cpp b/Source/WebKit/Shared/glib/ArgumentCodersGLib.cpp
new file mode 100644
index 0000000..bad36d9
--- /dev/null
+++ b/Source/WebKit/Shared/glib/ArgumentCodersGLib.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ArgumentCodersGLib.h"
+
+#include "DataReference.h"
+#include <glib.h>
+#include <wtf/text/CString.h>
+
+namespace IPC {
+
+void encode(Encoder& encoder, GVariant* variant)
+{
+    if (!variant) {
+        encoder << CString();
+        return;
+    }
+
+    encoder << CString(g_variant_get_type_string(variant));
+    encoder << DataReference(static_cast<const uint8_t*>(g_variant_get_data(variant)), g_variant_get_size(variant));
+}
+
+Optional<GRefPtr<GVariant>> decode(Decoder& decoder)
+{
+    CString variantTypeString;
+    if (!decoder.decode(variantTypeString))
+        return WTF::nullopt;
+
+    if (variantTypeString.isNull())
+        return GRefPtr<GVariant>();
+
+    if (!g_variant_type_string_is_valid(variantTypeString.data()))
+        return WTF::nullopt;
+
+    DataReference data;
+    if (!decoder.decode(data))
+        return WTF::nullopt;
+
+    auto* variantType = g_variant_type_new(variantTypeString.data());
+    GRefPtr<GVariant> variant = g_variant_new_from_data(variantType, data.data(), data.size(), FALSE, nullptr, nullptr);
+    g_variant_type_free(variantType);
+    return variant;
+}
+
+} // namespace IPC
diff --git a/Source/WebKit/Shared/glib/ArgumentCodersGLib.h b/Source/WebKit/Shared/glib/ArgumentCodersGLib.h
new file mode 100644
index 0000000..0f45705
--- /dev/null
+++ b/Source/WebKit/Shared/glib/ArgumentCodersGLib.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ArgumentCoders.h"
+#include <wtf/glib/GRefPtr.h>
+
+typedef struct _GVariant GVariant;
+
+namespace IPC {
+
+void encode(Encoder&, GVariant*);
+Optional<GRefPtr<GVariant>> decode(Decoder&);
+
+} // namespace IPC
diff --git a/Source/WebKit/Shared/glib/UserMessage.cpp b/Source/WebKit/Shared/glib/UserMessage.cpp
new file mode 100644
index 0000000..dc89e2d
--- /dev/null
+++ b/Source/WebKit/Shared/glib/UserMessage.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "UserMessage.h"
+
+#include "ArgumentCodersGLib.h"
+#include "Attachment.h"
+#include "DataReference.h"
+#include <gio/gunixfdlist.h>
+
+namespace WebKit {
+
+void UserMessage::encode(IPC::Encoder& encoder) const
+{
+    encoder.encodeEnum(type);
+    if (type == Type::Null)
+        return;
+
+    encoder << name;
+    if (type == Type::Error) {
+        encoder << errorCode;
+        return;
+    }
+
+    IPC::encode(encoder, parameters.get());
+
+    Vector<IPC::Attachment> attachments;
+    if (fileDescriptors) {
+        int length = g_unix_fd_list_get_length(fileDescriptors.get());
+        for (int i = 0; i < length; ++i)
+            attachments.append(IPC::Attachment(g_unix_fd_list_get(fileDescriptors.get(), i, nullptr)));
+    }
+    encoder << attachments;
+}
+
+Optional<UserMessage> UserMessage::decode(IPC::Decoder& decoder)
+{
+    UserMessage result;
+    if (!decoder.decodeEnum(result.type))
+        return WTF::nullopt;
+
+    if (result.type == Type::Null)
+        return result;
+
+    if (!decoder.decode(result.name))
+        return WTF::nullopt;
+
+    if (result.type == Type::Error) {
+        Optional<uint32_t> errorCode;
+        decoder >> errorCode;
+        if (!errorCode)
+            return WTF::nullopt;
+
+        result.errorCode = errorCode.value();
+        return result;
+    }
+
+    Optional<GRefPtr<GVariant>> parameters = IPC::decode(decoder);
+    if (!parameters)
+        return WTF::nullopt;
+    result.parameters = WTFMove(*parameters);
+
+    Optional<Vector<IPC::Attachment>> attachments;
+    decoder >> attachments;
+    if (!attachments)
+        return WTF::nullopt;
+    if (!attachments->isEmpty()) {
+        result.fileDescriptors = adoptGRef(g_unix_fd_list_new());
+        for (auto& attachment : *attachments) {
+            if (g_unix_fd_list_append(result.fileDescriptors.get(), attachment.releaseFileDescriptor(), nullptr) == -1)
+                return WTF::nullopt;
+        }
+    }
+
+    return result;
+}
+
+}
diff --git a/Source/WebKit/Shared/glib/UserMessage.h b/Source/WebKit/Shared/glib/UserMessage.h
new file mode 100644
index 0000000..bbdd64d
--- /dev/null
+++ b/Source/WebKit/Shared/glib/UserMessage.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/Vector.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/text/CString.h>
+
+typedef struct _GUnixFDList GUnixFDList;
+typedef struct _GVariant GVariant;
+
+namespace IPC {
+class Decoder;
+class Encoder;
+}
+
+namespace WebKit {
+
+struct UserMessage {
+    enum class Type { Null, Message, Error };
+
+    UserMessage()
+        : type(Type::Null)
+    {
+    }
+
+    UserMessage(const CString& name, uint32_t errorCode)
+        : type(Type::Error)
+        , name(name)
+        , errorCode(errorCode)
+    {
+    }
+
+    void encode(IPC::Encoder&) const;
+    static Optional<UserMessage> decode(IPC::Decoder&);
+
+    Type type { Type::Null };
+    CString name;
+    GRefPtr<GVariant> parameters;
+    GRefPtr<GUnixFDList> fileDescriptors;
+    uint32_t errorCode { 0 };
+};
+
+}
diff --git a/Source/WebKit/SourcesGTK.txt b/Source/WebKit/SourcesGTK.txt
index 1e4e42c..c9e153d 100644
--- a/Source/WebKit/SourcesGTK.txt
+++ b/Source/WebKit/SourcesGTK.txt
@@ -67,6 +67,7 @@
 Shared/API/glib/WebKitHitTestResult.cpp @no-unify
 Shared/API/glib/WebKitURIRequest.cpp @no-unify
 Shared/API/glib/WebKitURIResponse.cpp @no-unify
+Shared/API/glib/WebKitUserMessage.cpp @no-unify
 
 Shared/CoordinatedGraphics/CoordinatedGraphicsScene.cpp
 Shared/CoordinatedGraphics/SimpleViewportController.cpp
@@ -82,7 +83,9 @@
 
 Shared/cairo/ShareableBitmapCairo.cpp
 
+Shared/glib/ArgumentCodersGLib.cpp
 Shared/glib/ProcessExecutablePathGLib.cpp
+Shared/glib/UserMessage.cpp
 Shared/glib/WebContextMenuItemGlib.cpp
 
 Shared/gtk/ArgumentCodersGtk.cpp
@@ -404,6 +407,8 @@
 
 WebProcess/WebPage/atk/WebKitWebPageAccessibilityObject.cpp
 
+WebProcess/WebPage/glib/WebPageGLib.cpp
+
 WebProcess/WebPage/gstreamer/WebPageGStreamer.cpp
 
 WebProcess/WebPage/gtk/AcceleratedSurfaceWayland.cpp
diff --git a/Source/WebKit/SourcesWPE.txt b/Source/WebKit/SourcesWPE.txt
index a22e4f1..5b514d5 100644
--- a/Source/WebKit/SourcesWPE.txt
+++ b/Source/WebKit/SourcesWPE.txt
@@ -68,6 +68,7 @@
 Shared/API/glib/WebKitHitTestResult.cpp @no-unify
 Shared/API/glib/WebKitURIRequest.cpp @no-unify
 Shared/API/glib/WebKitURIResponse.cpp @no-unify
+Shared/API/glib/WebKitUserMessage.cpp @no-unify
 
 Shared/CoordinatedGraphics/CoordinatedGraphicsScene.cpp
 Shared/CoordinatedGraphics/SimpleViewportController.cpp
@@ -81,7 +82,9 @@
 
 Shared/cairo/ShareableBitmapCairo.cpp
 
+Shared/glib/ArgumentCodersGLib.cpp
 Shared/glib/ProcessExecutablePathGLib.cpp
+Shared/glib/UserMessage.cpp
 Shared/glib/WebContextMenuItemGlib.cpp
 
 Shared/libwpe/NativeWebKeyboardEventLibWPE.cpp
@@ -247,6 +250,8 @@
 
 WebProcess/WebPage/atk/WebKitWebPageAccessibilityObject.cpp
 
+WebProcess/WebPage/glib/WebPageGLib.cpp
+
 WebProcess/WebPage/gstreamer/WebPageGStreamer.cpp
 
 WebProcess/WebPage/libwpe/AcceleratedSurfaceLibWPE.cpp @no-unify
diff --git a/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp b/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp
index 6a49e99..126bccf 100644
--- a/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp
+++ b/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp
@@ -47,10 +47,12 @@
 #include "WebKitSettingsPrivate.h"
 #include "WebKitURISchemeRequestPrivate.h"
 #include "WebKitUserContentManagerPrivate.h"
+#include "WebKitUserMessagePrivate.h"
 #include "WebKitWebContextPrivate.h"
 #include "WebKitWebViewPrivate.h"
 #include "WebKitWebsiteDataManagerPrivate.h"
 #include "WebNotificationManagerProxy.h"
+#include "WebProcessMessages.h"
 #include "WebURLSchemeHandler.h"
 #include "WebsiteDataType.h"
 #include <JavaScriptCore/RemoteInspector.h>
@@ -123,6 +125,7 @@
     INITIALIZE_WEB_EXTENSIONS,
     INITIALIZE_NOTIFICATION_PERMISSIONS,
     AUTOMATION_STARTED,
+    USER_MESSAGE_RECEIVED,
 
     LAST_SIGNAL
 };
@@ -372,6 +375,12 @@
 
     priv->processPool = WebProcessPool::create(configuration);
     priv->processPool->setPrimaryDataStore(webkitWebsiteDataManagerGetDataStore(priv->websiteDataManager.get()));
+    priv->processPool->setUserMessageHandler([webContext](UserMessage&& message, CompletionHandler<void(UserMessage&&)>&& completionHandler) {
+        // Sink the floating ref.
+        GRefPtr<WebKitUserMessage> userMessage = webkitUserMessageCreate(WTFMove(message), WTFMove(completionHandler));
+        gboolean returnValue;
+        g_signal_emit(webContext, signals[USER_MESSAGE_RECEIVED], 0, userMessage.get(), &returnValue);
+    });
 
     webkitWebsiteDataManagerAddProcessPool(priv->websiteDataManager.get(), *priv->processPool);
 
@@ -415,6 +424,11 @@
         priv->faviconDatabase = nullptr;
     }
 
+    if (priv->processPool) {
+        priv->processPool->setUserMessageHandler(nullptr);
+        priv->processPool = nullptr;
+    }
+
     G_OBJECT_CLASS(webkit_web_context_parent_class)->dispose(object);
 }
 
@@ -574,6 +588,32 @@
             g_cclosure_marshal_VOID__OBJECT,
             G_TYPE_NONE, 1,
             WEBKIT_TYPE_AUTOMATION_SESSION);
+
+    /**
+     * WebKitWebContext::user-message-received:
+     * @context: the #WebKitWebContext
+     * @message: the #WebKitUserMessage received
+     *
+     * This signal is emitted when a #WebKitUserMessage is received from a
+     * #WebKitWebExtension. You can reply to the message using
+     * webkit_user_message_send_reply().
+     *
+     * You can handle the user message asynchronously by calling g_object_ref() on
+     * @message and returning %TRUE.
+     *
+     * Returns: %TRUE if the message was handled, or %FALSE otherwise.
+     *
+     * Since: 2.28
+     */
+    signals[USER_MESSAGE_RECEIVED] = g_signal_new(
+        "user-message-received",
+        G_TYPE_FROM_CLASS(gObjectClass),
+        G_SIGNAL_RUN_LAST,
+        G_STRUCT_OFFSET(WebKitWebContextClass, user_message_received),
+        g_signal_accumulator_true_handled, nullptr,
+        g_cclosure_marshal_generic,
+        G_TYPE_BOOLEAN, 1,
+        WEBKIT_TYPE_USER_MESSAGE);
 }
 
 static gpointer createDefaultWebContext(gpointer)
@@ -1680,6 +1720,27 @@
     context->priv->notificationProvider->setNotificationPermissions(WTFMove(map));
 }
 
+/**
+ * webkit_web_context_send_message_to_all_extensions:
+ * @context: the #WebKitWebContext
+ * @message: a #WebKitUserMessage
+ *
+ * Send @message to all #WebKitWebExtension<!-- -->s associated to @context.
+ * If @message is floating, it's consumed.
+ *
+ * Since: 2.28
+ */
+void webkit_web_context_send_message_to_all_extensions(WebKitWebContext* context, WebKitUserMessage* message)
+{
+    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
+    g_return_if_fail(WEBKIT_IS_USER_MESSAGE(message));
+
+    // We sink the reference in case of being floating.
+    GRefPtr<WebKitUserMessage> adoptedMessage = message;
+    for (auto& process : context->priv->processPool->processes())
+        process->send(Messages::WebProcess::SendMessageToWebExtension(webkitUserMessageGetMessage(message)), 0);
+}
+
 void webkitWebContextInitializeNotificationPermissions(WebKitWebContext* context)
 {
     g_signal_emit(context, signals[INITIALIZE_NOTIFICATION_PERMISSIONS], 0);
diff --git a/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp b/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp
index c3b5c9c..6ca1387 100644
--- a/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp
+++ b/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp
@@ -56,12 +56,14 @@
 #include "WebKitUIClient.h"
 #include "WebKitURIRequestPrivate.h"
 #include "WebKitURIResponsePrivate.h"
+#include "WebKitUserMessagePrivate.h"
 #include "WebKitWebContextPrivate.h"
 #include "WebKitWebResourcePrivate.h"
 #include "WebKitWebViewPrivate.h"
 #include "WebKitWebViewSessionStatePrivate.h"
 #include "WebKitWebsiteDataManagerPrivate.h"
 #include "WebKitWindowPropertiesPrivate.h"
+#include "WebPageMessages.h"
 #include <JavaScriptCore/APICast.h>
 #include <JavaScriptCore/JSRetainPtr.h>
 #include <jsc/JSCContextPrivate.h>
@@ -163,6 +165,8 @@
     SHOW_OPTION_MENU,
 #endif
 
+    USER_MESSAGE_RECEIVED,
+
     LAST_SIGNAL
 };
 
@@ -444,6 +448,11 @@
         webkitWebViewDidChangePageID(m_webView);
     }
 
+    void didReceiveUserMessage(WKWPE::View&, UserMessage&& message, CompletionHandler<void(UserMessage&&)>&& completionHandler) override
+    {
+        webkitWebViewDidReceiveUserMessage(m_webView, WTFMove(message), WTFMove(completionHandler));
+    }
+
     WebKitWebView* m_webView;
 };
 #endif
@@ -2079,6 +2088,34 @@
         GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE,
         GDK_TYPE_RECTANGLE | G_SIGNAL_TYPE_STATIC_SCOPE);
 #endif // PLATFORM(GTK)
+
+    /**
+     * WebKitWebView::user-message-received:
+     * @web_view: the #WebKitWebView on which the signal is emitted
+     * @message: the #WebKitUserMessage received
+     *
+     * This signal is emitted when a #WebKitUserMessage is received from the
+     * #WebKitWebPage corresponding to @web_view. You can reply to the message
+     * using webkit_user_message_send_reply().
+     *
+     * You can handle the user message asynchronously by calling g_object_ref() on
+     * @message and returning %TRUE. If the last reference of @message is removed
+     * and the message has not been replied to, the operation in the #WebKitWebPage will
+     * finish with error %WEBKIT_USER_MESSAGE_UNHANDLED_MESSAGE.
+     *
+     * Returns: %TRUE if the message was handled, or %FALSE otherwise.
+     *
+     * Since: 2.28
+     */
+    signals[USER_MESSAGE_RECEIVED] = g_signal_new(
+        "user-message-received",
+        G_TYPE_FROM_CLASS(webViewClass),
+        G_SIGNAL_RUN_LAST,
+        G_STRUCT_OFFSET(WebKitWebViewClass, user_message_received),
+        g_signal_accumulator_true_handled, nullptr,
+        g_cclosure_marshal_generic,
+        G_TYPE_BOOLEAN, 1,
+        WEBKIT_TYPE_USER_MESSAGE);
 }
 
 static void webkitWebViewCancelAuthenticationRequest(WebKitWebView* webView)
@@ -2582,6 +2619,14 @@
     g_object_notify(G_OBJECT(webView), "page-id");
 }
 
+void webkitWebViewDidReceiveUserMessage(WebKitWebView* webView, UserMessage&& message, CompletionHandler<void(UserMessage&&)>&& completionHandler)
+{
+    // Sink the floating ref.
+    GRefPtr<WebKitUserMessage> userMessage = webkitUserMessageCreate(WTFMove(message), WTFMove(completionHandler));
+    gboolean returnValue;
+    g_signal_emit(webView, signals[USER_MESSAGE_RECEIVED], 0, userMessage.get(), &returnValue);
+}
+
 #if PLATFORM(WPE)
 /**
  * webkit_web_view_get_backend:
@@ -4218,3 +4263,70 @@
         webView->priv->frameDisplayedCallbacks.removeFirstMatching(matchFunction);
 }
 #endif // PLATFORM(WPE)
+
+/**
+ * webkit_web_view_send_message_to_page:
+ * @web_view: a #WebKitWebView
+ * @message: a #WebKitUserMessage
+ * @cancellable: (nullable): a #GCancellable or %NULL to ignore
+ * @callback: (scope async): (nullable): A #GAsyncReadyCallback to call when the request is satisfied or %NULL
+ * @user_data: (closure): the data to pass to callback function
+ *
+ * Send @message to the #WebKitWebPage corresponding to @web_view. If @message is floating, it's consumed.
+ *
+ * If you don't expect any reply, or you simply want to ignore it, you can pass %NULL as @callback.
+ * When the operation is finished, @callback will be called. You can then call
+ * webkit_web_view_send_message_to_page_finish() to get the message reply.
+ *
+ * Since: 2.28
+ */
+void webkit_web_view_send_message_to_page(WebKitWebView* webView, WebKitUserMessage* message, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
+{
+    g_return_if_fail(WEBKIT_IS_WEB_VIEW(webView));
+    g_return_if_fail(WEBKIT_IS_USER_MESSAGE(message));
+
+    // We sink the reference in case of being floating.
+    GRefPtr<WebKitUserMessage> adoptedMessage = message;
+    auto& page = getPage(webView);
+    if (!callback) {
+        page.ensureRunningProcess().send(Messages::WebPage::SendMessageToWebExtension(webkitUserMessageGetMessage(message)), page.webPageID().toUInt64());
+        return;
+    }
+
+    GRefPtr<GTask> task = adoptGRef(g_task_new(webView, cancellable, callback, userData));
+    CompletionHandler<void(UserMessage&&)> completionHandler = [task = WTFMove(task)](UserMessage&& replyMessage) {
+        switch (replyMessage.type) {
+        case UserMessage::Type::Null:
+            g_task_return_new_error(task.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED, _("Operation was cancelled"));
+            break;
+        case UserMessage::Type::Message:
+            g_task_return_pointer(task.get(), g_object_ref_sink(webkitUserMessageCreate(WTFMove(replyMessage))), static_cast<GDestroyNotify>(g_object_unref));
+            break;
+        case UserMessage::Type::Error:
+            g_task_return_new_error(task.get(), WEBKIT_USER_MESSAGE_ERROR, replyMessage.errorCode, _("Message %s was not handled"), replyMessage.name.data());
+            break;
+        }
+    };
+    page.ensureRunningProcess().sendWithAsyncReply(Messages::WebPage::SendMessageToWebExtensionWithReply(webkitUserMessageGetMessage(message)),
+        WTFMove(completionHandler), page.webPageID().toUInt64());
+}
+
+/**
+ * webkit_web_view_send_message_to_page_finish:
+ * @web_view: a #WebKitWebView
+ * @result: a #GAsyncResult
+ * @error: return location for error or %NULL to ignor
+ *
+ * Finish an asynchronous operation started with webkit_web_view_send_message_to_page().
+ *
+ * Returns: (transfer full): a #WebKitUserMessage with the reply or %NULL in case of error.
+ *
+ * Since: 2.28
+ */
+WebKitUserMessage* webkit_web_view_send_message_to_page_finish(WebKitWebView* webView, GAsyncResult* result, GError** error)
+{
+    g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(webView), nullptr);
+    g_return_val_if_fail(g_task_is_valid(result, webView), nullptr);
+
+    return WEBKIT_USER_MESSAGE(g_task_propagate_pointer(G_TASK(result), error));
+}
diff --git a/Source/WebKit/UIProcess/API/glib/WebKitWebViewPrivate.h b/Source/WebKit/UIProcess/API/glib/WebKitWebViewPrivate.h
index 8c45055..3326414 100644
--- a/Source/WebKit/UIProcess/API/glib/WebKitWebViewPrivate.h
+++ b/Source/WebKit/UIProcess/API/glib/WebKitWebViewPrivate.h
@@ -28,6 +28,7 @@
 
 #include "APIPageConfiguration.h"
 #include "InstallMissingMediaPluginsPermissionRequest.h"
+#include "UserMessage.h"
 #include "WebContextMenuItemData.h"
 #include "WebEvent.h"
 #include "WebHitTestResultData.h"
@@ -101,3 +102,4 @@
 gboolean webkitWebViewScriptDialog(WebKitWebView*, WebKitScriptDialog*);
 gboolean webkitWebViewRunFileChooser(WebKitWebView*, WebKitFileChooserRequest*);
 void webkitWebViewDidChangePageID(WebKitWebView*);
+void webkitWebViewDidReceiveUserMessage(WebKitWebView*, WebKit::UserMessage&&, CompletionHandler<void(WebKit::UserMessage&&)>&&);
diff --git a/Source/WebKit/UIProcess/API/gtk/WebKitAutocleanups.h b/Source/WebKit/UIProcess/API/gtk/WebKitAutocleanups.h
index 78cec59..5757f57 100644
--- a/Source/WebKit/UIProcess/API/gtk/WebKitAutocleanups.h
+++ b/Source/WebKit/UIProcess/API/gtk/WebKitAutocleanups.h
@@ -62,6 +62,7 @@
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitUserContentFilterStore, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitUserContentManager, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitUserMediaPermissionRequest, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitUserMessage, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitWebContext, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitWebInspector, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitWebResource, g_object_unref)
diff --git a/Source/WebKit/UIProcess/API/gtk/WebKitUserMessage.h b/Source/WebKit/UIProcess/API/gtk/WebKitUserMessage.h
new file mode 100644
index 0000000..a15ed74
--- /dev/null
+++ b/Source/WebKit/UIProcess/API/gtk/WebKitUserMessage.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#if !defined(__WEBKIT2_H_INSIDE__) && !defined(WEBKIT2_COMPILATION) && !defined(__WEBKIT_WEB_EXTENSION_H_INSIDE__)
+#error "Only <webkit2/webkit2.h> can be included directly."
+#endif
+
+#ifndef WebKitUserMessage_h
+#define WebKitUserMessage_h
+
+#include <gio/gio.h>
+#include <webkit2/WebKitDefines.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_USER_MESSAGE            (webkit_user_message_get_type())
+#define WEBKIT_USER_MESSAGE(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_USER_MESSAGE, WebKitUserMessage))
+#define WEBKIT_IS_USER_MESSAGE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_USER_MESSAGE))
+#define WEBKIT_USER_MESSAGE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  WEBKIT_TYPE_USER_MESSAGE, WebKitUserMessageClass))
+#define WEBKIT_IS_USER_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  WEBKIT_TYPE_USER_MESSAGE))
+#define WEBKIT_USER_MESSAGE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  WEBKIT_TYPE_USER_MESSAGE, WebKitUserMessageClass))
+
+#define WEBKIT_USER_MESSAGE_ERROR webkit_user_message_error_quark ()
+
+typedef struct _WebKitUserMessage        WebKitUserMessage;
+typedef struct _WebKitUserMessageClass   WebKitUserMessageClass;
+typedef struct _WebKitUserMessagePrivate WebKitUserMessagePrivate;
+
+/**
+ * WebKitUserMessageError:
+ * @WEBKIT_USER_MESSAGE_UNHANDLED_MESSAGE: The message was not handled by the receiver.
+ *
+ * Enum values used to denote errors happening when sending user messages.
+ *
+ * Since: 2.28
+ */
+typedef enum {
+    WEBKIT_USER_MESSAGE_UNHANDLED_MESSAGE
+} WebKitUserMessageError;
+
+struct _WebKitUserMessage {
+    GInitiallyUnowned parent;
+
+    WebKitUserMessagePrivate *priv;
+};
+
+struct _WebKitUserMessageClass {
+    GInitiallyUnownedClass parent_class;
+
+    void (*_webkit_reserved0) (void);
+    void (*_webkit_reserved1) (void);
+    void (*_webkit_reserved2) (void);
+    void (*_webkit_reserved3) (void);
+};
+
+WEBKIT_API GType
+webkit_user_message_get_type         (void);
+
+WEBKIT_API GQuark
+webkit_user_message_error_quark      (void);
+
+WEBKIT_API WebKitUserMessage *
+webkit_user_message_new              (const char        *name,
+                                      GVariant          *parameters);
+
+WEBKIT_API WebKitUserMessage *
+webkit_user_message_new_with_fd_list (const char        *name,
+                                      GVariant          *parameters,
+                                      GUnixFDList       *fd_list);
+
+WEBKIT_API const char *
+webkit_user_message_get_name         (WebKitUserMessage *message);
+
+WEBKIT_API GVariant *
+webkit_user_message_get_parameters   (WebKitUserMessage *message);
+
+WEBKIT_API GUnixFDList *
+webkit_user_message_get_fd_list      (WebKitUserMessage *message);
+
+WEBKIT_API void
+webkit_user_message_send_reply       (WebKitUserMessage *message,
+                                      WebKitUserMessage *reply);
+
+G_END_DECLS
+
+#endif
diff --git a/Source/WebKit/UIProcess/API/gtk/WebKitWebContext.h b/Source/WebKit/UIProcess/API/gtk/WebKitWebContext.h
index 95ce5a5..4da9ce7 100644
--- a/Source/WebKit/UIProcess/API/gtk/WebKitWebContext.h
+++ b/Source/WebKit/UIProcess/API/gtk/WebKitWebContext.h
@@ -34,6 +34,7 @@
 #include <webkit2/WebKitNetworkProxySettings.h>
 #include <webkit2/WebKitSecurityManager.h>
 #include <webkit2/WebKitURISchemeRequest.h>
+#include <webkit2/WebKitUserMessage.h>
 #include <webkit2/WebKitWebsiteDataManager.h>
 
 G_BEGIN_DECLS
@@ -143,17 +144,18 @@
 struct _WebKitWebContextClass {
     GObjectClass parent;
 
-    void (* download_started)                    (WebKitWebContext        *context,
-                                                  WebKitDownload          *download);
-    void (* initialize_web_extensions)           (WebKitWebContext        *context);
-    void (* initialize_notification_permissions) (WebKitWebContext        *context);
-    void (* automation_started)                  (WebKitWebContext        *context,
-                                                  WebKitAutomationSession *session);
+    void     (* download_started)                    (WebKitWebContext        *context,
+                                                      WebKitDownload          *download);
+    void     (* initialize_web_extensions)           (WebKitWebContext        *context);
+    void     (* initialize_notification_permissions) (WebKitWebContext        *context);
+    void     (* automation_started)                  (WebKitWebContext        *context,
+                                                      WebKitAutomationSession *session);
+    gboolean (* user_message_received)               (WebKitWebContext        *context,
+                                                      WebKitUserMessage       *message);
 
     void (*_webkit_reserved0) (void);
     void (*_webkit_reserved1) (void);
     void (*_webkit_reserved2) (void);
-    void (*_webkit_reserved3) (void);
 };
 
 WEBKIT_API GType
@@ -318,6 +320,10 @@
                                                      GList                         *allowed_origins,
                                                      GList                         *disallowed_origins);
 
+WEBKIT_API void
+webkit_web_context_send_message_to_all_extensions   (WebKitWebContext              *context,
+                                                     WebKitUserMessage             *message);
+
 G_END_DECLS
 
 #endif
diff --git a/Source/WebKit/UIProcess/API/gtk/WebKitWebView.h b/Source/WebKit/UIProcess/API/gtk/WebKitWebView.h
index 8c219a3..4ff696b 100644
--- a/Source/WebKit/UIProcess/API/gtk/WebKitWebView.h
+++ b/Source/WebKit/UIProcess/API/gtk/WebKitWebView.h
@@ -49,6 +49,7 @@
 #include <webkit2/WebKitSettings.h>
 #include <webkit2/WebKitURIRequest.h>
 #include <webkit2/WebKitUserContentManager.h>
+#include <webkit2/WebKitUserMessage.h>
 #include <webkit2/WebKitWebContext.h>
 #include <webkit2/WebKitWebInspector.h>
 #include <webkit2/WebKitWebResource.h>
@@ -269,9 +270,10 @@
                                                 WebKitOptionMenu            *menu);
     void       (* web_process_terminated)      (WebKitWebView               *web_view,
                                                 WebKitWebProcessTerminationReason reason);
+    gboolean   (* user_message_received)       (WebKitWebView               *web_view,
+                                                WebKitUserMessage           *message);
 
     void (*_webkit_reserved0) (void);
-    void (*_webkit_reserved1) (void);
 };
 
 WEBKIT_API GType
@@ -552,6 +554,18 @@
 webkit_web_view_restore_session_state                (WebKitWebView             *web_view,
                                                       WebKitWebViewSessionState *state);
 
+WEBKIT_API void
+webkit_web_view_send_message_to_page                 (WebKitWebView             *web_view,
+                                                      WebKitUserMessage         *message,
+                                                      GCancellable              *cancellable,
+                                                      GAsyncReadyCallback        callback,
+                                                      gpointer                   user_data);
+
+WEBKIT_API WebKitUserMessage *
+webkit_web_view_send_message_to_page_finish          (WebKitWebView             *web_view,
+                                                      GAsyncResult              *result,
+                                                      GError                   **error);
+
 G_END_DECLS
 
 #endif
diff --git a/Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt b/Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt
index 566b8c6..5856cc4 100644
--- a/Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt
+++ b/Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt
@@ -70,6 +70,7 @@
 webkit_web_context_get_process_model
 webkit_web_context_set_process_model
 webkit_web_context_initialize_notification_permissions
+webkit_web_context_send_message_to_all_extensions
 
 <SUBSECTION URI Scheme>
 WebKitURISchemeRequestCallback
@@ -277,6 +278,8 @@
 webkit_web_view_get_session_state
 webkit_web_view_restore_session_state
 webkit_web_view_get_main_resource
+webkit_web_view_send_message_to_page
+webkit_web_view_send_message_to_page_finish
 
 <SUBSECTION WebKitJavascriptResult>
 WebKitJavascriptResult
@@ -1538,6 +1541,8 @@
 WebKitWebExtensionInitializeFunction
 WebKitWebExtensionInitializeWithUserDataFunction
 webkit_web_extension_get_page
+webkit_web_extension_send_message_to_context
+webkit_web_extension_send_message_to_context_finish
 
 <SUBSECTION Standard>
 WebKitWebExtensionClass
@@ -1562,6 +1567,8 @@
 webkit_web_page_get_uri
 webkit_web_page_get_main_frame
 webkit_web_page_get_editor
+webkit_web_page_send_message_to_view
+webkit_web_page_send_message_to_view_finish
 
 <SUBSECTION Standard>
 WebKitWebPageClass
@@ -1738,3 +1745,32 @@
 <FILE>WebKitURIUtilities</FILE>
 webkit_uri_for_display
 </SECTION>
+
+<SECTION>
+<FILE>WebKitUserMessage</FILE>
+WebKitUserMessage
+webkit_user_message_new
+webkit_user_message_new_with_fd_list
+webkit_user_message_get_name
+webkit_user_message_get_parameters
+webkit_user_message_get_fd_list
+webkit_user_message_send_reply
+
+<SUBSECTION WebKitUserMessageError>
+WEBKIT_USER_MESSAGE_ERROR
+WebKitUserMessageError
+webkit_user_message_error_quark
+
+<SUBSECTION Standard>
+WebKitUserMessageClass
+WEBKIT_TYPE_USER_MESSAGE
+WEBKIT_USER_MESSAGE
+WEBKIT_IS_USER_MESSAGE
+WEBKIT_USER_MESSAGE_CLASS
+WEBKIT_IS_USER_MESSAGE_CLASS
+WEBKIT_USER_MESSAGE_GET_CLASS
+
+<SUBSECTION Private>
+WebKitUserMessagePrivate
+webkit_user_message_get_type
+</SECTION>
diff --git a/Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-docs.sgml b/Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-docs.sgml
index c45311d..5daab08 100644
--- a/Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-docs.sgml
+++ b/Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-docs.sgml
@@ -63,6 +63,7 @@
     <xi:include href="xml/WebKitAutomationSession.xml"/>
     <xi:include href="xml/WebKitApplicationInfo.xml"/>
     <xi:include href="xml/WebKitGeolocationManager.xml"/>
+    <xi:include href="xml/WebKitUserMessage.xml"/>
   </chapter>
 
   <chapter>
diff --git a/Source/WebKit/UIProcess/API/gtk/webkit2.h b/Source/WebKit/UIProcess/API/gtk/webkit2.h
index f3edcce..16ef7eb 100644
--- a/Source/WebKit/UIProcess/API/gtk/webkit2.h
+++ b/Source/WebKit/UIProcess/API/gtk/webkit2.h
@@ -79,6 +79,7 @@
 #include <webkit2/WebKitUserContentFilterStore.h>
 #include <webkit2/WebKitUserContentManager.h>
 #include <webkit2/WebKitUserMediaPermissionRequest.h>
+#include <webkit2/WebKitUserMessage.h>
 #include <webkit2/WebKitVersion.h>
 #include <webkit2/WebKitWebContext.h>
 #include <webkit2/WebKitWebInspector.h>
diff --git a/Source/WebKit/UIProcess/API/wpe/APIViewClient.h b/Source/WebKit/UIProcess/API/wpe/APIViewClient.h
index 0589bb1..b13da6d 100644
--- a/Source/WebKit/UIProcess/API/wpe/APIViewClient.h
+++ b/Source/WebKit/UIProcess/API/wpe/APIViewClient.h
@@ -25,6 +25,9 @@
 
 #pragma once
 
+#include "UserMessage.h"
+#include <wtf/CompletionHandler.h>
+
 typedef struct OpaqueJSContext* JSGlobalContextRef;
 
 namespace WebKit {
@@ -46,6 +49,7 @@
     virtual void handleDownloadRequest(WKWPE::View&, WebKit::DownloadProxy&) { }
     virtual void willStartLoad(WKWPE::View&) { }
     virtual void didChangePageID(WKWPE::View&) { }
+    virtual void didReceiveUserMessage(WKWPE::View&, WebKit::UserMessage&&, CompletionHandler<void(WebKit::UserMessage&&)>&& completionHandler) { completionHandler(WebKit::UserMessage()); }
 };
 
 } // namespace API
diff --git a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp
index c8b4e79..217109b 100644
--- a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp
+++ b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp
@@ -425,4 +425,9 @@
     m_view.didChangePageID();
 }
 
+void PageClientImpl::sendMessageToWebView(UserMessage&& message, CompletionHandler<void(UserMessage&&)>&& completionHandler)
+{
+    m_view.didReceiveUserMessage(WTFMove(message), WTFMove(completionHandler));
+}
+
 } // namespace WebKit
diff --git a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.h b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.h
index 87b7bb9..fb38b19 100644
--- a/Source/WebKit/UIProcess/API/wpe/PageClientImpl.h
+++ b/Source/WebKit/UIProcess/API/wpe/PageClientImpl.h
@@ -42,6 +42,7 @@
 namespace WebKit {
 
 class ScrollGestureController;
+struct UserMessage;
 
 enum class UndoOrRedo : bool;
 
@@ -60,6 +61,8 @@
     AtkObject* accessible();
 #endif
 
+    void sendMessageToWebView(UserMessage&&, CompletionHandler<void(UserMessage&&)>&&);
+
 private:
     // PageClient
     std::unique_ptr<DrawingAreaProxy> createDrawingAreaProxy(WebProcessProxy&) override;
diff --git a/Source/WebKit/UIProcess/API/wpe/WPEView.cpp b/Source/WebKit/UIProcess/API/wpe/WPEView.cpp
index 0b9faa0..e0f5a78 100644
--- a/Source/WebKit/UIProcess/API/wpe/WPEView.cpp
+++ b/Source/WebKit/UIProcess/API/wpe/WPEView.cpp
@@ -210,6 +210,11 @@
     m_client->didChangePageID(*this);
 }
 
+void View::didReceiveUserMessage(UserMessage&& message, CompletionHandler<void(UserMessage&&)>&& completionHandler)
+{
+    m_client->didReceiveUserMessage(*this, WTFMove(message), WTFMove(completionHandler));
+}
+
 void View::setSize(const WebCore::IntSize& size)
 {
     m_size = size;
diff --git a/Source/WebKit/UIProcess/API/wpe/WPEView.h b/Source/WebKit/UIProcess/API/wpe/WPEView.h
index 9bc6b9f..14eb385 100644
--- a/Source/WebKit/UIProcess/API/wpe/WPEView.h
+++ b/Source/WebKit/UIProcess/API/wpe/WPEView.h
@@ -49,6 +49,7 @@
 class DownloadProxy;
 class WebPageGroup;
 class WebProcessPool;
+struct UserMessage;
 }
 
 namespace WKWPE {
@@ -68,6 +69,7 @@
     void handleDownloadRequest(WebKit::DownloadProxy&);
     void willStartLoad();
     void didChangePageID();
+    void didReceiveUserMessage(WebKit::UserMessage&&, CompletionHandler<void(WebKit::UserMessage&&)>&&);
 
     WebKit::WebPageProxy& page() { return *m_pageProxy; }
 
diff --git a/Source/WebKit/UIProcess/API/wpe/WebKitAutocleanups.h b/Source/WebKit/UIProcess/API/wpe/WebKitAutocleanups.h
index 1d206c6..45302f6 100644
--- a/Source/WebKit/UIProcess/API/wpe/WebKitAutocleanups.h
+++ b/Source/WebKit/UIProcess/API/wpe/WebKitAutocleanups.h
@@ -58,6 +58,7 @@
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitUserContentFilterStore, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitUserContentManager, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitUserMediaPermissionRequest, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitUserMessage, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitWebContext, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitWebResource, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitWebView, g_object_unref)
diff --git a/Source/WebKit/UIProcess/API/wpe/WebKitUserMessage.h b/Source/WebKit/UIProcess/API/wpe/WebKitUserMessage.h
new file mode 100644
index 0000000..51ff99c
--- /dev/null
+++ b/Source/WebKit/UIProcess/API/wpe/WebKitUserMessage.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#if !defined(__WEBKIT_H_INSIDE__) && !defined(WEBKIT2_COMPILATION) && !defined(__WEBKIT_WEB_EXTENSION_H_INSIDE__)
+#error "Only <wpe/webkit.h> can be included directly."
+#endif
+
+#ifndef WebKitUserMessage_h
+#define WebKitUserMessage_h
+
+#include <gio/gio.h>
+#include <wpe/WebKitDefines.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_USER_MESSAGE            (webkit_user_message_get_type())
+#define WEBKIT_USER_MESSAGE(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_USER_MESSAGE, WebKitUserMessage))
+#define WEBKIT_IS_USER_MESSAGE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_USER_MESSAGE))
+#define WEBKIT_USER_MESSAGE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  WEBKIT_TYPE_USER_MESSAGE, WebKitUserMessageClass))
+#define WEBKIT_IS_USER_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  WEBKIT_TYPE_USER_MESSAGE))
+#define WEBKIT_USER_MESSAGE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  WEBKIT_TYPE_USER_MESSAGE, WebKitUserMessageClass))
+
+#define WEBKIT_USER_MESSAGE_ERROR webkit_user_message_error_quark ()
+
+typedef struct _WebKitUserMessage        WebKitUserMessage;
+typedef struct _WebKitUserMessageClass   WebKitUserMessageClass;
+typedef struct _WebKitUserMessagePrivate WebKitUserMessagePrivate;
+
+/**
+ * WebKitUserMessageError:
+ * @WEBKIT_USER_MESSAGE_UNHANDLED_MESSAGE: The message was not handled by the receiver.
+ *
+ * Enum values used to denote errors happening when sending user messages.
+ *
+ * Since: 2.28
+ */
+typedef enum {
+    WEBKIT_USER_MESSAGE_UNHANDLED_MESSAGE
+} WebKitUserMessageError;
+
+struct _WebKitUserMessage {
+    GInitiallyUnowned parent;
+
+    WebKitUserMessagePrivate *priv;
+};
+
+struct _WebKitUserMessageClass {
+    GInitiallyUnownedClass parent_class;
+
+    void (*_webkit_reserved0) (void);
+    void (*_webkit_reserved1) (void);
+    void (*_webkit_reserved2) (void);
+    void (*_webkit_reserved3) (void);
+};
+
+WEBKIT_API GType
+webkit_user_message_get_type         (void);
+
+WEBKIT_API GQuark
+webkit_user_message_error_quark      (void);
+
+WEBKIT_API WebKitUserMessage *
+webkit_user_message_new              (const char        *name,
+                                      GVariant          *parameters);
+
+WEBKIT_API WebKitUserMessage *
+webkit_user_message_new_with_fd_list (const char        *name,
+                                      GVariant          *parameters,
+                                      GUnixFDList       *fd_list);
+
+WEBKIT_API const char *
+webkit_user_message_get_name         (WebKitUserMessage *message);
+
+WEBKIT_API GVariant *
+webkit_user_message_get_parameters   (WebKitUserMessage *message);
+
+WEBKIT_API GUnixFDList *
+webkit_user_message_get_fd_list      (WebKitUserMessage *message);
+
+WEBKIT_API void
+webkit_user_message_send_reply       (WebKitUserMessage *message,
+                                      WebKitUserMessage *reply);
+
+G_END_DECLS
+
+#endif
diff --git a/Source/WebKit/UIProcess/API/wpe/WebKitWebContext.h b/Source/WebKit/UIProcess/API/wpe/WebKitWebContext.h
index deb885c..99863fd 100644
--- a/Source/WebKit/UIProcess/API/wpe/WebKitWebContext.h
+++ b/Source/WebKit/UIProcess/API/wpe/WebKitWebContext.h
@@ -34,6 +34,7 @@
 #include <wpe/WebKitNetworkProxySettings.h>
 #include <wpe/WebKitSecurityManager.h>
 #include <wpe/WebKitURISchemeRequest.h>
+#include <wpe/WebKitUserMessage.h>
 #include <wpe/WebKitWebsiteDataManager.h>
 
 G_BEGIN_DECLS
@@ -143,17 +144,18 @@
 struct _WebKitWebContextClass {
     GObjectClass parent;
 
-    void (* download_started)                    (WebKitWebContext        *context,
-                                                  WebKitDownload          *download);
-    void (* initialize_web_extensions)           (WebKitWebContext        *context);
-    void (* initialize_notification_permissions) (WebKitWebContext        *context);
-    void (* automation_started)                  (WebKitWebContext        *context,
-                                                  WebKitAutomationSession *session);
+    void     (* download_started)                    (WebKitWebContext        *context,
+                                                      WebKitDownload          *download);
+    void     (* initialize_web_extensions)           (WebKitWebContext        *context);
+    void     (* initialize_notification_permissions) (WebKitWebContext        *context);
+    void     (* automation_started)                  (WebKitWebContext        *context,
+                                                      WebKitAutomationSession *session);
+    gboolean (* user_message_received)               (WebKitWebContext        *context,
+                                                      WebKitUserMessage       *message);
 
     void (*_webkit_reserved0) (void);
     void (*_webkit_reserved1) (void);
     void (*_webkit_reserved2) (void);
-    void (*_webkit_reserved3) (void);
 };
 
 WEBKIT_API GType
@@ -314,6 +316,10 @@
                                                      GList                         *allowed_origins,
                                                      GList                         *disallowed_origins);
 
+WEBKIT_API void
+webkit_web_context_send_message_to_all_extensions   (WebKitWebContext              *context,
+                                                     WebKitUserMessage             *message);
+
 G_END_DECLS
 
 #endif
diff --git a/Source/WebKit/UIProcess/API/wpe/WebKitWebView.h b/Source/WebKit/UIProcess/API/wpe/WebKitWebView.h
index 2a4a62042..9e5d3f4 100644
--- a/Source/WebKit/UIProcess/API/wpe/WebKitWebView.h
+++ b/Source/WebKit/UIProcess/API/wpe/WebKitWebView.h
@@ -46,6 +46,7 @@
 #include <wpe/WebKitSettings.h>
 #include <wpe/WebKitURIRequest.h>
 #include <wpe/WebKitUserContentManager.h>
+#include <wpe/WebKitUserMessage.h>
 #include <wpe/WebKitWebContext.h>
 #include <wpe/WebKitWebResource.h>
 #include <wpe/WebKitWebViewBackend.h>
@@ -241,6 +242,9 @@
                                                     WebKitNotification          *notification);
     void           (* web_process_terminated)      (WebKitWebView               *web_view,
                                                     WebKitWebProcessTerminationReason reason);
+    gboolean       (* user_message_received)       (WebKitWebView               *web_view,
+                                                    WebKitUserMessage           *message);
+
 
     void (*_webkit_reserved0) (void);
     void (*_webkit_reserved1) (void);
@@ -249,7 +253,6 @@
     void (*_webkit_reserved4) (void);
     void (*_webkit_reserved5) (void);
     void (*_webkit_reserved6) (void);
-    void (*_webkit_reserved7) (void);
 };
 
 WEBKIT_API GType
@@ -525,6 +528,18 @@
 webkit_web_view_get_background_color                 (WebKitWebView               *web_view,
                                                       WebKitColor                 *color);
 
+WEBKIT_API void
+webkit_web_view_send_message_to_page                 (WebKitWebView               *web_view,
+                                                      WebKitUserMessage           *message,
+                                                      GCancellable                *cancellable,
+                                                      GAsyncReadyCallback          callback,
+                                                      gpointer                     user_data);
+
+WEBKIT_API WebKitUserMessage *
+webkit_web_view_send_message_to_page_finish          (WebKitWebView               *web_view,
+                                                      GAsyncResult                *result,
+                                                      GError                     **error);
+
 G_END_DECLS
 
 #endif
diff --git a/Source/WebKit/UIProcess/API/wpe/docs/wpe-1.0-sections.txt b/Source/WebKit/UIProcess/API/wpe/docs/wpe-1.0-sections.txt
index 019f84b..67903f5 100644
--- a/Source/WebKit/UIProcess/API/wpe/docs/wpe-1.0-sections.txt
+++ b/Source/WebKit/UIProcess/API/wpe/docs/wpe-1.0-sections.txt
@@ -47,6 +47,7 @@
 webkit_web_context_get_process_model
 webkit_web_context_set_process_model
 webkit_web_context_initialize_notification_permissions
+webkit_web_context_send_message_to_all_extensions
 
 <SUBSECTION URI Scheme>
 WebKitURISchemeRequestCallback
@@ -251,6 +252,8 @@
 webkit_web_view_remove_frame_displayed_callback
 webkit_web_view_get_background_color
 webkit_web_view_set_background_color
+webkit_web_view_send_message_to_page
+webkit_web_view_send_message_to_page_finish
 
 <SUBSECTION WebKitJavascriptResult>
 WebKitJavascriptResult
@@ -588,6 +591,35 @@
 </SECTION>
 
 <SECTION>
+<FILE>WebKitUserMessage</FILE>
+WebKitUserMessage
+webkit_user_message_new
+webkit_user_message_new_with_fd_list
+webkit_user_message_get_name
+webkit_user_message_get_parameters
+webkit_user_message_get_fd_list
+webkit_user_message_send_reply
+
+<SUBSECTION WebKitUserMessageError>
+WEBKIT_USER_MESSAGE_ERROR
+WebKitUserMessageError
+webkit_user_message_error_quark
+
+<SUBSECTION Standard>
+WebKitUserMessageClass
+WEBKIT_TYPE_USER_MESSAGE
+WEBKIT_USER_MESSAGE
+WEBKIT_IS_USER_MESSAGE
+WEBKIT_USER_MESSAGE_CLASS
+WEBKIT_IS_USER_MESSAGE_CLASS
+WEBKIT_USER_MESSAGE_GET_CLASS
+
+<SUBSECTION Private>
+WebKitUserMessagePrivate
+webkit_user_message_get_type
+</SECTION>
+
+<SECTION>
 <FILE>WebKitWindowProperties</FILE>
 WebKitWindowProperties
 webkit_window_properties_get_toolbar_visible
diff --git a/Source/WebKit/UIProcess/API/wpe/docs/wpe-docs.sgml b/Source/WebKit/UIProcess/API/wpe/docs/wpe-docs.sgml
index 66614d9..2fe7b20 100644
--- a/Source/WebKit/UIProcess/API/wpe/docs/wpe-docs.sgml
+++ b/Source/WebKit/UIProcess/API/wpe/docs/wpe-docs.sgml
@@ -57,6 +57,7 @@
     <xi:include href="xml/WebKitApplicationInfo.xml"/>
     <xi:include href="xml/WebKitColor.xml"/>
     <xi:include href="xml/WebKitGeolocationManager.xml"/>
+    <xi:include href="xml/WebKitUserMessage.xml"/>
   </chapter>
 
   <chapter>
@@ -93,5 +94,10 @@
     <xi:include href="xml/api-index-2.26.xml"><xi:fallback /></xi:include>
   </index>
 
+  <index id="api-index-2-28" role="2.28">
+    <title>Index of new symbols in 2.28</title>
+    <xi:include href="xml/api-index-2.28.xml"><xi:fallback /></xi:include>
+  </index>
+
   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
 </book>
diff --git a/Source/WebKit/UIProcess/API/wpe/webkit.h b/Source/WebKit/UIProcess/API/wpe/webkit.h
index 48523b4..9cc31cb 100644
--- a/Source/WebKit/UIProcess/API/wpe/webkit.h
+++ b/Source/WebKit/UIProcess/API/wpe/webkit.h
@@ -73,6 +73,7 @@
 #include <wpe/WebKitUserContentFilterStore.h>
 #include <wpe/WebKitUserContentManager.h>
 #include <wpe/WebKitUserMediaPermissionRequest.h>
+#include <wpe/WebKitUserMessage.h>
 #include <wpe/WebKitVersion.h>
 #include <wpe/WebKitWebContext.h>
 #include <wpe/WebKitWebResource.h>
diff --git a/Source/WebKit/UIProcess/WebPageProxy.h b/Source/WebKit/UIProcess/WebPageProxy.h
index 848f968..0ebf71b 100644
--- a/Source/WebKit/UIProcess/WebPageProxy.h
+++ b/Source/WebKit/UIProcess/WebPageProxy.h
@@ -301,6 +301,7 @@
 struct WebAutocorrectionData;
 struct WebPopupItem;
 struct URLSchemeTaskParameters;
+struct UserMessage;
 
 enum class ProcessSwapRequestedByClient;
 enum class UndoOrRedo : bool;
@@ -1736,6 +1737,11 @@
     void reachedApplicationCacheOriginQuota(const String& originIdentifier, uint64_t currentQuota, uint64_t totalBytesNeeded, Messages::WebPageProxy::ReachedApplicationCacheOriginQuota::DelayedReply&&);
     void requestGeolocationPermissionForFrame(uint64_t geolocationID, WebCore::FrameIdentifier, String originIdentifier);
 
+#if PLATFORM(GTK) || PLATFORM(WPE)
+    void sendMessageToWebView(UserMessage&&);
+    void sendMessageToWebViewWithReply(UserMessage&&, CompletionHandler<void(UserMessage&&)>&&);
+#endif
+
 #if ENABLE(MEDIA_STREAM)
     UserMediaPermissionRequestManagerProxy& userMediaPermissionRequestManager();
 #endif
diff --git a/Source/WebKit/UIProcess/WebPageProxy.messages.in b/Source/WebKit/UIProcess/WebPageProxy.messages.in
index 2d0bff8..3043ece 100644
--- a/Source/WebKit/UIProcess/WebPageProxy.messages.in
+++ b/Source/WebKit/UIProcess/WebPageProxy.messages.in
@@ -576,4 +576,9 @@
 #if ENABLE(WEB_AUTHN)
     SetMockWebAuthenticationConfiguration(struct WebCore::MockWebAuthenticationConfiguration configuration);
 #endif
+
+#if PLATFORM(GTK) || PLATFORM(WPE)
+    SendMessageToWebView(struct WebKit::UserMessage userMessage)
+    SendMessageToWebViewWithReply(struct WebKit::UserMessage userMessage) -> (struct WebKit::UserMessage replyMessage) Async
+#endif
 }
diff --git a/Source/WebKit/UIProcess/WebProcessPool.h b/Source/WebKit/UIProcess/WebProcessPool.h
index c6ba0fc..77d5ae5 100644
--- a/Source/WebKit/UIProcess/WebProcessPool.h
+++ b/Source/WebKit/UIProcess/WebProcessPool.h
@@ -502,6 +502,9 @@
     void addSandboxPath(const CString& path, SandboxPermission permission) { m_extraSandboxPaths.add(path, permission); };
     const HashMap<CString, SandboxPermission>& sandboxPaths() const { return m_extraSandboxPaths; };
     bool sandboxEnabled() const { return m_sandboxEnabled; };
+
+    void setUserMessageHandler(Function<void(UserMessage&&, CompletionHandler<void(UserMessage&&)>&&)>&& handler) { m_userMessageHandler = WTFMove(handler); }
+    const Function<void(UserMessage&&, CompletionHandler<void(UserMessage&&)>&&)>& userMessageHandler() const { return m_userMessageHandler; }
 #endif
     
     void setWebProcessHasUploads(WebCore::ProcessIdentifier);
@@ -781,6 +784,8 @@
 #if PLATFORM(GTK) || PLATFORM(WPE)
     bool m_sandboxEnabled { false };
     HashMap<CString, SandboxPermission> m_extraSandboxPaths;
+
+    Function<void(UserMessage&&, CompletionHandler<void(UserMessage&&)>&&)> m_userMessageHandler;
 #endif
 
     HashMap<WebCore::ProcessIdentifier, std::unique_ptr<ProcessAssertion>> m_processesWithUploads;
diff --git a/Source/WebKit/UIProcess/WebProcessProxy.h b/Source/WebKit/UIProcess/WebProcessProxy.h
index a97399e..8a47e813ff 100644
--- a/Source/WebKit/UIProcess/WebProcessProxy.h
+++ b/Source/WebKit/UIProcess/WebProcessProxy.h
@@ -80,6 +80,7 @@
 class WebUserContentControllerProxy;
 class WebsiteDataStore;
 enum class WebsiteDataType;
+struct UserMessage;
 struct WebNavigationDataStore;
 struct WebPageCreationParameters;
 struct WebPreferencesStore;
@@ -425,6 +426,11 @@
 
     void maybeShutDown();
 
+#if PLATFORM(GTK) || PLATFORM(WPE)
+    void sendMessageToWebContext(UserMessage&&);
+    void sendMessageToWebContextWithReply(UserMessage&&, CompletionHandler<void(UserMessage&&)>&&);
+#endif
+
     enum class IsWeak { No, Yes };
     template<typename T> class WeakOrStrongPtr {
     public:
diff --git a/Source/WebKit/UIProcess/WebProcessProxy.messages.in b/Source/WebKit/UIProcess/WebProcessProxy.messages.in
index d0ab943..e119e6c 100644
--- a/Source/WebKit/UIProcess/WebProcessProxy.messages.in
+++ b/Source/WebKit/UIProcess/WebProcessProxy.messages.in
@@ -77,4 +77,9 @@
     # Plug-in messages.
     void AddPlugInAutoStartOriginHash(String pageOrigin, uint32_t hash)
     void PlugInDidReceiveUserInteraction(uint32_t hash)
+
+#if PLATFORM(GTK) || PLATFORM(WPE)
+    SendMessageToWebContext(struct WebKit::UserMessage userMessage)
+    SendMessageToWebContextWithReply(struct WebKit::UserMessage userMessage) -> (struct WebKit::UserMessage replyMessage) Async
+#endif
 }
diff --git a/Source/WebKit/UIProcess/glib/WebProcessProxyGLib.cpp b/Source/WebKit/UIProcess/glib/WebProcessProxyGLib.cpp
index 9ca1c6b..0a84a75 100644
--- a/Source/WebKit/UIProcess/glib/WebProcessProxyGLib.cpp
+++ b/Source/WebKit/UIProcess/glib/WebProcessProxyGLib.cpp
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "WebProcessProxy.h"
 
+#include "UserMessage.h"
 #include "WebProcessPool.h"
 #include "WebsiteDataStore.h"
 #include <WebCore/PlatformDisplay.h>
@@ -56,4 +57,15 @@
     }
 }
 
+void WebProcessProxy::sendMessageToWebContextWithReply(UserMessage&& message, CompletionHandler<void(UserMessage&&)>&& completionHandler)
+{
+    if (const auto& userMessageHandler = m_processPool->userMessageHandler())
+        userMessageHandler(WTFMove(message), WTFMove(completionHandler));
+}
+
+void WebProcessProxy::sendMessageToWebContext(UserMessage&& message)
+{
+    sendMessageToWebContextWithReply(WTFMove(message), [](UserMessage&&) { });
+}
+
 };
diff --git a/Source/WebKit/UIProcess/gtk/WebPageProxyGtk.cpp b/Source/WebKit/UIProcess/gtk/WebPageProxyGtk.cpp
index 0867d98..540e3bc 100644
--- a/Source/WebKit/UIProcess/gtk/WebPageProxyGtk.cpp
+++ b/Source/WebKit/UIProcess/gtk/WebPageProxyGtk.cpp
@@ -28,7 +28,9 @@
 #include "WebPageProxy.h"
 
 #include "PageClientImpl.h"
+#include "WebKitUserMessage.h"
 #include "WebKitWebViewBasePrivate.h"
+#include "WebKitWebViewPrivate.h"
 #include "WebPageMessages.h"
 #include "WebPasteboardProxy.h"
 #include "WebProcessProxy.h"
@@ -166,4 +168,19 @@
     webkitWebViewBaseShowEmojiChooser(WEBKIT_WEB_VIEW_BASE(viewWidget()), caretRect, WTFMove(completionHandler));
 }
 
+void WebPageProxy::sendMessageToWebViewWithReply(UserMessage&& message, CompletionHandler<void(UserMessage&&)>&& completionHandler)
+{
+    if (!WEBKIT_IS_WEB_VIEW(viewWidget())) {
+        completionHandler(UserMessage(message.name, WEBKIT_USER_MESSAGE_UNHANDLED_MESSAGE));
+        return;
+    }
+
+    webkitWebViewDidReceiveUserMessage(WEBKIT_WEB_VIEW(viewWidget()), WTFMove(message), WTFMove(completionHandler));
+}
+
+void WebPageProxy::sendMessageToWebView(UserMessage&& message)
+{
+    sendMessageToWebViewWithReply(WTFMove(message), [](UserMessage&&) { });
+}
+
 } // namespace WebKit
diff --git a/Source/WebKit/UIProcess/wpe/WebPageProxyWPE.cpp b/Source/WebKit/UIProcess/wpe/WebPageProxyWPE.cpp
index 902ee6f..6d1b7c5 100644
--- a/Source/WebKit/UIProcess/wpe/WebPageProxyWPE.cpp
+++ b/Source/WebKit/UIProcess/wpe/WebPageProxyWPE.cpp
@@ -82,4 +82,14 @@
     notImplemented();
 }
 
+void WebPageProxy::sendMessageToWebViewWithReply(UserMessage&& message, CompletionHandler<void(UserMessage&&)>&& completionHandler)
+{
+    static_cast<PageClientImpl&>(pageClient()).sendMessageToWebView(WTFMove(message), WTFMove(completionHandler));
+}
+
+void WebPageProxy::sendMessageToWebView(UserMessage&& message)
+{
+    sendMessageToWebViewWithReply(WTFMove(message), [](UserMessage&&) { });
+}
+
 } // namespace WebKit
diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitExtensionManager.h b/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitExtensionManager.h
index 984c3a9..8c9955d 100644
--- a/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitExtensionManager.h
+++ b/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitExtensionManager.h
@@ -42,6 +42,8 @@
 
     __attribute__((visibility("default"))) void initialize(InjectedBundle*, API::Object*);
 
+    WebKitWebExtension* extension() const { return m_extension.get(); }
+
 private:
     WebKitExtensionManager();
 
diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebExtension.cpp b/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebExtension.cpp
index f05de8e..8eb5179 100644
--- a/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebExtension.cpp
+++ b/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebExtension.cpp
@@ -23,10 +23,13 @@
 #include "APIDictionary.h"
 #include "APIInjectedBundleBundleClient.h"
 #include "APIString.h"
+#include "WebKitUserMessagePrivate.h"
 #include "WebKitWebExtensionPrivate.h"
 #include "WebKitWebPagePrivate.h"
 #include "WebProcess.h"
+#include "WebProcessProxyMessages.h"
 #include <WebCore/GCController.h>
+#include <glib/gi18n-lib.h>
 #include <wtf/HashMap.h>
 #include <wtf/glib/GRefPtr.h>
 #include <wtf/glib/WTFGType.h>
@@ -111,6 +114,7 @@
 
 enum {
     PAGE_CREATED,
+    USER_MESSAGE_RECEIVED,
 
     LAST_SIGNAL
 };
@@ -146,6 +150,28 @@
         g_cclosure_marshal_VOID__OBJECT,
         G_TYPE_NONE, 1,
         WEBKIT_TYPE_WEB_PAGE);
+
+    /**
+     * WebKitWebExtension::user-message-received:
+     * @extension: the #WebKitWebExtension on which the signal is emitted
+     * @message: the #WebKitUserMessage received
+     *
+     * This signal is emitted when a #WebKitUserMessage is received from the
+     * #WebKitWebContext corresponding to @extension. Messages sent by #WebKitWebContext
+     * are always broadcasted to all #WebKitWebExtension<!-- -->s and they can't be
+     * replied to. Calling webkit_user_message_send_reply() will do nothing.
+     *
+     * Since: 2.28
+     */
+    signals[USER_MESSAGE_RECEIVED] = g_signal_new(
+        "user-message-received",
+        G_TYPE_FROM_CLASS(klass),
+        G_SIGNAL_RUN_LAST,
+        0,
+        nullptr, nullptr,
+        g_cclosure_marshal_generic,
+        G_TYPE_NONE, 1,
+        WEBKIT_TYPE_USER_MESSAGE);
 }
 
 class WebExtensionInjectedBundleClient final : public API::InjectedBundle::Client {
@@ -200,6 +226,13 @@
     return extension;
 }
 
+void webkitWebExtensionDidReceiveUserMessage(WebKitWebExtension* extension, UserMessage&& message)
+{
+    // Sink the floating ref.
+    GRefPtr<WebKitUserMessage> userMessage = webkitUserMessageCreate(WTFMove(message), [](UserMessage&&) { });
+    g_signal_emit(extension, signals[USER_MESSAGE_RECEIVED], 0, userMessage.get());
+}
+
 void webkitWebExtensionSetGarbageCollectOnPageDestroy(WebKitWebExtension* extension)
 {
 #if ENABLE(DEVELOPER_MODE)
@@ -229,3 +262,68 @@
 
     return 0;
 }
+
+/**
+ * webkit_web_extension_send_message_to_context:
+ * @extension: a #WebKitWebExtension
+ * @message: a #WebKitUserMessage
+ * @cancellable: (nullable): a #GCancellable or %NULL to ignore
+ * @callback: (scope async): (nullable): A #GAsyncReadyCallback to call when the request is satisfied or %NULL
+ * @user_data: (closure): the data to pass to callback function
+ *
+ * Send @message to the #WebKitWebContext corresponding to @extension. If @message is floating, it's consumed.
+ *
+ * If you don't expect any reply, or you simply want to ignore it, you can pass %NULL as @calback.
+ * When the operation is finished, @callback will be called. You can then call
+ * webkit_web_extension_send_message_to_context_finish() to get the message reply.
+ *
+ * Since: 2.28
+ */
+void webkit_web_extension_send_message_to_context(WebKitWebExtension* extension, WebKitUserMessage* message, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
+{
+    g_return_if_fail(WEBKIT_IS_WEB_EXTENSION(extension));
+    g_return_if_fail(WEBKIT_IS_USER_MESSAGE(message));
+
+    // We sink the reference in case of being floating.
+    GRefPtr<WebKitUserMessage> adoptedMessage = message;
+    if (!callback) {
+        WebProcess::singleton().parentProcessConnection()->send(Messages::WebProcessProxy::SendMessageToWebContext(webkitUserMessageGetMessage(message)), 0);
+        return;
+    }
+
+    GRefPtr<GTask> task = adoptGRef(g_task_new(extension, cancellable, callback, userData));
+    CompletionHandler<void(UserMessage&&)> completionHandler = [task = WTFMove(task)](UserMessage&& replyMessage) {
+        switch (replyMessage.type) {
+        case UserMessage::Type::Null:
+            g_task_return_new_error(task.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED, _("Operation was cancelled"));
+            break;
+        case UserMessage::Type::Message:
+            g_task_return_pointer(task.get(), g_object_ref_sink(webkitUserMessageCreate(WTFMove(replyMessage))), static_cast<GDestroyNotify>(g_object_unref));
+            break;
+        case UserMessage::Type::Error:
+            g_task_return_new_error(task.get(), WEBKIT_USER_MESSAGE_ERROR, replyMessage.errorCode, _("Message %s was not handled"), replyMessage.name.data());
+            break;
+        }
+    };
+    WebProcess::singleton().parentProcessConnection()->sendWithAsyncReply(Messages::WebProcessProxy::SendMessageToWebContextWithReply(webkitUserMessageGetMessage(message)), WTFMove(completionHandler));
+}
+
+/**
+ * webkit_web_extension_send_message_to_context_finish:
+ * @extension: a #WebKitWebExtension
+ * @result: a #GAsyncResult
+ * @error: return location for error or %NULL to ignor
+ *
+ * Finish an asynchronous operation started with webkit_web_extension_send_message_to_context().
+ *
+ * Returns: (transfer full): a #WebKitUserMessage with the reply or %NULL in case of error.
+ *
+ * Since: 2.28
+ */
+WebKitUserMessage* webkit_web_extension_send_message_to_context_finish(WebKitWebExtension* extension, GAsyncResult* result, GError** error)
+{
+    g_return_val_if_fail(WEBKIT_IS_WEB_EXTENSION(extension), nullptr);
+    g_return_val_if_fail(g_task_is_valid(result, extension), nullptr);
+
+    return WEBKIT_USER_MESSAGE(g_task_propagate_pointer(G_TASK(result), error));
+}
diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebExtensionPrivate.h b/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebExtensionPrivate.h
index e2739a9..b54340f 100644
--- a/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebExtensionPrivate.h
+++ b/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebExtensionPrivate.h
@@ -20,7 +20,9 @@
 #pragma once
 
 #include "InjectedBundle.h"
+#include "UserMessage.h"
 #include "WebKitWebExtension.h"
 
 WebKitWebExtension* webkitWebExtensionCreate(WebKit::InjectedBundle*);
+void webkitWebExtensionDidReceiveUserMessage(WebKitWebExtension*, WebKit::UserMessage&&);
 void webkitWebExtensionSetGarbageCollectOnPageDestroy(WebKitWebExtension*);
diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebPage.cpp b/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebPage.cpp
index f91d1be..0c7e7f7 100644
--- a/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebPage.cpp
+++ b/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebPage.cpp
@@ -41,10 +41,12 @@
 #include "WebKitScriptWorldPrivate.h"
 #include "WebKitURIRequestPrivate.h"
 #include "WebKitURIResponsePrivate.h"
+#include "WebKitUserMessagePrivate.h"
 #include "WebKitWebEditorPrivate.h"
 #include "WebKitWebHitTestResultPrivate.h"
 #include "WebKitWebPagePrivate.h"
 #include "WebKitWebProcessEnumTypes.h"
+#include "WebPageProxyMessages.h"
 #include "WebProcess.h"
 #include <WebCore/Document.h>
 #include <WebCore/DocumentLoader.h>
@@ -70,6 +72,7 @@
     FORM_CONTROLS_ASSOCIATED,
     FORM_CONTROLS_ASSOCIATED_FOR_FRAME,
     WILL_SUBMIT_FORM,
+    USER_MESSAGE_RECEIVED,
 
     LAST_SIGNAL
 };
@@ -667,6 +670,34 @@
         WEBKIT_TYPE_FRAME,
         G_TYPE_PTR_ARRAY,
         G_TYPE_PTR_ARRAY);
+
+    /**
+     * WebKitWebPage::user-message-received:
+     * @web_page: the #WebKitWebPage on which the signal is emitted
+     * @message: the #WebKitUserMessage received
+     *
+     * This signal is emitted when a #WebKitUserMessage is received from the
+     * #WebKitWebView corresponding to @web_page. You can reply to the message
+     * using webkit_user_message_send_reply().
+     *
+     * You can handle the user message asynchronously by calling g_object_ref() on
+     * @message and returning %TRUE. If the last reference of @message is removed
+     * and the message has been replied, the operation in the #WebKitWebView will
+     * finish with error %WEBKIT_USER_MESSAGE_UNHANDLED_MESSAGE.
+     *
+     * Returns: %TRUE if the message was handled, or %FALSE otherwise.
+     *
+     * Since: 2.28
+     */
+    signals[USER_MESSAGE_RECEIVED] = g_signal_new(
+        "user-message-received",
+        G_TYPE_FROM_CLASS(klass),
+        G_SIGNAL_RUN_LAST,
+        0,
+        g_signal_accumulator_true_handled, nullptr,
+        g_cclosure_marshal_generic,
+        G_TYPE_BOOLEAN, 1,
+        WEBKIT_TYPE_USER_MESSAGE);
 }
 
 WebPage* webkitWebPageGetPage(WebKitWebPage *webPage)
@@ -733,6 +764,14 @@
         ASSERT_NOT_REACHED();
 }
 
+void webkitWebPageDidReceiveUserMessage(WebKitWebPage* webPage, UserMessage&& message, CompletionHandler<void(UserMessage&&)>&& completionHandler)
+{
+    // Sink the floating ref.
+    GRefPtr<WebKitUserMessage> userMessage = webkitUserMessageCreate(WTFMove(message), WTFMove(completionHandler));
+    gboolean returnValue;
+    g_signal_emit(webPage, signals[USER_MESSAGE_RECEIVED], 0, userMessage.get(), &returnValue);
+}
+
 /**
  * webkit_web_page_get_dom_document:
  * @web_page: a #WebKitWebPage
@@ -822,3 +861,68 @@
 
     return webPage->priv->webEditor.get();
 }
+
+/**
+ * webkit_web_page_send_message_to_view:
+ * @web_page: a #WebKitWebPage
+ * @message: a #WebKitUserMessage
+ * @cancellable: (nullable): a #GCancellable or %NULL to ignore
+ * @callback: (scope async): (nullable): A #GAsyncReadyCallback to call when the request is satisfied or %NULL
+ * @user_data: (closure): the data to pass to callback function
+ *
+ * Send @message to the #WebKitWebView corresponding to @web_page. If @message is floating, it's consumed.
+ *
+ * If you don't expect any reply, or you simply want to ignore it, you can pass %NULL as @callback.
+ * When the operation is finished, @callback will be called. You can then call
+ * webkit_web_page_send_message_to_view_finish() to get the message reply.
+ *
+ * Since: 2.28
+ */
+void webkit_web_page_send_message_to_view(WebKitWebPage* webPage, WebKitUserMessage* message, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
+{
+    g_return_if_fail(WEBKIT_IS_WEB_PAGE(webPage));
+    g_return_if_fail(WEBKIT_IS_USER_MESSAGE(message));
+
+    // We sink the reference in case of being floating.
+    GRefPtr<WebKitUserMessage> adoptedMessage = message;
+    if (!callback) {
+        webPage->priv->webPage->send(Messages::WebPageProxy::SendMessageToWebView(webkitUserMessageGetMessage(message)));
+        return;
+    }
+
+    GRefPtr<GTask> task = adoptGRef(g_task_new(webPage, cancellable, callback, userData));
+    CompletionHandler<void(UserMessage&&)> completionHandler = [task = WTFMove(task)](UserMessage&& replyMessage) {
+        switch (replyMessage.type) {
+        case UserMessage::Type::Null:
+            g_task_return_new_error(task.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED, _("Operation was cancelled"));
+            break;
+        case UserMessage::Type::Message:
+            g_task_return_pointer(task.get(), g_object_ref_sink(webkitUserMessageCreate(WTFMove(replyMessage))), static_cast<GDestroyNotify>(g_object_unref));
+            break;
+        case UserMessage::Type::Error:
+            g_task_return_new_error(task.get(), WEBKIT_USER_MESSAGE_ERROR, replyMessage.errorCode, _("Message %s was not handled"), replyMessage.name.data());
+            break;
+        }
+    };
+    webPage->priv->webPage->sendWithAsyncReply(Messages::WebPageProxy::SendMessageToWebViewWithReply(webkitUserMessageGetMessage(message)), WTFMove(completionHandler));
+}
+
+/**
+ * webkit_web_page_send_message_to_view_finish:
+ * @web_page: a #WebKitWebPage
+ * @result: a #GAsyncResult
+ * @error: return location for error or %NULL to ignor
+ *
+ * Finish an asynchronous operation started with webkit_web_page_send_message_to_view().
+ *
+ * Returns: (transfer full): a #WebKitUserMessage with the reply or %NULL in case of error.
+ *
+ * Since: 2.28
+ */
+WebKitUserMessage* webkit_web_page_send_message_to_view_finish(WebKitWebPage* webPage, GAsyncResult* result, GError** error)
+{
+    g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), nullptr);
+    g_return_val_if_fail(g_task_is_valid(result, webPage), nullptr);
+
+    return WEBKIT_USER_MESSAGE(g_task_propagate_pointer(G_TASK(result), error));
+}
diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebPagePrivate.h b/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebPagePrivate.h
index 8dcd42a..2405148 100644
--- a/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebPagePrivate.h
+++ b/Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebPagePrivate.h
@@ -17,15 +17,15 @@
  * Boston, MA 02110-1301, USA.
  */
 
-#ifndef WebKitWebPagePrivate_h
-#define WebKitWebPagePrivate_h
+#pragma once
 
 #include "APIDictionary.h"
+#include "UserMessage.h"
 #include "WebKitWebPage.h"
 #include "WebPage.h"
 
 WebKitWebPage* webkitWebPageCreate(WebKit::WebPage*);
 void webkitWebPageDidReceiveMessage(WebKitWebPage*, const String& messageName, API::Dictionary& message);
+void webkitWebPageDidReceiveUserMessage(WebKitWebPage*, WebKit::UserMessage&&, CompletionHandler<void(WebKit::UserMessage&&)>&&);
 WebKit::WebPage* webkitWebPageGetPage(WebKitWebPage*);
 
-#endif // WebKitWebPagePrivate_h
diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/gtk/WebKitWebExtension.h b/Source/WebKit/WebProcess/InjectedBundle/API/gtk/WebKitWebExtension.h
index 34136a3..5422fe5 100644
--- a/Source/WebKit/WebProcess/InjectedBundle/API/gtk/WebKitWebExtension.h
+++ b/Source/WebKit/WebProcess/InjectedBundle/API/gtk/WebKitWebExtension.h
@@ -26,6 +26,7 @@
 
 #include <glib-object.h>
 #include <webkit2/WebKitDefines.h>
+#include <webkit2/WebKitUserMessage.h>
 #include <webkit2/WebKitWebPage.h>
 
 G_BEGIN_DECLS
@@ -76,11 +77,23 @@
 };
 
 WEBKIT_API GType
-webkit_web_extension_get_type (void);
+webkit_web_extension_get_type                       (void);
 
 WEBKIT_API WebKitWebPage *
-webkit_web_extension_get_page (WebKitWebExtension *extension,
-                               guint64             page_id);
+webkit_web_extension_get_page                       (WebKitWebExtension *extension,
+                                                     guint64             page_id);
+
+WEBKIT_API void
+webkit_web_extension_send_message_to_context        (WebKitWebExtension *extension,
+                                                     WebKitUserMessage  *message,
+                                                     GCancellable       *cancellable,
+                                                     GAsyncReadyCallback callback,
+                                                     gpointer            user_data);
+
+WEBKIT_API WebKitUserMessage *
+webkit_web_extension_send_message_to_context_finish (WebKitWebExtension *extension,
+                                                     GAsyncResult       *result,
+                                                     GError            **error);
 
 G_END_DECLS
 
diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/gtk/WebKitWebExtensionAutocleanups.h b/Source/WebKit/WebProcess/InjectedBundle/API/gtk/WebKitWebExtensionAutocleanups.h
index f28b19b..28caf3a 100644
--- a/Source/WebKit/WebProcess/InjectedBundle/API/gtk/WebKitWebExtensionAutocleanups.h
+++ b/Source/WebKit/WebProcess/InjectedBundle/API/gtk/WebKitWebExtensionAutocleanups.h
@@ -29,6 +29,7 @@
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitFrame, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitScriptWorld, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitUserMessage, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitWebEditor, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitWebExtension, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitWebHitTestResult, g_object_unref)
diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/gtk/WebKitWebPage.h b/Source/WebKit/WebProcess/InjectedBundle/API/gtk/WebKitWebPage.h
index 0a486dc..315ac8b 100644
--- a/Source/WebKit/WebProcess/InjectedBundle/API/gtk/WebKitWebPage.h
+++ b/Source/WebKit/WebProcess/InjectedBundle/API/gtk/WebKitWebPage.h
@@ -27,6 +27,7 @@
 #include <glib-object.h>
 #include <webkit2/WebKitDefines.h>
 #include <webkit2/WebKitFrame.h>
+#include <webkit2/WebKitUserMessage.h>
 #include <webkit2/WebKitWebEditor.h>
 #include <webkitdom/webkitdom.h>
 
@@ -74,22 +75,34 @@
 };
 
 WEBKIT_API GType
-webkit_web_page_get_type         (void);
+webkit_web_page_get_type                    (void);
 
 WEBKIT_API WebKitDOMDocument *
-webkit_web_page_get_dom_document (WebKitWebPage *web_page);
+webkit_web_page_get_dom_document            (WebKitWebPage *web_page);
 
 WEBKIT_API guint64
-webkit_web_page_get_id           (WebKitWebPage *web_page);
+webkit_web_page_get_id                      (WebKitWebPage *web_page);
 
 WEBKIT_API const gchar *
-webkit_web_page_get_uri          (WebKitWebPage *web_page);
+webkit_web_page_get_uri                     (WebKitWebPage *web_page);
 
 WEBKIT_API WebKitFrame *
-webkit_web_page_get_main_frame   (WebKitWebPage *web_page);
+webkit_web_page_get_main_frame              (WebKitWebPage *web_page);
 
 WEBKIT_API WebKitWebEditor *
-webkit_web_page_get_editor       (WebKitWebPage *web_page);
+webkit_web_page_get_editor                  (WebKitWebPage *web_page);
+
+WEBKIT_API void
+webkit_web_page_send_message_to_view        (WebKitWebPage      *web_page,
+                                             WebKitUserMessage  *message,
+                                             GCancellable       *cancellable,
+                                             GAsyncReadyCallback callback,
+                                             gpointer            user_data);
+
+WEBKIT_API WebKitUserMessage *
+webkit_web_page_send_message_to_view_finish (WebKitWebPage      *web_page,
+                                             GAsyncResult       *result,
+                                             GError            **error);
 
 G_END_DECLS
 
diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/gtk/webkit-web-extension.h b/Source/WebKit/WebProcess/InjectedBundle/API/gtk/webkit-web-extension.h
index 1c95f1d..bc4b424 100644
--- a/Source/WebKit/WebProcess/InjectedBundle/API/gtk/webkit-web-extension.h
+++ b/Source/WebKit/WebProcess/InjectedBundle/API/gtk/webkit-web-extension.h
@@ -34,6 +34,7 @@
 #include <webkit2/WebKitScriptWorld.h>
 #include <webkit2/WebKitURIRequest.h>
 #include <webkit2/WebKitURIResponse.h>
+#include <webkit2/WebKitUserMessage.h>
 #include <webkit2/WebKitVersion.h>
 #include <webkit2/WebKitWebEditor.h>
 #include <webkit2/WebKitWebExtension.h>
diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/wpe/WebKitWebExtension.h b/Source/WebKit/WebProcess/InjectedBundle/API/wpe/WebKitWebExtension.h
index a6ffb48..09aee38 100644
--- a/Source/WebKit/WebProcess/InjectedBundle/API/wpe/WebKitWebExtension.h
+++ b/Source/WebKit/WebProcess/InjectedBundle/API/wpe/WebKitWebExtension.h
@@ -26,6 +26,7 @@
 
 #include <glib-object.h>
 #include <wpe/WebKitDefines.h>
+#include <wpe/WebKitUserMessage.h>
 #include <wpe/WebKitWebPage.h>
 
 G_BEGIN_DECLS
@@ -76,11 +77,23 @@
 };
 
 WEBKIT_API GType
-webkit_web_extension_get_type (void);
+webkit_web_extension_get_type                       (void);
 
 WEBKIT_API WebKitWebPage *
-webkit_web_extension_get_page (WebKitWebExtension *extension,
-                               guint64             page_id);
+webkit_web_extension_get_page                       (WebKitWebExtension *extension,
+                                                     guint64             page_id);
+
+WEBKIT_API void
+webkit_web_extension_send_message_to_context        (WebKitWebExtension *extension,
+                                                     WebKitUserMessage  *message,
+                                                     GCancellable       *cancellable,
+                                                     GAsyncReadyCallback callback,
+                                                     gpointer            user_data);
+
+WEBKIT_API WebKitUserMessage *
+webkit_web_extension_send_message_to_context_finish (WebKitWebExtension *extension,
+                                                     GAsyncResult       *result,
+                                                     GError            **error);
 
 G_END_DECLS
 
diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/wpe/WebKitWebExtensionAutocleanups.h b/Source/WebKit/WebProcess/InjectedBundle/API/wpe/WebKitWebExtensionAutocleanups.h
index 7330e4a..3540676 100644
--- a/Source/WebKit/WebProcess/InjectedBundle/API/wpe/WebKitWebExtensionAutocleanups.h
+++ b/Source/WebKit/WebProcess/InjectedBundle/API/wpe/WebKitWebExtensionAutocleanups.h
@@ -29,6 +29,7 @@
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitFrame, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitScriptWorld, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitUserMessage, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitWebEditor, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitWebExtension, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitWebHitTestResult, g_object_unref)
diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/wpe/WebKitWebPage.h b/Source/WebKit/WebProcess/InjectedBundle/API/wpe/WebKitWebPage.h
index 955f087..1841e4a 100644
--- a/Source/WebKit/WebProcess/InjectedBundle/API/wpe/WebKitWebPage.h
+++ b/Source/WebKit/WebProcess/InjectedBundle/API/wpe/WebKitWebPage.h
@@ -27,6 +27,7 @@
 #include <glib-object.h>
 #include <wpe/WebKitDefines.h>
 #include <wpe/WebKitFrame.h>
+#include <wpe/WebKitUserMessage.h>
 #include <wpe/WebKitWebEditor.h>
 #include <wpe/webkitdom.h>
 
@@ -72,22 +73,35 @@
 };
 
 WEBKIT_API GType
-webkit_web_page_get_type         (void);
+webkit_web_page_get_type                    (void);
 
 WEBKIT_API WebKitDOMDocument *
-webkit_web_page_get_dom_document (WebKitWebPage *web_page);
+webkit_web_page_get_dom_document            (WebKitWebPage *web_page);
 
 WEBKIT_API guint64
-webkit_web_page_get_id           (WebKitWebPage *web_page);
+webkit_web_page_get_id                      (WebKitWebPage *web_page);
 
 WEBKIT_API const gchar *
-webkit_web_page_get_uri          (WebKitWebPage *web_page);
+webkit_web_page_get_uri                     (WebKitWebPage *web_page);
 
 WEBKIT_API WebKitFrame *
-webkit_web_page_get_main_frame   (WebKitWebPage *web_page);
+webkit_web_page_get_main_frame              (WebKitWebPage *web_page);
 
 WEBKIT_API WebKitWebEditor *
-webkit_web_page_get_editor       (WebKitWebPage *web_page);
+webkit_web_page_get_editor                  (WebKitWebPage *web_page);
+
+WEBKIT_API void
+webkit_web_page_send_message_to_view        (WebKitWebPage      *web_page,
+                                             WebKitUserMessage  *message,
+                                             GCancellable       *cancellable,
+                                             GAsyncReadyCallback callback,
+                                             gpointer            user_data);
+
+WEBKIT_API WebKitUserMessage *
+webkit_web_page_send_message_to_view_finish (WebKitWebPage      *web_page,
+                                             GAsyncResult       *result,
+                                             GError            **error);
+
 
 G_END_DECLS
 
diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/wpe/docs/wpe-webextensions-1.0-sections.txt b/Source/WebKit/WebProcess/InjectedBundle/API/wpe/docs/wpe-webextensions-1.0-sections.txt
index e448296..5fda958 100644
--- a/Source/WebKit/WebProcess/InjectedBundle/API/wpe/docs/wpe-webextensions-1.0-sections.txt
+++ b/Source/WebKit/WebProcess/InjectedBundle/API/wpe/docs/wpe-webextensions-1.0-sections.txt
@@ -13,6 +13,8 @@
 WebKitWebExtensionInitializeFunction
 WebKitWebExtensionInitializeWithUserDataFunction
 webkit_web_extension_get_page
+webkit_web_extension_send_message_to_context
+webkit_web_extension_send_message_to_context_finish
 
 <SUBSECTION Standard>
 WebKitWebExtensionClass
@@ -37,6 +39,8 @@
 webkit_web_page_get_uri
 webkit_web_page_get_main_frame
 webkit_web_page_get_editor
+webkit_web_page_send_message_to_view
+webkit_web_page_send_message_to_view_finish
 
 <SUBSECTION Standard>
 WebKitWebPageClass
diff --git a/Source/WebKit/WebProcess/InjectedBundle/API/wpe/webkit-web-extension.h b/Source/WebKit/WebProcess/InjectedBundle/API/wpe/webkit-web-extension.h
index 69efdd9..d5d3354 100644
--- a/Source/WebKit/WebProcess/InjectedBundle/API/wpe/webkit-web-extension.h
+++ b/Source/WebKit/WebProcess/InjectedBundle/API/wpe/webkit-web-extension.h
@@ -34,6 +34,7 @@
 #include <wpe/WebKitScriptWorld.h>
 #include <wpe/WebKitURIRequest.h>
 #include <wpe/WebKitURIResponse.h>
+#include <wpe/WebKitUserMessage.h>
 #include <wpe/WebKitWebEditor.h>
 #include <wpe/WebKitWebExtension.h>
 #include <wpe/WebKitWebHitTestResult.h>
diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.h b/Source/WebKit/WebProcess/WebPage/WebPage.h
index 45efea7..22b3875 100644
--- a/Source/WebKit/WebProcess/WebPage/WebPage.h
+++ b/Source/WebKit/WebProcess/WebPage/WebPage.h
@@ -268,6 +268,8 @@
 struct InteractionInformationRequest;
 struct LoadParameters;
 struct PrintInfo;
+struct TextInputContext;
+struct UserMessage;
 struct WebAutocorrectionData;
 struct WebAutocorrectionContext;
 struct WebPageCreationParameters;
@@ -1630,6 +1632,11 @@
 
     void paintSnapshotAtSize(const WebCore::IntRect&, const WebCore::IntSize&, SnapshotOptions, WebCore::Frame&, WebCore::FrameView&, WebCore::GraphicsContext&);
 
+#if PLATFORM(GTK) || PLATFORM(WPE)
+    void sendMessageToWebExtension(UserMessage&&);
+    void sendMessageToWebExtensionWithReply(UserMessage&&, CompletionHandler<void(UserMessage&&)>&&);
+#endif
+
     WebCore::PageIdentifier m_identifier;
 
     std::unique_ptr<WebCore::Page> m_page;
diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.messages.in b/Source/WebKit/WebProcess/WebPage/WebPage.messages.in
index 1506489..f6c0264 100644
--- a/Source/WebKit/WebProcess/WebPage/WebPage.messages.in
+++ b/Source/WebKit/WebProcess/WebPage/WebPage.messages.in
@@ -580,4 +580,8 @@
     SystemPreviewActionTriggered(struct WebCore::SystemPreviewInfo previewInfo, String message)
 #endif
 
+#if PLATFORM(GTK) || PLATFORM(WPE)
+    SendMessageToWebExtension(struct WebKit::UserMessage userMessage)
+    SendMessageToWebExtensionWithReply(struct WebKit::UserMessage userMessage) -> (struct WebKit::UserMessage replyMessage) Async
+#endif
 }
diff --git a/Source/WebKit/WebProcess/WebPage/glib/WebPageGLib.cpp b/Source/WebKit/WebProcess/WebPage/glib/WebPageGLib.cpp
new file mode 100644
index 0000000..b0598c6
--- /dev/null
+++ b/Source/WebKit/WebProcess/WebPage/glib/WebPageGLib.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WebPage.h"
+
+#include "UserMessage.h"
+#include "WebKitExtensionManager.h"
+#include "WebKitUserMessage.h"
+#include "WebKitWebExtension.h"
+#include "WebKitWebPagePrivate.h"
+
+namespace WebKit {
+
+void WebPage::sendMessageToWebExtensionWithReply(UserMessage&& message, CompletionHandler<void(UserMessage&&)>&& completionHandler)
+{
+    auto* extension = WebKitExtensionManager::singleton().extension();
+    if (!extension) {
+        completionHandler(UserMessage(message.name, WEBKIT_USER_MESSAGE_UNHANDLED_MESSAGE));
+        return;
+    }
+
+    auto* page = webkit_web_extension_get_page(extension, m_identifier.toUInt64());
+    if (!page) {
+        completionHandler(UserMessage(message.name, WEBKIT_USER_MESSAGE_UNHANDLED_MESSAGE));
+        return;
+    }
+
+    webkitWebPageDidReceiveUserMessage(page, WTFMove(message), WTFMove(completionHandler));
+}
+
+void WebPage::sendMessageToWebExtension(UserMessage&& message)
+{
+    sendMessageToWebExtensionWithReply(WTFMove(message), [](UserMessage&&) { });
+}
+
+} // namespace WebKit
diff --git a/Source/WebKit/WebProcess/WebProcess.h b/Source/WebKit/WebProcess/WebProcess.h
index 798d086..dd60e8d 100644
--- a/Source/WebKit/WebProcess/WebProcess.h
+++ b/Source/WebKit/WebProcess/WebProcess.h
@@ -117,6 +117,7 @@
 class WebLoaderStrategy;
 class WebPage;
 class WebPageGroupProxy;
+struct UserMessage;
 struct WebProcessCreationParameters;
 struct WebProcessDataStoreParameters;
 class WebProcessSupplement;
@@ -465,6 +466,10 @@
 
     void clearCurrentModifierStateForTesting();
 
+#if PLATFORM(GTK) || PLATFORM(WPE)
+    void sendMessageToWebExtension(UserMessage&&);
+#endif
+
     RefPtr<WebConnectionToUIProcess> m_webConnection;
 
     HashMap<WebCore::PageIdentifier, RefPtr<WebPage>> m_pageMap;
diff --git a/Source/WebKit/WebProcess/WebProcess.messages.in b/Source/WebKit/WebProcess/WebProcess.messages.in
index 2476c43..c7f3e0a 100644
--- a/Source/WebKit/WebProcess/WebProcess.messages.in
+++ b/Source/WebKit/WebProcess/WebProcess.messages.in
@@ -157,4 +157,8 @@
 #if PLATFORM(IOS_FAMILY)
     UnblockAccessibilityServer(WebKit::SandboxExtension::Handle handle)
 #endif
+
+#if PLATFORM(GTK) || PLATFORM(WPE)
+    SendMessageToWebExtension(struct WebKit::UserMessage userMessage)
+#endif
 }
diff --git a/Source/WebKit/WebProcess/glib/WebProcessGLib.cpp b/Source/WebKit/WebProcess/glib/WebProcessGLib.cpp
index 9b00374..007f044 100644
--- a/Source/WebKit/WebProcess/glib/WebProcessGLib.cpp
+++ b/Source/WebKit/WebProcess/glib/WebProcessGLib.cpp
@@ -27,6 +27,8 @@
 #include "config.h"
 #include "WebProcess.h"
 
+#include "WebKitExtensionManager.h"
+#include "WebKitWebExtensionPrivate.h"
 #include "WebProcessCreationParameters.h"
 
 #if USE(GSTREAMER)
@@ -97,4 +99,10 @@
 {
 }
 
+void WebProcess::sendMessageToWebExtension(UserMessage&& message)
+{
+    if (auto* extension = WebKitExtensionManager::singleton().extension())
+        webkitWebExtensionDidReceiveUserMessage(extension, WTFMove(message));
+}
+
 } // namespace WebKit
diff --git a/Tools/ChangeLog b/Tools/ChangeLog
index 8930b31..072f817 100644
--- a/Tools/ChangeLog
+++ b/Tools/ChangeLog
@@ -1,3 +1,33 @@
+2019-10-15  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GTK][WPE] Add user messages API
+        https://bugs.webkit.org/show_bug.cgi?id=202847
+
+        Reviewed by Adrian Perez de Castro.
+
+        Add a test to check the new API.
+
+        * TestWebKitAPI/Tests/WebKitGLib/TestWebExtensions.cpp:
+        (UserMessageTest::webViewUserMessageReceivedCallback):
+        (UserMessageTest::webContextUserMessageReceivedCallback):
+        (UserMessageTest::UserMessageTest):
+        (UserMessageTest::~UserMessageTest):
+        (UserMessageTest::sendMessage):
+        (UserMessageTest::sendMedssageToAllExtensions):
+        (UserMessageTest::viewUserMessageReceived):
+        (UserMessageTest::contextUserMessageReceived):
+        (UserMessageTest::waitUntilViewMessageReceived):
+        (UserMessageTest::waitUntilContextMessageReceived):
+        (readFileDescritpor):
+        (testWebExtensionUserMessages):
+        (beforeAll):
+        * TestWebKitAPI/Tests/WebKitGLib/WebExtensionTest.cpp:
+        (documentLoadedCallback):
+        (pageMessageReceivedCallback):
+        (pageCreatedCallback):
+        (extensionMessageReceivedCallback):
+        (webkit_web_extension_initialize_with_user_data):
+
 2019-10-15  Aakash Jain  <aakash_jain@apple.com>
 
         Improve summary for WebKitPerl Tests build step
diff --git a/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebExtensions.cpp b/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebExtensions.cpp
index 24e1367..06b03ae 100644
--- a/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebExtensions.cpp
+++ b/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebExtensions.cpp
@@ -20,6 +20,7 @@
 #include "config.h"
 
 #include "WebViewTest.h"
+#include <gio/gunixfdlist.h>
 #include <wtf/glib/GRefPtr.h>
 
 static GUniquePtr<char> scriptDialogResult;
@@ -537,6 +538,263 @@
     checkTitle(test, proxy.get(), "Title6");
 }
 
+class UserMessageTest : public WebViewTest {
+public:
+    MAKE_GLIB_TEST_FIXTURE(UserMessageTest);
+
+    static gboolean webViewUserMessageReceivedCallback(WebKitWebView*, WebKitUserMessage* message, UserMessageTest* test)
+    {
+        return test->viewUserMessageReceived(message);
+    }
+
+    static gboolean webContextUserMessageReceivedCallback(WebKitWebContext*, WebKitUserMessage* message, UserMessageTest* test)
+    {
+        return test->contextUserMessageReceived(message);
+    }
+
+    UserMessageTest()
+    {
+        g_signal_connect(m_webContext.get(), "user-message-received", G_CALLBACK(webContextUserMessageReceivedCallback), this);
+        g_signal_connect(m_webView, "user-message-received", G_CALLBACK(webViewUserMessageReceivedCallback), this);
+    }
+
+    ~UserMessageTest()
+    {
+        g_signal_handlers_disconnect_matched(m_webContext.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
+        g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
+    }
+
+    WebKitUserMessage* sendMessage(WebKitUserMessage* message, GError** error = nullptr)
+    {
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(message));
+        m_receivedViewMessage = nullptr;
+        webkit_web_view_send_message_to_page(m_webView, message, nullptr, [](GObject*, GAsyncResult* result, gpointer userData) {
+            auto* test = static_cast<UserMessageTest*>(userData);
+            test->m_receivedViewMessage = adoptGRef(webkit_web_view_send_message_to_page_finish(test->m_webView, result, &test->m_receivedError.outPtr()));
+            if (test->m_receivedViewMessage)
+                test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(test->m_receivedViewMessage.get()));
+            else
+                g_assert_nonnull(test->m_receivedError.get());
+            test->quitMainLoop();
+        }, this);
+        g_main_loop_run(m_mainLoop);
+        if (error)
+            *error = m_receivedError.get();
+        return m_receivedViewMessage.get();
+    }
+
+    void sendMessageToAllExtensions(WebKitUserMessage* message)
+    {
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(message));
+        webkit_web_context_send_message_to_all_extensions(m_webContext.get(), message);
+    }
+
+    bool viewUserMessageReceived(WebKitUserMessage* message)
+    {
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(message));
+        if (!g_strcmp0(m_expectedViewMessageName.data(), webkit_user_message_get_name(message))) {
+            m_receivedViewMessage = message;
+            quitMainLoop();
+
+            return true;
+        }
+
+        return false;
+    }
+
+    bool contextUserMessageReceived(WebKitUserMessage* message)
+    {
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(message));
+        if (!g_strcmp0(m_expectedContextMessageName.data(), webkit_user_message_get_name(message))) {
+            m_receivedContextMessage = message;
+            quitMainLoop();
+
+            return true;
+        }
+
+        return false;
+    }
+
+    WebKitUserMessage* waitUntilViewMessageReceived(const char* messageName)
+    {
+        m_expectedViewMessageName = messageName;
+        g_main_loop_run(m_mainLoop);
+        m_expectedViewMessageName = { };
+        return m_receivedViewMessage.get();
+    }
+
+    WebKitUserMessage* waitUntilContextMessageReceived(const char* messageName)
+    {
+        m_expectedContextMessageName = messageName;
+        g_main_loop_run(m_mainLoop);
+        m_expectedContextMessageName = { };
+        return m_receivedContextMessage.get();
+    }
+
+    GRefPtr<WebKitUserMessage> m_receivedViewMessage;
+    GRefPtr<WebKitUserMessage> m_receivedContextMessage;
+    GUniqueOutPtr<GError> m_receivedError;
+    CString m_expectedViewMessageName;
+    CString m_expectedContextMessageName;
+};
+
+static bool readFileDescriptor(int fd, char** contents, gsize* length)
+{
+    GString* bufferString = g_string_new(nullptr);
+    while (true) {
+        gchar buffer[4096];
+        gssize bytesRead = read(fd, buffer, sizeof(buffer));
+        if (bytesRead == -1) {
+            if (errno == EAGAIN || errno == EWOULDBLOCK)
+                continue;
+
+            g_string_free(bufferString, TRUE);
+
+            return false;
+        }
+
+        if (!bytesRead)
+            break;
+
+        g_string_append_len(bufferString, buffer, bytesRead);
+    }
+
+    *length = bufferString->len;
+    *contents = g_string_free(bufferString, FALSE);
+
+    return true;
+}
+
+static void testWebExtensionUserMessages(UserMessageTest* test, gconstpointer)
+{
+    // Normal message with a reply.
+    auto* message = webkit_user_message_new("Test.Hello", g_variant_new("s", "WebProcess"));
+    g_assert_cmpstr(webkit_user_message_get_name(message), ==, "Test.Hello");
+    auto* parameters = webkit_user_message_get_parameters(message);
+    g_assert_nonnull(parameters);
+    const char* parameter = nullptr;
+    g_variant_get(parameters, "&s", &parameter);
+    g_assert_cmpstr(parameter, ==, "WebProcess");
+    g_assert_null(webkit_user_message_get_fd_list(message));
+    auto* reply = test->sendMessage(message);
+    g_assert_true(WEBKIT_IS_USER_MESSAGE(reply));
+    g_assert_cmpstr(webkit_user_message_get_name(reply), ==, "Test.Hello");
+    parameters = webkit_user_message_get_parameters(reply);
+    g_assert_nonnull(parameters);
+    parameter = nullptr;
+    g_variant_get(parameters, "&s", &parameter);
+    g_assert_cmpstr(parameter, ==, "UIProcess");
+    g_assert_null(webkit_user_message_get_fd_list(reply));
+
+    // Message with no parameters.
+    message = webkit_user_message_new("Test.Ping", nullptr);
+    g_assert_null(webkit_user_message_get_parameters(message));
+    reply = test->sendMessage(message);
+    g_assert_true(WEBKIT_IS_USER_MESSAGE(reply));
+    g_assert_cmpstr(webkit_user_message_get_name(reply), ==, "Test.Pong");
+    g_assert_null(webkit_user_message_get_parameters(reply));
+
+    // Message with maybe type in parameters.
+    message = webkit_user_message_new("Test.Optional", g_variant_new("(sms)", "Hello", "World"));
+    reply = test->sendMessage(message);
+    g_assert_true(WEBKIT_IS_USER_MESSAGE(reply));
+    g_assert_cmpstr(webkit_user_message_get_name(reply), ==, "Test.Optional");
+    parameters = webkit_user_message_get_parameters(reply);
+    g_assert_nonnull(parameters);
+    g_variant_get(parameters, "&s", &parameter);
+    g_assert_cmpstr(parameter, ==, "World");
+    message = webkit_user_message_new("Test.Optional", g_variant_new("(sms)", "Hello", nullptr));
+    reply = test->sendMessage(message);
+    g_assert_true(WEBKIT_IS_USER_MESSAGE(reply));
+    g_assert_cmpstr(webkit_user_message_get_name(reply), ==, "Test.Optional");
+    parameters = webkit_user_message_get_parameters(reply);
+    g_assert_nonnull(parameters);
+    g_variant_get(parameters, "&s", &parameter);
+    g_assert_cmpstr(parameter, ==, "NULL");
+
+    // Message with file descriptors.
+    GUniquePtr<char> filename(g_build_filename(Test::getResourcesDir().data(), "simple.json", nullptr));
+    reply = test->sendMessage(webkit_user_message_new("Test.OpenFile", g_variant_new("s", filename.get())));
+    g_assert_true(WEBKIT_IS_USER_MESSAGE(reply));
+    parameters = webkit_user_message_get_parameters(reply);
+    g_assert_nonnull(parameters);
+    gint32 handle;
+    g_variant_get(parameters, "h", &handle);
+    g_assert_cmpint(handle, ==, 0);
+    auto* fdList = webkit_user_message_get_fd_list(reply);
+    g_assert_true(G_IS_UNIX_FD_LIST(fdList));
+    test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(fdList));
+    g_assert_cmpint(g_unix_fd_list_get_length(fdList), ==, 1);
+    GUniqueOutPtr<GError> error;
+    int fd = g_unix_fd_list_get(fdList, handle, &error.outPtr());
+    g_assert_cmpint(fd, !=, -1);
+    g_assert_no_error(error.get());
+    GUniqueOutPtr<char> fdContents;
+    gsize fdContentsLength;
+    g_assert_true(readFileDescriptor(fd, &fdContents.outPtr(), &fdContentsLength));
+    close(fd);
+    GUniqueOutPtr<char> fileContents;
+    gsize fileContentsLength;
+    g_assert_true(g_file_get_contents(filename.get(), &fileContents.outPtr(), &fileContentsLength, nullptr));
+    g_assert_cmpmem(fdContents.get(), fdContentsLength, fileContents.get(), fileContentsLength);
+
+    // Unhandled message.
+    GError* messageError = nullptr;
+    reply = test->sendMessage(webkit_user_message_new("Test.Invalid", nullptr), &messageError);
+    g_assert_null(reply);
+    g_assert_error(messageError, WEBKIT_USER_MESSAGE_ERROR, WEBKIT_USER_MESSAGE_UNHANDLED_MESSAGE);
+
+    // Message that is never replied.
+    GRefPtr<WebKitWebView> webView = WEBKIT_WEB_VIEW(Test::createWebView(test->m_webContext.get()));
+    webkit_web_view_send_message_to_page(webView.get(), webkit_user_message_new("Test.Infinite", nullptr), nullptr,
+        [](GObject* object, GAsyncResult* result, gpointer userData) {
+            auto* test = static_cast<UserMessageTest*>(userData);
+            GUniqueOutPtr<GError> error;
+            g_assert_null(webkit_web_view_send_message_to_page_finish(WEBKIT_WEB_VIEW(object), result, &error.outPtr()));
+            g_assert_error(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED);
+            test->quitMainLoop();
+        }, test);
+    g_main_loop_run(test->m_mainLoop);
+
+    // Wait for received message.
+    test->loadHtml("<html><body></body></html>", nullptr);
+    message = test->waitUntilViewMessageReceived("DocumentLoaded");
+    g_assert_true(WEBKIT_IS_USER_MESSAGE(message));
+    g_assert_cmpstr(webkit_user_message_get_name(message), ==, "DocumentLoaded");
+
+    // Reply to a message received from the web process.
+    webkit_web_view_send_message_to_page(test->m_webView, webkit_user_message_new("Test.AsyncPing", nullptr), nullptr, nullptr, nullptr);
+    message = test->waitUntilViewMessageReceived("Test.Ping");
+    g_assert_true(WEBKIT_IS_USER_MESSAGE(message));
+    g_assert_cmpstr(webkit_user_message_get_name(message), ==, "Test.Ping");
+    webkit_user_message_send_reply(message, webkit_user_message_new("Test.Pong", nullptr));
+    message = test->waitUntilViewMessageReceived("Test.AsyncPong");
+    g_assert_true(WEBKIT_IS_USER_MESSAGE(message));
+    g_assert_cmpstr(webkit_user_message_get_name(message), ==, "Test.AsyncPong");
+
+    // Create a new page and wait for page created message.
+    webView = WEBKIT_WEB_VIEW(Test::createWebView(test->m_webContext.get()));
+    webkit_web_view_load_html(webView.get(), "<html><body></body></html>", nullptr);
+    message = test->waitUntilContextMessageReceived("PageCreated");
+    g_assert_true(WEBKIT_IS_USER_MESSAGE(message));
+    g_assert_cmpstr(webkit_user_message_get_name(message), ==, "PageCreated");
+    parameters = webkit_user_message_get_parameters(message);
+    g_assert_nonnull(parameters);
+    guint64 pageID;
+    g_variant_get(parameters, "(t)", &pageID);
+    g_assert_cmpuint(pageID, ==, webkit_web_view_get_page_id(webView.get()));
+
+    // Request to start a ping to all processes.
+    test->sendMessageToAllExtensions(webkit_user_message_new("Test.RequestPing", nullptr));
+    // We should received two ping requests.
+    GRefPtr<WebKitUserMessage> ping1 = test->waitUntilContextMessageReceived("Ping");
+    GRefPtr<WebKitUserMessage> ping2 = test->waitUntilContextMessageReceived("Ping");
+    webkit_user_message_send_reply(ping1.get(), webkit_user_message_new("Pong", nullptr));
+    test->waitUntilContextMessageReceived("Test.FinishedPingRequest");
+    webkit_user_message_send_reply(ping2.get(), webkit_user_message_new("Pong", nullptr));
+    test->waitUntilContextMessageReceived("Test.FinishedPingRequest");
+}
+
 void beforeAll()
 {
     WebViewTest::add("WebKitWebExtension", "dom-document-title", testWebExtensionGetTitle);
@@ -553,6 +811,7 @@
     WebViewTest::add("WebKitWebExtension", "form-controls-associated-signal", testWebExtensionFormControlsAssociated);
     FormSubmissionTest::add("WebKitWebExtension", "form-submission-steps", testWebExtensionFormSubmissionSteps);
     WebViewTest::add("WebKitWebExtension", "page-id", testWebExtensionPageID);
+    UserMessageTest::add("WebKitWebExtension", "user-messages", testWebExtensionUserMessages);
 }
 
 void afterAll()
diff --git a/Tools/TestWebKitAPI/Tests/WebKitGLib/WebExtensionTest.cpp b/Tools/TestWebKitAPI/Tests/WebKitGLib/WebExtensionTest.cpp
index b3b9c8b..6d2a486 100644
--- a/Tools/TestWebKitAPI/Tests/WebKitGLib/WebExtensionTest.cpp
+++ b/Tools/TestWebKitAPI/Tests/WebKitGLib/WebExtensionTest.cpp
@@ -21,7 +21,10 @@
 #include "WebProcessTest.h"
 #include <JavaScriptCore/JSContextRef.h>
 #include <JavaScriptCore/JSRetainPtr.h>
+#include <fcntl.h>
 #include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+#include <glib/gstdio.h>
 #if USE(GSTREAMER)
 #include <gst/gst.h>
 #endif
@@ -162,6 +165,8 @@
     G_GNUC_END_IGNORE_DEPRECATIONS;
 #endif
 
+    webkit_web_page_send_message_to_view(webPage, webkit_user_message_new("DocumentLoaded", nullptr), nullptr, nullptr, nullptr);
+
     gpointer data = g_object_get_data(G_OBJECT(extension), "dbus-connection");
     if (data)
         emitDocumentLoaded(G_DBUS_CONNECTION(data));
@@ -416,6 +421,75 @@
     }
 }
 
+static gboolean pageMessageReceivedCallback(WebKitWebPage* webPage, WebKitUserMessage* message, WebKitWebExtension* extension)
+{
+    const char* messageName = webkit_user_message_get_name(message);
+    if (!g_strcmp0(messageName, "Test.Hello")) {
+        auto* parameters = webkit_user_message_get_parameters(message);
+        g_assert_nonnull(parameters);
+        const char* parameter = nullptr;
+        g_variant_get(parameters, "&s", &parameter);
+        g_assert_cmpstr(parameter, ==, "WebProcess");
+        g_assert_null(webkit_user_message_get_fd_list(message));
+        webkit_user_message_send_reply(message, webkit_user_message_new("Test.Hello", g_variant_new("s", "UIProcess")));
+        return TRUE;
+    }
+
+    if (!g_strcmp0(messageName, "Test.Optional")) {
+        auto* parameters = webkit_user_message_get_parameters(message);
+        g_assert_nonnull(parameters);
+        const char* parameter1 = nullptr;
+        const char* parameter2 = nullptr;
+        g_variant_get(parameters, "(&sm&s)", &parameter1, &parameter2);
+        g_assert_cmpstr(parameter1, ==, "Hello");
+        webkit_user_message_send_reply(message, webkit_user_message_new("Test.Optional", g_variant_new("s", parameter2 ? parameter2 : "NULL")));
+        return TRUE;
+    }
+
+    if (!g_strcmp0(messageName, "Test.Ping")) {
+        g_assert_null(webkit_user_message_get_parameters(message));
+        webkit_user_message_send_reply(message, webkit_user_message_new("Test.Pong", nullptr));
+        return TRUE;
+    }
+
+    if (!g_strcmp0(messageName, "Test.OpenFile")) {
+        auto* parameters = webkit_user_message_get_parameters(message);
+        g_assert_nonnull(parameters);
+        const char* filename = nullptr;
+        g_variant_get(parameters, "&s", &filename);
+        int fd = g_open(filename, O_RDONLY, 0);
+        g_assert_cmpint(fd, !=, -1);
+        GRefPtr<GUnixFDList> fdList = adoptGRef(g_unix_fd_list_new());
+        GUniqueOutPtr<GError> error;
+        g_unix_fd_list_append(fdList.get(), fd, &error.outPtr());
+        g_assert_no_error(error.get());
+        close(fd);
+
+        webkit_user_message_send_reply(message, webkit_user_message_new_with_fd_list("Test.OpenFile", g_variant_new("h", 0), fdList.get()));
+        return TRUE;
+    }
+
+    if (!g_strcmp0(messageName, "Test.Infinite")) {
+        abort();
+        return TRUE;
+    }
+
+    if (!g_strcmp0(messageName, "Test.AsyncPing")) {
+        webkit_web_page_send_message_to_view(webPage, webkit_user_message_new("Test.Ping", nullptr), nullptr,
+            [](GObject* object, GAsyncResult* result, gpointer) {
+                auto* webPage = WEBKIT_WEB_PAGE(object);
+                GUniqueOutPtr<GError> error;
+                GRefPtr<WebKitUserMessage> reply = adoptGRef(webkit_web_page_send_message_to_view_finish(webPage, result, &error.outPtr()));
+                g_assert_no_error(error.get());
+                g_assert_cmpstr(webkit_user_message_get_name(reply.get()), ==, "Test.Pong");
+                webkit_web_page_send_message_to_view(webPage, webkit_user_message_new("Test.AsyncPong", nullptr), nullptr, nullptr, nullptr);
+            }, nullptr);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
 static void emitPageCreated(GDBusConnection* connection, guint64 pageID)
 {
     bool ok = g_dbus_connection_emit_signal(
@@ -436,6 +510,9 @@
     else
         delayedSignalsQueue.append(DelayedSignal(PageCreatedSignal, webkit_web_page_get_id(webPage)));
 
+    webkit_web_extension_send_message_to_context(extension, webkit_user_message_new("PageCreated", g_variant_new("(t)", webkit_web_page_get_id(webPage))),
+        nullptr, nullptr, nullptr);
+
     g_signal_connect(webPage, "document-loaded", G_CALLBACK(documentLoadedCallback), extension);
     g_signal_connect(webPage, "notify::uri", G_CALLBACK(uriChangedCallback), extension);
     g_signal_connect(webPage, "send-request", G_CALLBACK(sendRequestCallback), nullptr);
@@ -443,6 +520,26 @@
     g_signal_connect(webPage, "context-menu", G_CALLBACK(contextMenuCallback), nullptr);
     g_signal_connect(webPage, "form-controls-associated-for-frame", G_CALLBACK(formControlsAssociatedForFrameCallback), extension);
     g_signal_connect(webPage, "will-submit-form", G_CALLBACK(willSubmitFormCallback), extension);
+    g_signal_connect(webPage, "user-message-received", G_CALLBACK(pageMessageReceivedCallback), extension);
+}
+
+static gboolean extensionMessageReceivedCallback(WebKitWebExtension* extension, WebKitUserMessage* message)
+{
+    const char* messageName = webkit_user_message_get_name(message);
+    if (g_strcmp0(messageName, "RequestPing")) {
+        webkit_web_extension_send_message_to_context(extension, webkit_user_message_new("Ping", nullptr), nullptr,
+            [](GObject* object, GAsyncResult* result, gpointer) {
+                auto* extension = WEBKIT_WEB_EXTENSION(object);
+                GUniqueOutPtr<GError> error;
+                GRefPtr<WebKitUserMessage> reply = adoptGRef(webkit_web_extension_send_message_to_context_finish(extension, result, &error.outPtr()));
+                g_assert_no_error(error.get());
+                g_assert_cmpstr(webkit_user_message_get_name(reply.get()), ==, "Pong");
+                webkit_web_extension_send_message_to_context(extension, webkit_user_message_new("Test.FinishedPingRequest", nullptr), nullptr, nullptr, nullptr);
+            }, nullptr);
+        return TRUE;
+    }
+
+    return FALSE;
 }
 
 static char* echoCallback(const char* message)
@@ -614,6 +711,7 @@
     g_assert_cmpstr(webkit_script_world_get_name(isolatedWorld), ==, "WebExtensionTestScriptWorld");
     g_object_set_data_full(G_OBJECT(extension), "wk-script-world", isolatedWorld, g_object_unref);
 
+    g_signal_connect(extension, "user-message-received", G_CALLBACK(extensionMessageReceivedCallback), nullptr);
     g_signal_connect(extension, "page-created", G_CALLBACK(pageCreatedCallback), extension);
     g_signal_connect(webkit_script_world_get_default(), "window-object-cleared", G_CALLBACK(windowObjectCleared), nullptr);