File webkit2gtk3-libsoup2.patch of Package webkit2gtk3
diff -urpN webkitgtk-2.51.90.orig/Source/cmake/FindLibSoup.cmake webkitgtk-2.51.90/Source/cmake/FindLibSoup.cmake
--- webkitgtk-2.51.90.orig/Source/cmake/FindLibSoup.cmake 1969-12-31 18:00:00.000000000 -0600
+++ webkitgtk-2.51.90/Source/cmake/FindLibSoup.cmake 2026-01-24 10:41:12.911751287 -0600
@@ -0,0 +1,67 @@
+# - Try to find LibSoup 2.4
+# This module defines the following variables:
+#
+# LIBSOUP_FOUND - LibSoup 2.4 was found
+# LIBSOUP_INCLUDE_DIRS - the LibSoup 2.4 include directories
+# LIBSOUP_LIBRARIES - link these to use LibSoup 2.4
+#
+# Copyright (C) 2012 Raphael Kubo da Costa <rakuco@webkit.org>
+#
+# 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 THE COPYRIGHT HOLDER 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 THE COPYRIGHT HOLDER 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.
+
+if (NOT DEFINED LibSoup_FIND_VERSION)
+ message(FATAL_ERROR "No LibSoup version specified")
+endif ()
+
+if (LibSoup_FIND_VERSION VERSION_LESS 2.91)
+ set(LIBSOUP_API_VERSION "2.4")
+else ()
+ set(LIBSOUP_API_VERSION "3.0")
+endif ()
+
+# LibSoup does not provide an easy way to retrieve its version other than its
+# .pc file, so we need to rely on PC_LIBSOUP_VERSION and REQUIRE the .pc file
+# to be found.
+find_package(PkgConfig QUIET)
+pkg_check_modules(PC_LIBSOUP QUIET "libsoup-${LIBSOUP_API_VERSION}")
+
+find_path(LIBSOUP_INCLUDE_DIRS
+ NAMES libsoup/soup.h
+ HINTS ${PC_LIBSOUP_INCLUDEDIR}
+ ${PC_LIBSOUP_INCLUDE_DIRS}
+ PATH_SUFFIXES "libsoup-${LIBSOUP_API_VERSION}"
+)
+
+find_library(LIBSOUP_LIBRARIES
+ NAMES "soup-${LIBSOUP_API_VERSION}"
+ HINTS ${PC_LIBSOUP_LIBDIR}
+ ${PC_LIBSOUP_LIBRARY_DIRS}
+)
+
+include(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibSoup REQUIRED_VARS LIBSOUP_INCLUDE_DIRS LIBSOUP_LIBRARIES
+ VERSION_VAR PC_LIBSOUP_VERSION)
+
+mark_as_advanced(
+ LIBSOUP_INCLUDE_DIRS
+ LIBSOUP_LIBRARIES
+)
diff -urpN webkitgtk-2.51.90.orig/Source/cmake/OptionsGTK.cmake webkitgtk-2.51.90/Source/cmake/OptionsGTK.cmake
--- webkitgtk-2.51.90.orig/Source/cmake/OptionsGTK.cmake 2026-01-23 02:50:35.730358800 -0600
+++ webkitgtk-2.51.90/Source/cmake/OptionsGTK.cmake 2026-01-24 10:39:45.379550640 -0600
@@ -11,7 +11,6 @@ set(USER_AGENT_BRANDING "" CACHE STRING
find_package(GLib 2.70.0 REQUIRED COMPONENTS GioUnix Thread Module)
find_package(Cairo 1.16.0 REQUIRED)
find_package(LibGcrypt 1.7.0 REQUIRED)
-find_package(Soup3 3.0.0 REQUIRED)
find_package(Tasn1 REQUIRED)
find_package(HarfBuzz 2.7.4 REQUIRED COMPONENTS ICU)
find_package(ICU 70.1 REQUIRED COMPONENTS data i18n uc)
@@ -54,6 +53,7 @@ WEBKIT_OPTION_DEFINE(USE_LIBDRM "Whether
WEBKIT_OPTION_DEFINE(USE_LIBHYPHEN "Whether to enable the default automatic hyphenation implementation." PUBLIC ON)
WEBKIT_OPTION_DEFINE(USE_LIBSECRET "Whether to enable the persistent credential storage using libsecret." PUBLIC ON)
WEBKIT_OPTION_DEFINE(USE_SKIA_OPENTYPE_SVG "Whether to use the Skia built-in support for OpenType SVG fonts." PUBLIC ON)
+WEBKIT_OPTION_DEFINE(USE_SOUP2 "Whether to enable usage of Soup 2 instead of Soup 3." PUBLIC OFF)
WEBKIT_OPTION_DEFINE(USE_SYSTEM_SYSPROF_CAPTURE "Whether to use a system-provided libsysprof-capture" PUBLIC ON)
WEBKIT_OPTION_DEFINE(ENABLE_JSC_RESTRICTED_OPTIONS_BY_DEFAULT "Whether to enable dangerous development options in JSC by default." PRIVATE OFF)
@@ -61,6 +61,8 @@ WEBKIT_OPTION_DEPEND(ENABLE_DOCUMENTATIO
WEBKIT_OPTION_DEPEND(USE_GBM USE_LIBDRM)
WEBKIT_OPTION_DEPEND(USE_SKIA_OPENTYPE_SVG USE_SKIA)
+WEBKIT_OPTION_CONFLICT(USE_GTK4 USE_SOUP2)
+
# Private options specific to the GTK port. Changing these options is
# completely unsupported. They are intended for use only by WebKit developers.
WEBKIT_OPTION_DEFINE(USE_SPIEL "Whether to enable usage of LibSpiel for speech synthesis." PRIVATE OFF)
@@ -177,10 +179,32 @@ if (ENABLE_WAYLAND_TARGET AND NOT ${GTK_
set(ENABLE_WAYLAND_TARGET OFF)
endif ()
+if (USE_SOUP2)
+ set(SOUP_MINIMUM_VERSION 2.54.0)
+ set(SOUP_API_VERSION 2.4)
+else ()
+ set(SOUP_MINIMUM_VERSION 3.0.0)
+ set(SOUP_API_VERSION 3.0)
+ set(ENABLE_SERVER_PRECONNECT ON)
+endif ()
+find_package(LibSoup ${SOUP_MINIMUM_VERSION})
+
+if (NOT LibSoup_FOUND)
+if (USE_SOUP2)
+ message(FATAL_ERROR "libsoup is required.")
+else ()
+ message(FATAL_ERROR "libsoup 3 is required. Enable USE_SOUP2 to use libsoup 2 (disables HTTP/2)")
+endif ()
+endif ()
+
if (USE_GTK4)
set(WEBKITGTK_API_INFIX "")
set(WEBKITGTK_API_VERSION "6.0")
SET_AND_EXPOSE_TO_BUILD(ENABLE_2022_GLIB_API ON)
+elseif (USE_SOUP2)
+ set(WEBKITGTK_API_INFIX "2")
+ set(WEBKITGTK_API_VERSION "4.0")
+ SET_AND_EXPOSE_TO_BUILD(ENABLE_2022_GLIB_API OFF)
else ()
set(WEBKITGTK_API_INFIX "2")
set(WEBKITGTK_API_VERSION "4.1")
@@ -190,7 +214,10 @@ endif ()
EXPOSE_STRING_VARIABLE_TO_BUILD(WEBKITGTK_API_INFIX)
EXPOSE_STRING_VARIABLE_TO_BUILD(WEBKITGTK_API_VERSION)
-if (WEBKITGTK_API_VERSION VERSION_EQUAL "4.1")
+if (WEBKITGTK_API_VERSION VERSION_EQUAL "4.0")
+ CALCULATE_LIBRARY_VERSIONS_FROM_LIBTOOL_TRIPLE(WEBKIT 112 0 75)
+ CALCULATE_LIBRARY_VERSIONS_FROM_LIBTOOL_TRIPLE(JAVASCRIPTCORE 47 0 29)
+elseif (WEBKITGTK_API_VERSION VERSION_EQUAL "4.1")
CALCULATE_LIBRARY_VERSIONS_FROM_LIBTOOL_TRIPLE(WEBKIT 21 0 21)
CALCULATE_LIBRARY_VERSIONS_FROM_LIBTOOL_TRIPLE(JAVASCRIPTCORE 10 4 10)
elseif (WEBKITGTK_API_VERSION VERSION_EQUAL "6.0")
diff -urpN webkitgtk-2.51.90.orig/Source/cmake/WebKitFeatures.cmake webkitgtk-2.51.90/Source/cmake/WebKitFeatures.cmake
--- webkitgtk-2.51.90.orig/Source/cmake/WebKitFeatures.cmake 2026-01-23 02:35:26.907442300 -0600
+++ webkitgtk-2.51.90/Source/cmake/WebKitFeatures.cmake 2026-01-24 09:50:16.696693318 -0600
@@ -212,6 +212,7 @@ macro(WEBKIT_OPTION_BEGIN)
WEBKIT_OPTION_DEFINE(ENABLE_RESOURCE_USAGE "Toggle resource usage support" PRIVATE OFF)
WEBKIT_OPTION_DEFINE(ENABLE_SAMPLING_PROFILER "Toggle sampling profiler support" PRIVATE ${ENABLE_SAMPLING_PROFILER_DEFAULT})
WEBKIT_OPTION_DEFINE(ENABLE_SANDBOX_EXTENSIONS "Toggle sandbox extensions support" PRIVATE OFF)
+ WEBKIT_OPTION_DEFINE(ENABLE_SERVER_PRECONNECT "Toggle server preconnect support" PRIVATE OFF)
WEBKIT_OPTION_DEFINE(ENABLE_SERVICE_CONTROLS "Toggle service controls support" PRIVATE OFF)
WEBKIT_OPTION_DEFINE(ENABLE_SHAREABLE_RESOURCE "Toggle network shareable resources support" PRIVATE OFF)
WEBKIT_OPTION_DEFINE(ENABLE_SMOOTH_SCROLLING "Toggle smooth scrolling" PRIVATE ON)
diff -urpN webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/AuthenticationChallenge.h webkitgtk-2.51.90/Source/WebCore/platform/network/soup/AuthenticationChallenge.h
--- webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/AuthenticationChallenge.h 2026-01-23 02:35:25.971609600 -0600
+++ webkitgtk-2.51.90/Source/WebCore/platform/network/soup/AuthenticationChallenge.h 2026-01-24 09:48:37.065766734 -0600
@@ -56,6 +56,9 @@ public:
AuthenticationChallenge(SoupMessage*, GTlsClientConnection*);
AuthenticationChallenge(SoupMessage*, GTlsPassword*);
AuthenticationClient* authenticationClient() const { RELEASE_ASSERT_NOT_REACHED(); }
+#if USE(SOUP2)
+ SoupMessage* soupMessage() const { return m_soupMessage.get(); }
+#endif
SoupAuth* soupAuth() const { return m_soupAuth.get(); }
GTlsPassword* tlsPassword() const { return m_tlsPassword.get(); }
void setProposedCredential(const Credential& credential) { m_proposedCredential = credential; }
@@ -70,6 +73,9 @@ private:
friend class AuthenticationChallengeBase;
static bool platformCompare(const AuthenticationChallenge&, const AuthenticationChallenge&);
+#if USE(SOUP2)
+ GRefPtr<SoupMessage> m_soupMessage;
+#endif
GRefPtr<SoupAuth> m_soupAuth;
GRefPtr<GTlsPassword> m_tlsPassword;
uint32_t m_tlsPasswordFlags { 0 };
diff -urpN webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp webkitgtk-2.51.90/Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp
--- webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp 2026-01-23 02:35:25.971680900 -0600
+++ webkitgtk-2.51.90/Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp 2026-01-24 09:48:37.065891180 -0600
@@ -62,9 +62,16 @@ static ProtectionSpace protectionSpaceFr
else
scheme = ProtectionSpace::AuthenticationScheme::Unknown;
+#if USE(SOUP2)
+ auto host = url.host();
+ auto port = url.port();
+ if (!port)
+ port = defaultPortForProtocol(url.protocol());
+#else
URL authURL({ }, makeString("http://"_s, unsafeSpan(soup_auth_get_authority(soupAuth))));
auto host = authURL.host();
auto port = authURL.port();
+#endif
return ProtectionSpace(host.toString(), static_cast<int>(port.value_or(0)),
protectionSpaceServerTypeFromURL(url, soup_auth_is_for_proxy(soupAuth)),
@@ -77,6 +84,9 @@ AuthenticationChallenge::AuthenticationC
, retrying ? 1 : 0 // previousFailureCount
, soupMessage // failureResponse
, ResourceError::authenticationError(soupMessage))
+#if USE(SOUP2)
+ , m_soupMessage(soupMessage)
+#endif
, m_soupAuth(soupAuth)
{
}
@@ -130,6 +140,10 @@ bool AuthenticationChallenge::platformCo
if (a.tlsPasswordFlags() != b.tlsPasswordFlags())
return false;
+#if USE(SOUP2)
+ return a.soupMessage() == b.soupMessage();
+#endif
+
return true;
}
diff -urpN webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/CookieSoup.cpp webkitgtk-2.51.90/Source/WebCore/platform/network/soup/CookieSoup.cpp
--- webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/CookieSoup.cpp 2026-01-23 02:35:25.971680900 -0600
+++ webkitgtk-2.51.90/Source/WebCore/platform/network/soup/CookieSoup.cpp 2026-01-24 09:48:37.066076551 -0600
@@ -32,6 +32,7 @@
namespace WebCore {
+#if SOUP_CHECK_VERSION(2, 69, 90)
static Cookie::SameSitePolicy coreSameSitePolicy(SoupSameSitePolicy policy)
{
switch (policy) {
@@ -61,19 +62,40 @@ static SoupSameSitePolicy soupSameSitePo
ASSERT_NOT_REACHED();
return SOUP_SAME_SITE_POLICY_NONE;
}
+#endif
Cookie::Cookie(SoupCookie* cookie)
: name(String::fromUTF8(soup_cookie_get_name(cookie)))
, value(String::fromUTF8(soup_cookie_get_value(cookie)))
, domain(String::fromUTF8(soup_cookie_get_domain(cookie)))
, path(String::fromUTF8(soup_cookie_get_path(cookie)))
+#if USE(SOUP2)
+ , expires(soup_cookie_get_expires(cookie) ? std::make_optional(static_cast<double>(soup_date_to_time_t(soup_cookie_get_expires(cookie))) * 1000) : std::nullopt)
+#else
, expires(soup_cookie_get_expires(cookie) ? std::make_optional(static_cast<double>(g_date_time_to_unix(soup_cookie_get_expires(cookie))) * 1000) : std::nullopt)
+#endif
, httpOnly(soup_cookie_get_http_only(cookie))
, secure(soup_cookie_get_secure(cookie))
, session(!soup_cookie_get_expires(cookie))
- , sameSite(coreSameSitePolicy(soup_cookie_get_same_site_policy(cookie)))
+
+{
+#if SOUP_CHECK_VERSION(2, 69, 90)
+ sameSite = coreSameSitePolicy(soup_cookie_get_same_site_policy(cookie));
+#endif
+}
+
+#if USE(SOUP2)
+static SoupDate* msToSoupDate(double ms)
{
+ int year = msToYear(ms);
+ int dayOfYear = dayInYear(ms, year);
+ bool leapYear = isLeapYear(year);
+
+ // monthFromDayInYear() returns a value in the [0,11] range, while soup_date_new() expects
+ // a value in the [1,12] range, meaning we have to manually adjust the month value.
+ return soup_date_new(year, monthFromDayInYear(dayOfYear, leapYear) + 1, dayInMonthFromDayInYear(dayOfYear, leapYear), msToHours(ms), msToMinutes(ms), static_cast<int64_t>(ms / 1000) % 60);
}
+#endif
SoupCookie* Cookie::toSoupCookie() const
{
@@ -85,11 +107,19 @@ SoupCookie* Cookie::toSoupCookie() const
soup_cookie_set_http_only(soupCookie, httpOnly);
soup_cookie_set_secure(soupCookie, secure);
+#if SOUP_CHECK_VERSION(2, 69, 90)
soup_cookie_set_same_site_policy(soupCookie, soupSameSitePolicy(sameSite));
+#endif
if (!session && expires) {
+#if USE(SOUP2)
+ SoupDate* date = msToSoupDate(*expires);
+ soup_cookie_set_expires(soupCookie, date);
+ soup_date_free(date);
+#else
GRefPtr<GDateTime> date = adoptGRef(g_date_time_new_from_unix_utc(*expires / 1000.));
soup_cookie_set_expires(soupCookie, date.get());
+#endif
}
return soupCookie;
diff -urpN webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/GUniquePtrSoup.h webkitgtk-2.51.90/Source/WebCore/platform/network/soup/GUniquePtrSoup.h
--- webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/GUniquePtrSoup.h 2026-01-23 02:35:25.971680900 -0600
+++ webkitgtk-2.51.90/Source/WebCore/platform/network/soup/GUniquePtrSoup.h 2026-01-24 09:48:37.066166973 -0600
@@ -29,8 +29,17 @@
namespace WTF {
WTF_DEFINE_GPTR_DELETER(SoupCookie, soup_cookie_free)
+#if SOUP_CHECK_VERSION(2, 67, 1)
WTF_DEFINE_GPTR_DELETER(SoupHSTSPolicy, soup_hsts_policy_free)
+#endif
+#if USE(SOUP2)
+WTF_DEFINE_GPTR_DELETER(SoupURI, soup_uri_free)
+#endif
+#if SOUP_CHECK_VERSION(2, 99, 3)
WTF_DEFINE_GPTR_DELETER(SoupMessageHeaders, soup_message_headers_unref)
+#else
+WTF_DEFINE_GPTR_DELETER(SoupMessageHeaders, soup_message_headers_free)
+#endif
} // namespace WTF
diff -urpN webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp webkitgtk-2.51.90/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp
--- webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp 2026-01-23 02:35:25.971680900 -0600
+++ webkitgtk-2.51.90/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp 2026-01-24 09:48:37.066333799 -0600
@@ -353,8 +353,12 @@ void NetworkStorageSession::setCookieAcc
soupPolicy = SOUP_COOKIE_JAR_ACCEPT_NEVER;
break;
case HTTPCookieAcceptPolicy::OnlyFromMainDocumentDomain:
+#if SOUP_CHECK_VERSION(2, 71, 0)
soupPolicy = SOUP_COOKIE_JAR_ACCEPT_GRANDFATHERED_THIRD_PARTY;
break;
+#else
+ [[fallthrough]];
+#endif
case HTTPCookieAcceptPolicy::ExclusivelyFromMainDocumentDomain:
soupPolicy = SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY;
break;
@@ -370,8 +374,10 @@ HTTPCookieAcceptPolicy NetworkStorageSes
return HTTPCookieAcceptPolicy::AlwaysAccept;
case SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY:
return HTTPCookieAcceptPolicy::ExclusivelyFromMainDocumentDomain;
+#if SOUP_CHECK_VERSION(2, 71, 0)
case SOUP_COOKIE_JAR_ACCEPT_GRANDFATHERED_THIRD_PARTY:
return HTTPCookieAcceptPolicy::OnlyFromMainDocumentDomain;
+#endif
case SOUP_COOKIE_JAR_ACCEPT_NEVER:
return HTTPCookieAcceptPolicy::Never;
}
@@ -443,13 +449,21 @@ void NetworkStorageSession::setCookiesFr
// Cap lifetime of persistent, client-side cookies to a week.
if (cappedLifetime) {
if (auto* expiresDate = soup_cookie_get_expires(cookie.get())) {
+#if USE(SOUP2)
+ auto timeIntervalSinceNow = Seconds(static_cast<double>(soup_date_to_time_t(expiresDate))) - WallTime::now().secondsSinceEpoch();
+#else
auto timeIntervalSinceNow = Seconds(static_cast<double>(g_date_time_to_unix(expiresDate))) - WallTime::now().secondsSinceEpoch();
+#endif
if (timeIntervalSinceNow > cappedLifetime.value())
soup_cookie_set_max_age(cookie.get(), cappedLifetime->secondsAs<int>());
}
}
+#if SOUP_CHECK_VERSION(2, 67, 1)
soup_cookie_jar_add_cookie_full(jar, cookie.release(), origin.get(), firstPartyURI.get());
+#else
+ soup_cookie_jar_add_cookie_with_first_party(jar, firstPartyURI.get(), cookie.release());
+#endif
}
soup_cookies_free(existingCookies);
@@ -483,7 +497,12 @@ bool NetworkStorageSession::setCookieFro
}
soup_cookies_free(existingCookies);
+#if SOUP_CHECK_VERSION(2, 67, 1)
soup_cookie_jar_add_cookie_full(cookieStorage(), soupCookie.release(), uri.get(), firstPartyURI.get());
+#else
+ soup_cookie_jar_add_cookie_with_first_party(cookieStorage(), firstPartyURI.get(), soupCookie.release());
+ UNUSED_PARAM(uri);
+#endif
return true;
}
@@ -491,9 +510,16 @@ bool NetworkStorageSession::setCookieFro
void NetworkStorageSession::setCookies(const Vector<Cookie>& cookies, const URL& url, const URL& firstParty)
{
for (auto cookie : cookies) {
+#if SOUP_CHECK_VERSION(2, 67, 1)
auto origin = urlToSoupURI(url);
auto firstPartyURI = urlToSoupURI(firstParty);
+
soup_cookie_jar_add_cookie_full(cookieStorage(), cookie.toSoupCookie(), origin.get(), firstPartyURI.get());
+#else
+ UNUSED_PARAM(url);
+ UNUSED_PARAM(firstParty);
+ soup_cookie_jar_add_cookie(cookieStorage(), cookie.toSoupCookie());
+#endif
}
}
@@ -669,6 +695,7 @@ static std::optional<CookieList> lookupC
if (!uri)
return std::nullopt;
+#if SOUP_CHECK_VERSION(2, 69, 90)
auto firstPartyURI = urlToSoupURI(firstParty);
if (!firstPartyURI)
return std::nullopt;
@@ -676,6 +703,9 @@ static std::optional<CookieList> lookupC
auto cookieURI = sameSiteInfo.isSameSite ? urlToSoupURI(url) : nullptr;
CookieList cookies(soup_cookie_jar_get_cookie_list_with_same_site_info(session.cookieStorage(), uri.get(), firstPartyURI.get(), cookieURI.get(), forHTTPHeader == ForHTTPHeader::Yes,
sameSiteInfo.isSafeHTTPMethod, sameSiteInfo.isTopSite));
+#else
+ CookieList cookies(soup_cookie_jar_get_cookie_list(session.cookieStorage(), uri.get(), forHTTPHeader == ForHTTPHeader::Yes));
+#endif
if (!cookies)
return nullptr;
diff -urpN webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/ResourceErrorSoup.cpp webkitgtk-2.51.90/Source/WebCore/platform/network/soup/ResourceErrorSoup.cpp
--- webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/ResourceErrorSoup.cpp 2026-01-23 02:35:25.971680900 -0600
+++ webkitgtk-2.51.90/Source/WebCore/platform/network/soup/ResourceErrorSoup.cpp 2026-01-24 09:48:37.066663744 -0600
@@ -36,6 +36,12 @@
namespace WebCore {
+#if USE(SOUP2)
+#define SOUP_HTTP_ERROR_DOMAIN SOUP_HTTP_ERROR
+#else
+#define SOUP_HTTP_ERROR_DOMAIN SOUP_SESSION_ERROR
+#endif
+
ResourceError::ResourceError(const String& domain, int errorCode, const URL& failingURL, const String& localizedDescription, Type type, IsSanitized isSanitized)
: ResourceErrorBase(domain, errorCode, failingURL, localizedDescription, type, isSanitized)
{
@@ -77,20 +83,29 @@ auto ResourceError::ipcData() const -> s
ResourceError ResourceError::transportError(const URL& failingURL, int statusCode, const String& reasonPhrase)
{
- return ResourceError(String::fromLatin1(g_quark_to_string(SOUP_SESSION_ERROR)), statusCode, failingURL, reasonPhrase);
+ return ResourceError(String::fromLatin1(g_quark_to_string(SOUP_HTTP_ERROR_DOMAIN)), statusCode, failingURL, reasonPhrase);
}
ResourceError ResourceError::httpError(SoupMessage* message, GError* error)
{
ASSERT(message);
+#if USE(SOUP2)
+ if (SOUP_STATUS_IS_TRANSPORT_ERROR(message->status_code))
+ return transportError(soupURIToURL(soup_message_get_uri(message)), message->status_code, String::fromUTF8(message->reason_phrase));
+#endif
return genericGError(soupURIToURL(soup_message_get_uri(message)), error);
}
ResourceError ResourceError::authenticationError(SoupMessage* message)
{
ASSERT(message);
+#if USE(SOUP2)
+ return ResourceError(String::fromLatin1(g_quark_to_string(SOUP_HTTP_ERROR_DOMAIN)), message->status_code,
+ soupURIToURL(soup_message_get_uri(message)), String::fromUTF8(message->reason_phrase));
+#else
return ResourceError(String::fromLatin1(g_quark_to_string(SOUP_SESSION_ERROR)), soup_message_get_status(message),
soup_message_get_uri(message), String::fromUTF8(soup_message_get_reason_phrase(message)));
+#endif
}
ResourceError ResourceError::genericGError(const URL& failingURL, GError* error)
diff -urpN webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/ResourceRequest.h webkitgtk-2.51.90/Source/WebCore/platform/network/soup/ResourceRequest.h
--- webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/ResourceRequest.h 2026-01-23 02:35:25.971680900 -0600
+++ webkitgtk-2.51.90/Source/WebCore/platform/network/soup/ResourceRequest.h 2026-01-24 09:48:37.066761860 -0600
@@ -101,7 +101,11 @@ public:
private:
friend class ResourceRequestBase;
+#if USE(SOUP2)
+ GUniquePtr<SoupURI> createSoupURI() const;
+#else
GRefPtr<GUri> createSoupURI() const;
+#endif
void doUpdatePlatformRequest() { }
void doUpdateResourceRequest() { }
diff -urpN webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp webkitgtk-2.51.90/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp
--- webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp 2026-01-23 02:35:25.971680900 -0600
+++ webkitgtk-2.51.90/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp 2026-01-24 10:06:39.916093604 -0600
@@ -29,6 +29,7 @@
#include "MIMETypeRegistry.h"
#include "RegistrableDomain.h"
#include "SharedBuffer.h"
+#include "SoupVersioning.h"
#include "URLSoup.h"
#include "WebKitFormDataInputStream.h"
#include <wtf/text/CString.h>
@@ -71,6 +72,7 @@ GRefPtr<SoupMessage> ResourceRequest::cr
soup_message_set_first_party(soupMessage.get(), firstParty.get());
}
+#if SOUP_CHECK_VERSION(2, 69, 90)
if (!isSameSiteUnspecified()) {
if (isSameSite()) {
auto siteForCookies = urlToSoupURI(m_requestData.m_url);
@@ -78,6 +80,7 @@ GRefPtr<SoupMessage> ResourceRequest::cr
}
soup_message_set_is_top_level_navigation(soupMessage.get(), isTopSite());
}
+#endif
if (!acceptEncoding())
soup_message_disable_feature(soupMessage.get(), SOUP_TYPE_CONTENT_DECODER);
@@ -99,8 +102,12 @@ void ResourceRequest::updateSoupMessageB
auto& elements = formData->elements();
if (elements.size() == 1 && !formData->alwaysStream()) {
if (auto* vector = std::get_if<Vector<uint8_t>>(&elements[0].data)) {
+#if USE(SOUP2)
+ soup_message_body_append(soupMessage->request_body, SOUP_MEMORY_TEMPORARY, vector->span().data(), vector->size());
+#else
GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new_static(vector->span().data(), vector->size()));
soup_message_set_request_body_from_bytes(soupMessage, nullptr, bytes.get());
+#endif
return;
}
}
@@ -118,7 +125,18 @@ void ResourceRequest::updateSoupMessageB
return;
GRefPtr<GInputStream> stream = webkitFormDataInputStreamNew(WTF::move(resolvedFormData));
+#if USE(SOUP2)
+ if (GBytes* data = webkitFormDataInputStreamReadAll(WEBKIT_FORM_DATA_INPUT_STREAM(stream.get()))) {
+ soup_message_body_set_accumulate(soupMessage->request_body, FALSE);
+ auto* soupBuffer = soup_buffer_new_with_owner(g_bytes_get_data(data, nullptr),
+ g_bytes_get_size(data), data, reinterpret_cast<GDestroyNotify>(g_bytes_unref));
+ soup_message_body_append_buffer(soupMessage->request_body, soupBuffer);
+ soup_buffer_free(soupBuffer);
+ }
+ ASSERT(length == static_cast<uint64_t>(soupMessage->request_body->length));
+#else
soup_message_set_request_body(soupMessage, nullptr, stream.get(), length);
+#endif
}
GRefPtr<GInputStream> ResourceRequest::createBodyStream() const
@@ -164,10 +182,39 @@ unsigned initializeMaximumHTTPConnection
return 10000;
}
+#if USE(SOUP2)
+GUniquePtr<SoupURI> ResourceRequest::createSoupURI() const
+{
+ // WebKit does not support fragment identifiers in data URLs, but soup does.
+ // Before passing the URL to soup, we should make sure to urlencode any '#'
+ // characters, so that soup does not interpret them as fragment identifiers.
+ // See http://wkbug.com/68089
+ if (m_requestData.m_url.protocolIsData()) {
+ String urlString = makeStringByReplacingAll(m_requestData.m_url.string(), '#', "%23"_s);
+ return GUniquePtr<SoupURI>(soup_uri_new(urlString.utf8().data()));
+ }
+
+ GUniquePtr<SoupURI> soupURI = urlToSoupURI(m_requestData.m_url);
+
+ // Versions of libsoup prior to 2.42 have a soup_uri_new that will convert empty passwords that are not
+ // prefixed by a colon into null. Some parts of soup like the SoupAuthenticationManager will only be active
+ // when both the username and password are non-null. When we have credentials, empty usernames and passwords
+ // should be empty strings instead of null.
+ String urlUser = m_requestData.m_url.user();
+ String urlPass = m_requestData.m_url.password();
+ if (!urlUser.isEmpty() || !urlPass.isEmpty()) {
+ soup_uri_set_user(soupURI.get(), urlUser.utf8().data());
+ soup_uri_set_password(soupURI.get(), urlPass.utf8().data());
+ }
+
+ return soupURI;
+}
+#else
GRefPtr<GUri> ResourceRequest::createSoupURI() const
{
return m_requestData.m_url.createGUri();
}
+#endif
void ResourceRequest::updateFromDelegatePreservingOldProperties(const ResourceRequest& delegateProvidedRequest)
{
diff -urpN webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/ResourceResponseSoup.cpp webkitgtk-2.51.90/Source/WebCore/platform/network/soup/ResourceResponseSoup.cpp
--- webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/ResourceResponseSoup.cpp 2026-01-23 02:35:25.971680900 -0600
+++ webkitgtk-2.51.90/Source/WebCore/platform/network/soup/ResourceResponseSoup.cpp 2026-01-24 09:48:37.067159193 -0600
@@ -28,6 +28,7 @@
#include "HTTPHeaderNames.h"
#include "HTTPParsers.h"
#include "MIMETypeRegistry.h"
+#include "SoupVersioning.h"
#include "URLSoup.h"
#include <unicode/uset.h>
#include <wtf/text/CString.h>
@@ -46,9 +47,11 @@ ResourceResponse::ResourceResponse(SoupM
case SOUP_HTTP_1_1:
m_httpVersion = "HTTP/1.1"_s;
break;
+#if SOUP_CHECK_VERSION(2, 99, 3)
case SOUP_HTTP_2_0:
m_httpVersion = "HTTP/2"_s;
break;
+#endif
}
m_httpStatusCode = soup_message_get_status(soupMessage);
diff -urpN webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/SoupNetworkSession.cpp webkitgtk-2.51.90/Source/WebCore/platform/network/soup/SoupNetworkSession.cpp
--- webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/SoupNetworkSession.cpp 2026-01-23 02:35:25.971680900 -0600
+++ webkitgtk-2.51.90/Source/WebCore/platform/network/soup/SoupNetworkSession.cpp 2026-01-24 09:48:37.067279190 -0600
@@ -32,6 +32,7 @@
#include "AuthenticationChallenge.h"
#include "GUniquePtrSoup.h"
#include "Logging.h"
+#include "SoupVersioning.h"
#include "WebKitAutoconfigProxyResolver.h"
#include <glib/gstdio.h>
#include <libsoup/soup.h>
@@ -111,8 +112,12 @@ SoupNetworkSession::SoupNetworkSession(P
soup_session_add_feature_by_type(m_soupSession.get(), SOUP_TYPE_CONTENT_SNIFFER);
soup_session_add_feature_by_type(m_soupSession.get(), SOUP_TYPE_AUTH_NTLM);
+#if SOUP_CHECK_VERSION(2, 67, 1)
soup_session_add_feature_by_type(m_soupSession.get(), SOUP_TYPE_HSTS_ENFORCER);
+#endif
+#if SOUP_CHECK_VERSION(2, 67, 90)
soup_session_add_feature_by_type(m_soupSession.get(), SOUP_TYPE_WEBSOCKET_EXTENSION_MANAGER);
+#endif
if (!initialAcceptLanguages().isNull())
setAcceptLanguages(initialAcceptLanguages());
@@ -149,7 +154,11 @@ void SoupNetworkSession::setupLogger()
if (LogNetwork.state != WTFLogChannelState::On || soup_session_get_feature(m_soupSession.get(), SOUP_TYPE_LOGGER))
return;
+#if USE(SOUP2)
+ GRefPtr<SoupLogger> logger = adoptGRef(soup_logger_new(SOUP_LOGGER_LOG_BODY, -1));
+#else
GRefPtr<SoupLogger> logger = adoptGRef(soup_logger_new(SOUP_LOGGER_LOG_BODY));
+#endif
soup_session_add_feature(m_soupSession.get(), SOUP_SESSION_FEATURE(logger.get()));
soup_logger_set_printer(logger.get(), soupLogPrinter, nullptr, nullptr);
#endif
@@ -172,6 +181,7 @@ void SoupNetworkSession::setHSTSPersiste
if (m_sessionID.isEphemeral())
return;
+#if SOUP_CHECK_VERSION(2, 67, 1)
if (!FileSystem::makeAllDirectories(directory)) {
RELEASE_LOG_ERROR(Network, "Unable to create the HSTS storage directory \"%s\". Using a memory enforcer instead.", directory.utf8().data());
return;
@@ -182,10 +192,14 @@ void SoupNetworkSession::setHSTSPersiste
GRefPtr<SoupHSTSEnforcer> enforcer = adoptGRef(soup_hsts_enforcer_db_new(dbFilename.get()));
soup_session_remove_feature_by_type(m_soupSession.get(), SOUP_TYPE_HSTS_ENFORCER);
soup_session_add_feature(m_soupSession.get(), SOUP_SESSION_FEATURE(enforcer.get()));
+#else
+ UNUSED_PARAM(directory);
+#endif
}
void SoupNetworkSession::getHostNamesWithHSTSCache(HashSet<String>& hostNames)
{
+#if SOUP_CHECK_VERSION(2, 67, 91)
auto* enforcer = SOUP_HSTS_ENFORCER(soup_session_get_feature(m_soupSession.get(), SOUP_TYPE_HSTS_ENFORCER));
ASSERT(enforcer);
@@ -194,10 +208,14 @@ void SoupNetworkSession::getHostNamesWit
GUniquePtr<gchar> domain(static_cast<gchar*>(iter->data));
hostNames.add(String::fromUTF8(domain.get()));
}
+#else
+ UNUSED_PARAM(hostNames);
+#endif
}
void SoupNetworkSession::deleteHSTSCacheForHostNames(const Vector<String>& hostNames)
{
+#if SOUP_CHECK_VERSION(2, 67, 1)
auto* enforcer = SOUP_HSTS_ENFORCER(soup_session_get_feature(m_soupSession.get(), SOUP_TYPE_HSTS_ENFORCER));
ASSERT(enforcer);
@@ -205,22 +223,33 @@ void SoupNetworkSession::deleteHSTSCache
GUniquePtr<SoupHSTSPolicy> policy(soup_hsts_policy_new(hostName.utf8().data(), SOUP_HSTS_POLICY_MAX_AGE_PAST, FALSE));
soup_hsts_enforcer_set_policy(enforcer, policy.get());
}
+#else
+ UNUSED_PARAM(hostNames);
+#endif
}
void SoupNetworkSession::clearHSTSCache(WallTime modifiedSince)
{
+#if SOUP_CHECK_VERSION(2, 67, 91)
auto* enforcer = SOUP_HSTS_ENFORCER(soup_session_get_feature(m_soupSession.get(), SOUP_TYPE_HSTS_ENFORCER));
ASSERT(enforcer);
GUniquePtr<GList> policies(soup_hsts_enforcer_get_policies(enforcer, FALSE));
for (GList* iter = policies.get(); iter != nullptr; iter = iter->next) {
GUniquePtr<SoupHSTSPolicy> policy(static_cast<SoupHSTSPolicy*>(iter->data));
+#if USE(SOUP2)
+ auto modified = soup_date_to_time_t(policy.get()->expires) - policy.get()->max_age;
+#else
auto modified = g_date_time_to_unix(soup_hsts_policy_get_expires(policy.get())) - soup_hsts_policy_get_max_age(policy.get());
+#endif
if (modified >= modifiedSince.secondsSinceEpoch().seconds()) {
GUniquePtr<SoupHSTSPolicy> newPolicy(soup_hsts_policy_new(soup_hsts_policy_get_domain(policy.get()), SOUP_HSTS_POLICY_MAX_AGE_PAST, FALSE));
soup_hsts_enforcer_set_policy(enforcer, newPolicy.get());
}
}
+#else
+ UNUSED_PARAM(modifiedSince);
+#endif
}
static inline bool stringIsNumeric(const std::string_view& str)
diff -urpN webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/SoupVersioning.h webkitgtk-2.51.90/Source/WebCore/platform/network/soup/SoupVersioning.h
--- webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/SoupVersioning.h 1969-12-31 18:00:00.000000000 -0600
+++ webkitgtk-2.51.90/Source/WebCore/platform/network/soup/SoupVersioning.h 2026-01-24 09:48:37.067500780 -0600
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2021 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 <libsoup/soup.h>
+
+#if USE(SOUP2)
+
+static inline const char*
+soup_message_get_method(SoupMessage* message)
+{
+ g_return_val_if_fail(SOUP_IS_MESSAGE(message), nullptr);
+ return message->method;
+}
+
+static inline const char*
+soup_server_message_get_method(SoupMessage* message)
+{
+ return soup_message_get_method(message);
+}
+
+static inline SoupStatus
+soup_message_get_status(SoupMessage* message)
+{
+ g_return_val_if_fail(SOUP_IS_MESSAGE(message), SOUP_STATUS_NONE);
+ return static_cast<SoupStatus>(message->status_code);
+}
+
+static inline void
+soup_server_message_set_status(SoupMessage* message, unsigned statusCode, const char* reasonPhrase)
+{
+ if (reasonPhrase)
+ soup_message_set_status_full(message, statusCode, reasonPhrase);
+ else
+ soup_message_set_status(message, statusCode);
+}
+
+static inline const char*
+soup_message_get_reason_phrase(SoupMessage* message)
+{
+ g_return_val_if_fail(SOUP_IS_MESSAGE(message), nullptr);
+ return message->reason_phrase;
+}
+
+static inline SoupMessageHeaders*
+soup_message_get_request_headers(SoupMessage* message)
+{
+ g_return_val_if_fail(SOUP_IS_MESSAGE(message), nullptr);
+ return message->request_headers;
+}
+
+static inline SoupMessageHeaders*
+soup_server_message_get_request_headers(SoupMessage* message)
+{
+ return soup_message_get_request_headers(message);
+}
+
+static inline SoupMessageHeaders*
+soup_message_get_response_headers(SoupMessage* message)
+{
+ g_return_val_if_fail(SOUP_IS_MESSAGE(message), nullptr);
+ return message->response_headers;
+}
+
+static inline SoupMessageHeaders*
+soup_server_message_get_response_headers(SoupMessage* message)
+{
+ return soup_message_get_response_headers(message);
+}
+
+static inline SoupMessageBody*
+soup_server_message_get_response_body(SoupMessage* message)
+{
+ g_return_val_if_fail(SOUP_IS_MESSAGE(message), nullptr);
+ return message->response_body;
+}
+
+static inline void
+soup_server_message_set_response(SoupMessage* message, const char* contentType, SoupMemoryUse memoryUse, const char* responseBody, gsize length)
+{
+ return soup_message_set_response(message, contentType, memoryUse, responseBody, length);
+}
+
+static inline SoupURI*
+soup_server_message_get_uri(SoupMessage* message)
+{
+ return soup_message_get_uri(message);
+}
+
+static inline GTlsCertificate*
+soup_message_get_tls_peer_certificate(SoupMessage* message)
+{
+ g_return_val_if_fail(SOUP_IS_MESSAGE(message), nullptr);
+ GTlsCertificate* certificate = nullptr;
+ soup_message_get_https_status(message, &certificate, nullptr);
+ return certificate;
+}
+
+static inline GTlsCertificateFlags
+soup_message_get_tls_peer_certificate_errors(SoupMessage* message)
+{
+ g_return_val_if_fail(SOUP_IS_MESSAGE(message), static_cast<GTlsCertificateFlags>(0));
+ GTlsCertificateFlags flags = static_cast<GTlsCertificateFlags>(0);
+ soup_message_get_https_status(message, nullptr, &flags);
+ return flags;
+}
+
+static inline void
+soup_session_send_async(SoupSession* session, SoupMessage* message, int, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
+{
+ soup_session_send_async(session, message, cancellable, callback, userData);
+}
+
+static inline void
+soup_session_websocket_connect_async(SoupSession* session, SoupMessage* message, const char* origin, char** protocols, int, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
+{
+ soup_session_websocket_connect_async(session, message, origin, protocols, cancellable, callback, userData);
+}
+
+static inline void
+soup_auth_cancel(SoupAuth*)
+{
+}
+
+static inline void
+soup_session_set_proxy_resolver(SoupSession* session, GProxyResolver* resolver)
+{
+ g_object_set(session, "proxy-resolver", resolver, nullptr);
+}
+
+static inline GProxyResolver*
+soup_session_get_proxy_resolver(SoupSession* session)
+{
+ GRefPtr<GProxyResolver> resolver;
+ g_object_get(session, "proxy-resolver", &resolver.outPtr(), nullptr);
+ return resolver.get();
+}
+
+static inline void
+soup_session_set_accept_language(SoupSession* session, const char* acceptLanguage)
+{
+ g_object_set(session, "accept-language", acceptLanguage, nullptr);
+}
+
+static inline void
+soup_session_set_tls_database(SoupSession *session, GTlsDatabase *tls_database)
+{
+ g_object_set(session, "tls-database", tls_database, NULL);
+}
+
+#endif // USE(SOUP2)
diff -urpN webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/URLSoup.cpp webkitgtk-2.51.90/Source/WebCore/platform/network/soup/URLSoup.cpp
--- webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/URLSoup.cpp 2026-01-23 02:35:25.971680900 -0600
+++ webkitgtk-2.51.90/Source/WebCore/platform/network/soup/URLSoup.cpp 2026-01-24 09:48:37.067662947 -0600
@@ -30,6 +30,37 @@
namespace WebCore {
+#if USE(SOUP2)
+URL soupURIToURL(SoupURI* soupURI)
+{
+ if (!soupURI)
+ return URL();
+
+ GUniquePtr<gchar> urlString(soup_uri_to_string(soupURI, FALSE));
+ URL url { String::fromUTF8(urlString.get()) };
+ if (url.isValid()) {
+ // Motivated by https://bugs.webkit.org/show_bug.cgi?id=38956. libsoup
+ // does not add the password to the URL when calling
+ // soup_uri_to_string, and thus the requests are not properly
+ // built. Fixing soup_uri_to_string is a no-no as the maintainer does
+ // not want to break compatibility with previous implementations
+ if (soupURI->password)
+ url.setPassword(String::fromUTF8(soupURI->password));
+ }
+
+ return url;
+}
+
+GUniquePtr<SoupURI> urlToSoupURI(const URL& url)
+{
+ if (!url.isValid())
+ return nullptr;
+
+ return GUniquePtr<SoupURI>(soup_uri_new(url.string().utf8().data()));
+}
+
+#else // !USE(SOUP2)
+
URL soupURIToURL(GUri* uri)
{
return uri;
@@ -39,5 +70,6 @@ GRefPtr<GUri> urlToSoupURI(const URL& ur
{
return url.createGUri();
}
+#endif // USE(SOUP2)
} // namespace WebCore
diff -urpN webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/URLSoup.h webkitgtk-2.51.90/Source/WebCore/platform/network/soup/URLSoup.h
--- webkitgtk-2.51.90.orig/Source/WebCore/platform/network/soup/URLSoup.h 2026-01-23 02:35:25.971680900 -0600
+++ webkitgtk-2.51.90/Source/WebCore/platform/network/soup/URLSoup.h 2026-01-24 09:48:37.067755843 -0600
@@ -33,6 +33,11 @@ class URL;
}
namespace WebCore {
+#if USE(SOUP2)
+URL soupURIToURL(SoupURI*);
+GUniquePtr<SoupURI> urlToSoupURI(const URL&);
+#else
URL soupURIToURL(GUri*);
GRefPtr<GUri> urlToSoupURI(const URL&);
+#endif
} // namespace WebCore
diff -urpN webkitgtk-2.51.90.orig/Source/WebCore/platform/Soup.cmake webkitgtk-2.51.90/Source/WebCore/platform/Soup.cmake
--- webkitgtk-2.51.90.orig/Source/WebCore/platform/Soup.cmake 2026-01-23 02:35:25.739458800 -0600
+++ webkitgtk-2.51.90/Source/WebCore/platform/Soup.cmake 2026-01-24 10:48:30.074067023 -0600
@@ -16,7 +16,14 @@ list(APPEND WebCore_PRIVATE_FRAMEWORK_HE
platform/network/soup/ResourceResponse.h
platform/network/soup/SoupNetworkProxySettings.h
platform/network/soup/SoupNetworkSession.h
+ platform/network/soup/SoupVersioning.h
platform/network/soup/URLSoup.h
)
-list(APPEND WebCore_LIBRARIES Soup3::Soup3)
+list(APPEND WebCore_SYSTEM_INCLUDE_DIRECTORIES
+ ${LIBSOUP_INCLUDE_DIRS}
+)
+
+list(APPEND WebCore_LIBRARIES
+ ${LIBSOUP_LIBRARIES}
+)
diff -urpN webkitgtk-2.51.90.orig/Source/WebDriver/PlatformGTK.cmake webkitgtk-2.51.90/Source/WebDriver/PlatformGTK.cmake
--- webkitgtk-2.51.90.orig/Source/WebDriver/PlatformGTK.cmake 2026-01-23 02:35:26.158415800 -0600
+++ webkitgtk-2.51.90/Source/WebDriver/PlatformGTK.cmake 2026-01-24 11:10:46.440111444 -0600
@@ -2,6 +2,10 @@ set(WebDriver_OUTPUT_NAME WebKitWebDrive
add_definitions(-DLIBEXECDIR="${CMAKE_INSTALL_FULL_LIBEXECDIR}")
+list(APPEND WebDriver_SYSTEM_INCLUDE_DIRECTORIES
+ "${LIBSOUP_INCLUDE_DIRS}"
+)
+
list(APPEND WebDriver_SOURCES
glib/SessionHostGlib.cpp
glib/WebDriverServiceGLib.cpp
@@ -17,4 +21,6 @@ if (ENABLE_WEBDRIVER_BIDI)
list(APPEND WebDriver_SOURCES soup/WebSocketServerSoup.cpp)
endif ()
-list(APPEND WebDriver_LIBRARIES Soup3::Soup3)
+list(APPEND WebDriver_LIBRARIES
+ ${LIBSOUP_LIBRARIES}
+)
diff -urpN webkitgtk-2.51.90.orig/Source/WebDriver/soup/HTTPServerSoup.cpp webkitgtk-2.51.90/Source/WebDriver/soup/HTTPServerSoup.cpp
--- webkitgtk-2.51.90.orig/Source/WebDriver/soup/HTTPServerSoup.cpp 2026-01-23 02:35:26.160735400 -0600
+++ webkitgtk-2.51.90/Source/WebDriver/soup/HTTPServerSoup.cpp 2026-01-24 09:48:37.067979948 -0600
@@ -60,6 +60,25 @@ bool HTTPServer::listen(const std::optio
return false;
}
+#if USE(SOUP2)
+ soup_server_add_handler(m_soupServer.get(), nullptr, [](SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer userData) {
+ auto* httpServer = static_cast<HTTPServer*>(userData);
+ GRefPtr<SoupMessage> protectedMessage = message;
+ soup_server_pause_message(server, message);
+ httpServer->m_requestHandler.handleRequest({ String::fromUTF8(message->method), String::fromUTF8(path), message->request_body->data, static_cast<size_t>(message->request_body->length) },
+ [server, message = WTF::Move(protectedMessage)](HTTPRequestHandler::Response&& response) {
+ soup_message_set_status(message.get(), response.statusCode);
+ if (!response.data.isNull()) {
+ // ยง6.3 Processing Model.
+ // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-send-a-response
+ soup_message_headers_append(message->response_headers, "Content-Type", response.contentType.utf8().data());
+ soup_message_headers_append(message->response_headers, "Cache-Control", "no-cache");
+ soup_message_body_append(message->response_body, SOUP_MEMORY_COPY, response.data.data(), response.data.length());
+ }
+ soup_server_unpause_message(server, message.get());
+ });
+ }, this, nullptr);
+#else
soup_server_add_handler(m_soupServer.get(), nullptr, [](SoupServer* server, SoupServerMessage* message, const char* path, GHashTable*, gpointer userData) {
auto& httpServer = *static_cast<HTTPServer*>(userData);
GRefPtr<SoupServerMessage> protectedMessage = message;
@@ -86,6 +105,7 @@ bool HTTPServer::listen(const std::optio
soup_server_unpause_message(server, message.get());
});
}, this, nullptr);
+#endif
return true;
}
diff -urpN webkitgtk-2.51.90.orig/Source/WebDriver/soup/WebSocketServerSoup.cpp webkitgtk-2.51.90/Source/WebDriver/soup/WebSocketServerSoup.cpp
--- webkitgtk-2.51.90.orig/Source/WebDriver/soup/WebSocketServerSoup.cpp 2026-01-23 02:35:26.161816600 -0600
+++ webkitgtk-2.51.90/Source/WebDriver/soup/WebSocketServerSoup.cpp 2026-01-24 09:48:37.068120484 -0600
@@ -41,6 +41,8 @@
namespace WebDriver {
+#if !USE(SOUP2)
+
static bool soupServerListen(SoupServer* server, const String& host, unsigned port, GError** error)
{
static const auto options = static_cast<SoupServerListenOptions>(0);
@@ -134,9 +136,17 @@ static void handleWebSocketConnection(So
g_signal_connect(connection, "message", G_CALLBACK(handleWebSocketMessage), webSocketServer);
}
+#endif // !USE(SOUP2)
std::optional<String> WebSocketServer::listen(const String& host, unsigned port)
{
+#if USE(SOUP2)
+ UNUSED_PARAM(host);
+ UNUSED_PARAM(port);
+ RELEASE_LOG(WebDriverBiDi, "WebSockets support not implemented yet with libsoup2");
+ return std::nullopt;
+#else
+
m_soupServer = adoptGRef(soup_server_new("server-header", "WebKitWebDriver-WSS", nullptr));
GUniqueOutPtr<GError> error;
if (!soupServerListen(m_soupServer.get(), host, port, &error.outPtr())) {
@@ -158,18 +168,28 @@ std::optional<String> WebSocketServer::l
{ "/session"_s }
);
return getWebSocketURL(m_listener, nullString());
+#endif
}
void WebSocketServer::sendMessage(WebSocketMessageHandler::Connection connection, const String& message)
{
+#if USE(SOUP2)
+ UNUSED_PARAM(connection);
+ UNUSED_PARAM(message);
+ RELEASE_LOG(WebDriverBiDi, "WebSockets support not implemented yet with libsoup2");
+#else
ASSERT(connection);
RELEASE_LOG(WebDriverBiDi, "Sending message: %s", message.utf8().data());
GRefPtr<GBytes> rawMessage = adoptGRef(g_bytes_new(message.utf8().data(), message.utf8().length()));
soup_websocket_connection_send_message(connection.get(), SOUP_WEBSOCKET_DATA_TEXT, rawMessage.get());
+#endif
}
void WebSocketServer::disconnect()
{
+#if USE(SOUP2)
+ RELEASE_LOG(WebDriverBiDi, "WebSockets support not implemented yet with libsoup2");
+#else
if (!m_soupServer)
return;
@@ -181,16 +201,22 @@ void WebSocketServer::disconnect()
soup_server_disconnect(m_soupServer.get());
m_soupServer = nullptr;
+#endif
}
void WebSocketServer::disconnectSession(const String& sessionId)
{
+#if USE(SOUP2)
+ UNUSED_PARAM(sessionId);
+ RELEASE_LOG(WebDriverBiDi, "WebSockets support not implemented yet with libsoup2");
+#else
auto connection = this->connection(sessionId);
if (!connection || !connection->get())
return;
soup_websocket_connection_close(connection->get(), SOUP_WEBSOCKET_CLOSE_NORMAL, nullptr);
g_signal_handlers_disconnect_by_data(connection->get(), this);
+#endif
}
} // namespace WebDriver
diff -urpN webkitgtk-2.51.90.orig/Source/WebKit/gtk/gtk3-webkitgtk.toml.in webkitgtk-2.51.90/Source/WebKit/gtk/gtk3-webkitgtk.toml.in
--- webkitgtk-2.51.90.orig/Source/WebKit/gtk/gtk3-webkitgtk.toml.in 2026-01-23 02:35:26.798896600 -0600
+++ webkitgtk-2.51.90/Source/WebKit/gtk/gtk3-webkitgtk.toml.in 2026-01-24 09:34:19.302780692 -0600
@@ -5,7 +5,7 @@ authors = "The WebKit GTK and WPE teams"
version = "@PROJECT_VERSION@"
repository_url = "https://github.com/WebKit/WebKit"
website_url = "https://webkit.org"
-dependencies = ["GObject-2.0", "Gio-2.0", "Gtk-@GTK_API_VERSION@.0", "Soup-3.0"]
+dependencies = ["GObject-2.0", "Gio-2.0", "Gtk-@GTK_API_VERSION@.0", "Soup-@SOUP_API_VERSION@"]
devhelp = true
search_index = true
@@ -24,10 +24,10 @@ name = "Gtk"
description = "The GTK widget toolkit"
docs_url = "https://docs.gtk.org/gtk@GTK_API_VERSION@/"
-[dependencies."Soup-3.0"]
+[dependencies."Soup-@SOUP_API_VERSION@"]
name = "Soup"
description = "HTTP client/server library"
-docs_url = "https://gnome.pages.gitlab.gnome.org/libsoup/libsoup-3.0"
+docs_url = "https://gnome.pages.gitlab.gnome.org/libsoup/libsoup-@SOUP_API_VERSION@"
[theme]
name = "basic"
diff -urpN webkitgtk-2.51.90.orig/Source/WebKit/gtk/gtk4-webkitgtk.toml.in webkitgtk-2.51.90/Source/WebKit/gtk/gtk4-webkitgtk.toml.in
--- webkitgtk-2.51.90.orig/Source/WebKit/gtk/gtk4-webkitgtk.toml.in 2026-01-23 02:35:26.798896600 -0600
+++ webkitgtk-2.51.90/Source/WebKit/gtk/gtk4-webkitgtk.toml.in 2026-01-24 09:34:19.304098353 -0600
@@ -5,7 +5,7 @@ authors = "The WebKit GTK and WPE teams"
version = "@PROJECT_VERSION@"
repository_url = "https://github.com/WebKit/WebKit"
website_url = "https://webkit.org"
-dependencies = ["GObject-2.0", "Gio-2.0", "Gtk-@GTK_API_VERSION@.0", "Soup-3.0"]
+dependencies = ["GObject-2.0", "Gio-2.0", "Gtk-@GTK_API_VERSION@.0", "Soup-@SOUP_API_VERSION@"]
devhelp = true
search_index = true
@@ -24,10 +24,10 @@ name = "Gtk"
description = "The GTK widget toolkit"
docs_url = "https://docs.gtk.org/gtk@GTK_API_VERSION@/"
-[dependencies."Soup-3.0"]
+[dependencies."Soup-@SOUP_API_VERSION@"]
name = "Soup"
description = "HTTP client/server library"
-docs_url = "https://gnome.pages.gitlab.gnome.org/libsoup/libsoup-3.0"
+docs_url = "https://gnome.pages.gitlab.gnome.org/libsoup/libsoup-@SOUP_API_VERSION@"
[theme]
name = "basic"
diff -urpN webkitgtk-2.51.90.orig/Source/WebKit/gtk/webkitgtk.pc.in webkitgtk-2.51.90/Source/WebKit/gtk/webkitgtk.pc.in
--- webkitgtk-2.51.90.orig/Source/WebKit/gtk/webkitgtk.pc.in 2026-01-23 02:35:26.798896600 -0600
+++ webkitgtk-2.51.90/Source/WebKit/gtk/webkitgtk.pc.in 2026-01-24 09:34:19.304457855 -0600
@@ -8,6 +8,6 @@ Name: WebKitGTK
Description: Web content engine for GTK
URL: https://webkitgtk.org
Version: @PROJECT_VERSION@
-Requires: glib-2.0 @GTK_PC_NAME@ libsoup-3.0 javascriptcoregtk-@WEBKITGTK_API_VERSION@
+Requires: glib-2.0 @GTK_PC_NAME@ libsoup-@SOUP_API_VERSION@ javascriptcoregtk-@WEBKITGTK_API_VERSION@
Libs: -L${libdir} -lwebkit@WEBKITGTK_API_INFIX@gtk-@WEBKITGTK_API_VERSION@
Cflags: -I${includedir}/webkitgtk-@WEBKITGTK_API_VERSION@
diff -urpN webkitgtk-2.51.90.orig/Source/WebKit/gtk/webkitgtk-web-process-extension.pc.in webkitgtk-2.51.90/Source/WebKit/gtk/webkitgtk-web-process-extension.pc.in
--- webkitgtk-2.51.90.orig/Source/WebKit/gtk/webkitgtk-web-process-extension.pc.in 2026-01-23 02:35:26.798896600 -0600
+++ webkitgtk-2.51.90/Source/WebKit/gtk/webkitgtk-web-process-extension.pc.in 2026-01-24 09:34:19.304218461 -0600
@@ -8,6 +8,6 @@ Name: WebKitGTK web process extensions
Description: Web content engine for GTK - web process extensions
URL: https://webkitgtk.org
Version: @PROJECT_VERSION@
-Requires: glib-2.0 @GTK_PC_NAME@ libsoup-3.0 javascriptcoregtk-@WEBKITGTK_API_VERSION@
+Requires: glib-2.0 @GTK_PC_NAME@ libsoup-@SOUP_API_VERSION@ javascriptcoregtk-@WEBKITGTK_API_VERSION@
Libs: -L${libdir} -lwebkit@WEBKITGTK_API_INFIX@gtk-@WEBKITGTK_API_VERSION@
Cflags: -I${includedir}/webkitgtk-@WEBKITGTK_API_VERSION@
diff -urpN webkitgtk-2.51.90.orig/Source/WebKit/gtk/webkitgtk-web-process-extension.toml.in webkitgtk-2.51.90/Source/WebKit/gtk/webkitgtk-web-process-extension.toml.in
--- webkitgtk-2.51.90.orig/Source/WebKit/gtk/webkitgtk-web-process-extension.toml.in 2026-01-23 02:35:26.798896600 -0600
+++ webkitgtk-2.51.90/Source/WebKit/gtk/webkitgtk-web-process-extension.toml.in 2026-01-24 09:34:19.304340562 -0600
@@ -5,7 +5,7 @@ authors = "The WebKit GTK and WPE teams"
version = "@PROJECT_VERSION@"
repository_url = "https://github.com/WebKit/WebKit"
website_url = "https://webkit.org"
-dependencies = ["GObject-2.0", "Gio-2.0", "Soup-3.0"]
+dependencies = ["GObject-2.0", "Gio-2.0", "Soup-@SOUP_API_VERSION@"]
devhelp = true
search_index = true
@@ -19,10 +19,10 @@ name = "Gio"
description = "GObject Interfaces and Objects, Networking, IPC, and I/O"
docs_url = "https://docs.gtk.org/gio/"
-[dependencies."Soup-3.0"]
+[dependencies."Soup-@SOUP_API_VERSION@"]
name = "Soup"
description = "HTTP client/server library"
-docs_url = "https://gnome.pages.gitlab.gnome.org/libsoup/libsoup-3.0"
+docs_url = "https://gnome.pages.gitlab.gnome.org/libsoup/libsoup-@SOUP_API_VERSION@"
[theme]
name = "basic"
diff -urpN webkitgtk-2.51.90.orig/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.cpp webkitgtk-2.51.90/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.cpp
--- webkitgtk-2.51.90.orig/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.cpp 2026-01-23 02:35:26.334077800 -0600
+++ webkitgtk-2.51.90/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.cpp 2026-01-24 10:31:25.881576337 -0600
@@ -44,6 +44,7 @@
#include <WebCore/SharedBuffer.h>
#include <WebCore/ShouldRelaxThirdPartyCookieBlocking.h>
#include <WebCore/SoupNetworkSession.h>
+#include <WebCore/SoupVersioning.h>
#include <WebCore/TimingAllowOrigin.h>
#include <pal/text/TextEncoding.h>
#include <wtf/MainThread.h>
@@ -66,6 +67,10 @@ NetworkDataTaskSoup::NetworkDataTaskSoup
{
auto request = parameters.request;
if (request.url().protocolIsInHTTPFamily()) {
+#if USE(SOUP2)
+ m_networkLoadMetrics.fetchStart = MonotonicTime::now();
+ m_networkLoadMetrics.redirectStart = m_networkLoadMetrics.fetchStart;
+#endif
auto url = request.url();
if (m_storedCredentialsPolicy == StoredCredentialsPolicy::Use) {
m_user = url.user();
@@ -156,18 +161,28 @@ void NetworkDataTaskSoup::createRequest(
}
if (m_shouldPreconnectOnly == PreconnectOnly::Yes) {
+#if !USE(SOUP2)
g_signal_connect(m_soupMessage.get(), "accept-certificate", G_CALLBACK(acceptCertificateCallback), this);
+#endif
return;
}
m_networkLoadMetrics.redirectCount = m_currentRequest.redirectCount();
unsigned messageFlags = SOUP_MESSAGE_NO_REDIRECT;
+#if !USE(SOUP2)
messageFlags |= SOUP_MESSAGE_COLLECT_METRICS;
+#endif
if (m_shouldContentSniff == ContentSniffingPolicy::DoNotSniffContent)
soup_message_disable_feature(m_soupMessage.get(), SOUP_TYPE_CONTENT_SNIFFER);
if (m_user.isEmpty() && m_password.isEmpty() && m_storedCredentialsPolicy == StoredCredentialsPolicy::DoNotUse) {
+#if SOUP_CHECK_VERSION(2, 57, 1)
messageFlags |= SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE;
+#else
+ // In case credential is not available and credential storage should not to be used,
+ // disable authentication manager so that credentials stored in libsoup are not used.
+ soup_message_disable_feature(m_soupMessage.get(), SOUP_TYPE_AUTH_MANAGER);
+#endif
}
soup_message_set_flags(m_soupMessage.get(), static_cast<SoupMessageFlags>(soup_message_get_flags(m_soupMessage.get()) | messageFlags));
@@ -180,18 +195,37 @@ void NetworkDataTaskSoup::createRequest(
soup_message_disable_feature(m_soupMessage.get(), SOUP_TYPE_COOKIE_JAR);
m_isBlockingCookies = shouldBlockCookies;
+#if SOUP_CHECK_VERSION(2, 67, 1)
if ((m_currentRequest.url().protocolIs("https"_s) && !shouldAllowHSTSPolicySetting()) || (m_currentRequest.url().protocolIs("http"_s) && !shouldAllowHSTSProtocolUpgrade()))
soup_message_disable_feature(m_soupMessage.get(), SOUP_TYPE_HSTS_ENFORCER);
- else
+ else {
+#if USE(SOUP2)
+ g_signal_connect(soup_session_get_feature(static_cast<NetworkSessionSoup&>(*m_session).soupSession(), SOUP_TYPE_HSTS_ENFORCER), "hsts-enforced", G_CALLBACK(hstsEnforced), this);
+#else
g_signal_connect(m_soupMessage.get(), "hsts-enforced", G_CALLBACK(hstsEnforced), this);
+#endif
+ }
+#endif
// Make sure we have an Accept header for subresources; some sites want this to serve some of their subresources.
auto* requestHeaders = soup_message_get_request_headers(m_soupMessage.get());
if (!soup_message_headers_get_one(requestHeaders, "Accept"))
soup_message_headers_append(requestHeaders, "Accept", "*/*");
+#if USE(SOUP2)
+ // In the case of XHR .send() and .send("") explicitly tell libsoup to send a zero content-lenght header
+ // for consistency with other UA implementations like Firefox. It's done in the backend here instead of
+ // in XHR code since in XHR CORS checking prevents us from this kind of late header manipulation.
+ if ((m_soupMessage->method == SOUP_METHOD_POST || m_soupMessage->method == SOUP_METHOD_PUT) && !m_soupMessage->request_body->length)
+ soup_message_headers_set_content_length(m_soupMessage->request_headers, 0);
+#endif
+
g_signal_connect(m_soupMessage.get(), "got-headers", G_CALLBACK(gotHeadersCallback), this);
g_signal_connect(m_soupMessage.get(), "wrote-body-data", G_CALLBACK(wroteBodyDataCallback), this);
+#if USE(SOUP2)
+ g_signal_connect(static_cast<NetworkSessionSoup&>(*m_session).soupSession(), "authenticate", G_CALLBACK(authenticateCallback), this);
+ g_signal_connect(m_soupMessage.get(), "network-event", G_CALLBACK(networkEventCallback), this);
+#else
g_signal_connect(m_soupMessage.get(), "authenticate", G_CALLBACK(authenticateCallback), this);
g_signal_connect(m_soupMessage.get(), "accept-certificate", G_CALLBACK(acceptCertificateCallback), this);
g_signal_connect(m_soupMessage.get(), "got-body", G_CALLBACK(gotBodyCallback), this);
@@ -201,6 +235,7 @@ void NetworkDataTaskSoup::createRequest(
}
g_signal_connect(m_soupMessage.get(), "request-certificate", G_CALLBACK(requestCertificateCallback), this);
g_signal_connect(m_soupMessage.get(), "request-certificate-password", G_CALLBACK(requestCertificatePasswordCallback), this);
+#endif
g_signal_connect(m_soupMessage.get(), "restarted", G_CALLBACK(restartedCallback), this);
g_signal_connect(m_soupMessage.get(), "starting", G_CALLBACK(startingCallback), this);
if (m_shouldContentSniff == ContentSniffingPolicy::SniffContent)
@@ -226,14 +261,27 @@ void NetworkDataTaskSoup::clearRequest()
m_isBlockingCookies = false;
if (m_soupMessage) {
g_signal_handlers_disconnect_matched(m_soupMessage.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
+#if USE(SOUP2)
+ if (m_session)
+ soup_session_cancel_message(static_cast<NetworkSessionSoup&>(*m_session).soupSession(), m_soupMessage.get(), SOUP_STATUS_CANCELLED);
+#else
if (m_networkLoadMetrics.fetchStart && !m_networkLoadMetrics.responseEnd) {
auto* metrics = soup_message_get_metrics(m_soupMessage.get());
auto responseEnd = Seconds::fromMicroseconds(soup_message_metrics_get_response_end(metrics));
m_networkLoadMetrics.responseEnd = MonotonicTime::fromRawSeconds(responseEnd.seconds());
m_networkLoadMetrics.markComplete();
}
+#endif
m_soupMessage = nullptr;
}
+ if (m_session) {
+#if USE(SOUP2)
+ g_signal_handlers_disconnect_matched(static_cast<NetworkSessionSoup&>(*m_session).soupSession(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
+#if SOUP_CHECK_VERSION(2, 67, 1)
+ g_signal_handlers_disconnect_by_data(soup_session_get_feature(static_cast<NetworkSessionSoup&>(*m_session).soupSession(), SOUP_TYPE_HSTS_ENFORCER), this);
+#endif
+#endif
+ }
}
void NetworkDataTaskSoup::resume()
@@ -250,8 +298,12 @@ void NetworkDataTaskSoup::resume()
if (m_soupMessage && !m_cancellable) {
m_cancellable = adoptGRef(g_cancellable_new());
if (m_shouldPreconnectOnly == PreconnectOnly::Yes) {
+#if !USE(SOUP2)
soup_session_preconnect_async(static_cast<NetworkSessionSoup&>(*m_session).soupSession(), m_soupMessage.get(), RunLoopSourcePriority::AsyncIONetwork, m_cancellable.get(),
reinterpret_cast<GAsyncReadyCallback>(preconnectCallback), protectedThis.leakRef());
+#else
+ RELEASE_ASSERT_NOT_REACHED();
+#endif
} else {
// We need to protect cancellable here, because soup_session_send_async uses it after emitting SoupSession::request-queued, and we
// might cancel the operation in a feature callback emitted on request-queued, for example hsts-enforced.
@@ -259,8 +311,12 @@ void NetworkDataTaskSoup::resume()
soup_session_send_async(static_cast<NetworkSessionSoup&>(*m_session).soupSession(), m_soupMessage.get(), RunLoopSourcePriority::AsyncIONetwork, m_cancellable.get(),
reinterpret_cast<GAsyncReadyCallback>(sendRequestCallback), new SendRequestData({ m_soupMessage, WTF::move(protectedThis) }));
if (!g_cancellable_is_cancelled(protectCancellable.get()) && !m_networkLoadMetrics.fetchStart) {
+#if USE(SOUP2)
+ m_networkLoadMetrics.fetchStart = MonotonicTime::now();
+#else
auto* metrics = soup_message_get_metrics(m_soupMessage.get());
m_networkLoadMetrics.fetchStart = MonotonicTime::fromRawSeconds(Seconds::fromMicroseconds(soup_message_metrics_get_fetch_start(metrics)).seconds());
+#endif
if (!m_networkLoadMetrics.redirectStart)
m_networkLoadMetrics.redirectStart = m_networkLoadMetrics.fetchStart;
}
@@ -302,6 +358,11 @@ void NetworkDataTaskSoup::cancel()
m_state = State::Canceling;
+#if USE(SOUP2)
+ if (m_soupMessage)
+ soup_session_cancel_message(static_cast<NetworkSessionSoup&>(*m_session).soupSession(), m_soupMessage.get(), SOUP_STATUS_CANCELLED);
+#endif
+
g_cancellable_cancel(m_cancellable.get());
if (isDownload())
@@ -396,6 +457,10 @@ void NetworkDataTaskSoup::didSendRequest
else
m_inputStream = WTF::move(inputStream);
+#if USE(SOUP2)
+ m_networkLoadMetrics.responseStart = MonotonicTime::now();
+#endif
+
dispatchDidReceiveResponse();
}
@@ -440,6 +505,7 @@ void NetworkDataTaskSoup::dispatchDidRec
});
}
+#if !USE(SOUP2)
void NetworkDataTaskSoup::preconnectCallback(SoupSession* session, GAsyncResult* result, NetworkDataTaskSoup* task)
{
RefPtr<NetworkDataTaskSoup> protectedThis = adoptRef(task);
@@ -455,12 +521,33 @@ void NetworkDataTaskSoup::preconnectCall
task->clearRequest();
task->dispatchDidCompleteWithError(resourceError);
}
+#endif
void NetworkDataTaskSoup::dispatchDidCompleteWithError(const ResourceError& error)
{
+#if USE(SOUP2)
+ m_networkLoadMetrics.responseEnd = MonotonicTime::now();
+ m_networkLoadMetrics.markComplete();
+#endif
+
m_client->didCompleteWithError(error, m_networkLoadMetrics);
}
+#if USE(SOUP2)
+gboolean NetworkDataTaskSoup::tlsConnectionAcceptCertificateCallback(GTlsConnection* connection, GTlsCertificate* certificate, GTlsCertificateFlags errors, NetworkDataTaskSoup* task)
+{
+ if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
+ task->clearRequest();
+ return FALSE;
+ }
+
+ auto* connectionMessage = g_object_get_data(G_OBJECT(connection), "wk-soup-message");
+ if (connectionMessage != task->m_soupMessage.get())
+ return FALSE;
+
+ return task->acceptCertificate(certificate, errors);
+}
+#else
gboolean NetworkDataTaskSoup::acceptCertificateCallback(SoupMessage* message, GTlsCertificate* certificate, GTlsCertificateFlags errors, NetworkDataTaskSoup* task)
{
if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client)
@@ -470,6 +557,7 @@ gboolean NetworkDataTaskSoup::acceptCert
return task->acceptCertificate(certificate, errors);
}
+#endif
bool NetworkDataTaskSoup::acceptCertificate(GTlsCertificate* certificate, GTlsCertificateFlags tlsErrors)
{
@@ -534,6 +622,26 @@ void NetworkDataTaskSoup::applyAuthentic
m_password = String();
}
+#if USE(SOUP2)
+void NetworkDataTaskSoup::authenticateCallback(SoupSession* session, SoupMessage* soupMessage, SoupAuth* soupAuth, gboolean retrying, NetworkDataTaskSoup* task)
+{
+ ASSERT(session == static_cast<NetworkSessionSoup&>(*task->m_session).soupSession());
+
+ // We don't return early here in case the given soupMessage is different to m_soupMessage when
+ // it's proxy authentication and the request URL is HTTPS, because in that case libsoup uses a
+ // tunnel internally and the SoupMessage used for the authentication is the tunneling one.
+ // See https://bugs.webkit.org/show_bug.cgi?id=175378.
+ if (soupMessage != task->m_soupMessage.get() && (soupMessage->status_code != SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED || !task->m_currentRequest.url().protocolIs("https"_s)))
+ return;
+
+ if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
+ task->clearRequest();
+ return;
+ }
+
+ task->authenticate(AuthenticationChallenge(soupMessage, soupAuth, retrying));
+}
+#else
gboolean NetworkDataTaskSoup::authenticateCallback(SoupMessage* soupMessage, SoupAuth* soupAuth, gboolean retrying, NetworkDataTaskSoup* task)
{
if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
@@ -545,6 +653,7 @@ gboolean NetworkDataTaskSoup::authentica
task->authenticate(AuthenticationChallenge(soupMessage, soupAuth, retrying));
return TRUE;
}
+#endif
static inline bool isAuthenticationFailureStatusCode(int httpStatusCode)
{
@@ -564,13 +673,21 @@ void NetworkDataTaskSoup::completeAuthen
soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());
break;
case ProtectionSpace::AuthenticationScheme::ClientCertificatePINRequested: {
+#if USE(SOUP2)
+ ASSERT_NOT_REACHED();
+#else
CString password = credential.password().utf8();
g_tls_password_set_value(challenge.tlsPassword(), reinterpret_cast<const unsigned char*>(password.data()), password.length());
soup_message_tls_client_certificate_password_request_complete(m_soupMessage.get());
+#endif
break;
}
case ProtectionSpace::AuthenticationScheme::ClientCertificateRequested:
+#if USE(SOUP2)
+ ASSERT_NOT_REACHED();
+#else
soup_message_set_tls_client_certificate(m_soupMessage.get(), credential.certificate());
+#endif
break;
case ProtectionSpace::AuthenticationScheme::ServerTrustEvaluationRequested:
case ProtectionSpace::AuthenticationScheme::Unknown:
@@ -591,10 +708,18 @@ void NetworkDataTaskSoup::cancelAuthenti
soup_auth_cancel(challenge.soupAuth());
break;
case ProtectionSpace::AuthenticationScheme::ClientCertificatePINRequested:
+#if USE(SOUP2)
+ ASSERT_NOT_REACHED();
+#else
soup_message_tls_client_certificate_password_request_complete(m_soupMessage.get());
+#endif
break;
case ProtectionSpace::AuthenticationScheme::ClientCertificateRequested:
+#if USE(SOUP2)
+ ASSERT_NOT_REACHED();
+#else
soup_message_set_tls_client_certificate(m_soupMessage.get(), nullptr);
+#endif
break;
case ProtectionSpace::AuthenticationScheme::ServerTrustEvaluationRequested:
case ProtectionSpace::AuthenticationScheme::Unknown:
@@ -629,6 +754,10 @@ void NetworkDataTaskSoup::authenticate(A
}
}
+#if USE(SOUP2)
+ soup_session_pause_message(static_cast<NetworkSessionSoup&>(*m_session).soupSession(), challenge.soupMessage());
+#endif
+
// We could also do this before we even start the request, but that would be at the expense
// of all request latency, versus a one-time latency for the small subset of requests that
// use HTTP authentication. In the end, this doesn't matter much, because persistent credentials
@@ -683,6 +812,10 @@ void NetworkDataTaskSoup::continueAuthen
completeAuthentication(challenge, credential);
} else
cancelAuthentication(challenge);
+
+#if USE(SOUP2)
+ soup_session_unpause_message(static_cast<NetworkSessionSoup&>(*m_session).soupSession(), challenge.soupMessage());
+#endif
});
}
@@ -769,7 +902,11 @@ void NetworkDataTaskSoup::continueHTTPRe
static const unsigned maxRedirects = 20;
if (m_currentRequest.redirectCount() > maxRedirects) {
+#if USE(SOUP2)
+ didFail(ResourceError::transportError(m_currentRequest.url(), SOUP_STATUS_TOO_MANY_REDIRECTS, "Too many redirects"_s));
+#else
didFail(ResourceError(String::fromLatin1(g_quark_to_string(SOUP_SESSION_ERROR)), SOUP_SESSION_ERROR_TOO_MANY_REDIRECTS, m_currentRequest.url(), String::fromUTF8("Too many redirects")));
+#endif
return;
}
@@ -847,8 +984,10 @@ void NetworkDataTaskSoup::continueHTTPRe
auto request = newRequest;
if (request.url().protocolIsInHTTPFamily()) {
m_networkLoadMetrics.fetchStart = { };
+#if !USE(SOUP2)
m_networkLoadMetrics.responseEnd = { };
m_networkLoadMetrics.complete = false;
+#endif
applyAuthenticationToRequest(request);
if (!request.hasHTTPHeaderField(HTTPHeaderName::UserAgent))
@@ -1022,13 +1161,16 @@ static AtomString soupHTTPVersionToStrin
return "http/1.0"_s;
case SOUP_HTTP_1_1:
return "http/1.1"_s;
+#if SOUP_CHECK_VERSION(2, 99, 3)
case SOUP_HTTP_2_0:
return "h2"_s;
+#endif
}
return { };
}
+#if !USE(SOUP2)
static String tlsProtocolVersionToString(GTlsProtocolVersion version)
{
switch (version) {
@@ -1052,6 +1194,7 @@ static String tlsProtocolVersionToString
return { };
}
+#endif
WebCore::AdditionalNetworkLoadMetricsForWebInspector& NetworkDataTaskSoup::additionalNetworkLoadMetricsForWebInspector()
{
@@ -1060,6 +1203,15 @@ WebCore::AdditionalNetworkLoadMetricsFor
return *m_networkLoadMetrics.additionalNetworkLoadMetricsForWebInspector;
}
+#if USE(SOUP2)
+static void addHeaderSizes(const char *name, const char *value, gpointer pointer)
+{
+ uint64_t* size = static_cast<uint64_t*>(pointer);
+ // Each header is formatted as "<name>: <value>\r\n"
+ *size += strlen(name) + strlen(value) + 4;
+}
+#endif
+
void NetworkDataTaskSoup::didGetHeaders()
{
// We are a bit more conservative with the persistent credential storage than the session store,
@@ -1073,6 +1225,7 @@ void NetworkDataTaskSoup::didGetHeaders(
m_credentialForPersistentStorage = Credential();
}
+#if !USE(SOUP2)
auto* metrics = soup_message_get_metrics(m_soupMessage.get());
auto responseStart = Seconds::fromMicroseconds(soup_message_metrics_get_response_start(metrics));
auto responseStartTime = MonotonicTime::fromRawSeconds(responseStart.seconds());
@@ -1090,6 +1243,7 @@ void NetworkDataTaskSoup::didGetHeaders(
}
m_networkLoadMetrics.responseStart = responseStartTime;
+#endif
// Soup adds more headers to the request after starting signal is emitted, and got-headers
// is the first one we receive after starting, so we use it also to get information about the
@@ -1107,6 +1261,7 @@ void NetworkDataTaskSoup::didGetHeaders(
additionalMetrics.requestHeaders = WTF::move(requestHeaders);
additionalMetrics.priority = toNetworkLoadPriority(soup_message_get_priority(m_soupMessage.get()));
+#if !USE(SOUP2)
additionalMetrics.connectionIdentifier = String::number(soup_message_get_connection_id(m_soupMessage.get()));
auto* address = soup_message_get_remote_address(m_soupMessage.get());
if (G_IS_INET_SOCKET_ADDRESS(address)) {
@@ -1116,11 +1271,27 @@ void NetworkDataTaskSoup::didGetHeaders(
additionalMetrics.tlsProtocol = tlsProtocolVersionToString(soup_message_get_tls_protocol_version(m_soupMessage.get()));
additionalMetrics.tlsCipher = String::fromUTF8(soup_message_get_tls_ciphersuite_name(m_soupMessage.get()));
additionalMetrics.responseHeaderBytesReceived = soup_message_metrics_get_response_header_bytes_received(metrics);
+#else
+ {
+ auto* requestHeaders = soup_message_get_request_headers(m_soupMessage.get());
+ uint64_t requestHeadersSize = 0;
+ soup_message_headers_foreach(requestHeaders, addHeaderSizes, &requestHeadersSize);
+ additionalMetrics.requestHeaderBytesSent = requestHeadersSize;
+ }
+
+ {
+ auto* responseHeaders = soup_message_get_response_headers(m_soupMessage.get());
+ uint64_t responseHeadersSize = 0;
+ soup_message_headers_foreach(responseHeaders, addHeaderSizes, &responseHeadersSize);
+ additionalMetrics.responseHeaderBytesReceived = responseHeadersSize;
+ }
+#endif
}
m_networkLoadMetrics.protocol = soupHTTPVersionToString(soup_message_get_http_version(m_soupMessage.get()));
}
+#if !USE(SOUP2)
void NetworkDataTaskSoup::wroteHeadersCallback(SoupMessage* soupMessage, NetworkDataTaskSoup* task)
{
if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
@@ -1176,22 +1347,35 @@ gboolean NetworkDataTaskSoup::requestCer
task->authenticate(AuthenticationChallenge(soupMessage, tlsPassword));
return TRUE;
}
+#endif
+#if USE(SOUP2)
+void NetworkDataTaskSoup::wroteBodyDataCallback(SoupMessage* soupMessage, SoupBuffer* buffer, NetworkDataTaskSoup* task)
+#else
void NetworkDataTaskSoup::wroteBodyDataCallback(SoupMessage* soupMessage, unsigned length, NetworkDataTaskSoup* task)
+#endif
{
if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
task->clearRequest();
return;
}
ASSERT(task->m_soupMessage.get() == soupMessage);
+#if USE(SOUP2)
+ task->didWriteBodyData(buffer->length);
+#else
task->didWriteBodyData(length);
+#endif
}
void NetworkDataTaskSoup::didWriteBodyData(uint64_t bytesSent)
{
RefPtr<NetworkDataTaskSoup> protectedThis(this);
m_bodyDataTotalBytesSent += bytesSent;
+#if USE(SOUP2)
+ m_client->didSendData(m_bodyDataTotalBytesSent, m_soupMessage->request_body->length);
+#else
m_client->didSendData(m_bodyDataTotalBytesSent, soup_message_headers_get_content_length(soup_message_get_request_headers(m_soupMessage.get())));
+#endif
}
void NetworkDataTaskSoup::download()
@@ -1334,6 +1518,62 @@ void NetworkDataTaskSoup::didFail(const
dispatchDidCompleteWithError(error);
}
+#if USE(SOUP2)
+void NetworkDataTaskSoup::networkEventCallback(SoupMessage* soupMessage, GSocketClientEvent event, GIOStream* stream, NetworkDataTaskSoup* task)
+{
+ if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client)
+ return;
+
+ ASSERT(task->m_soupMessage.get() == soupMessage);
+ task->networkEvent(event, stream);
+}
+
+void NetworkDataTaskSoup::networkEvent(GSocketClientEvent event, GIOStream* stream)
+{
+ auto time = MonotonicTime::now();
+ switch (event) {
+ case G_SOCKET_CLIENT_RESOLVING:
+ m_networkLoadMetrics.domainLookupStart = time;
+ break;
+ case G_SOCKET_CLIENT_RESOLVED:
+ m_networkLoadMetrics.domainLookupEnd = time;
+ break;
+ case G_SOCKET_CLIENT_CONNECTING:
+ m_networkLoadMetrics.connectStart = time;
+ break;
+ case G_SOCKET_CLIENT_CONNECTED:
+ if (shouldCaptureExtraNetworkLoadMetrics() && G_IS_SOCKET_CONNECTION(stream)) {
+ GRefPtr<GSocketAddress> address = adoptGRef(g_socket_connection_get_remote_address(G_SOCKET_CONNECTION(stream), nullptr));
+ if (G_IS_INET_SOCKET_ADDRESS(address.get())) {
+ GUniquePtr<char> ipAddress(g_inet_address_to_string(g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(address.get()))));
+ additionalNetworkLoadMetricsForWebInspector().remoteAddress = makeString(unsafeSpan(ipAddress.get()), ':', g_inet_socket_address_get_port(G_INET_SOCKET_ADDRESS(address.get())));
+ }
+ }
+ // Web Timing considers that connection time involves dns, proxy & TLS negotiation...
+ // so we better pick G_SOCKET_CLIENT_COMPLETE for connectEnd.
+ break;
+ case G_SOCKET_CLIENT_PROXY_NEGOTIATING:
+ break;
+ case G_SOCKET_CLIENT_PROXY_NEGOTIATED:
+ break;
+ case G_SOCKET_CLIENT_TLS_HANDSHAKING:
+ m_networkLoadMetrics.secureConnectionStart = time;
+ RELEASE_ASSERT(G_IS_TLS_CONNECTION(stream));
+ g_object_set_data(G_OBJECT(stream), "wk-soup-message", m_soupMessage.get());
+ g_signal_connect(stream, "accept-certificate", G_CALLBACK(tlsConnectionAcceptCertificateCallback), this);
+ break;
+ case G_SOCKET_CLIENT_TLS_HANDSHAKED:
+ break;
+ case G_SOCKET_CLIENT_COMPLETE:
+ m_networkLoadMetrics.connectEnd = time;
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+}
+#endif
+
void NetworkDataTaskSoup::startingCallback(SoupMessage* soupMessage, NetworkDataTaskSoup* task)
{
if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client)
@@ -1343,6 +1583,8 @@ void NetworkDataTaskSoup::startingCallba
task->didStartRequest();
}
+#if SOUP_CHECK_VERSION(2, 67, 1)
+
bool NetworkDataTaskSoup::shouldAllowHSTSPolicySetting() const
{
// Follow Apple's HSTS abuse mitigation 1:
@@ -1366,7 +1608,11 @@ void NetworkDataTaskSoup::protocolUpgrad
continueHTTPRedirection();
}
+#if USE(SOUP2)
+void NetworkDataTaskSoup::hstsEnforced(SoupHSTSEnforcer*, SoupMessage* soupMessage, NetworkDataTaskSoup* task)
+#else
void NetworkDataTaskSoup::hstsEnforced(SoupMessage* soupMessage, NetworkDataTaskSoup* task)
+#endif
{
if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
task->clearRequest();
@@ -1377,8 +1623,15 @@ void NetworkDataTaskSoup::hstsEnforced(S
task->protocolUpgradedViaHSTS(soupMessage);
}
+#endif
+
void NetworkDataTaskSoup::didStartRequest()
{
+#if USE(SOUP2)
+ m_networkLoadMetrics.requestStart = MonotonicTime::now();
+ if (!m_networkLoadMetrics.secureConnectionStart && m_currentRequest.url().protocolIs("https"_s))
+ m_networkLoadMetrics.secureConnectionStart = WebCore::reusedTLSConnectionSentinel;
+#else
auto* metrics = soup_message_get_metrics(m_soupMessage.get());
auto domainLookupStart = Seconds::fromMicroseconds(soup_message_metrics_get_dns_start(metrics));
auto domainLookupEnd = Seconds::fromMicroseconds(soup_message_metrics_get_dns_end(metrics));
@@ -1396,6 +1649,7 @@ void NetworkDataTaskSoup::didStartReques
else
m_networkLoadMetrics.secureConnectionStart = MonotonicTime::fromRawSeconds(secureConnectionStart.seconds());
m_networkLoadMetrics.requestStart = MonotonicTime::fromRawSeconds(requestStart.seconds());
+#endif
}
void NetworkDataTaskSoup::restartedCallback(SoupMessage* soupMessage, NetworkDataTaskSoup* task)
@@ -1412,9 +1666,13 @@ void NetworkDataTaskSoup::restartedCallb
void NetworkDataTaskSoup::didRestart()
{
m_networkLoadMetrics = NetworkLoadMetrics::emptyMetrics();
+#if USE(SOUP2)
+ m_networkLoadMetrics.fetchStart = MonotonicTime::now();
+#else
auto* metrics = soup_message_get_metrics(m_soupMessage.get());
m_networkLoadMetrics.fetchStart = MonotonicTime::fromRawSeconds(Seconds::fromMicroseconds(soup_message_metrics_get_fetch_start(metrics)).seconds());
m_currentRequest.updateSoupMessageBody(m_soupMessage.get(), m_session->blobRegistry());
+#endif
m_networkLoadMetrics.redirectStart = m_networkLoadMetrics.fetchStart;
}
diff -urpN webkitgtk-2.51.90.orig/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.h webkitgtk-2.51.90/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.h
--- webkitgtk-2.51.90.orig/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.h 2026-01-23 02:35:26.335345700 -0600
+++ webkitgtk-2.51.90/Source/WebKit/NetworkProcess/soup/NetworkDataTaskSoup.h 2026-01-24 09:48:37.069305410 -0600
@@ -79,9 +79,15 @@ private:
void dispatchDidReceiveResponse();
void dispatchDidCompleteWithError(const WebCore::ResourceError&);
+#if !USE(SOUP2)
static void preconnectCallback(SoupSession*, GAsyncResult*, NetworkDataTaskSoup*);
+#endif
+#if USE(SOUP2)
+ static gboolean tlsConnectionAcceptCertificateCallback(GTlsConnection*, GTlsCertificate*, GTlsCertificateFlags, NetworkDataTaskSoup*);
+#else
static gboolean acceptCertificateCallback(SoupMessage*, GTlsCertificate*, GTlsCertificateFlags, NetworkDataTaskSoup*);
+#endif
bool acceptCertificate(GTlsCertificate*, GTlsCertificateFlags);
static void didSniffContentCallback(SoupMessage*, const char* contentType, GHashTable* parameters, NetworkDataTaskSoup*);
@@ -89,7 +95,11 @@ private:
bool persistentCredentialStorageEnabled() const;
void applyAuthenticationToRequest(WebCore::ResourceRequest&);
+#if USE(SOUP2)
+ static void authenticateCallback(SoupSession*, SoupMessage*, SoupAuth*, gboolean retrying, NetworkDataTaskSoup*);
+#else
static gboolean authenticateCallback(SoupMessage*, SoupAuth*, gboolean retrying, NetworkDataTaskSoup*);
+#endif
void authenticate(WebCore::AuthenticationChallenge&&);
void continueAuthenticate(WebCore::AuthenticationChallenge&&);
void completeAuthentication(const WebCore::AuthenticationChallenge&, const WebCore::Credential&);
@@ -114,14 +124,20 @@ private:
static void gotHeadersCallback(SoupMessage*, NetworkDataTaskSoup*);
void didGetHeaders();
+#if USE(SOUP2)
+ static void wroteBodyDataCallback(SoupMessage*, SoupBuffer*, NetworkDataTaskSoup*);
+#else
static void wroteBodyDataCallback(SoupMessage*, unsigned, NetworkDataTaskSoup*);
+#endif
void didWriteBodyData(uint64_t bytesSent);
+#if !USE(SOUP2)
static void wroteHeadersCallback(SoupMessage*, NetworkDataTaskSoup*);
static void wroteBodyCallback(SoupMessage*, NetworkDataTaskSoup*);
static void gotBodyCallback(SoupMessage*, NetworkDataTaskSoup*);
static gboolean requestCertificateCallback(SoupMessage*, GTlsClientConnection*, NetworkDataTaskSoup*);
static gboolean requestCertificatePasswordCallback(SoupMessage*, GTlsPassword*, NetworkDataTaskSoup*);
+#endif
void download();
static void writeDownloadCallback(GOutputStream*, GAsyncResult*, NetworkDataTaskSoup*);
@@ -133,11 +149,26 @@ private:
void didFail(const WebCore::ResourceError&);
+#if USE(SOUP2)
+ static void networkEventCallback(SoupMessage*, GSocketClientEvent, GIOStream*, NetworkDataTaskSoup*);
+ void networkEvent(GSocketClientEvent, GIOStream*);
+#endif
+
+#if SOUP_CHECK_VERSION(2, 49, 91)
static void startingCallback(SoupMessage*, NetworkDataTaskSoup*);
+#else
+ static void requestStartedCallback(SoupSession*, SoupMessage*, SoupSocket*, NetworkDataTaskSoup*);
+#endif
+#if SOUP_CHECK_VERSION(2, 67, 1)
bool shouldAllowHSTSPolicySetting() const;
bool shouldAllowHSTSProtocolUpgrade() const;
void protocolUpgradedViaHSTS(SoupMessage*);
+#if USE(SOUP2)
+ static void hstsEnforced(SoupHSTSEnforcer*, SoupMessage*, NetworkDataTaskSoup*);
+#else
static void hstsEnforced(SoupMessage*, NetworkDataTaskSoup*);
+#endif
+#endif
void didStartRequest();
static void restartedCallback(SoupMessage*, NetworkDataTaskSoup*);
void didRestart();
diff -urpN webkitgtk-2.51.90.orig/Source/WebKit/NetworkProcess/soup/NetworkSessionSoup.cpp webkitgtk-2.51.90/Source/WebKit/NetworkProcess/soup/NetworkSessionSoup.cpp
--- webkitgtk-2.51.90.orig/Source/WebKit/NetworkProcess/soup/NetworkSessionSoup.cpp 2026-01-23 02:35:26.335345700 -0600
+++ webkitgtk-2.51.90/Source/WebKit/NetworkProcess/soup/NetworkSessionSoup.cpp 2026-01-24 09:48:37.069444864 -0600
@@ -95,9 +95,31 @@ void NetworkSessionSoup::setCookiePersis
void NetworkSessionSoup::clearCredentials(WallTime)
{
+#if SOUP_CHECK_VERSION(2, 57, 1)
soup_auth_manager_clear_cached_credentials(SOUP_AUTH_MANAGER(soup_session_get_feature(soupSession(), SOUP_TYPE_AUTH_MANAGER)));
+#endif
}
+#if USE(SOUP2)
+static gboolean webSocketAcceptCertificateCallback(GTlsConnection* connection, GTlsCertificate* certificate, GTlsCertificateFlags errors, NetworkSessionSoup* session)
+{
+ if (DeprecatedGlobalSettings::allowsAnySSLCertificate())
+ return TRUE;
+
+ auto* soupMessage = static_cast<SoupMessage*>(g_object_get_data(G_OBJECT(connection), "wk-soup-message"));
+ return !session->soupNetworkSession().checkTLSErrors(soupURIToURL(soup_message_get_uri(soupMessage)), certificate, errors);
+}
+
+static void webSocketMessageNetworkEventCallback(SoupMessage* soupMessage, GSocketClientEvent event, GIOStream* connection, NetworkSessionSoup* session)
+{
+ if (event != G_SOCKET_CLIENT_TLS_HANDSHAKING)
+ return;
+
+ g_object_set_data(G_OBJECT(connection), "wk-soup-message", soupMessage);
+ g_signal_connect(connection, "accept-certificate", G_CALLBACK(webSocketAcceptCertificateCallback), session);
+}
+#endif
+
RefPtr<WebSocketTask> NetworkSessionSoup::createWebSocketTask(WebPageProxyIdentifier webPageProxyID, std::optional<FrameIdentifier> frameID, std::optional<PageIdentifier> pageID, NetworkSocketChannel& channel, const ResourceRequest& request, const String& protocol, const ClientOrigin&, bool, bool, OptionSet<WebCore::AdvancedPrivacyProtections>, StoredCredentialsPolicy)
{
GRefPtr<SoupMessage> soupMessage = request.createSoupMessage(blobRegistry());
@@ -105,12 +127,16 @@ RefPtr<WebSocketTask> NetworkSessionSoup
return nullptr;
if (request.url().protocolIs("wss"_s)) {
+#if USE(SOUP2)
+ g_signal_connect(soupMessage.get(), "network-event", G_CALLBACK(webSocketMessageNetworkEventCallback), this);
+#else
g_signal_connect(soupMessage.get(), "accept-certificate", G_CALLBACK(+[](SoupMessage* message, GTlsCertificate* certificate, GTlsCertificateFlags errors, NetworkSessionSoup* session) -> gboolean {
if (DeprecatedGlobalSettings::allowsAnySSLCertificate())
return TRUE;
return !session->soupNetworkSession().checkTLSErrors(soup_message_get_uri(message), certificate, errors);
}), this);
+#endif
}
bool shouldBlockCookies = checkedNetworkStorageSession()->shouldBlockCookies(request, frameID, pageID, networkProcess().shouldRelaxThirdPartyCookieBlockingForPage(webPageProxyID), WebCore::IsKnownCrossSiteTracker::No);
diff -urpN webkitgtk-2.51.90.orig/Source/WebKit/NetworkProcess/soup/WebSocketTaskSoup.cpp webkitgtk-2.51.90/Source/WebKit/NetworkProcess/soup/WebSocketTaskSoup.cpp
--- webkitgtk-2.51.90.orig/Source/WebKit/NetworkProcess/soup/WebSocketTaskSoup.cpp 2026-01-23 02:35:26.336075800 -0600
+++ webkitgtk-2.51.90/Source/WebKit/NetworkProcess/soup/WebSocketTaskSoup.cpp 2026-01-24 10:35:34.670312389 -0600
@@ -33,6 +33,7 @@
#include <WebCore/HTTPParsers.h>
#include <WebCore/ResourceRequest.h>
#include <WebCore/ResourceResponse.h>
+#include <WebCore/SoupVersioning.h>
#include <WebCore/ThreadableWebSocketChannel.h>
#include <wtf/RunLoop.h>
#include <wtf/TZoneMallocInlines.h>
@@ -43,10 +44,17 @@
namespace WebKit {
-static inline bool isConnectionError(GError* error)
+static inline bool isConnectionError(GError* error, SoupMessage* message)
{
+#if USE(SOUP2)
+ return g_error_matches(error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_NOT_WEBSOCKET)
+ && message
+ && (message->status_code == SOUP_STATUS_CANT_CONNECT || message->status_code == SOUP_STATUS_CANT_CONNECT_PROXY);
+#else
+ UNUSED_PARAM(message);
// If not a SOUP_WEBSOCKET_ERROR_NOT_WEBSOCKET, then it's a connection error.
return error && !g_error_matches(error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_NOT_WEBSOCKET);
+#endif
}
WTF_MAKE_TZONE_ALLOCATED_IMPL(WebSocketTask);
@@ -73,6 +81,12 @@ WebSocketTask::WebSocketTask(NetworkSock
protocolsSpan[i++] = g_strdup(subprotocol.trim(isASCIIWhitespaceWithoutFF<char16_t>).utf8().data());
}
+#if USE(SOUP2)
+ // Ensure a new connection is used for WebSockets.
+ // FIXME: this is done by libsoup since 2.69.1 and 2.68.4, so it can be removed when bumping the libsoup requirement.
+ // See https://bugs.webkit.org/show_bug.cgi?id=203404
+ soup_message_set_flags(msg, static_cast<SoupMessageFlags>(soup_message_get_flags(msg) | SOUP_MESSAGE_NEW_CONNECTION));
+#else
{
// No need to subscribe to the "request-certificate" signal, just set the client certificate upfront.
auto protectionSpace = WebCore::AuthenticationChallenge::protectionSpaceForClientCertificate(WebCore::soupURIToURL(soup_message_get_uri(msg)));
@@ -87,6 +101,7 @@ WebSocketTask::WebSocketTask(NetworkSock
soup_message_tls_client_certificate_password_request_complete(msg);
return TRUE;
}), this);
+#endif
soup_session_websocket_connect_async(session, msg, nullptr, protocols.get(), RunLoopSourcePriority::AsyncIONetwork, m_cancellable.get(),
[] (GObject* session, GAsyncResult* result, gpointer userData) {
@@ -95,7 +110,7 @@ WebSocketTask::WebSocketTask(NetworkSock
if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
return;
auto* task = static_cast<WebSocketTask*>(userData);
- if (isConnectionError(error.get())) {
+ if (isConnectionError(error.get(), task->m_handshakeMessage.get())) {
task->m_delayErrorMessage = String::fromUTF8(error->message);
task->m_delayFailTimer.startOneShot(NetworkProcess::randomClosedPortDelay());
return;
@@ -127,6 +142,7 @@ RefPtr<NetworkSocketChannel> WebSocketTa
String WebSocketTask::acceptedExtensions() const
{
+#if SOUP_CHECK_VERSION(2, 67, 90)
StringBuilder result;
GList* extensions = soup_websocket_connection_get_extensions(m_connection.get());
for (auto* it = extensions; it; it = g_list_next(it)) {
@@ -141,15 +157,20 @@ String WebSocketTask::acceptedExtensions
result.append(String::fromUTF8(params.get()));
}
return result.toStringPreserveCapacity();
+#else
+ return { };
+#endif
}
void WebSocketTask::didConnect(GRefPtr<SoupWebsocketConnection>&& connection)
{
m_connection = WTF::move(connection);
+#if SOUP_CHECK_VERSION(2, 56, 0)
// Use the same maximum payload length as WebKit internal implementation for backwards compatibility.
static const uint64_t maxPayloadLength = UINT64_C(0x7FFFFFFFFFFFFFFF);
soup_websocket_connection_set_max_incoming_payload_size(m_connection.get(), maxPayloadLength);
+#endif
g_signal_connect_swapped(m_connection.get(), "message", reinterpret_cast<GCallback>(didReceiveMessageCallback), this);
g_signal_connect_swapped(m_connection.get(), "error", reinterpret_cast<GCallback>(didReceiveErrorCallback), this);
@@ -234,9 +255,13 @@ void WebSocketTask::didClose(unsigned sh
void WebSocketTask::sendString(std::span<const uint8_t> utf8, CompletionHandler<void()>&& callback)
{
if (m_connection && soup_websocket_connection_get_state(m_connection.get()) == SOUP_WEBSOCKET_STATE_OPEN) {
+#if SOUP_CHECK_VERSION(2, 67, 3)
// Soup is going to copy the data immediately, so we can use g_bytes_new_static() here to avoid more data copies.
GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new_static(utf8.data(), utf8.size()));
soup_websocket_connection_send_message(m_connection.get(), SOUP_WEBSOCKET_DATA_TEXT, bytes.get());
+#else
+ soup_websocket_connection_send_text(m_connection.get(), CString(utf8).data());
+#endif
}
callback();
}
@@ -259,8 +284,10 @@ void WebSocketTask::close(int32_t code,
return;
}
+#if SOUP_CHECK_VERSION(2, 67, 90)
if (code == WebCore::ThreadableWebSocketChannel::CloseEventCodeNotSpecified)
code = SOUP_WEBSOCKET_CLOSE_NO_STATUS;
+#endif
if (soup_websocket_connection_get_state(m_connection.get()) == SOUP_WEBSOCKET_STATE_OPEN)
soup_websocket_connection_close(m_connection.get(), code, reason.utf8().data());
diff -urpN webkitgtk-2.51.90.orig/Source/WebKit/PlatformGTK.cmake webkitgtk-2.51.90/Source/WebKit/PlatformGTK.cmake
--- webkitgtk-2.51.90.orig/Source/WebKit/PlatformGTK.cmake 2026-01-23 02:35:26.361826000 -0600
+++ webkitgtk-2.51.90/Source/WebKit/PlatformGTK.cmake 2026-01-24 14:29:58.236509857 -0600
@@ -341,6 +341,7 @@ list(APPEND WebKit_PRIVATE_INCLUDE_DIREC
list(APPEND WebKit_SYSTEM_INCLUDE_DIRECTORIES
${GSTREAMER_INCLUDE_DIRS}
${GSTREAMER_PBUTILS_INCLUDE_DIRS}
+ ${LIBSOUP_INCLUDE_DIRS}
)
list(APPEND WebKit_INTERFACE_INCLUDE_DIRECTORIES
@@ -612,7 +613,7 @@ GI_INTROSPECT(WebKit${WEBKITGTK_API_INFI
DEPENDENCIES
JavaScriptCore
Gtk-${GTK_API_VERSION}.0:${GTK_PKGCONFIG_PACKAGE}
- Soup-3.0:libsoup-3.0
+ Soup-${SOUP_API_VERSION}:libsoup-${SOUP_API_VERSION}
SOURCES
${WebKitGTK_INSTALLED_HEADERS}
${WEBKITGTK_SOURCES_FOR_INTROSPECTION}
@@ -648,7 +649,7 @@ GI_INTROSPECT(${WEBKITGTK_WEB_PROCESS_EX
DEPENDENCIES
JavaScriptCore
Gtk-${GTK_API_VERSION}.0:${GTK_PKGCONFIG_PACKAGE}
- Soup-3.0:libsoup-3.0
+ Soup-${SOUP_API_VERSION}:libsoup-${SOUP_API_VERSION}
SOURCES
${WebKitDOM_SOURCES_FOR_INTROSPECTION}
${WebKitWebProcessExtension_INSTALLED_HEADERS}
diff -urpN webkitgtk-2.51.90.orig/Source/WebKit/UIProcess/Inspector/glib/RemoteInspectorHTTPServer.cpp webkitgtk-2.51.90/Source/WebKit/UIProcess/Inspector/glib/RemoteInspectorHTTPServer.cpp
--- webkitgtk-2.51.90.orig/Source/WebKit/UIProcess/Inspector/glib/RemoteInspectorHTTPServer.cpp 2026-01-23 02:35:26.582229400 -0600
+++ webkitgtk-2.51.90/Source/WebKit/UIProcess/Inspector/glib/RemoteInspectorHTTPServer.cpp 2026-01-24 09:48:41.118240148 -0600
@@ -29,6 +29,7 @@
#if ENABLE(REMOTE_INSPECTOR)
#include "RemoteInspectorClient.h"
+#include <WebCore/SoupVersioning.h>
#include <wtf/FileSystem.h>
#include <wtf/URL.h>
#include <wtf/glib/GSpanExtras.h>
@@ -54,14 +55,22 @@ bool RemoteInspectorHTTPServer::start(GR
}
soup_server_add_handler(m_server.get(), nullptr,
+#if USE(SOUP2)
+ [](SoupServer*, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer userData) {
+#else
[](SoupServer*, SoupServerMessage* message, const char* path, GHashTable*, gpointer userData) {
+#endif
auto& httpServer = *static_cast<RemoteInspectorHTTPServer*>(userData);
auto status = httpServer.handleRequest(path, soup_server_message_get_response_headers(message), soup_server_message_get_response_body(message));
soup_server_message_set_status(message, status, nullptr);
}, this, nullptr);
soup_server_add_websocket_handler(m_server.get(), "/socket", nullptr, nullptr,
+#if USE(SOUP2)
+ [](SoupServer*, SoupWebsocketConnection* connection, const char* path, SoupClientContext*, gpointer userData) {
+#else
[](SoupServer*, SoupServerMessage*, const char* path, SoupWebsocketConnection* connection, gpointer userData) {
+#endif
auto& httpServer = *static_cast<RemoteInspectorHTTPServer*>(userData);
httpServer.handleWebSocket(path, connection);
}, this, nullptr);
@@ -144,8 +153,12 @@ void RemoteInspectorHTTPServer::sendMess
auto utf8 = message.utf8();
// Soup is going to copy the data immediately, so we can use g_bytes_new_static() here to avoid more data copies.
+#if SOUP_CHECK_VERSION(2, 67, 3)
GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new_static(utf8.data(), utf8.length()));
soup_websocket_connection_send_message(webSocketConnection, SOUP_WEBSOCKET_DATA_TEXT, bytes.get());
+#else
+ soup_websocket_connection_send_text(webSocketConnection, CString(utf8).data());
+#endif
}
void RemoteInspectorHTTPServer::targetDidClose(uint64_t connectionID, uint64_t targetID)
diff -urpN webkitgtk-2.51.90.orig/Source/WebKit/WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMComment.cpp webkitgtk-2.51.90/Source/WebKit/WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMComment.cpp
--- webkitgtk-2.51.90.orig/Source/WebKit/WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMComment.cpp 2026-01-23 02:35:26.727445000 -0600
+++ webkitgtk-2.51.90/Source/WebKit/WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMComment.cpp 2026-01-24 09:48:41.118524206 -0600
@@ -24,7 +24,6 @@
#include "DOMObjectCache.h"
#include <WebCore/DOMException.h>
#include <WebCore/Document.h>
-#include <WebCore/ShadowRoot.h>
#include "GObjectEventListener.h"
#include <WebCore/JSExecState.h>
#include "WebKitDOMCommentPrivate.h"
diff -urpN webkitgtk-2.51.90.orig/Source/WebKit/WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMDocumentType.cpp webkitgtk-2.51.90/Source/WebKit/WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMDocumentType.cpp
--- webkitgtk-2.51.90.orig/Source/WebKit/WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMDocumentType.cpp 2026-01-23 02:35:26.727445000 -0600
+++ webkitgtk-2.51.90/Source/WebKit/WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMDocumentType.cpp 2026-01-24 09:48:41.118768489 -0600
@@ -24,7 +24,6 @@
#include "DOMObjectCache.h"
#include <WebCore/DOMException.h>
#include <WebCore/Document.h>
-#include <WebCore/EventTargetInlines.h>
#include "GObjectEventListener.h"
#include <WebCore/JSExecState.h>
#include "WebKitDOMDocumentTypePrivate.h"
diff -urpN webkitgtk-2.51.90.orig/Source/WTF/wtf/PlatformEnableGlib.h webkitgtk-2.51.90/Source/WTF/wtf/PlatformEnableGlib.h
--- webkitgtk-2.51.90.orig/Source/WTF/wtf/PlatformEnableGlib.h 2026-01-23 02:35:24.927470200 -0600
+++ webkitgtk-2.51.90/Source/WTF/wtf/PlatformEnableGlib.h 2026-01-24 09:48:37.065220930 -0600
@@ -55,10 +55,6 @@
#define ENABLE_OPENTYPE_VERTICAL 1
#endif
-#if !defined(ENABLE_SERVER_PRECONNECT)
-#define ENABLE_SERVER_PRECONNECT 1
-#endif
-
#if !defined(ENABLE_SCROLLING_THREAD) && USE(COORDINATED_GRAPHICS)
#define ENABLE_SCROLLING_THREAD 1
#endif
diff -urpN webkitgtk-2.51.90.orig/Source/WTF/wtf/Platform.h webkitgtk-2.51.90/Source/WTF/wtf/Platform.h
--- webkitgtk-2.51.90.orig/Source/WTF/wtf/Platform.h 2026-01-23 02:35:24.927470200 -0600
+++ webkitgtk-2.51.90/Source/WTF/wtf/Platform.h 2026-01-24 09:48:37.064998258 -0600
@@ -104,8 +104,12 @@
#endif
#if USE(SOUP)
+#if USE(SOUP2)
+#define SOUP_VERSION_MIN_REQUIRED SOUP_VERSION_2_54
+#else
#define SOUP_VERSION_MIN_REQUIRED SOUP_VERSION_3_0
#endif
+#endif
#if PLATFORM(COCOA)
/* Cocoa defines a series of platform macros for debugging. */
diff -urpN webkitgtk-2.51.90.orig/Tools/MiniBrowser/gtk/CMakeLists.txt webkitgtk-2.51.90/Tools/MiniBrowser/gtk/CMakeLists.txt
--- webkitgtk-2.51.90.orig/Tools/MiniBrowser/gtk/CMakeLists.txt 2026-01-23 02:35:26.955072000 -0600
+++ webkitgtk-2.51.90/Tools/MiniBrowser/gtk/CMakeLists.txt 2026-01-24 17:29:47.909069023 -0600
@@ -26,8 +26,13 @@ set(MiniBrowser_PRIVATE_INCLUDE_DIRECTOR
${CMAKE_SOURCE_DIR}/Source
)
+set(MiniBrowser_SYSTEM_INCLUDE_DIRECTORIES
+ ${LIBSOUP_INCLUDE_DIRS}
+)
+
set(MiniBrowser_PRIVATE_LIBRARIES
${JavaScriptCore_LIBRARY_NAME}
+ ${LIBSOUP_LIBRARIES}
GTK::GTK
WebKit::WebKit
)