224 lines
8.4 KiB
Diff
224 lines
8.4 KiB
Diff
From 6162785d997fcfa4efaf6ec83670b2fab8cca6bd Mon Sep 17 00:00:00 2001
|
|
From: Michael Gratton <mike@vee.net>
|
|
Date: Fri, 28 Aug 2020 09:49:46 +1000
|
|
Subject: [PATCH 073/124] GearyWebExtension: Add support for sending messages
|
|
from JS to client
|
|
|
|
Define a vala-backed JS class in the extension and make that available
|
|
to pages when they are registered. Add some helper JS to PageState for
|
|
defining message sending functions. Listen for these in
|
|
Components.WebView and dispatch to the registered callback for it.
|
|
---
|
|
.../components/components-web-view.vala | 55 ++++++++++++++++
|
|
.../web-process/web-process-extension.vala | 63 +++++++++++++++++++
|
|
ui/components-web-view.js | 9 +++
|
|
3 files changed, 127 insertions(+)
|
|
|
|
diff --git a/src/client/components/components-web-view.vala b/src/client/components/components-web-view.vala
|
|
index 2b373170..c746c441 100644
|
|
--- a/src/client/components/components-web-view.vala
|
|
+++ b/src/client/components/components-web-view.vala
|
|
@@ -198,6 +198,24 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
|
|
/** Delegate for UserContentManager message callbacks. */
|
|
public delegate void JavaScriptMessageHandler(WebKit.JavascriptResult js_result);
|
|
|
|
+ /**
|
|
+ * Delegate for message handler callbacks.
|
|
+ *
|
|
+ * @see register_message_callback
|
|
+ */
|
|
+ protected delegate void MessageCallback(GLib.Variant? parameters);
|
|
+
|
|
+ // Work around for not being able to put delegates in a Gee collection.
|
|
+ private class MessageCallable {
|
|
+
|
|
+ public unowned MessageCallback handler;
|
|
+
|
|
+ public MessageCallable(MessageCallback handler) {
|
|
+ this.handler = handler;
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
/**
|
|
* Determines if the view's content has been fully loaded.
|
|
*
|
|
@@ -263,6 +281,8 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
|
|
|
|
private Gee.List<ulong> registered_message_handlers =
|
|
new Gee.LinkedList<ulong>();
|
|
+ private Gee.Map<string,MessageCallable> message_handlers =
|
|
+ new Gee.HashMap<string,MessageCallable>();
|
|
|
|
private double webkit_reported_height = 0;
|
|
|
|
@@ -359,6 +379,7 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
|
|
this.user_content_manager.disconnect(id);
|
|
}
|
|
this.registered_message_handlers.clear();
|
|
+ this.message_handlers.clear();
|
|
base.destroy();
|
|
}
|
|
|
|
@@ -568,6 +589,14 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
|
|
}
|
|
}
|
|
|
|
+ /**
|
|
+ * Registers a callback for a specific WebKit user message.
|
|
+ */
|
|
+ protected void register_message_callback(string name,
|
|
+ MessageCallback handler) {
|
|
+ this.message_handlers.set(name, new MessageCallable(handler));
|
|
+ }
|
|
+
|
|
private void init(Application.Configuration config) {
|
|
// XXX get the allow prefix from the extension somehow
|
|
|
|
@@ -595,6 +624,8 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
|
|
SELECTION_CHANGED, on_selection_changed
|
|
);
|
|
|
|
+ this.user_message_received.connect(this.on_message_received);
|
|
+
|
|
// Manage zoom level, ensure it's sane
|
|
config.bind(Application.Configuration.CONVERSATION_VIEWER_ZOOM_KEY, this, "zoom_level");
|
|
if (this.zoom_level < ZOOM_MIN) {
|
|
@@ -803,6 +834,30 @@ public abstract class Components.WebView : WebKit.WebView, Geary.BaseInterface {
|
|
}
|
|
}
|
|
|
|
+ private bool on_message_received(WebKit.UserMessage message) {
|
|
+ if (message.name == MESSAGE_EXCEPTION_NAME) {
|
|
+ var detail = new GLib.VariantDict(message.parameters);
|
|
+ var name = detail.lookup_value("name", GLib.VariantType.STRING) as string;
|
|
+ var log_message = detail.lookup_value("message", GLib.VariantType.STRING) as string;
|
|
+ warning(
|
|
+ "Error sending message from JS: %s: %s",
|
|
+ name ?? "unknown",
|
|
+ log_message ?? "unknown"
|
|
+ );
|
|
+ } else if (this.message_handlers.has_key(message.name)) {
|
|
+ debug(
|
|
+ "Message received: %s(%s)",
|
|
+ message.name,
|
|
+ message.parameters != null ? message.parameters.print(true) : ""
|
|
+ );
|
|
+ MessageCallable callback = this.message_handlers.get(message.name);
|
|
+ callback.handler(message.parameters);
|
|
+ } else {
|
|
+ warning("Message with unknown handler received: %s", message.name);
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+
|
|
}
|
|
|
|
// XXX this needs to be moved into the libsoup bindings
|
|
diff --git a/src/client/web-process/web-process-extension.vala b/src/client/web-process/web-process-extension.vala
|
|
index 89d9a1e3..31f2b0f0 100644
|
|
--- a/src/client/web-process/web-process-extension.vala
|
|
+++ b/src/client/web-process/web-process-extension.vala
|
|
@@ -38,6 +38,8 @@ public class GearyWebExtension : Object {
|
|
|
|
private const string[] ALLOWED_SCHEMES = { "cid", "geary", "data", "blob" };
|
|
|
|
+ private const string EXTENSION_CLASS_VAR = "_GearyWebExtension";
|
|
+ private const string EXTENSION_CLASS_SEND = "send";
|
|
private const string REMOTE_LOAD_VAR = "_gearyAllowRemoteResourceLoads";
|
|
|
|
private WebKit.WebExtension extension;
|
|
@@ -180,6 +182,25 @@ public class GearyWebExtension : Object {
|
|
WebKit.WebPage page) {
|
|
WebKit.Frame frame = page.get_main_frame();
|
|
JSC.Context context = frame.get_js_context();
|
|
+
|
|
+ var extension_class = context.register_class(
|
|
+ this.get_type().name(),
|
|
+ null,
|
|
+ null,
|
|
+ null
|
|
+ );
|
|
+ extension_class.add_method(
|
|
+ EXTENSION_CLASS_SEND,
|
|
+ (instance, values) => {
|
|
+ return this.on_page_send_message(page, values);
|
|
+ },
|
|
+ GLib.Type.NONE
|
|
+ );
|
|
+ context.set_value(
|
|
+ EXTENSION_CLASS_VAR,
|
|
+ new JSC.Value.object(context, extension_class, extension_class)
|
|
+ );
|
|
+
|
|
context.set_value(
|
|
REMOTE_LOAD_VAR,
|
|
new JSC.Value.boolean(context, false)
|
|
@@ -259,4 +280,46 @@ public class GearyWebExtension : Object {
|
|
return true;
|
|
}
|
|
|
|
+ private bool on_page_send_message(WebKit.WebPage page,
|
|
+ GLib.GenericArray<JSC.Value> args) {
|
|
+ WebKit.UserMessage? message = null;
|
|
+ if (args.length > 0) {
|
|
+ var name = args.get(0).to_string();
|
|
+ GLib.Variant? parameters = null;
|
|
+ if (args.length > 1) {
|
|
+ JSC.Value param_value = args.get(1);
|
|
+ try {
|
|
+ int len = Util.JS.to_int32(
|
|
+ param_value.object_get_property("length")
|
|
+ );
|
|
+ if (len == 1) {
|
|
+ parameters = Util.JS.value_to_variant(
|
|
+ param_value.object_get_property_at_index(0)
|
|
+ );
|
|
+ } else if (len > 1) {
|
|
+ parameters = Util.JS.value_to_variant(param_value);
|
|
+ }
|
|
+ } catch (Util.JS.Error err) {
|
|
+ message = to_exception_message(
|
|
+ this.get_type().name(), err.message
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+ if (message == null) {
|
|
+ message = new WebKit.UserMessage(name, parameters);
|
|
+ }
|
|
+ }
|
|
+ if (message == null) {
|
|
+ var log_message = "Not enough parameters for JS call to %s.%s()".printf(
|
|
+ EXTENSION_CLASS_VAR,
|
|
+ EXTENSION_CLASS_SEND
|
|
+ );
|
|
+ debug(log_message);
|
|
+ message = to_exception_message(this.get_type().name(), log_message);
|
|
+ }
|
|
+
|
|
+ page.send_message_to_view.begin(message, null);
|
|
+ return true;
|
|
+ }
|
|
+
|
|
}
|
|
diff --git a/ui/components-web-view.js b/ui/components-web-view.js
|
|
index 0f932a19..35e82dfc 100644
|
|
--- a/ui/components-web-view.js
|
|
+++ b/ui/components-web-view.js
|
|
@@ -200,3 +200,12 @@ PageState.prototype = {
|
|
throw this.testResult;
|
|
}
|
|
};
|
|
+
|
|
+let MessageSender = function(name) {
|
|
+ return function() {
|
|
+ // Since typeof(arguments) == 'object', convert to an array so
|
|
+ // that Components.WebView.MessageCallback callbacks get
|
|
+ // arrays or tuples rather than dicts as arguments
|
|
+ _GearyWebExtension.send(name, Array.from(arguments));
|
|
+ };
|
|
+};
|
|
--
|
|
2.29.2
|
|
|