gjdwebserver-overlay/mail-client/geary/files/0071-Components.WebView-Check-for-pass-up-exceptions-when.patch
Gerben Jan Dijkman c00ddb4dce Added Geary
2021-03-23 14:05:24 +01:00

267 lines
11 KiB
Diff

From c813aa5707acc5226a57dca82449dc709969d05a Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Thu, 27 Aug 2020 16:18:45 +1000
Subject: [PATCH 071/124] Components.WebView: Check for pass up exceptions when
calling JS code
Update web extension to check for errors when invoking page state
methods and pass a message back if found. Check for this, decode and
throw a vala error in the WebView if found.
---
.../components/components-web-view.vala | 56 ++++++++++++++---
.../web-process/web-process-extension.vala | 39 ++++++++++--
test/js/components-page-state-test.vala | 60 +++++++++++++++++++
ui/components-web-view.js | 4 ++
4 files changed, 146 insertions(+), 13 deletions(-)
diff --git a/src/client/components/components-web-view.vala b/src/client/components/components-web-view.vala
index 368b6a8d..2b373170 100644
--- a/src/client/components/components-web-view.vala
+++ b/src/client/components/components-web-view.vala
@@ -26,6 +26,10 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
/** URI Scheme and delimiter for images loaded by Content-ID. */
public const string CID_URL_PREFIX = "cid:";
+ // Keep these in sync with GearyWebExtension
+ private const string MESSAGE_RETURN_VALUE_NAME = "__return__";
+ private const string MESSAGE_EXCEPTION_NAME = "__exception__";
+
// WebKit message handler names
private const string COMMAND_STACK_CHANGED = "commandStackChanged";
private const string CONTENT_LOADED = "contentLoaded";
@@ -467,9 +471,7 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
protected async void call_void(Util.JS.Callable target,
GLib.Cancellable? cancellable)
throws GLib.Error {
- yield send_message_to_page(
- target.to_message(), cancellable
- );
+ yield call_impl(target, cancellable);
}
/**
@@ -488,12 +490,10 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
protected async T call_returning<T>(Util.JS.Callable target,
GLib.Cancellable? cancellable)
throws GLib.Error {
- WebKit.UserMessage? response = yield send_message_to_page(
- target.to_message(), cancellable
- );
+ WebKit.UserMessage? response = yield call_impl(target, cancellable);
if (response == null) {
throw new Util.JS.Error.TYPE(
- "Method call did not return a value: %s", target.to_string()
+ "Method call %s did not return a value", target.to_string()
);
}
GLib.Variant? param = response.parameters;
@@ -612,6 +612,48 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
"monospace-font", SettingsBindFlags.DEFAULT);
}
+ private async WebKit.UserMessage? call_impl(Util.JS.Callable target,
+ GLib.Cancellable? cancellable)
+ throws GLib.Error {
+ WebKit.UserMessage? response = yield send_message_to_page(
+ target.to_message(), cancellable
+ );
+ if (response != null) {
+ var response_name = response.name;
+ if (response_name == MESSAGE_EXCEPTION_NAME) {
+ var exception = new GLib.VariantDict(response.parameters);
+ var name = exception.lookup_value("name", GLib.VariantType.STRING) as string;
+ var message = exception.lookup_value("message", GLib.VariantType.STRING) as string;
+ var backtrace = exception.lookup_value("backtrace_string", GLib.VariantType.STRING) as string;
+ var source = exception.lookup_value("source_uri", GLib.VariantType.STRING) as string;
+ var line = exception.lookup_value("line_number", GLib.VariantType.UINT32);
+ var column = exception.lookup_value("column_number", GLib.VariantType.UINT32);
+
+ var log_message = "Method call %s raised %s exception at %s:%d:%d: %s".printf(
+ target.to_string(),
+ name ?? "unknown",
+ source ?? "unknown",
+ (line != null ? (int) line.get_uint32() : -1),
+ (column != null ? (int) column.get_uint32() : -1),
+ message ?? "unknown"
+ );
+ debug(log_message);
+ if (backtrace != null) {
+ debug(backtrace);
+ }
+
+ throw new Util.JS.Error.EXCEPTION(log_message);
+ } else if (response_name != MESSAGE_RETURN_VALUE_NAME) {
+ throw new Util.JS.Error.TYPE(
+ "Method call %s returned unknown name: %s",
+ target.to_string(),
+ response_name
+ );
+ }
+ }
+ return response;
+ }
+
private void handle_cid_request(WebKit.URISchemeRequest request) {
if (!handle_internal_response(request)) {
request.finish_error(new FileError.NOENT("Unknown CID"));
diff --git a/src/client/web-process/web-process-extension.vala b/src/client/web-process/web-process-extension.vala
index 86f7f44c..7aa6dd3c 100644
--- a/src/client/web-process/web-process-extension.vala
+++ b/src/client/web-process/web-process-extension.vala
@@ -31,6 +31,8 @@ public void webkit_web_extension_initialize_with_user_data(WebKit.WebExtension e
public class GearyWebExtension : Object {
private const string PAGE_STATE_OBJECT_NAME = "geary";
+
+ // Keep these in sync with Components.WebView
private const string MESSAGE_RETURN_VALUE_NAME = "__return__";
private const string MESSAGE_EXCEPTION_NAME = "__exception__";
@@ -199,12 +201,37 @@ public class GearyWebExtension : Object {
// rain hail or shine.
// https://bugs.webkit.org/show_bug.cgi?id=215880
- message.send_reply(
- new WebKit.UserMessage(
- MESSAGE_RETURN_VALUE_NAME,
- Util.JS.value_to_variant(ret)
- )
- );
+ JSC.Exception? thrown = context.get_exception();
+ if (thrown != null) {
+ var detail = new GLib.VariantDict();
+ if (thrown.get_message() != null) {
+ detail.insert_value("name", new GLib.Variant.string(thrown.get_name()));
+ }
+ if (thrown.get_message() != null) {
+ detail.insert_value("message", new GLib.Variant.string(thrown.get_message()));
+ }
+ if (thrown.get_backtrace_string() != null) {
+ detail.insert_value("backtrace_string", new GLib.Variant.string(thrown.get_backtrace_string()));
+ }
+ if (thrown.get_source_uri() != null) {
+ detail.insert_value("source_uri", new GLib.Variant.string(thrown.get_source_uri()));
+ }
+ detail.insert_value("line_number", new GLib.Variant.uint32(thrown.get_line_number()));
+ detail.insert_value("column_number", new GLib.Variant.uint32(thrown.get_column_number()));
+ message.send_reply(
+ new WebKit.UserMessage(
+ MESSAGE_EXCEPTION_NAME,
+ detail.end()
+ )
+ );
+ } else {
+ message.send_reply(
+ new WebKit.UserMessage(
+ MESSAGE_RETURN_VALUE_NAME,
+ Util.JS.value_to_variant(ret)
+ )
+ );
+ }
} catch (GLib.Error err) {
debug("Failed to handle message: %s", err.message);
}
diff --git a/test/js/components-page-state-test.vala b/test/js/components-page-state-test.vala
index 562c6cda..bf952416 100644
--- a/test/js/components-page-state-test.vala
+++ b/test/js/components-page-state-test.vala
@@ -31,7 +31,9 @@ class Components.PageStateTest : WebViewTestCase<WebView> {
base("Components.PageStateTest");
add_test("content_loaded", content_loaded);
add_test("call_void", call_void);
+ add_test("call_void_throws", call_void_throws);
add_test("call_returning", call_returning);
+ add_test("call_returning_throws", call_returning_throws);
try {
WebView.load_resources(GLib.File.new_for_path("/tmp"));
@@ -68,6 +70,35 @@ class Components.PageStateTest : WebViewTestCase<WebView> {
assert_test_result("void");
}
+ public void call_void_throws() throws GLib.Error {
+ load_body_fixture("OHHAI");
+ var test_article = this.test_view as TestWebView;
+
+ try {
+ test_article.call_void.begin(
+ new Util.JS.Callable("testThrow").string("void message"),
+ this.async_completion
+ );
+ test_article.call_void.end(this.async_result());
+ assert_not_reached();
+ } catch (Util.JS.Error.EXCEPTION err) {
+ assert_string(
+ err.message
+ ).contains(
+ "testThrow"
+ // WebKitGTK doesn't actually pass any details through:
+ // https://bugs.webkit.org/show_bug.cgi?id=215877
+ // ).contains(
+ // "Error"
+ // ).contains(
+ // "void message"
+ // ).contains(
+ // "components-web-view.js"
+ );
+ assert_test_result("void message");
+ }
+ }
+
public void call_returning() throws GLib.Error {
load_body_fixture("OHHAI");
var test_article = this.test_view as TestWebView;
@@ -81,6 +112,35 @@ class Components.PageStateTest : WebViewTestCase<WebView> {
assert_test_result("check 1-2");
}
+ public void call_returning_throws() throws GLib.Error {
+ load_body_fixture("OHHAI");
+ var test_article = this.test_view as TestWebView;
+
+ try {
+ test_article.call_returning.begin(
+ new Util.JS.Callable("testThrow").string("return message"),
+ this.async_completion
+ );
+ test_article.call_returning.end(this.async_result());
+ assert_not_reached();
+ } catch (Util.JS.Error.EXCEPTION err) {
+ assert_string(
+ err.message
+ ).contains(
+ "testThrow"
+ // WebKitGTK doesn't actually pass any details through:
+ // https://bugs.webkit.org/show_bug.cgi?id=215877
+ // ).contains(
+ // "Error"
+ // ).contains(
+ // "return message"
+ // ).contains(
+ // "components-web-view.js"
+ );
+ assert_test_result("return message");
+ }
+ }
+
protected override WebView set_up_test_view() {
WebKit.UserScript test_script;
test_script = new WebKit.UserScript(
diff --git a/ui/components-web-view.js b/ui/components-web-view.js
index 289abca0..0f932a19 100644
--- a/ui/components-web-view.js
+++ b/ui/components-web-view.js
@@ -194,5 +194,9 @@ PageState.prototype = {
testReturn: function(value) {
this.testResult = value;
return value;
+ },
+ testThrow: function(value) {
+ this.testResult = value;
+ throw this.testResult;
}
};
--
2.29.2