267 lines
11 KiB
Diff
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
|
|
|