# HG changeset patch # User tdrhq@soc.pidgin.im # Date 1249889601 0 # Node ID b89351c7580b7388dd64c87d61c3e63d9cc2c1ec # Parent 7b92a2b852db9c9121dbb66517f514cf978e8f28 safely execute JS scripts only after loading is done. Untested code as of now, will test it in next commit. diff -r 7b92a2b852db -r b89351c7580b pidgin/gtkwebview.c --- a/pidgin/gtkwebview.c Mon Aug 10 05:56:08 2009 +0000 +++ b/pidgin/gtkwebview.c Mon Aug 10 07:33:21 2009 +0000 @@ -41,9 +41,19 @@ static WebKitWebViewClass *parent_class = NULL; +struct GtkWebViewPriv { + GHashTable *images; /**< a map from id to temporary file for the image */ + gboolean empty; /**< whether anything has been appended **/ + + /* JS execute queue */ + GQueue *js_queue; + gboolean is_loading; +}; + GtkWidget* gtk_webview_new () { - return GTK_WIDGET(g_object_new(gtk_webview_get_type(), NULL)); + GtkWebView* ret = GTK_WEBVIEW (g_object_new(gtk_webview_get_type(), NULL)); + return GTK_WIDGET (ret); } static char* @@ -53,10 +63,10 @@ FILE *file; PurpleStoredImage* img; - if (!view->images) - view->images = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); + if (!view->priv->images) + view->priv->images = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); - filename = (char*) g_hash_table_lookup (view->images, GINT_TO_POINTER (id)); + filename = (char*) g_hash_table_lookup (view->priv->images, GINT_TO_POINTER (id)); if (filename) return filename; /* else get from img store */ @@ -65,7 +75,7 @@ img = purple_imgstore_find_by_id (id); fwrite (purple_imgstore_get_data (img), purple_imgstore_get_size (img), 1, file); - g_hash_table_insert (view->images, GINT_TO_POINTER (id), filename); + g_hash_table_insert (view->priv->images, GINT_TO_POINTER (id), filename); fclose (file); return filename; } @@ -79,9 +89,9 @@ static void clear_images (GtkWebView* view) { - if (!view->images) return; - g_hash_table_foreach (view->images, clear_single_image, NULL); - g_hash_table_unref (view->images); + if (!view->priv->images) return; + g_hash_table_foreach (view->priv->images, clear_single_image, NULL); + g_hash_table_unref (view->priv->images); } /* @@ -146,6 +156,7 @@ gtk_webview_finalize (GObject *view) { clear_images (GTK_WEBVIEW (view)); + g_free (GTK_WEBVIEW(view)->priv); G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT(view)); } @@ -173,6 +184,39 @@ return TRUE; } +static gboolean +process_js_script_queue (GtkWebView *view) +{ + char *script; + if (view->priv->is_loading) return FALSE; /* we will be called when loaded */ + if (!view->priv->js_queue || g_queue_is_empty (view->priv->js_queue)) + return FALSE; /* nothing to do! */ + + script = g_queue_pop_head (view->priv->js_queue); + webkit_web_view_execute_script (WEBKIT_WEB_VIEW(view), script); + g_free (script); + + return TRUE; /* there may be more for now */ +} + +static void +webview_load_started (WebKitWebView *view, + WebKitWebFrame *frame, + gpointer userdata) +{ + /* is there a better way to test for is_loading? */ + GTK_WEBVIEW(view)->priv->is_loading = true; +} + +static void +webview_load_finished (WebKitWebView *view, + WebKitWebFrame *frame, + gpointer userdata) +{ + GTK_WEBVIEW(view)->priv->is_loading = false; + g_idle_add ((GSourceFunc) process_js_script_queue, view); +} + char* gtk_webview_execute_script (GtkWebView *view, const char *script) { @@ -192,13 +236,30 @@ return cstr; } +void +gtk_webview_safe_execute_script (GtkWebView *view, const char* script) +{ + g_queue_push_tail (view->priv->js_queue, g_strdup (script)); + g_idle_add ((GSourceFunc)process_js_script_queue, view); +} + static void gtk_webview_init (GtkWebView *view, gpointer userdata) { + view->priv = g_new0 (struct GtkWebViewPriv, 1); g_signal_connect (view, "navigation-policy-decision-requested", G_CALLBACK (webview_link_clicked), view); - view->empty = TRUE; + + g_signal_connect (view, "load-started", + G_CALLBACK (webview_load_started), + view); + + g_signal_connect (view, "load-finished", + G_CALLBACK (webview_load_finished), + view); + + view->priv->empty = TRUE; } @@ -254,7 +315,7 @@ char* script = g_strdup_printf ("document.write(%s)", escaped); printf ("script: %s\n", script); webkit_web_view_execute_script (WEBKIT_WEB_VIEW (view), script); - view->empty = FALSE; + view->priv->empty = FALSE; g_free (script); g_free (escaped); } @@ -273,7 +334,7 @@ gboolean gtk_webview_is_empty (GtkWebView *view) { - return view->empty; + return view->priv->empty; } GType gtk_webview_get_type () diff -r 7b92a2b852db -r b89351c7580b pidgin/gtkwebview.h --- a/pidgin/gtkwebview.h Mon Aug 10 05:56:08 2009 +0000 +++ b/pidgin/gtkwebview.h Mon Aug 10 07:33:21 2009 +0000 @@ -35,17 +35,17 @@ #define GTK_WEBVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_WEBVIEW, GtkWebView)) #define GTK_WEBVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_WEBVIEW, GtkWebViewClass)) #define GTK_IS_WEBVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_WEBVIEW)) -#define GTK_IS_IMHTML_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_WEBVIEW)) +#define GTK_IS_WEBVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_WEBVIEW)) +struct GtkWebViewPriv; + struct _GtkWebView { WebKitWebView webkit_web_view; /*< private >*/ - GHashTable *images; /**< a map from id to temporary file for the image */ - gboolean empty; /**< whether anything has been appended **/ - char *script_return; /**< the last value returned from a script **/ + struct GtkWebViewPriv* priv; }; typedef struct _GtkWebView GtkWebView; @@ -106,7 +106,8 @@ * Executes javascript and returns the answer of the script * formatted as string. The return value needs to be freed using * g_free. If the return values is not required you may instead - * use webkit_web_view_execute_script. + * use webkit_web_view_execute_script, or even better + * gtk_webview_safe_execute_script. * * @param webview The GtkWebView object * @param script The JavaScript to execute @@ -116,6 +117,18 @@ char* gtk_webview_execute_script (GtkWebView *webview, const char *script); /** + * Execute the JavaScript only after the webkit_webview_load_string + * loads completely. We also guarantee that the scripts are executed + * in the order they are called here.This is useful to avoid race + * conditions when calls JS functions immediately after opening the + * page. + * + * @param webview the GtkWebView object + * @param script the script to execute + */ +void gtk_webview_safe_execute_script (GtkWebView *webview, const char* script); + +/** * Get the current contents of the GtkWebView object. * * @param webview The GtkWebView object diff -r 7b92a2b852db -r b89351c7580b pidgin/plugins/adiumthemes/webkit.c --- a/pidgin/plugins/adiumthemes/webkit.c Mon Aug 10 05:56:08 2009 +0000 +++ b/pidgin/plugins/adiumthemes/webkit.c Mon Aug 10 07:33:21 2009 +0000 @@ -564,7 +564,7 @@ wk_script->script = script; wk_script->webkit = webkit; - purple_webkit_execute_script (wk_script); + g_idle_add (purple_webkit_execute_script, wk_script); g_free(smileyed); g_free(msg);