add internal query handler for fetch errors

Any errors from the fetch which are not already handled are
  reported with an internal query page instead of a modal
  dialog.

This is much less invasive for the user and much more in
  keeping with how this is handled by other browsers.

The handler is similar to the timeout handler but the
  functionality is kept separate as it is intended timeout
  handling be extended in future.
This commit is contained in:
Vincent Sanders 2019-10-30 18:56:38 +00:00
parent 76eac19227
commit 4b0c3f0efe
5 changed files with 275 additions and 27 deletions

View File

@ -94,6 +94,11 @@ static const char *privacy_description_fallback = "A privacy error occurred whil
*/
static const char *timeout_description_fallback = "A connection to %s could not be established. The site may be temporarily unavailable or too busy to respond.";
/**
* fetcherror query description if messages fails to retrieve usable text
*/
static const char *fetcherror_description_fallback = "An error occoured when connecting to %s";
/**
* issue fetch callbacks with locking
*/
@ -1239,6 +1244,130 @@ fetch_about_query_timeout_handler_aborted:
}
/**
* Handler to generate about scheme fetch error query page
*
* \param ctx The fetcher context.
* \return true if handled false if aborted.
*/
static bool
fetch_about_query_fetcherror_handler(struct fetch_about_context *ctx)
{
nserror res;
char *url_s;
size_t url_l;
const char *reason = "";
const char *title;
struct nsurl *siteurl = NULL;
char *description = NULL;
const struct fetch_multipart_data *curmd; /* mutipart data iterator */
/* extract parameters from multipart post data */
curmd = ctx->multipart;
while (curmd != NULL) {
if (strcmp(curmd->name, "siteurl") == 0) {
res = nsurl_create(curmd->value, &siteurl);
if (res != NSERROR_OK) {
return fetch_about_srverror(ctx);
}
} else if (strcmp(curmd->name, "reason") == 0) {
reason = curmd->value;
}
curmd = curmd->next;
}
if (siteurl == NULL) {
return fetch_about_srverror(ctx);
}
/* content is going to return ok */
fetch_set_http_code(ctx->fetchh, 200);
/* content type */
if (fetch_about_send_header(ctx, "Content-Type: text/html; charset=utf-8")) {
goto fetch_about_query_fetcherror_handler_aborted;
}
title = messages_get("FetchErrorTitle");
res = ssenddataf(ctx,
"<html>\n<head>\n"
"<title>%s</title>\n"
"<link rel=\"stylesheet\" type=\"text/css\" "
"href=\"resource:internal.css\">\n"
"</head>\n"
"<body id =\"fetcherror\">\n"
"<h1>%s</h1>\n",
title, title);
if (res != NSERROR_OK) {
goto fetch_about_query_fetcherror_handler_aborted;
}
res = ssenddataf(ctx,
"<form method=\"post\""
" enctype=\"multipart/form-data\">");
if (res != NSERROR_OK) {
goto fetch_about_query_fetcherror_handler_aborted;
}
res = get_query_description(siteurl,
"FetchErrorDescription",
fetcherror_description_fallback,
&description);
if (res == NSERROR_OK) {
res = ssenddataf(ctx, "<div><p>%s</p></div>", description);
free(description);
if (res != NSERROR_OK) {
goto fetch_about_query_fetcherror_handler_aborted;
}
}
res = ssenddataf(ctx, "<div><p>%s</p></div>", reason);
if (res != NSERROR_OK) {
goto fetch_about_query_fetcherror_handler_aborted;
}
res = ssenddataf(ctx,
"<div id=\"buttons\">"
"<input type=\"submit\" id=\"back\" name=\"back\" "
"value=\"%s\" class=\"default-action\">"
"<input type=\"submit\" id=\"retry\" name=\"retry\" "
"value=\"%s\">"
"</div>",
messages_get("Backtoprevious"),
messages_get("TryAgain"));
if (res != NSERROR_OK) {
goto fetch_about_query_fetcherror_handler_aborted;
}
res = nsurl_get(siteurl, NSURL_COMPLETE, &url_s, &url_l);
if (res != NSERROR_OK) {
url_s = strdup("");
}
res = ssenddataf(ctx,
"<input type=\"hidden\" name=\"siteurl\" value=\"%s\">",
url_s);
free(url_s);
if (res != NSERROR_OK) {
goto fetch_about_query_fetcherror_handler_aborted;
}
res = ssenddataf(ctx, "</form></body>\n</html>\n");
if (res != NSERROR_OK) {
goto fetch_about_query_fetcherror_handler_aborted;
}
fetch_about_send_finished(ctx);
nsurl_unref(siteurl);
return true;
fetch_about_query_fetcherror_handler_aborted:
nsurl_unref(siteurl);
return false;
}
/* Forward declaration because this handler requires the handler table. */
static bool fetch_about_about_handler(struct fetch_about_context *ctx);
@ -1352,6 +1481,13 @@ struct about_handlers about_handler_list[] = {
NULL,
fetch_about_query_timeout_handler,
true
},
{
"query/fetcherror",
SLEN("query/fetcherror"),
NULL,
fetch_about_query_fetcherror_handler,
true
}
};

View File

@ -1216,6 +1216,50 @@ browser_window__handle_timeout(struct browser_window *bw, nsurl *url)
}
/**
* Handle non specific errors during a fetch
*/
static nserror
browser_window__handle_fetcherror(struct browser_window *bw,
const char *reason,
nsurl *url)
{
struct browser_fetch_parameters params;
nserror err;
memset(&params, 0, sizeof(params));
params.url = nsurl_ref(corestring_nsurl_about_query_fetcherror);
params.referrer = nsurl_ref(url);
params.flags = BW_NAVIGATE_HISTORY | BW_NAVIGATE_NO_TERMINAL_HISTORY_UPDATE | BW_NAVIGATE_INTERNAL;
err = fetch_multipart_data_new_kv(&params.post_multipart,
"siteurl",
nsurl_access(url));
if (err != NSERROR_OK) {
goto out;
}
err = fetch_multipart_data_new_kv(&params.post_multipart,
"reason",
reason);
if (err != NSERROR_OK) {
goto out;
}
/* Now we issue the fetch */
bw->internal_nav = true;
err = browser_window__navigate_internal(bw, &params);
if (err != NSERROR_OK) {
goto out;
}
out:
browser_window__free_fetch_parameters(&params);
return err;
}
/**
* Handle errors during content fetch
*/
@ -1226,39 +1270,14 @@ browser_window__handle_error(struct browser_window *bw,
{
const char *message = event->data.errordata.errormsg;
nserror code = event->data.errordata.errorcode;
bool do_warning = true;
nserror res;
nsurl *url = hlcache_handle_get_url(c);
/* Unexpected OK? */
assert(code != NSERROR_OK);
switch (code) {
case NSERROR_BAD_AUTH:
do_warning = false;
break;
case NSERROR_BAD_CERTS:
do_warning = false;
break;
case NSERROR_BAD_REDIRECT:
/* The message is already filled out */
break;
case NSERROR_TIMEOUT:
do_warning = false;
break;
default:
if (message == NULL) {
message = messages_get_errorcode(code);
}
break;
}
if (do_warning) {
browser_window_set_status(bw, message);
/* Only warn the user about errors in top-level windows */
if (bw->browser_window_type == BROWSER_WINDOW_NORMAL) {
guit->misc->warning(message, NULL);
}
if (message == NULL) {
message = messages_get_errorcode(code);
}
if (c == bw->loading_content) {
@ -1274,13 +1293,17 @@ browser_window__handle_error(struct browser_window *bw,
case NSERROR_BAD_AUTH:
res = browser_window__handle_login(bw, message, url);
break;
case NSERROR_BAD_CERTS:
res = browser_window__handle_bad_certs(bw, url);
break;
case NSERROR_TIMEOUT:
res = browser_window__handle_timeout(bw, url);
break;
default:
res = browser_window__handle_fetcherror(bw, message, url);
break;
}
@ -2411,6 +2434,8 @@ is_internal_navigate_url(nsurl *url)
is_internal = true;
} else if (path == corestring_lwc_query_timeout) {
is_internal = true;
} else if (path == corestring_lwc_query_fetcherror) {
is_internal = true;
}
}
lwc_string_unref(path);
@ -3613,6 +3638,42 @@ navigate_internal_query_timeout(struct browser_window *bw,
}
/**
* Internal navigation handler for the fetch error query page.
*
* If the parameters indicate we're processing a *response* from the handler
* then we deal with that, otherwise we pass it on to the about: handler
*/
static nserror
navigate_internal_query_fetcherror(struct browser_window *bw,
struct browser_fetch_parameters *params)
{
bool is_retry = false, is_back = false;
NSLOG(netsurf, INFO, "bw:%p params:%p", bw, params);
assert(params->post_multipart != NULL);
is_retry = fetch_multipart_data_find(params->post_multipart, "retry") != NULL;
is_back = fetch_multipart_data_find(params->post_multipart, "back") != NULL;
if (is_back) {
/* do a rough-and-ready nav to the old 'current'
* parameters, with any post data stripped away
*/
return browser_window__reload_current_parameters(bw);
}
if (is_retry) {
/* Finally navigate to the original loading parameters */
bw->internal_nav = false;
return navigate_internal_real(bw, &bw->loading_parameters);
}
return navigate_internal_real(bw, params);
}
/**
* dispatch to internal query handlers or normal navigation
*
@ -3651,6 +3712,10 @@ browser_window__navigate_internal(struct browser_window *bw,
lwc_string_unref(path);
return navigate_internal_query_timeout(bw, params);
}
if (path == corestring_lwc_query_fetcherror) {
lwc_string_unref(path);
return navigate_internal_query_fetcherror(bw, params);
}
lwc_string_unref(path);
/* Fall through to a normal about: fetch */

View File

@ -1096,6 +1096,13 @@ en.all.Backtoprevious: Back
en.all.TryAgain: Try Again
# Fetch error interface
# =======================
#
en.all.FetchErrorTitle:Error occured fetching page
en.all.FetchErrorDescription:An error occoured when connecting to %s
# SSL certificate viewer
# ======================
#

View File

@ -381,3 +381,41 @@ body#timeout div#buttons {
body#timeout div#buttons input#back {
margin-right: 1em;
}
/*
* fetch error query styling
*/
body#fetcherror {
max-width: 45em;
}
body#fetcherror h1 {
padding: 0.8em 0.4em 0.5em 0.4em;
border-bottom: 0.1em solid #444;
margin: 0 0 1.3em 0;
background: #c55;
color: white;
}
body#fetcherror form {
/* Just to center the form on the page */
margin: 0 auto;
/* To see the outline of the form */
padding: 1em;
border: 1px solid #CCC;
border-radius: 1em;
}
body#fetcherror form div + div {
margin-top: 1em;
}
body#fetcherror div#buttons {
text-align: right;
margin-right: 1em;
}
body#fetcherror div#buttons input#back {
margin-right: 1em;
}

View File

@ -149,6 +149,7 @@ CORESTRING_LWC_VALUE(no_store, "no-store");
CORESTRING_LWC_VALUE(query_auth, "query/auth");
CORESTRING_LWC_VALUE(query_ssl, "query/ssl");
CORESTRING_LWC_VALUE(query_timeout, "query/timeout");
CORESTRING_LWC_VALUE(query_fetcherror, "query/fetcherror");
/* mime types */
CORESTRING_LWC_VALUE(multipart_form_data, "multipart/form-data");
@ -362,6 +363,7 @@ CORESTRING_NSURL(about_blank, "about:blank");
CORESTRING_NSURL(about_query_ssl, "about:query/ssl");
CORESTRING_NSURL(about_query_auth, "about:query/auth");
CORESTRING_NSURL(about_query_timeout, "about:query/timeout");
CORESTRING_NSURL(about_query_fetcherror, "about:query/fetcherror");
#undef CORESTRING_LWC_STRING
#undef CORESTRING_DOM_STRING