Handle link-clicked events through modest in webkit
[modest] / src / widgets / modest-webkit-mime-part-view.c
1 /* Copyright (c) 2006, 2007, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <config.h>
31 #include <widgets/modest-webkit-mime-part-view.h>
32 #include <string.h>
33 #include <webkit/webkit.h>
34 #include <tny-stream.h>
35 #include <tny-mime-part-view.h>
36 #include "modest-tny-mime-part.h"
37 #include <modest-stream-text-to-html.h>
38 #include <modest-text-utils.h>
39 #include <modest-conf.h>
40 #include <modest-runtime.h>
41 #include <widgets/modest-mime-part-view.h>
42 #include <widgets/modest-zoomable.h>
43 #include <libgnomevfs/gnome-vfs.h>
44 #include <gdk/gdkkeysyms.h>
45 #include <modest-ui-constants.h>
46 #include <modest-tny-stream-webkit.h>
47
48 /* gobject structure methods */
49 static void    modest_webkit_mime_part_view_class_init (ModestWebkitMimePartViewClass *klass);
50 static void    tny_mime_part_view_init                  (gpointer g, gpointer iface_data);
51 static void    modest_mime_part_view_init               (gpointer g, gpointer iface_data);
52 static void    modest_zoomable_init                     (gpointer g, gpointer iface_data);
53 static void    modest_isearch_view_init                 (gpointer g, gpointer iface_data);
54 static void    modest_webkit_mime_part_view_init       (ModestWebkitMimePartView *self);
55 static void    modest_webkit_mime_part_view_finalize   (GObject *self);
56 static void    modest_webkit_mime_part_view_dispose    (GObject *self);
57
58 /* Webkit signal handlers */
59 static void on_resource_request_starting (WebKitWebView *webview,
60                                           WebKitWebFrame *frame,
61                                           WebKitWebResource *resource,
62                                           WebKitNetworkRequest *request,
63                                           WebKitNetworkResponse *response,
64                                           gpointer userdata);
65 static gboolean on_new_window_policy_decision_requested (WebKitWebView             *web_view,
66                                                          WebKitWebFrame            *frame,
67                                                          WebKitNetworkRequest      *request,
68                                                          WebKitWebNavigationAction *navigation_action,
69                                                          WebKitWebPolicyDecision   *policy_decision,
70                                                          gpointer                   user_data);
71 static gboolean on_navigation_policy_decision_requested (WebKitWebView             *web_view,
72                                                          WebKitWebFrame            *frame,
73                                                          WebKitNetworkRequest      *request,
74                                                          WebKitWebNavigationAction *navigation_action,
75                                                          WebKitWebPolicyDecision   *policy_decision,
76                                                          gpointer                   user_data);
77 static WebKitNavigationResponse on_navigation_requested                      (WebKitWebView        *web_view,
78                                                                               WebKitWebFrame       *frame,
79                                                                               WebKitNetworkRequest *request,
80                                                                               gpointer              user_data);
81 static void      on_notify_style  (GObject *obj, GParamSpec *spec, gpointer userdata);
82 static gboolean  update_style     (ModestWebkitMimePartView *self);
83 /* TnyMimePartView implementation */
84 static void modest_webkit_mime_part_view_clear (TnyMimePartView *self);
85 static void modest_webkit_mime_part_view_clear_default (TnyMimePartView *self);
86 static void modest_webkit_mime_part_view_set_part (TnyMimePartView *self, TnyMimePart *part);
87 static void modest_webkit_mime_part_view_set_part_default (TnyMimePartView *self, TnyMimePart *part);
88 static TnyMimePart* modest_webkit_mime_part_view_get_part (TnyMimePartView *self);
89 static TnyMimePart* modest_webkit_mime_part_view_get_part_default (TnyMimePartView *self);
90 /* ModestMimePartView implementation */
91 static gboolean modest_webkit_mime_part_view_is_empty (ModestMimePartView *self);
92 static gboolean modest_webkit_mime_part_view_is_empty_default (ModestMimePartView *self);
93 static gboolean modest_webkit_mime_part_view_get_view_images (ModestMimePartView *self);
94 static gboolean modest_webkit_mime_part_view_get_view_images_default (ModestMimePartView *self);
95 static void     modest_webkit_mime_part_view_set_view_images (ModestMimePartView *self, gboolean view_images);
96 static void     modest_webkit_mime_part_view_set_view_images_default (ModestMimePartView *self, gboolean view_images);
97 static gboolean modest_webkit_mime_part_view_has_external_images (ModestMimePartView *self);
98 static gboolean modest_webkit_mime_part_view_has_external_images_default (ModestMimePartView *self);
99 /* ModestZoomable implementation */
100 static gdouble modest_webkit_mime_part_view_get_zoom (ModestZoomable *self);
101 static void modest_webkit_mime_part_view_set_zoom (ModestZoomable *self, gdouble value);
102 static gboolean modest_webkit_mime_part_view_zoom_minus (ModestZoomable *self);
103 static gboolean modest_webkit_mime_part_view_zoom_plus (ModestZoomable *self);
104 static gdouble modest_webkit_mime_part_view_get_zoom_default (ModestZoomable *self);
105 static void modest_webkit_mime_part_view_set_zoom_default (ModestZoomable *self, gdouble value);
106 static gboolean modest_webkit_mime_part_view_zoom_minus_default (ModestZoomable *self);
107 static gboolean modest_webkit_mime_part_view_zoom_plus_default (ModestZoomable *self);
108 /* ModestISearchView implementation */
109 static gboolean modest_webkit_mime_part_view_search                    (ModestISearchView *self, const gchar *string);
110 static gboolean modest_webkit_mime_part_view_search_next               (ModestISearchView *self);
111 static gboolean modest_webkit_mime_part_view_get_selection_area        (ModestISearchView *self, gint *x, gint *y, 
112                                                                          gint *width, gint *height);
113 static gboolean modest_webkit_mime_part_view_search_default            (ModestISearchView *self, const gchar *string);
114 static gboolean modest_webkit_mime_part_view_search_next_default       (ModestISearchView *self);
115 static gboolean modest_webkit_mime_part_view_get_selection_area_default (ModestISearchView *self, gint *x, gint *y, 
116                                                                           gint *width, gint *height);
117
118
119 /* internal api */
120 static TnyMimePart  *get_part   (ModestWebkitMimePartView *self);
121 static void          set_html_part   (ModestWebkitMimePartView *self, TnyMimePart *part, const gchar *encoding);
122 static void          set_text_part   (ModestWebkitMimePartView *self, TnyMimePart *part);
123 static void          set_empty_part  (ModestWebkitMimePartView *self);
124 static void          set_part   (ModestWebkitMimePartView *self, TnyMimePart *part);
125 static gboolean      is_empty   (ModestWebkitMimePartView *self);
126 static gboolean      get_view_images   (ModestWebkitMimePartView *self);
127 static void          set_view_images   (ModestWebkitMimePartView *self, gboolean view_images);
128 static gboolean      has_external_images   (ModestWebkitMimePartView *self);
129 static void          set_zoom   (ModestWebkitMimePartView *self, gdouble zoom);
130 static gdouble       get_zoom   (ModestWebkitMimePartView *self);
131 static gboolean      search             (ModestWebkitMimePartView *self, const gchar *string);
132 static gboolean      search_next        (ModestWebkitMimePartView *self);
133 static gboolean      get_selection_area (ModestWebkitMimePartView *self, gint *x, gint *y,
134                                          gint *width, gint *height);
135
136 typedef struct _ModestWebkitMimePartViewPrivate ModestWebkitMimePartViewPrivate;
137 struct _ModestWebkitMimePartViewPrivate {
138         TnyMimePart *part;
139         gdouble current_zoom;
140         gboolean view_images;
141         gboolean has_external_images;
142         GSList *sighandlers;
143 };
144
145 #define MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
146                                                                                        MODEST_TYPE_WEBKIT_MIME_PART_VIEW, \
147                                                                                        ModestWebkitMimePartViewPrivate))
148
149 enum {
150         STOP_STREAMS_SIGNAL,
151         LIMIT_ERROR_SIGNAL,
152         LAST_SIGNAL
153 };
154
155 static WebKitWebViewClass *parent_class = NULL;
156
157 static guint signals[LAST_SIGNAL] = {0};
158
159 GtkWidget *
160 modest_webkit_mime_part_view_new ()
161 {
162         return g_object_new (MODEST_TYPE_WEBKIT_MIME_PART_VIEW, NULL);
163 }
164
165 /* GOBJECT IMPLEMENTATION */
166 GType
167 modest_webkit_mime_part_view_get_type (void)
168 {
169         static GType my_type = 0;
170         if (!my_type) {
171                 static const GTypeInfo my_info = {
172                         sizeof(ModestWebkitMimePartViewClass),
173                         NULL,           /* base init */
174                         NULL,           /* base finalize */
175                         (GClassInitFunc) modest_webkit_mime_part_view_class_init,
176                         NULL,           /* class finalize */
177                         NULL,           /* class data */
178                         sizeof(ModestWebkitMimePartView),
179                         1,              /* n_preallocs */
180                         (GInstanceInitFunc) modest_webkit_mime_part_view_init,
181                         NULL
182                 };
183
184                 static const GInterfaceInfo tny_mime_part_view_info = 
185                 {
186                   (GInterfaceInitFunc) tny_mime_part_view_init, /* interface_init */
187                   NULL,         /* interface_finalize */
188                   NULL          /* interface_data */
189                 };
190
191                 static const GInterfaceInfo modest_mime_part_view_info = 
192                 {
193                   (GInterfaceInitFunc) modest_mime_part_view_init, /* interface_init */
194                   NULL,         /* interface_finalize */
195                   NULL          /* interface_data */
196                 };
197
198                 static const GInterfaceInfo modest_zoomable_info = 
199                 {
200                   (GInterfaceInitFunc) modest_zoomable_init, /* interface_init */
201                   NULL,         /* interface_finalize */
202                   NULL          /* interface_data */
203                 };
204
205                 static const GInterfaceInfo modest_isearch_view_info = 
206                 {
207                   (GInterfaceInitFunc) modest_isearch_view_init, /* interface_init */
208                   NULL,         /* interface_finalize */
209                   NULL          /* interface_data */
210                 };
211
212                 my_type = g_type_register_static (WEBKIT_TYPE_WEB_VIEW,
213                                                   "ModestWebkitMimePartView",
214                                                   &my_info, 0);
215
216                 g_type_add_interface_static (my_type, TNY_TYPE_MIME_PART_VIEW, 
217                         &tny_mime_part_view_info);
218
219                 g_type_add_interface_static (my_type, MODEST_TYPE_MIME_PART_VIEW, 
220                         &modest_mime_part_view_info);
221
222                 g_type_add_interface_static (my_type, MODEST_TYPE_ZOOMABLE, 
223                         &modest_zoomable_info);
224                 g_type_add_interface_static (my_type, MODEST_TYPE_ISEARCH_VIEW, 
225                         &modest_isearch_view_info);
226         }
227         return my_type;
228 }
229
230 static void
231 modest_webkit_mime_part_view_class_init (ModestWebkitMimePartViewClass *klass)
232 {
233         GObjectClass *gobject_class;
234         GtkBindingSet *binding_set;
235
236         gobject_class = (GObjectClass*) klass;
237
238         parent_class            = g_type_class_peek_parent (klass);
239         gobject_class->dispose = modest_webkit_mime_part_view_dispose;
240         gobject_class->finalize = modest_webkit_mime_part_view_finalize;
241
242         klass->get_part_func = modest_webkit_mime_part_view_get_part_default;
243         klass->set_part_func = modest_webkit_mime_part_view_set_part_default;
244         klass->clear_func = modest_webkit_mime_part_view_clear_default;
245         klass->is_empty_func = modest_webkit_mime_part_view_is_empty_default;
246         klass->get_view_images_func = modest_webkit_mime_part_view_get_view_images_default;
247         klass->set_view_images_func = modest_webkit_mime_part_view_set_view_images_default;
248         klass->has_external_images_func = modest_webkit_mime_part_view_has_external_images_default;
249         klass->get_zoom_func = modest_webkit_mime_part_view_get_zoom_default;
250         klass->set_zoom_func = modest_webkit_mime_part_view_set_zoom_default;
251         klass->zoom_minus_func = modest_webkit_mime_part_view_zoom_minus_default;
252         klass->zoom_plus_func = modest_webkit_mime_part_view_zoom_plus_default;
253         klass->search_func = modest_webkit_mime_part_view_search_default;
254         klass->search_next_func = modest_webkit_mime_part_view_search_next_default;
255         klass->get_selection_area_func = modest_webkit_mime_part_view_get_selection_area_default;
256
257         binding_set = gtk_binding_set_by_class (klass);
258         gtk_binding_entry_skip (binding_set, GDK_Down, 0);
259         gtk_binding_entry_skip (binding_set, GDK_Up, 0);
260         gtk_binding_entry_skip (binding_set, GDK_KP_Up, 0);
261         gtk_binding_entry_skip (binding_set, GDK_KP_Down, 0);
262         gtk_binding_entry_skip (binding_set, GDK_Page_Down, 0);
263         gtk_binding_entry_skip (binding_set, GDK_Page_Up, 0);
264         gtk_binding_entry_skip (binding_set, GDK_KP_Page_Up, 0);
265         gtk_binding_entry_skip (binding_set, GDK_KP_Page_Down, 0);
266         gtk_binding_entry_skip (binding_set, GDK_Home, 0);
267         gtk_binding_entry_skip (binding_set, GDK_End, 0);
268         gtk_binding_entry_skip (binding_set, GDK_KP_Home, 0);
269         gtk_binding_entry_skip (binding_set, GDK_KP_End, 0);
270
271         g_type_class_add_private (gobject_class, sizeof(ModestWebkitMimePartViewPrivate));
272
273         signals[STOP_STREAMS_SIGNAL] = 
274                 g_signal_new ("stop-streams",
275                               G_TYPE_FROM_CLASS (gobject_class),
276                               G_SIGNAL_RUN_FIRST,
277                               G_STRUCT_OFFSET (ModestWebkitMimePartViewClass,stop_streams),
278                               NULL, NULL,
279                               g_cclosure_marshal_VOID__VOID,
280                               G_TYPE_NONE, 0);
281
282         signals[LIMIT_ERROR_SIGNAL] = 
283                 g_signal_new ("limit-error",
284                               G_TYPE_FROM_CLASS (gobject_class),
285                               G_SIGNAL_RUN_FIRST,
286                               G_STRUCT_OFFSET (ModestWebkitMimePartViewClass,limit_error),
287                               NULL, NULL,
288                               g_cclosure_marshal_VOID__VOID,
289                               G_TYPE_NONE, 0);
290
291 }
292
293 static void
294 modest_webkit_mime_part_view_init (ModestWebkitMimePartView *self)
295 {
296         ModestWebkitMimePartViewPrivate *priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (self);
297         GdkColor base;
298         GdkColor text;
299         WebKitWebSettings *settings;
300
301         gdk_color_parse ("#fff", &base);
302         gdk_color_parse ("#000", &text);
303         gtk_widget_modify_base (GTK_WIDGET (self), GTK_STATE_NORMAL, &base);
304         gtk_widget_modify_text (GTK_WIDGET (self), GTK_STATE_NORMAL, &text);
305
306         priv->sighandlers = NULL;
307
308         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
309                                                        G_OBJECT(self), "notify::style",
310                                                        G_CALLBACK (on_notify_style), (gpointer) self);
311         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
312                                                        G_OBJECT (self), "resource-request-starting",
313                                                        G_CALLBACK (on_resource_request_starting), (gpointer) self);
314         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
315                                                        G_OBJECT (self), "navigation-policy-decision-requested",
316                                                        G_CALLBACK (on_navigation_policy_decision_requested), (gpointer) self);
317         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
318                                                        G_OBJECT (self), "new-window-policy-decision-requested",
319                                                        G_CALLBACK (on_new_window_policy_decision_requested), (gpointer) self);
320         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
321                                                        G_OBJECT (self), "navigation-requested",
322                                                        G_CALLBACK (on_navigation_requested), (gpointer) self);
323
324         priv->part = NULL;
325         priv->current_zoom = 1.0;
326         priv->view_images = FALSE;
327         priv->has_external_images = FALSE;
328
329         settings = webkit_web_settings_new ();
330         g_object_set (G_OBJECT (settings),
331                       "auto-load-images", FALSE,
332                       "enable-html5-database", FALSE,
333                       "enable-html5-local-storage", FALSE, 
334                       "enable-offline-web-application-cache", FALSE,
335                       "enable-plugins", FALSE,
336                       "enable-private-browsing", TRUE,
337                       "enable-scripts", FALSE,
338                       NULL);
339         webkit_web_view_set_settings (WEBKIT_WEB_VIEW (self), settings);
340         g_object_unref (settings);
341         g_object_set (G_OBJECT (self), 
342                       "editable", FALSE,
343                       NULL);
344 }
345
346 static void
347 modest_webkit_mime_part_view_finalize (GObject *obj)
348 {
349         ModestWebkitMimePartViewPrivate *priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (obj);
350
351         modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
352         priv->sighandlers = NULL;
353
354         G_OBJECT_CLASS (parent_class)->finalize (obj);
355 }
356
357 static void
358 modest_webkit_mime_part_view_dispose (GObject *obj)
359 {
360         ModestWebkitMimePartViewPrivate *priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (obj);
361
362         g_signal_emit (G_OBJECT (obj), signals[STOP_STREAMS_SIGNAL], 0);
363
364         if (priv->part) {
365                 g_object_unref (priv->part);
366                 priv->part = NULL;
367         }
368
369         G_OBJECT_CLASS (parent_class)->dispose (obj);
370 }
371
372 /* WEBKIT SIGNALS HANDLERS */
373
374 static WebKitNavigationResponse
375 on_navigation_requested                      (WebKitWebView        *web_view,
376                                               WebKitWebFrame       *frame,
377                                               WebKitNetworkRequest *request,
378                                               gpointer              user_data)
379 {
380         const gchar *uri;
381
382         uri = webkit_network_request_get_uri (request);
383         if (g_strcmp0 (uri, "about:blank") == 0) {
384                 return WEBKIT_NAVIGATION_RESPONSE_ACCEPT;
385         } else if (g_str_has_prefix (uri, "cid:") == 0) {
386                 return WEBKIT_NAVIGATION_RESPONSE_DOWNLOAD;
387         } else {
388                 return WEBKIT_NAVIGATION_RESPONSE_ACCEPT;
389         }
390 }
391
392 static void
393 on_resource_request_starting (WebKitWebView *webview,
394                               WebKitWebFrame *frame,
395                               WebKitWebResource *resource,
396                               WebKitNetworkRequest *request,
397                               WebKitNetworkResponse *response,
398                               gpointer userdata)
399 {
400         ModestWebkitMimePartView *self = (ModestWebkitMimePartView *) userdata;
401         g_return_if_fail (MODEST_IS_WEBKIT_MIME_PART_VIEW (self));
402
403         if (g_str_has_prefix (webkit_network_request_get_uri (request), "http:")) {
404                 ModestWebkitMimePartViewPrivate *priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (self);
405
406                 if (!priv->view_images)
407                         priv->has_external_images = TRUE;
408         }
409
410         webkit_network_request_set_uri (request, "about:blank");
411 }
412
413 static gboolean
414 on_navigation_policy_decision_requested (WebKitWebView             *web_view,
415                                          WebKitWebFrame            *frame,
416                                          WebKitNetworkRequest      *request,
417                                          WebKitWebNavigationAction *navigation_action,
418                                          WebKitWebPolicyDecision   *policy_decision,
419                                          gpointer                   user_data)
420 {
421         WebKitWebNavigationReason reason;
422         reason = webkit_web_navigation_action_get_reason (navigation_action);
423         if (reason == WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) {
424                 const gchar *uri;
425                 gboolean result;
426
427                 webkit_web_policy_decision_ignore (policy_decision);
428                 uri = webkit_network_request_get_uri (WEBKIT_NETWORK_REQUEST (request));
429                 g_signal_emit_by_name (G_OBJECT (user_data), "activate-link", uri, &result);
430
431                 return TRUE;
432         } else if (reason != WEBKIT_WEB_NAVIGATION_REASON_OTHER) {
433                 webkit_web_policy_decision_ignore (policy_decision);
434                 return TRUE;
435         }
436         return FALSE;
437 }
438
439 static gboolean
440 on_new_window_policy_decision_requested (WebKitWebView             *web_view,
441                                          WebKitWebFrame            *frame,
442                                          WebKitNetworkRequest      *request,
443                                          WebKitWebNavigationAction *navigation_action,
444                                          WebKitWebPolicyDecision   *policy_decision,
445                                          gpointer                   user_data)
446 {
447         WebKitWebNavigationReason reason;
448         reason = webkit_web_navigation_action_get_reason (navigation_action);
449         if (reason == WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) {
450                 const gchar *uri;
451                 gboolean result;
452
453                 webkit_web_policy_decision_ignore (policy_decision);
454                 uri = webkit_network_request_get_uri (WEBKIT_NETWORK_REQUEST (request));
455                 g_signal_emit_by_name (G_OBJECT (user_data), "activate-link", uri, &result);
456
457                 return TRUE;
458         } else if (reason != WEBKIT_WEB_NAVIGATION_REASON_OTHER) {
459                 webkit_web_policy_decision_ignore (policy_decision);
460                 return TRUE;
461         }
462         return FALSE;
463 }
464
465 static void 
466 on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata)
467 {
468         if (strcmp ("style", spec->name) == 0) {
469                 g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc) update_style, 
470                                  g_object_ref (obj), g_object_unref);
471                 gtk_widget_queue_draw (GTK_WIDGET (obj));
472         }
473 }
474
475 gboolean
476 same_color (GdkColor *a, GdkColor *b)
477 {
478         return ((a->red == b->red) && 
479                 (a->green == b->green) && 
480                 (a->blue == b->blue));
481 }
482
483 static gboolean
484 update_style (ModestWebkitMimePartView *self)
485 {
486         GdkColor base;
487         GdkColor text;
488         GtkRcStyle *rc_style;
489
490         gdk_threads_enter ();
491
492         if (GTK_WIDGET_VISIBLE (self)) {
493                 rc_style = gtk_widget_get_modifier_style (GTK_WIDGET (self));
494
495                 gdk_color_parse ("#fff", &base);
496                 gdk_color_parse ("#000", &text);
497
498                 if (!same_color (&(rc_style->base[GTK_STATE_NORMAL]), &base) &&
499                     !same_color (&(rc_style->text[GTK_STATE_NORMAL]), &text)) {
500
501                         rc_style->base[GTK_STATE_NORMAL] = base;
502                         rc_style->text[GTK_STATE_NORMAL] = text;
503                         gtk_widget_modify_style (GTK_WIDGET (self), rc_style);
504                 }
505         }
506
507         gdk_threads_leave ();
508
509         return FALSE;
510 }
511
512
513 /* INTERNAL API */
514 static void
515 decode_to_stream_cb (TnyMimePart *self,
516                      gboolean cancelled,
517                      TnyStream *stream,
518                      GError *err,
519                      gpointer user_data)
520 {
521         ModestWebkitMimePartView *view = (ModestWebkitMimePartView *) user_data;
522
523         if (MODEST_IS_STREAM_TEXT_TO_HTML (stream)) {
524                 if (tny_stream_write (stream, "\n", 1) == -1) {
525                         g_warning ("failed to write CR in %s", __FUNCTION__);
526                 }
527                 if (modest_stream_text_to_html_limit_reached (MODEST_STREAM_TEXT_TO_HTML (stream))) {
528                         g_signal_emit (G_OBJECT (view), signals[LIMIT_ERROR_SIGNAL], 0);
529                 }
530                 tny_stream_reset (stream);
531         } else {
532                 if (modest_tny_stream_webkit_limit_reached (MODEST_TNY_STREAM_WEBKIT (stream))) {
533                         g_signal_emit (G_OBJECT (view), signals[LIMIT_ERROR_SIGNAL], 0);
534                 }
535         }
536         tny_stream_close (stream);
537 }
538
539 static void
540 set_html_part (ModestWebkitMimePartView *self, TnyMimePart *part, const gchar *encoding)
541 {
542         TnyStream *tny_stream;
543
544         g_return_if_fail (self);
545         g_return_if_fail (part);
546
547         g_signal_emit (G_OBJECT (self), signals[STOP_STREAMS_SIGNAL], 0);
548
549         tny_stream     = TNY_STREAM(modest_tny_stream_webkit_new (WEBKIT_WEB_VIEW (self), "text/html", encoding));
550         modest_tny_stream_webkit_set_max_size (MODEST_TNY_STREAM_WEBKIT (tny_stream), 128*1024);
551         tny_stream_reset (tny_stream);
552
553         tny_mime_part_decode_to_stream_async (TNY_MIME_PART (part),
554                                               tny_stream, decode_to_stream_cb,
555                                               NULL, self);
556         g_object_unref (tny_stream);
557 }
558
559 static void
560 set_text_part (ModestWebkitMimePartView *self, TnyMimePart *part)
561 {
562         TnyStream* text_to_html_stream, *tny_stream;
563
564         g_return_if_fail (self);
565         g_return_if_fail (part);
566
567         g_signal_emit (G_OBJECT (self), signals[STOP_STREAMS_SIGNAL], 0);
568
569         tny_stream =  TNY_STREAM(modest_tny_stream_webkit_new (WEBKIT_WEB_VIEW (self), "text/html", "utf-8"));
570         modest_tny_stream_webkit_set_max_size (MODEST_TNY_STREAM_WEBKIT (tny_stream), 128*1024);
571         text_to_html_stream = TNY_STREAM (modest_stream_text_to_html_new (tny_stream));
572         modest_stream_text_to_html_set_linkify_limit (MODEST_STREAM_TEXT_TO_HTML (text_to_html_stream),
573                                                       64*1024);
574         modest_stream_text_to_html_set_full_limit (MODEST_STREAM_TEXT_TO_HTML (text_to_html_stream),
575                                                    128*1024);
576         modest_stream_text_to_html_set_line_limit (MODEST_STREAM_TEXT_TO_HTML (text_to_html_stream),
577                                                    1024);
578
579         tny_mime_part_decode_to_stream_async (TNY_MIME_PART (part),
580                                               text_to_html_stream, decode_to_stream_cb,
581                                               NULL, self);
582
583         g_object_unref (G_OBJECT(text_to_html_stream));
584         g_object_unref (G_OBJECT(tny_stream));
585 }
586
587 static void
588 set_empty_part (ModestWebkitMimePartView *self)
589 {
590         g_return_if_fail (self);
591
592         g_signal_emit (G_OBJECT (self), signals[STOP_STREAMS_SIGNAL], 0);
593         webkit_web_view_load_string (WEBKIT_WEB_VIEW (self), "", "text/plain", "utf-8", NULL);
594 }
595
596 static void
597 set_part (ModestWebkitMimePartView *self, TnyMimePart *part)
598 {
599         ModestWebkitMimePartViewPrivate *priv;
600         gchar *header_content_type, *header_content_type_lower;
601         const gchar *tmp;
602         gchar *charset = NULL;
603
604         g_return_if_fail (self);
605         
606         priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE(self);
607         priv->has_external_images = FALSE;
608
609         if (part != priv->part) {
610                 if (priv->part)
611                         g_object_unref (G_OBJECT(priv->part));
612                 if (part)
613                         g_object_ref   (G_OBJECT(part));
614                 priv->part = part;
615         }
616         
617         if (!part) {
618                 set_empty_part (self);
619                 return;
620         }
621
622         header_content_type = modest_tny_mime_part_get_header_value (part, "Content-Type");
623         if (header_content_type) {
624                 header_content_type = g_strstrip (header_content_type);
625                 header_content_type_lower = g_ascii_strdown (header_content_type, -1);
626         } else {
627                 header_content_type_lower = NULL;
628         }
629
630         if (header_content_type_lower) {
631                 tmp = strstr (header_content_type_lower, "charset=");
632                 if (tmp) {
633                         const gchar *tmp2;
634                         tmp = tmp + strlen ("charset=");
635                         
636                         tmp2 = strstr (tmp, ";");
637                         if (tmp2) {
638                                 charset = g_strndup (tmp, tmp2-tmp);
639                         } else {
640                                 charset = g_strdup (tmp);
641                         }
642                 }
643         }
644
645         if (tny_mime_part_content_type_is (part, "text/html")) {
646                 set_html_part (self, part, charset);
647         } else {
648                 if (tny_mime_part_content_type_is (part, "message/rfc822")) {
649                         if (header_content_type) {
650                                 if (g_str_has_prefix (header_content_type_lower, "text/html"))
651                                         set_html_part (self, part, charset);
652                                 else 
653                                         set_text_part (self, part);
654
655                         } else {
656                                 set_text_part (self, part);
657                         }
658                 } else {
659                         set_text_part (self, part);
660                 }
661         }
662         g_free (header_content_type_lower);
663         g_free (header_content_type);
664
665 }
666
667 static TnyMimePart*
668 get_part (ModestWebkitMimePartView *self)
669 {
670         TnyMimePart *part;
671
672         g_return_val_if_fail (MODEST_IS_WEBKIT_MIME_PART_VIEW (self), NULL);
673
674         part = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE(self)->part;
675
676         if (part)
677                 g_object_ref (part);
678         
679         return part;
680 }
681
682 static gboolean      
683 is_empty   (ModestWebkitMimePartView *self)
684 {
685         return FALSE;
686 }
687
688 static gboolean      
689 get_view_images   (ModestWebkitMimePartView *self)
690 {
691         ModestWebkitMimePartViewPrivate *priv;
692
693         g_return_val_if_fail (MODEST_IS_WEBKIT_MIME_PART_VIEW (self), FALSE);
694
695         priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (self);
696         return priv->view_images;
697 }
698
699 static void
700 set_view_images   (ModestWebkitMimePartView *self, gboolean view_images)
701 {
702         ModestWebkitMimePartViewPrivate *priv;
703
704         g_return_if_fail (MODEST_IS_WEBKIT_MIME_PART_VIEW (self));
705
706         priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (self);
707         priv->view_images = view_images;
708 }
709
710 static gboolean      
711 has_external_images   (ModestWebkitMimePartView *self)
712 {
713         ModestWebkitMimePartViewPrivate *priv;
714
715         g_return_val_if_fail (MODEST_IS_WEBKIT_MIME_PART_VIEW (self), FALSE);
716
717         priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (self);
718         return priv->has_external_images;
719 }
720
721 static void
722 set_zoom (ModestWebkitMimePartView *self, gdouble zoom)
723 {
724         ModestWebkitMimePartViewPrivate *priv;
725
726         g_return_if_fail (MODEST_IS_WEBKIT_MIME_PART_VIEW (self));
727
728         priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (self);
729         priv->current_zoom = zoom;
730
731         gtk_widget_queue_resize (GTK_WIDGET (self));
732 }
733
734 static gdouble
735 get_zoom (ModestWebkitMimePartView *self)
736 {
737         ModestWebkitMimePartViewPrivate *priv;
738
739         g_return_val_if_fail (MODEST_IS_WEBKIT_MIME_PART_VIEW (self), 1.0);
740
741         priv = MODEST_WEBKIT_MIME_PART_VIEW_GET_PRIVATE (self);
742
743         return priv->current_zoom;
744 }
745
746 static gboolean
747 search (ModestWebkitMimePartView *self, 
748         const gchar *string)
749 {
750         return FALSE;
751 }
752
753 static gboolean
754 search_next (ModestWebkitMimePartView *self)
755 {
756         return FALSE;
757 }
758
759 static gboolean
760 get_selection_area (ModestWebkitMimePartView *self, 
761                     gint *x, gint *y,
762                     gint *width, gint *height)
763 {
764         return FALSE;
765 }
766
767
768 /* TNY MIME PART IMPLEMENTATION */
769
770 static void
771 tny_mime_part_view_init (gpointer g, gpointer iface_data)
772 {
773         TnyMimePartViewIface *klass = (TnyMimePartViewIface *)g;
774
775         klass->get_part = modest_webkit_mime_part_view_get_part;
776         klass->set_part = modest_webkit_mime_part_view_set_part;
777         klass->clear = modest_webkit_mime_part_view_clear;
778
779         return;
780 }
781
782 static TnyMimePart* 
783 modest_webkit_mime_part_view_get_part (TnyMimePartView *self)
784 {
785         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->get_part_func (self);
786 }
787
788
789 static TnyMimePart* 
790 modest_webkit_mime_part_view_get_part_default (TnyMimePartView *self)
791 {
792         return TNY_MIME_PART (get_part (MODEST_WEBKIT_MIME_PART_VIEW (self)));
793 }
794
795 static void
796 modest_webkit_mime_part_view_set_part (TnyMimePartView *self,
797                                         TnyMimePart *part)
798 {
799         MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->set_part_func (self, part);
800 }
801
802 static void
803 modest_webkit_mime_part_view_set_part_default (TnyMimePartView *self,
804                                                 TnyMimePart *part)
805 {
806         g_return_if_fail ((part == NULL) || TNY_IS_MIME_PART (part));
807
808         set_part (MODEST_WEBKIT_MIME_PART_VIEW (self), part);
809 }
810
811 static void
812 modest_webkit_mime_part_view_clear (TnyMimePartView *self)
813 {
814         MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->clear_func (self);
815 }
816
817 static void
818 modest_webkit_mime_part_view_clear_default (TnyMimePartView *self)
819 {
820         set_part (MODEST_WEBKIT_MIME_PART_VIEW (self), NULL);
821 }
822
823 /* MODEST MIME PART VIEW IMPLEMENTATION */
824
825 static void
826 modest_mime_part_view_init (gpointer g, gpointer iface_data)
827 {
828         ModestMimePartViewIface *klass = (ModestMimePartViewIface *)g;
829
830         klass->is_empty_func = modest_webkit_mime_part_view_is_empty;
831         klass->get_view_images_func = modest_webkit_mime_part_view_get_view_images;
832         klass->set_view_images_func = modest_webkit_mime_part_view_set_view_images;
833         klass->has_external_images_func = modest_webkit_mime_part_view_has_external_images;
834
835         return;
836 }
837
838 static gboolean
839 modest_webkit_mime_part_view_is_empty (ModestMimePartView *self)
840 {
841         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->is_empty_func (self);
842 }
843
844 static gboolean
845 modest_webkit_mime_part_view_get_view_images (ModestMimePartView *self)
846 {
847         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->get_view_images_func (self);
848 }
849
850 static void
851 modest_webkit_mime_part_view_set_view_images (ModestMimePartView *self, gboolean view_images)
852 {
853         MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->set_view_images_func (self, view_images);
854 }
855
856 static gboolean
857 modest_webkit_mime_part_view_has_external_images (ModestMimePartView *self)
858 {
859         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->has_external_images_func (self);
860 }
861
862 static gboolean
863 modest_webkit_mime_part_view_is_empty_default (ModestMimePartView *self)
864 {
865         return is_empty (MODEST_WEBKIT_MIME_PART_VIEW (self));
866 }
867
868 static gboolean
869 modest_webkit_mime_part_view_get_view_images_default (ModestMimePartView *self)
870 {
871         return get_view_images (MODEST_WEBKIT_MIME_PART_VIEW (self));
872 }
873
874 static void
875 modest_webkit_mime_part_view_set_view_images_default (ModestMimePartView *self, gboolean view_images)
876 {
877         set_view_images (MODEST_WEBKIT_MIME_PART_VIEW (self), view_images);
878 }
879
880 static gboolean
881 modest_webkit_mime_part_view_has_external_images_default (ModestMimePartView *self)
882 {
883         return has_external_images (MODEST_WEBKIT_MIME_PART_VIEW (self));
884 }
885
886
887 /* MODEST ZOOMABLE IMPLEMENTATION */
888 static void
889 modest_zoomable_init (gpointer g, gpointer iface_data)
890 {
891         ModestZoomableIface *klass = (ModestZoomableIface *)g;
892         
893         klass->get_zoom_func = modest_webkit_mime_part_view_get_zoom;
894         klass->set_zoom_func = modest_webkit_mime_part_view_set_zoom;
895         klass->zoom_minus_func = modest_webkit_mime_part_view_zoom_minus;
896         klass->zoom_plus_func = modest_webkit_mime_part_view_zoom_plus;
897
898         return;
899 }
900
901 static gdouble
902 modest_webkit_mime_part_view_get_zoom (ModestZoomable *self)
903 {
904         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->get_zoom_func (self);
905 }
906
907 static gdouble
908 modest_webkit_mime_part_view_get_zoom_default (ModestZoomable *self)
909 {
910         return get_zoom (MODEST_WEBKIT_MIME_PART_VIEW (self));
911 }
912
913 static void
914 modest_webkit_mime_part_view_set_zoom (ModestZoomable *self, gdouble value)
915 {
916         MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->set_zoom_func (self, value);
917 }
918
919 static void
920 modest_webkit_mime_part_view_set_zoom_default (ModestZoomable *self, gdouble value)
921 {
922         set_zoom (MODEST_WEBKIT_MIME_PART_VIEW (self), value);
923 }
924
925 static gboolean
926 modest_webkit_mime_part_view_zoom_minus (ModestZoomable *self)
927 {
928         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->zoom_minus_func (self);
929 }
930
931 static gboolean
932 modest_webkit_mime_part_view_zoom_minus_default (ModestZoomable *self)
933 {
934         /* operation not supported in ModestWebkitMimePartView */
935         return FALSE;
936 }
937
938 static gboolean
939 modest_webkit_mime_part_view_zoom_plus (ModestZoomable *self)
940 {
941         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->zoom_plus_func (self);
942 }
943
944 static gboolean
945 modest_webkit_mime_part_view_zoom_plus_default (ModestZoomable *self)
946 {
947         /* operation not supported in ModestWebkitMimePartView */
948         return FALSE;
949 }
950
951 /* ISEARCH VIEW IMPLEMENTATION */
952 static void
953 modest_isearch_view_init (gpointer g, gpointer iface_data)
954 {
955         ModestISearchViewIface *klass = (ModestISearchViewIface *)g;
956         
957         klass->search_func = modest_webkit_mime_part_view_search;
958         klass->search_next_func = modest_webkit_mime_part_view_search_next;
959         klass->get_selection_area_func = modest_webkit_mime_part_view_get_selection_area;
960
961         return;
962 }
963
964 static gboolean 
965 modest_webkit_mime_part_view_search (ModestISearchView *self, const gchar *string)
966 {
967         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->search_func (self, string);
968 }
969
970 static gboolean 
971 modest_webkit_mime_part_view_search_default (ModestISearchView *self, const gchar *string)
972 {
973         return search (MODEST_WEBKIT_MIME_PART_VIEW (self), string);
974 }
975
976 static gboolean 
977 modest_webkit_mime_part_view_search_next(ModestISearchView *self)
978 {
979         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->search_next_func (self);
980 }
981
982 static gboolean 
983 modest_webkit_mime_part_view_search_next_default (ModestISearchView *self)
984 {
985         return search_next (MODEST_WEBKIT_MIME_PART_VIEW (self));
986 }
987
988 static gboolean 
989 modest_webkit_mime_part_view_get_selection_area (ModestISearchView *self, gint *x, gint *y, 
990                                                   gint *width, gint *height)
991 {
992         return MODEST_WEBKIT_MIME_PART_VIEW_GET_CLASS (self)->get_selection_area_func (self, x, y, width, height);
993 }
994
995 static gboolean 
996 modest_webkit_mime_part_view_get_selection_area_default (ModestISearchView *self, gint *x, gint *y, 
997                                                           gint *width, gint *height)
998 {
999         return get_selection_area (MODEST_WEBKIT_MIME_PART_VIEW (self), x, y, width, height);
1000 }