1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-30 23:18:10 +00:00

Browser: Add error page

Add a simple HTML error page that gets loaded into the HtmlView when
loading the page fails.

Closes #1210 and #1516
This commit is contained in:
Linus Groh 2020-03-31 23:59:11 +01:00 committed by Andreas Kling
parent b1365d94f4
commit 021d78f8f7
5 changed files with 79 additions and 28 deletions

21
Base/res/html/error.html Normal file
View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<title>Error!</title>
<style>
h1 {
display: inline;
}
header {
margin-bottom: 10px;
}
</style>
</head>
<body>
<header>
<img src="file:///res/icons/32x32/msgbox-warning.png" alt="Warning" width="24" height="24">
<h1>Failed to load %s</h1>
</header>
<p>Error: %s</p>
</body>
</html>

View file

@ -346,23 +346,51 @@ void HtmlView::load(const URL& url)
if (on_load_start) if (on_load_start)
on_load_start(url); on_load_start(url);
ResourceLoader::the().load(url, [this, url](auto data) { ResourceLoader::the().load(
if (data.is_null()) { url,
dbg() << "Load failed!"; [this, url](auto data) {
ASSERT_NOT_REACHED(); if (data.is_null()) {
} load_error_page(url, "No data");
return;
}
RefPtr<Document> document; RefPtr<Document> document;
if (url.path().ends_with(".png")) { if (url.path().ends_with(".png")) {
document = create_image_document(data, url); document = create_image_document(data, url);
} else { } else {
document = parse_html_document(data, url); document = parse_html_document(data, url);
} }
ASSERT(document); ASSERT(document);
set_document(document); set_document(document);
if (on_title_change) if (on_title_change)
on_title_change(document->title()); on_title_change(document->title());
}); },
[this, url](auto error) {
load_error_page(url, error);
});
}
void HtmlView::load_error_page(const URL& failed_url, const String& error)
{
auto error_page_url = "file:///res/html/error.html";
ResourceLoader::the().load(
error_page_url,
[this, failed_url, error](auto data) {
ASSERT(!data.is_null());
auto html = String::format(
String::copy(data).characters(),
escape_html_entities(failed_url.to_string()).characters(),
escape_html_entities(error).characters());
auto document = parse_html_document(html, failed_url);
ASSERT(document);
set_document(document);
if (on_title_change)
on_title_change(document->title());
},
[](auto error) {
dbg() << "Failed to load error page: " << error;
ASSERT_NOT_REACHED();
});
} }
const LayoutDocument* HtmlView::layout_root() const const LayoutDocument* HtmlView::layout_root() const

View file

@ -50,6 +50,7 @@ public:
void reload(); void reload();
void load(const URL&); void load(const URL&);
void load_error_page(const URL&, const String& error);
void scroll_to_anchor(const StringView&); void scroll_to_anchor(const StringView&);
URL url() const; URL url() const;

View file

@ -26,9 +26,9 @@
#include <AK/SharedBuffer.h> #include <AK/SharedBuffer.h>
#include <LibCore/File.h> #include <LibCore/File.h>
#include <LibWeb/ResourceLoader.h>
#include <LibProtocol/Client.h> #include <LibProtocol/Client.h>
#include <LibProtocol/Download.h> #include <LibProtocol/Download.h>
#include <LibWeb/ResourceLoader.h>
namespace Web { namespace Web {
@ -45,36 +45,37 @@ ResourceLoader::ResourceLoader()
{ {
} }
void ResourceLoader::load(const URL& url, Function<void(const ByteBuffer&)> callback) void ResourceLoader::load(const URL& url, Function<void(const ByteBuffer&)> success_callback, Function<void(const String&)> error_callback)
{ {
if (url.protocol() == "file") { if (url.protocol() == "file") {
auto f = Core::File::construct(); auto f = Core::File::construct();
f->set_filename(url.path()); f->set_filename(url.path());
if (!f->open(Core::IODevice::OpenMode::ReadOnly)) { if (!f->open(Core::IODevice::OpenMode::ReadOnly)) {
dbg() << "ResourceLoader::load: Error: " << f->error_string(); dbg() << "ResourceLoader::load: Error: " << f->error_string();
callback({}); if (error_callback)
error_callback(f->error_string());
return; return;
} }
auto data = f->read_all(); auto data = f->read_all();
deferred_invoke([data = move(data), callback = move(callback)](auto&) { deferred_invoke([data = move(data), success_callback = move(success_callback)](auto&) {
callback(data); success_callback(data);
}); });
return; return;
} }
if (url.protocol() == "http") { if (url.protocol() == "http") {
auto download = protocol_client().start_download(url.to_string()); auto download = protocol_client().start_download(url.to_string());
download->on_finish = [this, callback = move(callback)](bool success, const ByteBuffer& payload, auto) { download->on_finish = [this, success_callback = move(success_callback), error_callback = move(error_callback)](bool success, const ByteBuffer& payload, auto) {
--m_pending_loads; --m_pending_loads;
if (on_load_counter_change) if (on_load_counter_change)
on_load_counter_change(); on_load_counter_change();
if (!success) { if (!success) {
dbg() << "HTTP load failed!"; if (error_callback)
callback({}); error_callback("HTTP load failed");
return; return;
} }
callback(ByteBuffer::copy(payload.data(), payload.size())); success_callback(ByteBuffer::copy(payload.data(), payload.size()));
}; };
++m_pending_loads; ++m_pending_loads;
if (on_load_counter_change) if (on_load_counter_change)
@ -82,8 +83,8 @@ void ResourceLoader::load(const URL& url, Function<void(const ByteBuffer&)> call
return; return;
} }
dbg() << "Unimplemented protocol: " << url.protocol(); if (error_callback)
ASSERT_NOT_REACHED(); error_callback(String::format("Protocol not implemented: %s", url.protocol().characters()));
} }
} }

View file

@ -41,7 +41,7 @@ class ResourceLoader : public Core::Object {
public: public:
static ResourceLoader& the(); static ResourceLoader& the();
void load(const URL&, Function<void(const ByteBuffer&)>); void load(const URL&, Function<void(const ByteBuffer&)> success_callback, Function<void(const String&)> error_callback = nullptr);
Function<void()> on_load_counter_change; Function<void()> on_load_counter_change;