diff options
Diffstat (limited to 'src/xwidget.c')
-rw-r--r-- | src/xwidget.c | 533 |
1 files changed, 268 insertions, 265 deletions
diff --git a/src/xwidget.c b/src/xwidget.c index 82449f7a215..62df6657e9f 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -21,90 +21,14 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include "xwidget.h" -#include <signal.h> - -#include <stdio.h> -#include <setjmp.h> -#ifdef HAVE_X_WINDOWS - #include "lisp.h" #include "blockinput.h" -#include "syssignal.h" - -#include "xterm.h" -#include <X11/cursorfont.h> - -#ifndef makedev -# include <sys/types.h> -#endif - -#ifdef BSD_SYSTEM -# include <sys/ioctl.h> -#endif - -#include "systime.h" - -#ifndef INCLUDED_FCNTL -# include <fcntl.h> -#endif -#include <ctype.h> -#include <errno.h> -#include <setjmp.h> -#include <sys/stat.h> - -#include "charset.h" -#include "character.h" -#include "coding.h" -#include "ccl.h" #include "frame.h" -#include "dispextern.h" -#include "fontset.h" -#include "termhooks.h" -#include "termopts.h" -#include "termchar.h" -#include "disptab.h" -#include "buffer.h" -#include "window.h" #include "keyboard.h" -#include "intervals.h" -#include "process.h" -#include "atimer.h" -#include "keymap.h" - - -#ifdef USE_X_TOOLKIT -#include <X11/Shell.h> -#endif -#include <X11/extensions/Xcomposite.h> -#include <X11/extensions/Xrender.h> -#include <cairo.h> -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> -#endif -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif - #include "gtkutil.h" -#include "font.h" -#endif /* HAVE_X_WINDOWS */ - -#include <gtk/gtk.h> -#include <gdk/gdk.h> - -#include <gtk/gtkx.h> - -#include "emacsgtkfixed.h" -#include <wchar.h> - -#include <webkit/webkitwebview.h> -#include <webkit/webkitwebplugindatabase.h> -#include <webkit/webkitwebplugin.h> -#include <webkit/webkitglobals.h> -#include <webkit/webkitwebnavigationaction.h> -#include <webkit/webkitdownload.h> -#include <webkit/webkitwebpolicydecision.h> +#include <webkit2/webkit2.h> +#include <JavaScriptCore/JavaScript.h> static struct xwidget * allocate_xwidget (void) @@ -124,34 +48,19 @@ allocate_xwidget_view (void) static struct xwidget_view *xwidget_view_lookup (struct xwidget *, struct window *); -static void webkit_document_load_finished_cb (WebKitWebView *, WebKitWebFrame *, - gpointer); -static gboolean webkit_download_cb (WebKitWebView *, WebKitDownload *, gpointer); +static void webkit_view_load_changed_cb (WebKitWebView *, + WebKitLoadEvent, + gpointer); +static void webkit_javascript_finished_cb (GObject *, + GAsyncResult *, + gpointer); +static gboolean webkit_download_cb (WebKitWebContext *, WebKitDownload *, gpointer); static gboolean -webkit_mime_type_policy_typedecision_requested_cb (WebKitWebView *, - WebKitWebFrame *, - WebKitNetworkRequest *, - gchar *, - WebKitWebPolicyDecision *, - gpointer); - -static gboolean -webkit_new_window_policy_decision_requested_cb (WebKitWebView *, - WebKitWebFrame *, - WebKitNetworkRequest *, - WebKitWebNavigationAction *, - WebKitWebPolicyDecision *, - gpointer); - -static gboolean -webkit_navigation_policy_decision_requested_cb (WebKitWebView *, - WebKitWebFrame *, - WebKitNetworkRequest *, - WebKitWebNavigationAction *, - WebKitWebPolicyDecision *, - gpointer); - +webkit_decide_policy_cb (WebKitWebView *, + WebKitPolicyDecision *, + WebKitPolicyDecisionType, + gpointer); DEFUN ("make-xwidget", @@ -194,25 +103,9 @@ Returns the newly constructed xwidget, or nil if construction fails. */) gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width, xw->height); - /* WebKit OSR is the only scrolled component at the moment. */ - xw->widgetscrolledwindow_osr = NULL; - if (EQ (xw->type, Qwebkit)) { - xw->widgetscrolledwindow_osr = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_min_content_height - (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr), - xw->height); - gtk_scrolled_window_set_min_content_width - (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr), - xw->width); - gtk_scrolled_window_set_policy - (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr), - GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS); - xw->widget_osr = webkit_web_view_new (); - gtk_container_add (GTK_CONTAINER (xw->widgetscrolledwindow_osr), - GTK_WIDGET (WEBKIT_WEB_VIEW (xw->widget_osr))); } gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width, @@ -221,7 +114,7 @@ Returns the newly constructed xwidget, or nil if construction fails. */) if (EQ (xw->type, Qwebkit)) { gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr), - xw->widgetscrolledwindow_osr); + GTK_WIDGET (WEBKIT_WEB_VIEW (xw->widget_osr))); } else { @@ -231,7 +124,6 @@ Returns the newly constructed xwidget, or nil if construction fails. */) gtk_widget_show (xw->widget_osr); gtk_widget_show (xw->widgetwindow_osr); - gtk_widget_show (xw->widgetscrolledwindow_osr); /* Store some xwidget data in the gtk widgets for convenient retrieval in the event handlers. */ @@ -242,29 +134,17 @@ Returns the newly constructed xwidget, or nil if construction fails. */) if (EQ (xw->type, Qwebkit)) { g_signal_connect (G_OBJECT (xw->widget_osr), - "document-load-finished", - G_CALLBACK (webkit_document_load_finished_cb), xw); + "load-changed", + G_CALLBACK (webkit_view_load_changed_cb), xw); - g_signal_connect (G_OBJECT (xw->widget_osr), - "download-requested", + g_signal_connect (G_OBJECT (webkit_web_context_get_default ()), + "download-started", G_CALLBACK (webkit_download_cb), xw); g_signal_connect (G_OBJECT (xw->widget_osr), - "mime-type-policy-decision-requested", - G_CALLBACK - (webkit_mime_type_policy_typedecision_requested_cb), - xw); - - g_signal_connect (G_OBJECT (xw->widget_osr), - "new-window-policy-decision-requested", - G_CALLBACK - (webkit_new_window_policy_decision_requested_cb), - xw); - - g_signal_connect (G_OBJECT (xw->widget_osr), - "navigation-policy-decision-requested", + "decide-policy", G_CALLBACK - (webkit_navigation_policy_decision_requested_cb), + (webkit_decide_policy_cb), xw); } @@ -358,81 +238,221 @@ store_xwidget_event_string (struct xwidget *xw, const char *eventname, kbd_buffer_store_event (&event); } -/* TODO deprecated, use load-status. */ +static void +store_xwidget_js_callback_event (struct xwidget *xw, + Lisp_Object proc, + Lisp_Object argument) +{ + struct input_event event; + Lisp_Object xwl; + XSETXWIDGET (xwl, xw); + EVENT_INIT (event); + event.kind = XWIDGET_EVENT; + event.frame_or_window = Qnil; + event.arg = list4 (intern ("javascript-callback"), xwl, proc, argument); + kbd_buffer_store_event (&event); +} + + void -webkit_document_load_finished_cb (WebKitWebView *webkitwebview, - WebKitWebFrame *arg1, - gpointer data) +webkit_view_load_changed_cb (WebKitWebView *webkitwebview, + WebKitLoadEvent load_event, + gpointer data) { - struct xwidget *xw = g_object_get_data (G_OBJECT (webkitwebview), - XG_XWIDGET); + switch (load_event) { + case WEBKIT_LOAD_FINISHED: + { + struct xwidget *xw = g_object_get_data (G_OBJECT (webkitwebview), + XG_XWIDGET); + store_xwidget_event_string (xw, "load-changed", ""); + break; + } + default: + break; + } +} + +/* Recursively convert a JavaScript value to a Lisp value. */ +static Lisp_Object +webkit_js_to_lisp (JSContextRef context, JSValueRef value) +{ + switch (JSValueGetType (context, value)) + { + case kJSTypeString: + { + JSStringRef js_str_value; + gchar *str_value; + gsize str_length; + + js_str_value = JSValueToStringCopy (context, value, NULL); + str_length = JSStringGetMaximumUTF8CStringSize (js_str_value); + str_value = (gchar *)g_malloc (str_length); + JSStringGetUTF8CString (js_str_value, str_value, str_length); + JSStringRelease (js_str_value); + return build_string (str_value); + } + case kJSTypeBoolean: + return (JSValueToBoolean (context, value)) ? Qt : Qnil; + case kJSTypeNumber: + return make_number (JSValueToNumber (context, value, NULL)); + case kJSTypeObject: + { + if (JSValueIsArray (context, value)) + { + JSStringRef pname = JSStringCreateWithUTF8CString("length"); + JSValueRef len = JSObjectGetProperty (context, (JSObjectRef) value, pname, NULL); + int n = JSValueToNumber (context, len, NULL); + JSStringRelease(pname); + + Lisp_Object obj; + struct Lisp_Vector *p = allocate_vector (n); + + for (int i = 0; i < n; ++i) + { + p->contents[i] = + webkit_js_to_lisp (context, + JSObjectGetPropertyAtIndex (context, + (JSObjectRef) value, + i, NULL)); + } + XSETVECTOR (obj, p); + return obj; + } + else + { + JSPropertyNameArrayRef properties = + JSObjectCopyPropertyNames (context, (JSObjectRef) value); + + int n = JSPropertyNameArrayGetCount (properties); + Lisp_Object obj; + + /* TODO: can we use a regular list here? */ + struct Lisp_Vector *p = allocate_vector (n); + + for (int i = 0; i < n; ++i) + { + JSStringRef name = JSPropertyNameArrayGetNameAtIndex (properties, i); + JSValueRef property = JSObjectGetProperty (context, + (JSObjectRef) value, + name, NULL); + gchar *str_name; + gsize str_length; + str_length = JSStringGetMaximumUTF8CStringSize (name); + str_name = (gchar *)g_malloc (str_length); + JSStringGetUTF8CString (name, str_name, str_length); + JSStringRelease (name); + + p->contents[i] = + Fcons (build_string (str_name), + webkit_js_to_lisp (context, property)); + } + + JSPropertyNameArrayRelease (properties); + XSETVECTOR (obj, p); + return obj; + } + } + case kJSTypeUndefined: + case kJSTypeNull: + default: + return Qnil; + } +} + +static void +webkit_javascript_finished_cb (GObject *webview, + GAsyncResult *result, + gpointer lisp_callback) +{ + WebKitJavascriptResult *js_result; + JSValueRef value; + JSGlobalContextRef context; + GError *error = NULL; + struct xwidget *xw = g_object_get_data (G_OBJECT (webview), + XG_XWIDGET); - store_xwidget_event_string (xw, "document-load-finished", ""); + js_result = webkit_web_view_run_javascript_finish + (WEBKIT_WEB_VIEW (webview), result, &error); + + if (!js_result) + { + g_warning ("Error running javascript: %s", error->message); + g_error_free (error); + return; + } + + context = webkit_javascript_result_get_global_context (js_result); + value = webkit_javascript_result_get_value (js_result); + Lisp_Object lisp_value = webkit_js_to_lisp (context, value); + webkit_javascript_result_unref (js_result); + + /* Register an xwidget event here, which then runs the callback. + This ensures that the callback runs in sync with the Emacs + event loop. */ + store_xwidget_js_callback_event (xw, (Lisp_Object)lisp_callback, + lisp_value); } + gboolean -webkit_download_cb (WebKitWebView *webkitwebview, +webkit_download_cb (WebKitWebContext *webkitwebcontext, WebKitDownload *arg1, gpointer data) { - struct xwidget *xw = g_object_get_data (G_OBJECT (webkitwebview), + WebKitWebView *view = webkit_download_get_web_view(arg1); + WebKitURIRequest *request = webkit_download_get_request(arg1); + struct xwidget *xw = g_object_get_data (G_OBJECT (view), XG_XWIDGET); - store_xwidget_event_string (xw, "download-requested", - webkit_download_get_uri (arg1)); + + store_xwidget_event_string (xw, "download-started", + webkit_uri_request_get_uri(request)); return FALSE; } static gboolean -webkit_mime_type_policy_typedecision_requested_cb (WebKitWebView *webView, - WebKitWebFrame *frame, - WebKitNetworkRequest *request, - gchar *mimetype, - WebKitWebPolicyDecision *policy_decision, - gpointer user_data) +webkit_decide_policy_cb (WebKitWebView *webView, + WebKitPolicyDecision *decision, + WebKitPolicyDecisionType type, + gpointer user_data) { - /* This function makes webkit send a download signal for all unknown - mime types. TODO: Defer the decision to Lisp, so that it's - possible to make Emacs handle mime text for instance. */ - if (!webkit_web_view_can_show_mime_type (webView, mimetype)) + switch (type) { + case WEBKIT_POLICY_DECISION_TYPE_RESPONSE: + /* This function makes webkit send a download signal for all unknown + mime types. TODO: Defer the decision to Lisp, so that it's + possible to make Emacs handle mime text for instance. */ { - webkit_web_policy_decision_download (policy_decision); - return TRUE; + WebKitResponsePolicyDecision *response = + WEBKIT_RESPONSE_POLICY_DECISION (decision); + if (!webkit_response_policy_decision_is_mime_type_supported (response)) + { + webkit_policy_decision_download (decision); + return TRUE; + } + else + return FALSE; + break; } - else + case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION: + case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION: + { + WebKitNavigationPolicyDecision *navigation_decision = + WEBKIT_NAVIGATION_POLICY_DECISION (decision); + WebKitNavigationAction *navigation_action = + webkit_navigation_policy_decision_get_navigation_action (navigation_decision); + WebKitURIRequest *request = + webkit_navigation_action_get_request (navigation_action); + + struct xwidget *xw = g_object_get_data (G_OBJECT (webView), XG_XWIDGET); + store_xwidget_event_string (xw, "decide-policy", + webkit_uri_request_get_uri (request)); + return FALSE; + break; + } + default: return FALSE; + } } -static gboolean -webkit_new_window_policy_decision_requested_cb (WebKitWebView *webView, - WebKitWebFrame *frame, - WebKitNetworkRequest *request, - WebKitWebNavigationAction *navigation_action, - WebKitWebPolicyDecision *policy_decision, - gpointer user_data) -{ - struct xwidget *xw = g_object_get_data (G_OBJECT (webView), XG_XWIDGET); - webkit_web_navigation_action_get_original_uri (navigation_action); - - store_xwidget_event_string (xw, "new-window-policy-decision-requested", - webkit_web_navigation_action_get_original_uri - (navigation_action)); - return FALSE; -} - -static gboolean -webkit_navigation_policy_decision_requested_cb (WebKitWebView *webView, - WebKitWebFrame *frame, - WebKitNetworkRequest *request, - WebKitWebNavigationAction *navigation_action, - WebKitWebPolicyDecision *policy_decision, - gpointer user_data) -{ - struct xwidget *xw = g_object_get_data (G_OBJECT (webView), XG_XWIDGET); - store_xwidget_event_string (xw, "navigation-policy-decision-requested", - webkit_web_navigation_action_get_original_uri - (navigation_action)); - return FALSE; -} /* For gtk3 offscreen rendered widgets. */ static gboolean @@ -445,10 +465,7 @@ xwidget_osr_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data) cairo_rectangle (cr, 0, 0, xv->clip_right, xv->clip_bottom); cairo_clip (cr); - if (xw->widgetscrolledwindow_osr != NULL) - gtk_widget_draw (xw->widgetscrolledwindow_osr, cr); - else - gtk_widget_draw (xw->widget_osr, cr); + gtk_widget_draw (xw->widget_osr, cr); return FALSE; } @@ -565,12 +582,16 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) xwidget on screen. Moving and clipping is done here. Also view initialization. */ struct xwidget *xww = s->xwidget; - struct xwidget_view *xv = xwidget_view_lookup (xww, s->w); + struct xwidget_view *xv; int clip_right; int clip_bottom; int clip_top; int clip_left; + /* FIXME: The result of this call is discarded. + What if the lookup fails? */ + xwidget_view_lookup (xww, s->w); + int x = s->x; int y = s->y + (s->height / 2) - (xww->height / 2); @@ -660,39 +681,51 @@ DEFUN ("xwidget-webkit-goto-uri", return Qnil; } - -DEFUN ("xwidget-webkit-execute-script", - Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script, +DEFUN ("xwidget-webkit-zoom", + Fxwidget_webkit_zoom, Sxwidget_webkit_zoom, 2, 2, 0, - doc: /* Make the Webkit XWIDGET execute JavaScript SCRIPT. */) - (Lisp_Object xwidget, Lisp_Object script) + doc: /* Change the zoom factor of the xwidget webkit instance +referenced by XWIDGET. */) + (Lisp_Object xwidget, Lisp_Object factor) { WEBKIT_FN_INIT (); - CHECK_STRING (script); - webkit_web_view_execute_script (WEBKIT_WEB_VIEW (xw->widget_osr), - SSDATA (script)); + if (FLOATP (factor)) + { + double zoom_change = XFLOAT_DATA (factor); + webkit_web_view_set_zoom_level + (WEBKIT_WEB_VIEW (xw->widget_osr), + webkit_web_view_get_zoom_level + (WEBKIT_WEB_VIEW (xw->widget_osr)) + zoom_change); + } return Qnil; } -DEFUN ("xwidget-webkit-get-title", - Fxwidget_webkit_get_title, Sxwidget_webkit_get_title, - 1, 1, 0, - doc: /* Return the title from the Webkit instance in XWIDGET. -This can be used to work around the lack of a return value from the -exec method. */ ) - (Lisp_Object xwidget) + +DEFUN ("xwidget-webkit-execute-script", + Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script, + 2, 3, 0, + doc: /* Make the Webkit XWIDGET execute JavaScript SCRIPT. If +FUN is provided, feed the JavaScript return value to the single +argument procedure FUN.*/) + (Lisp_Object xwidget, Lisp_Object script, Lisp_Object fun) { - /* TODO support multibyte strings. */ WEBKIT_FN_INIT (); - const gchar *str = - webkit_web_view_get_title (WEBKIT_WEB_VIEW (xw->widget_osr)); - if (str == 0) - { - /* TODO maybe return Qnil instead. I suppose webkit returns - null pointer when doc is not properly loaded or something. */ - return build_string (""); - } - return build_string (str); + CHECK_STRING (script); + if (!NILP (fun) && !FUNCTIONP (fun)) + wrong_type_argument (Qinvalid_function, fun); + + void *callback = (FUNCTIONP (fun)) ? + &webkit_javascript_finished_cb : NULL; + + /* JavaScript execution happens asynchronously. If an elisp + callback function is provided we pass it to the C callback + procedure that retrieves the return value. */ + webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (xw->widget_osr), + SSDATA (script), + NULL, /* cancelable */ + callback, + (gpointer) fun); + return Qnil; } DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, @@ -712,21 +745,11 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, /* If there is an offscreen widget resize it first. */ if (xw->widget_osr) { - /* Use minimum size. */ - gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), - xw->width, xw->height); - gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width, xw->height); - gtk_scrolled_window_set_min_content_height - (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr), - xw->height); - gtk_scrolled_window_set_min_content_width - (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr), - xw->width); - gtk_container_resize_children (GTK_CONTAINER (xw->widgetwindow_osr)); - + gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width, + xw->height); } for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail)) @@ -745,30 +768,6 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, -DEFUN ("xwidget-set-adjustment", - Fxwidget_set_adjustment, Sxwidget_set_adjustment, 4, 4, 0, - doc: /* Set native scrolling for XWIDGET. -AXIS can be `vertical' or `horizontal'. -If RELATIVE is t, scroll relative, otherwise absolutely. -VALUE is the amount to scroll, either relatively or absolutely. */) - (Lisp_Object xwidget, Lisp_Object axis, Lisp_Object relative, - Lisp_Object value) -{ - CHECK_XWIDGET (xwidget); - CHECK_NUMBER (value); - struct xwidget *xw = XXWIDGET (xwidget); - GtkAdjustment *adjustment - = ((EQ (Qhorizontal, axis) - ? gtk_scrolled_window_get_hadjustment - : gtk_scrolled_window_get_vadjustment) - (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr))); - double final_value = XINT (value); - if (EQ (Qt, relative)) - final_value += gtk_adjustment_get_value (adjustment); - gtk_adjustment_set_value (adjustment, final_value); - return Qnil; -} - DEFUN ("xwidget-size-request", Fxwidget_size_request, Sxwidget_size_request, @@ -973,8 +972,8 @@ syms_of_xwidget (void) defsubr (&Sset_xwidget_query_on_exit_flag); defsubr (&Sxwidget_webkit_goto_uri); + defsubr (&Sxwidget_webkit_zoom); defsubr (&Sxwidget_webkit_execute_script); - defsubr (&Sxwidget_webkit_get_title); DEFSYM (Qwebkit, "webkit"); defsubr (&Sxwidget_size_request); @@ -984,8 +983,6 @@ syms_of_xwidget (void) defsubr (&Sxwidget_buffer); defsubr (&Sset_xwidget_plist); - defsubr (&Sxwidget_set_adjustment); - DEFSYM (Qxwidget, "xwidget"); DEFSYM (QCxwidget, ":xwidget"); @@ -1145,7 +1142,13 @@ xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix) { /* The only call to xwidget_end_redisplay is in dispnew. xwidget_end_redisplay (w->current_matrix); */ - xwidget_touch (xwidget_view_lookup (glyph->u.xwidget, w)); + struct xwidget_view *xv + = xwidget_view_lookup (glyph->u.xwidget, w); + /* FIXME: Is it safe to assume xwidget_view_lookup + always succeeds here? If so, this comment can be removed. + If not, the code probably needs fixing. */ + eassume (xv); + xwidget_touch (xv); } } } |