mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
LibWebView+UI: Show an error page when we cannot sanitize a URL
Previously, if search was disabled, entering non-URL text would just silently drop the search query (and on Qt, we would reload the current URL). We now detect that the query did not result in a navigation and load an error page instead, which directs the user to enable search.
This commit is contained in:
committed by
Andreas Kling
parent
83eda625d5
commit
fdbdb0ecd2
Notes:
github-actions[bot]
2026-04-24 18:18:24 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/fdbdb0ecd28 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/9072
89
Libraries/LibWebView/ErrorHTML.h
Normal file
89
Libraries/LibWebView/ErrorHTML.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2026-present, the Ladybird developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/StringView.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
// FIXME: Move these to an HTML file in Base/res/ladybird.
|
||||
|
||||
constexpr inline auto ERROR_HTML_HEADER = R"~~~(
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Error!</title>
|
||||
<style>
|
||||
:root {{
|
||||
color-scheme: light dark;
|
||||
font-family: system-ui, sans-serif;
|
||||
}}
|
||||
body {{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
}}
|
||||
header {{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}}
|
||||
svg {{
|
||||
height: 64px;
|
||||
width: auto;
|
||||
stroke: currentColor;
|
||||
fill: none;
|
||||
stroke-width: 1.5;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}}
|
||||
h1 {{
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
}}
|
||||
p {{
|
||||
font-size: 1rem;
|
||||
color: #555;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
{}
|
||||
<h1>{}</h1>
|
||||
</header>
|
||||
)~~~"sv;
|
||||
|
||||
constexpr inline auto ERROR_HTML_FOOTER = R"~~~(
|
||||
</body>
|
||||
</html>
|
||||
)~~~"sv;
|
||||
|
||||
constexpr inline auto ERROR_SVG = R"~~~(
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17.5 21.5">
|
||||
<path d="M11.75.75h-9c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2v-13l-5-5z" />
|
||||
<path d="M10.75.75v4c0 1.1.9 2 2 2h4M5.75 9.75v2M11.75 9.75v2M5.75 16.75c1-2.67 5-2.67 6 0" />
|
||||
</svg>
|
||||
)~~~"sv;
|
||||
|
||||
constexpr inline auto CRASH_ERROR_SVG = R"~~~(
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17.5 21.5">
|
||||
<path class="b" d="M11.75.75h-9c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2v-13l-5-5z" />
|
||||
<path class="b" d="M10.75.75v4c0 1.1.9 2 2 2h4M4.75 9.75l2 2M10.75 9.75l2 2M12.75 9.75l-2 2M6.75 9.75l-2 2M5.75 16.75c1-2.67 5-2.67 6 0" />
|
||||
</svg>
|
||||
)~~~"sv;
|
||||
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <LibWeb/Infra/Strings.h>
|
||||
#include <LibWebView/Application.h>
|
||||
#include <LibWebView/BookmarkStore.h>
|
||||
#include <LibWebView/ErrorHTML.h>
|
||||
#include <LibWebView/HelperProcess.h>
|
||||
#include <LibWebView/HistoryStore.h>
|
||||
#include <LibWebView/Menu.h>
|
||||
@@ -186,6 +187,17 @@ void ViewImplementation::load_html(StringView html)
|
||||
client().async_load_html(page_id(), html);
|
||||
}
|
||||
|
||||
void ViewImplementation::load_navigation_error_page(StringView text)
|
||||
{
|
||||
auto message = MUST(String::formatted("Failed to load \"{}\"", text));
|
||||
|
||||
StringBuilder builder;
|
||||
builder.appendff(ERROR_HTML_HEADER, ERROR_SVG, message);
|
||||
builder.append("<p>If you were trying to enter a search query, please enable search in <a href=\"about:settings#search\">settings</a>.</p>"sv);
|
||||
builder.append(ERROR_HTML_FOOTER);
|
||||
load_html(builder.string_view());
|
||||
}
|
||||
|
||||
void ViewImplementation::reload()
|
||||
{
|
||||
client().async_reload(page_id());
|
||||
@@ -692,25 +704,13 @@ void ViewImplementation::handle_web_content_process_crash(LoadErrorPage load_err
|
||||
handle_resize();
|
||||
|
||||
if (load_error_page == LoadErrorPage::Yes) {
|
||||
auto escaped_url = escape_html_entities(m_url.serialize());
|
||||
|
||||
StringBuilder builder;
|
||||
builder.append("<!DOCTYPE html>"sv);
|
||||
builder.append("<html lang=\"en\"><head><meta charset=\"UTF-8\"><title>Error!</title><style>"
|
||||
":root { color-scheme: light dark; font-family: system-ui, sans-serif; }"
|
||||
"body { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; box-sizing: border-box; margin: 0; padding: 1rem; text-align: center; }"
|
||||
"header { display: flex; flex-direction: column; align-items: center; gap: 2rem; margin-bottom: 1rem; }"
|
||||
"svg { height: 64px; width: auto; stroke: currentColor; fill: none; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }"
|
||||
"h1 { margin: 0; font-size: 1.5rem; }"
|
||||
"p { font-size: 1rem; color: #555; }"
|
||||
"</style></head><body>"sv);
|
||||
builder.append("<header>"sv);
|
||||
builder.append("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 17.5 21.5\">"sv);
|
||||
builder.append("<path class=\"b\" d=\"M11.75.75h-9c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2v-13l-5-5z\"/>"sv);
|
||||
builder.append("<path class=\"b\" d=\"M10.75.75v4c0 1.1.9 2 2 2h4M4.75 9.75l2 2M10.75 9.75l2 2M12.75 9.75l-2 2M6.75 9.75l-2 2M5.75 16.75c1-2.67 5-2.67 6 0\"/></svg>"sv);
|
||||
auto escaped_url = escape_html_entities(m_url.to_byte_string());
|
||||
builder.append("<h1>Ladybird flew off-course!</h1>"sv);
|
||||
builder.appendff(ERROR_HTML_HEADER, CRASH_ERROR_SVG, "Ladybird flew off-course!"sv);
|
||||
builder.appendff("<p>The web page <a href=\"{}\">{}</a> has crashed.<br><br>You can reload the page to try again.</p>", escaped_url, escaped_url);
|
||||
builder.append("</body></html>"sv);
|
||||
load_html(builder.to_byte_string());
|
||||
builder.append(ERROR_HTML_FOOTER);
|
||||
load_html(builder.string_view());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,8 @@ public:
|
||||
|
||||
void load(URL::URL const&);
|
||||
void load_html(StringView);
|
||||
void load_navigation_error_page(StringView);
|
||||
|
||||
void reload();
|
||||
void traverse_the_history_by_delta(int delta);
|
||||
|
||||
|
||||
@@ -609,6 +609,8 @@ static NSInteger autocomplete_suggestion_index(NSString* suggestion_text, Vector
|
||||
|
||||
if (auto url = WebView::sanitize_url(location, WebView::Application::settings().search_engine()); url.has_value()) {
|
||||
[self loadURL:*url];
|
||||
} else {
|
||||
[[[self tab] web_view] view].load_navigation_error_page(location);
|
||||
}
|
||||
|
||||
self.current_inline_autocomplete_suggestion = nil;
|
||||
|
||||
@@ -180,8 +180,8 @@ LocationEdit::LocationEdit(QWidget* parent)
|
||||
auto ctrl_held = QApplication::keyboardModifiers() & Qt::ControlModifier;
|
||||
auto append_tld = ctrl_held ? WebView::AppendTLD::Yes : WebView::AppendTLD::No;
|
||||
|
||||
if (auto url = WebView::sanitize_url(query, WebView::Application::settings().search_engine(), append_tld); url.has_value())
|
||||
set_url(url.release_value());
|
||||
auto url = WebView::sanitize_url(query, WebView::Application::settings().search_engine(), append_tld);
|
||||
set_url(AK::move(url));
|
||||
});
|
||||
|
||||
connect(this, &QLineEdit::textEdited, this, [this] {
|
||||
@@ -229,8 +229,8 @@ void LocationEdit::focusOutEvent(QFocusEvent* event)
|
||||
|
||||
if (m_url_is_hidden) {
|
||||
m_url_is_hidden = false;
|
||||
if (text().isEmpty())
|
||||
setText(qstring_from_ak_string(m_url.serialize()));
|
||||
if (text().isEmpty() && m_url.has_value())
|
||||
setText(qstring_from_ak_string(m_url->serialize()));
|
||||
}
|
||||
|
||||
if (event->reason() != Qt::PopupFocusReason) {
|
||||
@@ -245,7 +245,8 @@ void LocationEdit::keyPressEvent(QKeyEvent* event)
|
||||
if (m_autocomplete->close())
|
||||
return;
|
||||
reset_autocomplete_state();
|
||||
setText(qstring_from_ak_string(m_url.serialize()));
|
||||
if (m_url.has_value())
|
||||
setText(qstring_from_ak_string(m_url->serialize()));
|
||||
clearFocus();
|
||||
return;
|
||||
}
|
||||
@@ -331,14 +332,14 @@ void LocationEdit::highlight_location()
|
||||
QCoreApplication::sendEvent(this, &event);
|
||||
}
|
||||
|
||||
void LocationEdit::set_url(URL::URL url)
|
||||
void LocationEdit::set_url(Optional<URL::URL> url)
|
||||
{
|
||||
m_url = AK::move(url);
|
||||
|
||||
if (m_url_is_hidden) {
|
||||
clear();
|
||||
} else {
|
||||
setText(qstring_from_ak_string(m_url.serialize()));
|
||||
} else if (m_url.has_value()) {
|
||||
setText(qstring_from_ak_string(m_url->serialize()));
|
||||
setCursorPosition(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ class LocationEdit final
|
||||
public:
|
||||
explicit LocationEdit(QWidget*);
|
||||
|
||||
URL::URL const& url() const { return m_url; }
|
||||
void set_url(URL::URL);
|
||||
Optional<URL::URL const&> url() const { return m_url; }
|
||||
void set_url(Optional<URL::URL>);
|
||||
|
||||
bool url_is_hidden() const { return m_url_is_hidden; }
|
||||
void set_url_is_hidden(bool url_is_hidden) { m_url_is_hidden = url_is_hidden; }
|
||||
@@ -52,7 +52,7 @@ private:
|
||||
|
||||
Autocomplete* m_autocomplete { nullptr };
|
||||
|
||||
URL::URL m_url;
|
||||
Optional<URL::URL> m_url;
|
||||
bool m_url_is_hidden { false };
|
||||
|
||||
bool m_is_applying_inline_autocomplete { false };
|
||||
|
||||
@@ -485,9 +485,14 @@ void Tab::load_html(StringView html)
|
||||
|
||||
void Tab::location_edit_return_pressed()
|
||||
{
|
||||
if (m_location_edit->text().isEmpty())
|
||||
auto text = m_location_edit->text();
|
||||
if (text.isEmpty())
|
||||
return;
|
||||
navigate(m_location_edit->url());
|
||||
|
||||
if (auto url = m_location_edit->url(); url.has_value())
|
||||
navigate(*url);
|
||||
else
|
||||
view().load_navigation_error_page(ak_string_from_qstring(text));
|
||||
}
|
||||
|
||||
void Tab::open_file()
|
||||
|
||||
Reference in New Issue
Block a user