Mercurial > pidgin
changeset 32063:b3bc3cadc679
merge of '2892be2675013db56566e8db49e1e9cd1dd2c079'
and 'afac9e1953739f4439b9907dcc941bf5ef457d7f'
author | tdrhq@soc.pidgin.im |
---|---|
date | Fri, 07 Aug 2009 18:27:52 +0000 |
parents | 0907b0c775bb (current diff) ac42a0dfda48 (diff) |
children | d0be198c4694 |
files | |
diffstat | 2 files changed, 410 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkwebview.c Fri Aug 07 18:27:52 2009 +0000 @@ -0,0 +1,264 @@ + +#include <ctype.h> +#include <string.h> +#include <glib.h> +#include <glib/gstdio.h> +#include <JavaScriptCore/JavaScript.h> + +#include "util.h" +#include "gtkwebview.h" + +static WebKitWebViewClass *parent_class = NULL; + +GtkWidget* gtk_webview_new () +{ + return GTK_WIDGET(g_object_new(gtk_webview_get_type(), NULL)); +} + +static char* +get_img_filename_by_id (GtkWebView* view, int id) +{ + char *filename = NULL; + FILE *file; + PurpleStoredImage* img; + + if (!view->images) + view->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)); + if (filename) return filename; + + /* else get from img store */ + file = purple_mkstemp (&filename, TRUE); + + 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); + fclose (file); + return filename; +} + +static void +clear_single_image (gpointer key, gpointer value, gpointer userdata) +{ + g_unlink ((char*) value); +} + +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); +} + +/* + * Replace all <img id=""> tags with <img src="">. I hoped to never + * write any HTML parsing code, but I'm forced to do this, until + * purple changes the way it works. + */ +static char* +replace_img_id_with_src (GtkWebView *view, const char* html) +{ + GString *buffer = g_string_sized_new (strlen (html)); + const char* cur = html; + char *id; + int nid; + + while (*cur) { + const char* img = strstr (cur, "<img"); + if (!img) { + g_string_append (buffer, cur); + break; + } else + g_string_append_len (buffer, cur, img - cur); + + cur = strstr (img, "/>"); + if (!cur) + cur = strstr (img, ">"); + + if (!cur) { /*oops, invalid html */ + g_string_printf (buffer, "%s", html); + break; + } + + if (strstr (img, "src=") || !strstr (img, "id=")) { + g_string_printf (buffer, "%s", html); + break; + } + + /* now I _kinda_ know that it has an id=, and does not have a src= */ + /* todo: take care of src= and id= appearing in strings? */ + id = strstr (img, "id=") + 3; + + /* *id can't be \0, since a ">" appears after this */ + if (isdigit (*id)) + nid = atoi (id); + else + nid = atoi (id+1); + + /* let's dump this, tag and then dump the src information */ + g_string_append_len (buffer, img, cur - img); + + g_string_append_printf (buffer, " src='file://%s' ", get_img_filename_by_id (view, nid)); + } + return g_string_free (buffer, FALSE); +} + +static void +gtk_webview_finalize (GObject *view) +{ + clear_images (GTK_WEBVIEW (view)); + G_OBJECT_CLASS (parent_class)->finalize (G_OBJECT(view)); +} + +static void +gtk_webview_class_init (GtkWebViewClass *klass, gpointer userdata) +{ + parent_class = g_type_class_ref (webkit_web_view_get_type ()); + G_OBJECT_CLASS (klass)->finalize = gtk_webview_finalize; +} + +static gboolean +webview_link_clicked (WebKitWebView *view, + WebKitWebFrame *frame, + WebKitNetworkRequest *request, + WebKitWebNavigationAction *navigation_action, + WebKitWebPolicyDecision *policy_decision) +{ + const gchar *uri; + + uri = webkit_network_request_get_uri (request); + + /* the gtk imhtml way was to create an idle cb, not sure + * why, so right now just using purple_notify_uri directly */ + purple_notify_uri (NULL, uri); + return TRUE; +} + +char* +gtk_webview_execute_script (GtkWebView *view, const char *script) +{ + JSStringRef js_script = JSStringCreateWithUTF8CString (script); + JSContextRef ctxt = webkit_web_frame_get_global_context ( + webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view)) + ); + JSValueRef ret = JSEvaluateScript (ctxt, js_script, NULL, NULL, 0, NULL); + JSStringRef ret_as_str = JSValueToStringCopy (ctxt, ret, NULL); + + size_t cstr_len = JSStringGetMaximumUTF8CStringSize (ret_as_str); + char *cstr = g_new0(char, cstr_len + 1); + + JSStringGetUTF8CString (ret_as_str, cstr, cstr_len); + + /* TODO: I'm not sure what, if at all, I need to free here! */ + return cstr; +} + +static void +gtk_webview_init (GtkWebView *view, gpointer userdata) +{ + g_signal_connect (view, "navigation-policy-decision-requested", + G_CALLBACK (webview_link_clicked), + view); + view->empty = TRUE; +} + + +void +gtk_webview_load_html_string_with_imgstore (GtkWebView* view, const char* html) +{ + char* html_imged; + + clear_images (view); + html_imged = replace_img_id_with_src (view, html); + printf ("%s\n", html_imged); + webkit_web_view_load_html_string (WEBKIT_WEB_VIEW (view), html_imged, "file:///"); + g_free (html_imged); +} + +/* taken from sean's webkit plugin */ +char *gtk_webview_quote_js_string(const char *text) +{ + GString *str = g_string_new("\""); + const char *cur = text; + + while (cur && *cur) { + switch (*cur) { + case '\\': + g_string_append(str, "\\\\"); + break; + case '\"': + g_string_append(str, "\\\""); + break; + case '\r': + g_string_append(str, "<br/>"); + break; + case '\n': + break; + default: + g_string_append_c(str, *cur); + } + cur ++; + } + g_string_append_c (str, '"'); + return g_string_free (str, FALSE); +} + + +/* this is a "hack", my plan is to eventually handle this + * correctly using a signals and a plugin: the plugin will have + * the information as to what javascript function to call. It seems + * wrong to hardcode that here. + */ +void +gtk_webview_append_html (GtkWebView* view, const char* html) +{ + char* escaped = gtk_webview_quote_js_string (html); + 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; + g_free (script); + g_free (escaped); +} + +char* +gtk_webview_get_markup (GtkWebView *view) +{ + return gtk_webview_execute_script (view, "document.body.innerHTML"); +} + +char* +gtk_webview_get_text (GtkWebView *view) +{ + return gtk_webview_execute_script (view, "document.body.textContent"); +} + +gboolean gtk_webview_is_empty (GtkWebView *view) +{ + return view->empty; +} + +GType gtk_webview_get_type () +{ + static GType mview_type = 0; + if (!mview_type) { + static const GTypeInfo mview_info = { + sizeof (GtkWebViewClass), + NULL, + NULL, + (GClassInitFunc) gtk_webview_class_init, + NULL, + NULL, + sizeof (GtkWebView), + 0, + (GInstanceInitFunc) gtk_webview_init, + NULL + }; + mview_type = g_type_register_static(webkit_web_view_get_type (), + "GtkWebView", &mview_info, 0); + } + return mview_type; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkwebview.h Fri Aug 07 18:27:52 2009 +0000 @@ -0,0 +1,146 @@ +/** + * @file gtkwebview.h Wrapper over the Gtk WebKitWebView component + * @ingroup pidgin + */ + +/* Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PIDGIN_WEBVIEW_H_ +#define _PIDGIN_WEBVIEW_H_ + +#include <glib.h> +#include <gtk/gtk.h> +#include <webkit/webkit.h> + +#include "notify.h" + +#define GTK_TYPE_WEBVIEW (gtk_webview_get_type()) +#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)) + + +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 **/ +}; + +typedef struct _GtkWebView GtkWebView; + +struct _GtkWebViewClass +{ + WebKitWebViewClass parent; +}; + +typedef struct _GtkWebViewClass GtkWebViewClass; + + +/** + * Returns the GType for a GtkWebView widget + * + * @return the GType for GtkWebView widget + */ +GType gtk_webview_get_type (); + +/** + * Create a new GtkWebView object + * + * @return a GtkWidget corresponding to the GtkWebView object + */ +GtkWidget* gtk_webview_new (); + +/** + * A very basic routine to append html, which can be considered + * equivalent to a "document.write" using JavaScript. + * + * @param webview The GtkWebView object + * @param markup The html markup to append + */ +void gtk_webview_append_html (GtkWebView *webview, const char* markup); + +/** + * Rather than use webkit_webview_load_string, this routine + * parses and displays the <img id=?> tags that make use of the + * Pidgin imgstore. + * + * @param webview The GtkWebView object + * @param html The HTML content to load + */ +void gtk_webview_load_html_string_with_imgstore (GtkWebView* webview, const char* html); + +/** + * (To be changed, right now it just tests whether an append has been + * called since the last clear or since the Widget was created. So it + * does not test for load_string's called in between. + * + * @param webview The GtkWebView object + * + * @return gboolean indicating whether the webview is empty. + */ +gboolean gtk_webview_is_empty (GtkWebView *webview); + +/** + * 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. + * + * @param webview The GtkWebView object + * @param script The JavaScript to execute + * + * @return the return value of the script. + */ +char* gtk_webview_execute_script (GtkWebView *webview, const char *script); + +/** + * Get the current contents of the GtkWebView object. + * + * @param webview The GtkWebView object + * + * @return a string with the contents. Needs to be g_free'd after use. + */ +char* gtk_webview_get_markup (GtkWebView *webview); + +/** + * Returns the contents of the GtkWebView object, stripped of markups + * + * @param webview The GtkWebView object + * + * @return a string with the contents. Needs to be g_free'd after use. + */ +char* gtk_webview_get_text (GtkWebView *view); + +/** + * A convenience routine to quote a string for use as a JavaScript + * string. For instance, "hello 'world'" becomes "'hello \\'world\\''" + * + * @param str The string to escape and quote + * + * @return the quoted string. + */ +char* gtk_webview_quote_js_string (const char* str); + +#endif /* _PIDGIN_WEBVIEW_H_ */