safely execute JS scripts only after loading is done. Untested code as of now, will test it in next commit. soc.2009.webkitmessageview

Mon, 10 Aug 2009 07:33:21 +0000

author
Arnold Noronha <tdrhq@soc.pidgin.im>
date
Mon, 10 Aug 2009 07:33:21 +0000
branch
soc.2009.webkitmessageview
changeset 32460
5771be5530d8
parent 32459
04857274f841
child 32461
72fe247cc953

safely execute JS scripts only after loading is done. Untested code as of now, will test it in next commit.

pidgin/gtkwebview.c file | annotate | diff | comparison | revisions
pidgin/gtkwebview.h file | annotate | diff | comparison | revisions
pidgin/plugins/adiumthemes/webkit.c file | annotate | diff | comparison | revisions
--- 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 ()
--- 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
--- 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);

mercurial