diff --git a/Libraries/LibWeb/HTML/Navigable.cpp b/Libraries/LibWeb/HTML/Navigable.cpp
index 6072da8ee73..a5f4c709966 100644
--- a/Libraries/LibWeb/HTML/Navigable.cpp
+++ b/Libraries/LibWeb/HTML/Navigable.cpp
@@ -1349,6 +1349,12 @@ WebIDL::ExceptionOr Navigable::navigate(NavigateParams params)
auto& active_document = *this->active_document();
auto& realm = active_document.realm();
+ // AD-HOC: If we are not able to continue in this process, request a new process from the UI.
+ if (!active_document.page().client().is_url_suitable_for_same_process_navigation(active_document.url(), params.url)) {
+ active_document.page().client().request_new_process_for_navigation(params.url);
+ return {};
+ }
+
// 2. Let sourceSnapshotParams be the result of snapshotting source snapshot params given sourceDocument.
auto source_snapshot_params = source_document->snapshot_source_snapshot_params();
diff --git a/Libraries/LibWeb/Page/Page.h b/Libraries/LibWeb/Page/Page.h
index 47b77e287fb..9704c71b046 100644
--- a/Libraries/LibWeb/Page/Page.h
+++ b/Libraries/LibWeb/Page/Page.h
@@ -321,6 +321,8 @@ public:
virtual Page& page() = 0;
virtual Page const& page() const = 0;
virtual bool is_connection_open() const = 0;
+ virtual bool is_url_suitable_for_same_process_navigation([[maybe_unused]] URL::URL const& current_url, [[maybe_unused]] URL::URL const& target_url) const { return true; }
+ virtual void request_new_process_for_navigation(URL::URL const&) { }
virtual Gfx::Palette palette() const = 0;
virtual DevicePixelRect screen_rect() const = 0;
virtual double device_pixels_per_css_pixel() const = 0;
diff --git a/Libraries/LibWebView/CMakeLists.txt b/Libraries/LibWebView/CMakeLists.txt
index bbab6874b47..acd6e5c8ac3 100644
--- a/Libraries/LibWebView/CMakeLists.txt
+++ b/Libraries/LibWebView/CMakeLists.txt
@@ -16,6 +16,7 @@ set(SOURCES
ProcessHandle.cpp
ProcessManager.cpp
SearchEngine.cpp
+ SiteIsolation.cpp
SourceHighlighter.cpp
URL.cpp
UserAgent.cpp
diff --git a/Libraries/LibWebView/SiteIsolation.cpp b/Libraries/LibWebView/SiteIsolation.cpp
new file mode 100644
index 00000000000..93584ca121b
--- /dev/null
+++ b/Libraries/LibWebView/SiteIsolation.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2025, Tim Flynn
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include
+#include
+#include
+#include
+
+namespace WebView {
+
+bool is_url_suitable_for_same_process_navigation(URL::URL const& current_url, URL::URL const& target_url)
+{
+ // Allow navigating from about:blank to any site.
+ if (Web::HTML::url_matches_about_blank(current_url))
+ return true;
+
+ // Allow cross-scheme non-HTTP(S) navigation. Disallow cross-scheme HTTP(s) navigation.
+ auto current_url_is_http = Web::Fetch::Infrastructure::is_http_or_https_scheme(current_url.scheme());
+ auto target_url_is_http = Web::Fetch::Infrastructure::is_http_or_https_scheme(target_url.scheme());
+
+ if (!current_url_is_http || !target_url_is_http)
+ return !current_url_is_http && !target_url_is_http;
+
+ // Disallow cross-site HTTP(S) navigation.
+ return current_url.origin().is_same_site(target_url.origin());
+}
+
+}
diff --git a/Libraries/LibWebView/SiteIsolation.h b/Libraries/LibWebView/SiteIsolation.h
new file mode 100644
index 00000000000..c31c8c1bf78
--- /dev/null
+++ b/Libraries/LibWebView/SiteIsolation.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2025, Tim Flynn
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include
+
+namespace WebView {
+
+[[nodiscard]] bool is_url_suitable_for_same_process_navigation(URL::URL const& current_url, URL::URL const& target_url);
+
+}
diff --git a/Libraries/LibWebView/ViewImplementation.cpp b/Libraries/LibWebView/ViewImplementation.cpp
index 271e334d5f3..3358847f5da 100644
--- a/Libraries/LibWebView/ViewImplementation.cpp
+++ b/Libraries/LibWebView/ViewImplementation.cpp
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
#include
#include
@@ -89,6 +90,21 @@ u64 ViewImplementation::page_id() const
return m_client_state.page_index;
}
+void ViewImplementation::create_new_process_for_cross_site_navigation(URL::URL const& url)
+{
+ if (m_client_state.client)
+ client().async_close_server();
+
+ initialize_client();
+ VERIFY(m_client_state.client);
+
+ // Don't keep a stale backup bitmap around.
+ m_backup_bitmap = nullptr;
+ handle_resize();
+
+ load(url);
+}
+
void ViewImplementation::server_did_paint(Badge, i32 bitmap_id, Gfx::IntSize size)
{
if (m_client_state.back_bitmap.id == bitmap_id) {
diff --git a/Libraries/LibWebView/ViewImplementation.h b/Libraries/LibWebView/ViewImplementation.h
index 70dcb1eee26..c1f8ed73592 100644
--- a/Libraries/LibWebView/ViewImplementation.h
+++ b/Libraries/LibWebView/ViewImplementation.h
@@ -58,6 +58,8 @@ public:
String const& handle() const { return m_client_state.client_handle; }
+ void create_new_process_for_cross_site_navigation(URL::URL const&);
+
void server_did_paint(Badge, i32 bitmap_id, Gfx::IntSize size);
void set_window_position(Gfx::IntPoint);
diff --git a/Libraries/LibWebView/WebContentClient.cpp b/Libraries/LibWebView/WebContentClient.cpp
index f4cbf963e25..29d664096d9 100644
--- a/Libraries/LibWebView/WebContentClient.cpp
+++ b/Libraries/LibWebView/WebContentClient.cpp
@@ -62,6 +62,12 @@ void WebContentClient::did_paint(u64 page_id, Gfx::IntRect rect, i32 bitmap_id)
view->server_did_paint({}, bitmap_id, rect.size());
}
+void WebContentClient::did_request_new_process_for_navigation(u64 page_id, URL::URL url)
+{
+ if (auto view = view_for_page_id(page_id); view.has_value())
+ view->create_new_process_for_cross_site_navigation(url);
+}
+
void WebContentClient::did_start_loading(u64 page_id, URL::URL url, bool is_redirect)
{
if (auto process = WebView::Application::the().find_process(m_process_handle.pid); process.has_value())
diff --git a/Libraries/LibWebView/WebContentClient.h b/Libraries/LibWebView/WebContentClient.h
index 06eec41a253..28dd0d71376 100644
--- a/Libraries/LibWebView/WebContentClient.h
+++ b/Libraries/LibWebView/WebContentClient.h
@@ -52,6 +52,7 @@ private:
virtual void die() override;
virtual void did_paint(u64 page_id, Gfx::IntRect, i32) override;
+ virtual void did_request_new_process_for_navigation(u64 page_id, URL::URL url) override;
virtual void did_finish_loading(u64 page_id, URL::URL) override;
virtual void did_request_refresh(u64 page_id) override;
virtual void did_request_cursor_change(u64 page_id, Gfx::Cursor) override;
diff --git a/Services/WebContent/PageClient.cpp b/Services/WebContent/PageClient.cpp
index 1049fef6aa8..2e917bae179 100644
--- a/Services/WebContent/PageClient.cpp
+++ b/Services/WebContent/PageClient.cpp
@@ -29,6 +29,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -134,6 +135,16 @@ bool PageClient::is_connection_open() const
return client().is_open();
}
+bool PageClient::is_url_suitable_for_same_process_navigation(URL::URL const& current_url, URL::URL const& target_url) const
+{
+ return WebView::is_url_suitable_for_same_process_navigation(current_url, target_url);
+}
+
+void PageClient::request_new_process_for_navigation(URL::URL const& url)
+{
+ client().async_did_request_new_process_for_navigation(m_id, url);
+}
+
Gfx::Palette PageClient::palette() const
{
return Gfx::Palette(*m_palette_impl);
diff --git a/Services/WebContent/PageClient.h b/Services/WebContent/PageClient.h
index a7a5029a4a7..f3ea3df8a35 100644
--- a/Services/WebContent/PageClient.h
+++ b/Services/WebContent/PageClient.h
@@ -111,6 +111,8 @@ private:
// ^PageClient
virtual bool is_connection_open() const override;
+ virtual bool is_url_suitable_for_same_process_navigation(URL::URL const& current_url, URL::URL const& target_url) const override;
+ virtual void request_new_process_for_navigation(URL::URL const&) override;
virtual Gfx::Palette palette() const override;
virtual Web::DevicePixelRect screen_rect() const override { return m_screen_rect; }
virtual Web::CSS::PreferredColorScheme preferred_color_scheme() const override { return m_preferred_color_scheme; }
diff --git a/Services/WebContent/WebContentClient.ipc b/Services/WebContent/WebContentClient.ipc
index 766a8dbb1e6..42e71257772 100644
--- a/Services/WebContent/WebContentClient.ipc
+++ b/Services/WebContent/WebContentClient.ipc
@@ -23,6 +23,7 @@
endpoint WebContentClient
{
+ did_request_new_process_for_navigation(u64 page_id, URL::URL url) =|
did_start_loading(u64 page_id, URL::URL url, bool is_redirect) =|
did_finish_loading(u64 page_id, URL::URL url) =|
did_request_refresh(u64 page_id) =|