aboutsummaryrefslogtreecommitdiffstats
path: root/src/xwidget.c
diff options
context:
space:
mode:
authorRicardo Wurmus2016-10-25 23:00:35 -0700
committerPaul Eggert2016-10-25 23:07:13 -0700
commit623deaf406a85d8262bc1735009b3ee0535cc688 (patch)
treeae248862709bdfed77a25a6bcd542467500a1a3d /src/xwidget.c
parentd781662873f228b110a128f7a2b6583a4d5e0a3a (diff)
downloademacs-623deaf406a85d8262bc1735009b3ee0535cc688.tar.gz
emacs-623deaf406a85d8262bc1735009b3ee0535cc688.zip
xwidget: Pass JavaScript return value to optional callback procedure
* lisp/xwidget.el (xwidget-webkit-execute-script): Accept optional callback argument. (xwidget-webkit-callback): Handle "javascript-callback" event type. * src/xwidget.c (xwidget-webkit-execute-script): Accept optional argument FUN, a Lisp procedure to execute on the JavaScript return value. (store_xwidget_js_callback_event, webkit_javascript_finished_cb, webkit_js_to_lisp): New procedures.
Diffstat (limited to 'src/xwidget.c')
-rw-r--r--src/xwidget.c165
1 files changed, 158 insertions, 7 deletions
diff --git a/src/xwidget.c b/src/xwidget.c
index 78349a819a9..4f53b9301c4 100644
--- a/src/xwidget.c
+++ b/src/xwidget.c
@@ -28,6 +28,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
28#include "gtkutil.h" 28#include "gtkutil.h"
29 29
30#include <webkit2/webkit2.h> 30#include <webkit2/webkit2.h>
31#include <JavaScriptCore/JavaScript.h>
31 32
32static struct xwidget * 33static struct xwidget *
33allocate_xwidget (void) 34allocate_xwidget (void)
@@ -50,6 +51,9 @@ static struct xwidget_view *xwidget_view_lookup (struct xwidget *,
50static void webkit_view_load_changed_cb (WebKitWebView *, 51static void webkit_view_load_changed_cb (WebKitWebView *,
51 WebKitLoadEvent, 52 WebKitLoadEvent,
52 gpointer); 53 gpointer);
54static void webkit_javascript_finished_cb (GObject *,
55 GAsyncResult *,
56 gpointer);
53static gboolean webkit_download_cb (WebKitWebContext *, WebKitDownload *, gpointer); 57static gboolean webkit_download_cb (WebKitWebContext *, WebKitDownload *, gpointer);
54 58
55static gboolean 59static gboolean
@@ -251,6 +255,22 @@ store_xwidget_event_string (struct xwidget *xw, const char *eventname,
251 kbd_buffer_store_event (&event); 255 kbd_buffer_store_event (&event);
252} 256}
253 257
258static void
259store_xwidget_js_callback_event (struct xwidget *xw,
260 Lisp_Object proc,
261 Lisp_Object argument)
262{
263 struct input_event event;
264 Lisp_Object xwl;
265 XSETXWIDGET (xwl, xw);
266 EVENT_INIT (event);
267 event.kind = XWIDGET_EVENT;
268 event.frame_or_window = Qnil;
269 event.arg = list4 (intern ("javascript-callback"), xwl, proc, argument);
270 kbd_buffer_store_event (&event);
271}
272
273
254void 274void
255webkit_view_load_changed_cb (WebKitWebView *webkitwebview, 275webkit_view_load_changed_cb (WebKitWebView *webkitwebview,
256 WebKitLoadEvent load_event, 276 WebKitLoadEvent load_event,
@@ -269,6 +289,128 @@ webkit_view_load_changed_cb (WebKitWebView *webkitwebview,
269 } 289 }
270} 290}
271 291
292/* Recursively convert a JavaScript value to a Lisp value. */
293Lisp_Object
294webkit_js_to_lisp (JSContextRef context, JSValueRef value)
295{
296 switch (JSValueGetType (context, value))
297 {
298 case kJSTypeString:
299 {
300 JSStringRef js_str_value;
301 gchar *str_value;
302 gsize str_length;
303
304 js_str_value = JSValueToStringCopy (context, value, NULL);
305 str_length = JSStringGetMaximumUTF8CStringSize (js_str_value);
306 str_value = (gchar *)g_malloc (str_length);
307 JSStringGetUTF8CString (js_str_value, str_value, str_length);
308 JSStringRelease (js_str_value);
309 return build_string (str_value);
310 }
311 case kJSTypeBoolean:
312 return (JSValueToBoolean (context, value)) ? Qt : Qnil;
313 case kJSTypeNumber:
314 return make_number (JSValueToNumber (context, value, NULL));
315 case kJSTypeObject:
316 {
317 if (JSValueIsArray (context, value))
318 {
319 JSStringRef pname = JSStringCreateWithUTF8CString("length");
320 JSValueRef len = JSObjectGetProperty (context, (JSObjectRef) value, pname, NULL);
321 int n = JSValueToNumber (context, len, NULL);
322 JSStringRelease(pname);
323
324 Lisp_Object obj;
325 struct Lisp_Vector *p = allocate_vector (n);
326
327 for (int i = 0; i < n; ++i)
328 {
329 p->contents[i] =
330 webkit_js_to_lisp (context,
331 JSObjectGetPropertyAtIndex (context,
332 (JSObjectRef) value,
333 i, NULL));
334 }
335 XSETVECTOR (obj, p);
336 return obj;
337 }
338 else
339 {
340 JSPropertyNameArrayRef properties =
341 JSObjectCopyPropertyNames (context, (JSObjectRef) value);
342
343 int n = JSPropertyNameArrayGetCount (properties);
344 Lisp_Object obj;
345
346 // TODO: can we use a regular list here?
347 struct Lisp_Vector *p = allocate_vector (n);
348
349 for (int i = 0; i < n; ++i)
350 {
351 JSStringRef name = JSPropertyNameArrayGetNameAtIndex (properties, i);
352 JSValueRef property = JSObjectGetProperty (context,
353 (JSObjectRef) value,
354 name, NULL);
355 gchar *str_name;
356 gsize str_length;
357 str_length = JSStringGetMaximumUTF8CStringSize (name);
358 str_name = (gchar *)g_malloc (str_length);
359 JSStringGetUTF8CString (name, str_name, str_length);
360 JSStringRelease (name);
361
362 p->contents[i] =
363 Fcons (build_string (str_name),
364 webkit_js_to_lisp (context, property));
365 }
366
367 JSPropertyNameArrayRelease (properties);
368 XSETVECTOR (obj, p);
369 return obj;
370 }
371 }
372 case kJSTypeUndefined:
373 case kJSTypeNull:
374 default:
375 return Qnil;
376 }
377}
378
379static void
380webkit_javascript_finished_cb (GObject *webview,
381 GAsyncResult *result,
382 gpointer lisp_callback)
383{
384 WebKitJavascriptResult *js_result;
385 JSValueRef value;
386 JSGlobalContextRef context;
387 GError *error = NULL;
388 struct xwidget *xw = g_object_get_data (G_OBJECT (webview),
389 XG_XWIDGET);
390
391 js_result = webkit_web_view_run_javascript_finish
392 (WEBKIT_WEB_VIEW (webview), result, &error);
393
394 if (!js_result)
395 {
396 g_warning ("Error running javascript: %s", error->message);
397 g_error_free (error);
398 return;
399 }
400
401 context = webkit_javascript_result_get_global_context (js_result);
402 value = webkit_javascript_result_get_value (js_result);
403 Lisp_Object lisp_value = webkit_js_to_lisp (context, value);
404 webkit_javascript_result_unref (js_result);
405
406 // Register an xwidget event here, which then runs the callback.
407 // This ensures that the callback runs in sync with the Emacs
408 // event loop.
409 store_xwidget_js_callback_event (xw, (Lisp_Object)lisp_callback,
410 lisp_value);
411}
412
413
272gboolean 414gboolean
273webkit_download_cb (WebKitWebContext *webkitwebcontext, 415webkit_download_cb (WebKitWebContext *webkitwebcontext,
274 WebKitDownload *arg1, 416 WebKitDownload *arg1,
@@ -562,19 +704,28 @@ DEFUN ("xwidget-webkit-goto-uri",
562 704
563DEFUN ("xwidget-webkit-execute-script", 705DEFUN ("xwidget-webkit-execute-script",
564 Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script, 706 Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script,
565 2, 2, 0, 707 2, 3, 0,
566 doc: /* Make the Webkit XWIDGET execute JavaScript SCRIPT. */) 708 doc: /* Make the Webkit XWIDGET execute JavaScript SCRIPT. If
567 (Lisp_Object xwidget, Lisp_Object script) 709FUN is provided, feed the JavaScript return value to the single
710argument procedure FUN.*/)
711 (Lisp_Object xwidget, Lisp_Object script, Lisp_Object fun)
568{ 712{
569 WEBKIT_FN_INIT (); 713 WEBKIT_FN_INIT ();
570 CHECK_STRING (script); 714 CHECK_STRING (script);
571 // TODO: provide callback function to do something with the return 715 if (!NILP (fun) && (!FUNCTIONP (fun)))
572 // value! This allows us to get rid of the title hack. 716 wrong_type_argument (Qinvalid_function, fun);
717
718 void *callback = (FUNCTIONP (fun)) ?
719 &webkit_javascript_finished_cb : NULL;
720
721 // JavaScript execution happens asynchronously. If an elisp
722 // callback function is provided we pass it to the C callback
723 // procedure that retrieves the return value.
573 webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (xw->widget_osr), 724 webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (xw->widget_osr),
574 SSDATA (script), 725 SSDATA (script),
575 NULL, /*cancellable*/ 726 NULL, /*cancellable*/
576 NULL, /*callback*/ 727 callback,
577 NULL /*user data*/); 728 (gpointer) fun);
578 return Qnil; 729 return Qnil;
579} 730}
580 731