dbsoft / dwindows

Dynamic Windows Library Mirror
Other
8 stars 1 forks source link

Implement mechanism to allow Javascript in web widget to call native code #5

Open PierceNg opened 1 year ago

PierceNg commented 1 year ago

Inspiration from webview (which covers WebKitGTK on Linux, Cocoa WebKit on macOS and WebView2 on Windows):

// Binds a native C callback so that it will appear under the given name as a
// global JavaScript function. Internally it uses webview_init(). Callback
// receives a request string and a user-provided argument pointer. Request
// string is a JSON array of all the arguments passed to the JavaScript
// function.
WEBVIEW_API void webview_bind(webview_t w, const char *name,
                              void (*fn)(const char *seq, const char *req, void *arg),
                              void *arg);

// Removes a native C callback that was previously set by webview_bind.
WEBVIEW_API void webview_unbind(webview_t w, const char *name);

Use case: An app injects initial HTML content into the web widget using a file: URL. Action attached to a HTML button invokes a Javascript function which in turns invokes the native function through such a binding. This together with dw_html_javascript_run enable bidirectional Javascript-in-webwidget and native code in-process "remote procedure calls".

Example HTML snippet (from my Pascal binding to webview):

<html>    
<head></head>
<body>
  <button onClick="SayHello()">Say Hello</button>
  <div id="greeting"></div>
  <script>var SayHello = function() {
      HostSayHello().then((x) => document.getElementById("greeting").innerHTML = x.result)
  }</script>    
</body>
</html>

In the snippet, HostSayHello is the native (in my case Pascal) function that is made available to Javascript in the web widget. When the button is clicked, the native function returns a JSON object to the caller; it could also choose to inject Javascript to update the DOM (which my demo does).

dbsoft commented 1 year ago

This week is super busy, but next week I'll take a look at this proposal/idea and see if there is something I can implement with the maximum platform coverage as possible and in a consistent way.

dbsoft commented 1 year ago

So I have been doing my research... It looks like this should be possible on the following platforms: Mac 10.10+, GTK3/4 (with WebKit2GTK), Windows 7+ (with WebView2), iOS and Android.

I have a plan in mind for implementing this but I haven't started coding yet, probably later this week unless I run into problems with my plan.

These are some notes for myself for the implementation: Windows: https://learn.microsoft.com/en-us/microsoft-edge/webview2/how-to/hostobject?tabs=win32 Mac/iOS: https://developer.apple.com/documentation/webkit/wkusercontentcontroller/1537172-addscriptmessagehandler Android: http://rapidprogrammer.com/android-how-to-call-native-java-methods-from-webview-javascript GTK: https://webkitgtk.org/reference/webkit2gtk/2.25.2/WebKitUserContentManager.html

Each of these implementations makes the native methods accessible in slightly different ways, such as: window.webkit.messageHandlers for GTK/Mac/iOS (the WebKit based ones) chrome.webview.hostObjects for Edge WebView2 and via our Java/Kotlin object for Android

So we will do the same sort of thing that the WebView (linked in the issue description) people did, creating a Javascript function to inject, which handles the implementation differences allowing the same code to run on any of the supported platforms.

Also one last thing is undecided, if there will be a single callback for all injected functions in a given HTML widget (identified by the javascript function name) or if there will be a callback for each injected function. Thoughts? I am leaning towards a single callback for each HTML widget.

PierceNg commented 1 year ago

Thank you for working on this.

From application programmer's perspective, a single callback per web widget just means that the application-supplied callback needs to dispatch using the Javascript function name to the right native routine, whereas one callback per Javascript function means you've programmed the dispatching into the library.

As an application programmer I prefer one callback per function. 😀 But one callback overall is fine if it keeps the library implementation simple.

dbsoft commented 1 year ago

Well, if it is one signal per widget I can use the existing signal system... if it is per function then I need a separate HTML specific callback system. That is why I was leaning towards that, but will see what I can do.

dbsoft commented 1 year ago

Just wanted to let you know, I have been caught up in preparing what will hopefully be a stable/official Pale Moon release for Mac... so I have not been able to work on this, but I have some in progress code which I will get back to fairly soon. Should have code public to test by the end of the month.

dbsoft commented 1 year ago

In the process of committing the initial implementation of this, so far I have completed the WebKit based versions: Mac/iOS/GTK3/GTK4. I have commited failure versions: OS/2, GTK2 and the new platform template. Windows support with Edge WebView2 support is complete. Android support is complete, needed to bump up the version requirements to handle this, Android now requires Android 8 (API 26 Oreo).

PierceNg commented 5 months ago

Tested dw_html_javascript_add() and dw_html_javascript_run() on Linux GTK3 and Android successfully.

For Android, use of POST_NOTIFICATIONS requires build.gradle's android.compileSdk and android.defaultConfig.targetSdk to be set to 33, otherwise the build fails with unresolved permission.

PierceNg commented 5 months ago

Tested dw_html_javascript_add() and dw_html_javascript_run() on iOS successfully.