evolution-3.6.4/mail/e-mail-display.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found e-mail-display.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found e-mail-display.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
   1 /*
   2  * e-mail-display.c
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2 of the License, or (at your option) version 3.
   8  *
   9  * This program is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  16  *
  17  *
  18  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  19  *
  20  */
  21 
  22 #ifdef HAVE_CONFIG_H
  23 #include <config.h>
  24 #endif
  25 
  26 #include "e-mail-display.h"
  27 #include "e-mail-display-popup-extension.h"
  28 
  29 #include <glib/gi18n.h>
  30 #include <gdk/gdk.h>
  31 
  32 #include <em-format/e-mail-part-utils.h>
  33 #include <em-format/e-mail-formatter-extension.h>
  34 #include <em-format/e-mail-extension-registry.h>
  35 #include <em-format/e-mail-part-attachment.h>
  36 #include <em-format/e-mail-formatter-print.h>
  37 
  38 #include "e-util/e-marshal.h"
  39 #include "e-util/e-util.h"
  40 #include "e-util/e-plugin-ui.h"
  41 #include "e-util/e-file-request.h"
  42 #include "e-util/e-stock-request.h"
  43 #include "mail/em-composer-utils.h"
  44 #include "mail/em-utils.h"
  45 #include "mail/e-mail-request.h"
  46 #include "mail/e-http-request.h"
  47 #include "widgets/misc/e-attachment-bar.h"
  48 #include "widgets/misc/e-attachment-button.h"
  49 
  50 #include <camel/camel.h>
  51 
  52 #define d(x)
  53 
  54 G_DEFINE_TYPE (
  55 	EMailDisplay,
  56 	e_mail_display,
  57 	E_TYPE_WEB_VIEW);
  58 
  59 #define E_MAIL_DISPLAY_GET_PRIVATE(obj) \
  60 	(G_TYPE_INSTANCE_GET_PRIVATE \
  61 	((obj), E_TYPE_MAIL_DISPLAY, EMailDisplayPrivate))
  62 
  63 struct _EMailDisplayPrivate {
  64 	EMailPartList *part_list;
  65 	EMailFormatterMode mode;
  66 	EMailFormatter *formatter;
  67 
  68 	gboolean headers_collapsable;
  69 	gboolean headers_collapsed;
  70 
  71 	gint force_image_load: 1;
  72 
  73 	GSettings *settings;
  74 
  75 	GHashTable *widgets;
  76 
  77 	guint scheduled_reload;
  78 };
  79 
  80 enum {
  81 	PROP_0,
  82 	PROP_MODE,
  83 	PROP_PART_LIST,
  84 	PROP_HEADERS_COLLAPSABLE,
  85 	PROP_HEADERS_COLLAPSED,
  86 	PROP_FORMATTER
  87 };
  88 
  89 static CamelDataCache *emd_global_http_cache = NULL;
  90 
  91 static const gchar *ui =
  92 "<ui>"
  93 "  <popup name='context'>"
  94 "    <placeholder name='custom-actions-1'>"
  95 "      <menuitem action='add-to-address-book'/>"
  96 "      <menuitem action='send-reply'/>"
  97 "    </placeholder>"
  98 "    <placeholder name='custom-actions-3'>"
  99 "      <menu action='search-folder-menu'>"
 100 "        <menuitem action='search-folder-recipient'/>"
 101 "        <menuitem action='search-folder-sender'/>"
 102 "      </menu>"
 103 "    </placeholder>"
 104 "  </popup>"
 105 "</ui>";
 106 
 107 static const gchar *image_ui =
 108 "<ui>"
 109 "  <popup name='context'>"
 110 "    <placeholder name='custom-actions-2'>"
 111 "      <menuitem action='image-save'/>"
 112 "    </placeholder>"
 113 "  </popup>"
 114 "</ui>";
 115 
 116 static GtkActionEntry mailto_entries[] = {
 117 
 118 	{ "add-to-address-book",
 119 	  "contact-new",
 120 	  N_("_Add to Address Book..."),
 121 	  NULL,
 122 	  NULL,  /* XXX Add a tooltip! */
 123 	  NULL   /* Handled by EMailReader */ },
 124 
 125 	{ "search-folder-recipient",
 126 	  NULL,
 127 	  N_("_To This Address"),
 128 	  NULL,
 129 	  NULL,  /* XXX Add a tooltip! */
 130 	  NULL   /* Handled by EMailReader */ },
 131 
 132 	{ "search-folder-sender",
 133 	  NULL,
 134 	  N_("_From This Address"),
 135 	  NULL,
 136 	  NULL,  /* XXX Add a tooltip! */
 137 	  NULL   /* Handled by EMailReader */ },
 138 
 139 	{ "send-reply",
 140 	  NULL,
 141 	  N_("Send _Reply To..."),
 142 	  NULL,
 143 	  N_("Send a reply message to this address"),
 144 	  NULL   /* Handled by EMailReader */ },
 145 
 146 	/*** Menus ***/
 147 
 148 	{ "search-folder-menu",
 149 	  "folder-saved-search",
 150 	  N_("Create Search _Folder"),
 151 	  NULL,
 152 	  NULL,
 153 	  NULL }
 154 };
 155 
 156 static GtkActionEntry image_entries[] = {
 157 
 158 	{ "image-save",
 159 	  GTK_STOCK_SAVE,
 160 	  N_("Save _Image..."),
 161 	  NULL,
 162 	  N_("Save the image to a file"),
 163 	  NULL /* Handled by EMailReader */ },
 164 };
 165 
 166 static void
 167 formatter_image_loading_policy_changed_cb (GObject *object,
 168                                            GParamSpec *pspec,
 169                                            gpointer user_data)
 170 {
 171 	EMailDisplay *display = user_data;
 172 	EMailFormatter *formatter = E_MAIL_FORMATTER (object);
 173 	EMailImageLoadingPolicy policy;
 174 
 175 	policy = e_mail_formatter_get_image_loading_policy (formatter);
 176 
 177 	if (policy == E_MAIL_IMAGE_LOADING_POLICY_ALWAYS)
 178 		e_mail_display_load_images (display);
 179 	else
 180 		e_mail_display_reload (display);
 181 }
 182 
 183 static gboolean
 184 mail_display_image_exists_in_cache (const gchar *image_uri)
 185 {
 186 	gchar *image_filename, *uri_md5;
 187 	gboolean exists;
 188 
 189 	g_return_val_if_fail (emd_global_http_cache != NULL, FALSE);
 190 
 191 	uri_md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, image_uri, -1);
 192 	image_filename = camel_data_cache_get_filename (emd_global_http_cache, "http", uri_md5);
 193 
 194 	exists = image_filename && g_file_test (image_filename, G_FILE_TEST_EXISTS);
 195 
 196 	g_free (uri_md5);
 197 	g_free (image_filename);
 198 
 199 	return exists;
 200 }
 201 
 202 static void
 203 mail_display_update_formatter_colors (EMailDisplay *display)
 204 {
 205 	GtkStyle *style;
 206 	GtkStateType state;
 207 
 208 	if (display->priv->formatter == NULL)
 209 		return;
 210 
 211 	style = gtk_widget_get_style (GTK_WIDGET (display));
 212 	state = gtk_widget_get_state (GTK_WIDGET (display));
 213 	e_mail_formatter_set_style (display->priv->formatter, style, state);
 214 }
 215 
 216 static void
 217 mail_display_plugin_widget_disconnect_children (GtkWidget *widget,
 218                                                 gpointer mail_display)
 219 {
 220 	g_signal_handlers_disconnect_by_data (widget, mail_display);
 221 }
 222 
 223 static void
 224 mail_display_plugin_widget_disconnect (gpointer widget_uri,
 225                                        gpointer widget,
 226                                        gpointer mail_display)
 227 {
 228 	if (E_IS_ATTACHMENT_BAR (widget))
 229 		g_signal_handlers_disconnect_by_data (widget, mail_display);
 230 	else if (E_IS_ATTACHMENT_BUTTON (widget))
 231 		g_signal_handlers_disconnect_by_data (widget, mail_display);
 232 	else if (GTK_IS_CONTAINER (widget))
 233 		gtk_container_foreach (
 234 			widget,
 235 			mail_display_plugin_widget_disconnect_children,
 236 			mail_display);
 237 }
 238 
 239 static gboolean
 240 mail_display_process_mailto (EWebView *web_view,
 241                              const gchar *mailto_uri,
 242                              gpointer user_data)
 243 {
 244 	g_return_val_if_fail (E_IS_WEB_VIEW (web_view), FALSE);
 245 	g_return_val_if_fail (mailto_uri != NULL, FALSE);
 246 
 247 	if (g_ascii_strncasecmp (mailto_uri, "mailto:", 7) == 0) {
 248 		EShell *shell;
 249 		EMailPartList *part_list;
 250 
 251 		part_list = E_MAIL_DISPLAY (web_view)->priv->part_list;
 252 
 253 		shell = e_shell_get_default ();
 254 		em_utils_compose_new_message_with_mailto (
 255 			shell, mailto_uri, part_list->folder);
 256 
 257 		return TRUE;
 258 	}
 259 
 260 	return FALSE;
 261 }
 262 
 263 static gboolean
 264 mail_display_link_clicked (WebKitWebView *web_view,
 265                            WebKitWebFrame *frame,
 266                            WebKitNetworkRequest *request,
 267                            WebKitWebNavigationAction *navigation_action,
 268                            WebKitWebPolicyDecision *policy_decision,
 269                            gpointer user_data)
 270 {
 271 	const gchar *uri = webkit_network_request_get_uri (request);
 272 
 273 	if (g_str_has_prefix (uri, "file://")) {
 274 		gchar *filename;
 275 
 276 		filename = g_filename_from_uri (uri, NULL, NULL);
 277 
 278 		if (g_file_test (filename, G_FILE_TEST_IS_DIR)) {
 279 			webkit_web_policy_decision_ignore (policy_decision);
 280 			webkit_network_request_set_uri (request, "about:blank");
 281 			g_free (filename);
 282 			return TRUE;
 283 		}
 284 
 285 		g_free (filename);
 286 	}
 287 
 288 	if (mail_display_process_mailto (E_WEB_VIEW (web_view), uri, NULL)) {
 289 		/* do nothing, function handled the "mailto:" uri already */
 290 		webkit_web_policy_decision_ignore (policy_decision);
 291 		return TRUE;
 292 
 293 	} else if (g_ascii_strncasecmp (uri, "thismessage:", 12) == 0) {
 294 		/* ignore */
 295 		webkit_web_policy_decision_ignore (policy_decision);
 296 		return TRUE;
 297 
 298 	} else if (g_ascii_strncasecmp (uri, "cid:", 4) == 0) {
 299 		/* ignore */
 300 		webkit_web_policy_decision_ignore (policy_decision);
 301 		return TRUE;
 302 
 303 	}
 304 
 305 	/* Let WebKit handle it. */
 306 	return FALSE;
 307 }
 308 
 309 static void
 310 webkit_request_load_from_file (WebKitNetworkRequest *request,
 311                                const gchar *path)
 312 {
 313 	gchar *data = NULL;
 314 	gsize length = 0;
 315 	gchar *b64, *new_uri;
 316 	gchar *ct;
 317 
 318 	if (!g_file_get_contents (path, &data, &length, NULL))
 319 		return;
 320 
 321 	b64 = g_base64_encode ((guchar *) data, length);
 322 	ct = g_content_type_guess (path, NULL, 0, NULL);
 323 
 324 	new_uri =  g_strdup_printf ("data:%s;base64,%s", ct, b64);
 325 	webkit_network_request_set_uri (request, new_uri);
 326 
 327 	g_free (b64);
 328 	g_free (new_uri);
 329 	g_free (ct);
 330 	g_free (data);
 331 }
 332 
 333 static void
 334 mail_display_resource_requested (WebKitWebView *web_view,
 335                                  WebKitWebFrame *frame,
 336                                  WebKitWebResource *resource,
 337                                  WebKitNetworkRequest *request,
 338                                  WebKitNetworkResponse *response,
 339                                  gpointer user_data)
 340 {
 341 	EMailDisplay *display = E_MAIL_DISPLAY (web_view);
 342 	EMailPartList *part_list;
 343 	const gchar *uri = webkit_network_request_get_uri (request);
 344 
 345 	part_list = display->priv->part_list;
 346 	if (part_list == NULL)
 347 		return;
 348 
 349 	/* Redirect cid:part_id to mail://mail_id/cid:part_id */
 350 	if (g_str_has_prefix (uri, "cid:")) {
 351 		gchar *new_uri;
 352 
 353 		/* Always write raw content of CID object. */
 354 		new_uri = e_mail_part_build_uri (
 355 			part_list->folder, part_list->message_uid,
 356 			"part_id", G_TYPE_STRING, uri,
 357 			"mode", G_TYPE_INT, E_MAIL_FORMATTER_MODE_CID, NULL);
 358 
 359 		webkit_network_request_set_uri (request, new_uri);
 360 
 361 		g_free (new_uri);
 362 
 363 	/* WebKit won't allow to load a local file when displaying
 364 	 * "remote" mail:// protocol, so we need to handle this manually. */
 365 	} else if (g_str_has_prefix (uri, "file:")) {
 366 		gchar *path;
 367 
 368 		path = g_filename_from_uri (uri, NULL, NULL);
 369 		if (path == NULL)
 370 			return;
 371 
 372 		webkit_request_load_from_file (request, path);
 373 
 374 		g_free (path);
 375 
 376 	/* Redirect http(s) request to evo-http(s) protocol.
 377 	 * See EMailRequest for further details about this. */
 378 	} else if (g_str_has_prefix (uri, "http:") || g_str_has_prefix (uri, "https:") ||
 379 	    g_str_has_prefix (uri, "evo-http:") || g_str_has_prefix (uri, "evo-https:")) {
 380 		gchar *new_uri, *mail_uri, *enc;
 381 		SoupURI *soup_uri;
 382 		GHashTable *query;
 383 		gboolean image_exists;
 384 		EMailImageLoadingPolicy image_policy;
 385 
 386 		/* Check Evolution's cache */
 387 		image_exists = mail_display_image_exists_in_cache (uri);
 388 
 389 		/* If the URI is not cached and we are not allowed to load it
 390 		 * then redirect to invalid URI, so that webkit would display
 391 		 * a native placeholder for it. */
 392 		image_policy = e_mail_formatter_get_image_loading_policy (
 393 			display->priv->formatter);
 394 		if (!image_exists && !display->priv->force_image_load &&
 395 		    (image_policy == E_MAIL_IMAGE_LOADING_POLICY_NEVER)) {
 396 			webkit_network_request_set_uri (request, "about:blank");
 397 			return;
 398 		}
 399 
 400 		new_uri = g_strconcat ("evo-", uri, NULL);
 401 		mail_uri = e_mail_part_build_uri (
 402 			part_list->folder,
 403 			part_list->message_uid, NULL, NULL);
 404 
 405 		soup_uri = soup_uri_new (new_uri);
 406 		if (soup_uri->query)
 407 			query = soup_form_decode (soup_uri->query);
 408 		else
 409 			query = g_hash_table_new_full (
 410 				g_str_hash, g_str_equal,
 411 				g_free, g_free);
 412 		enc = soup_uri_encode (mail_uri, NULL);
 413 		g_hash_table_insert (query, g_strdup ("__evo-mail"), enc);
 414 
 415 		if (display->priv->force_image_load) {
 416 			g_hash_table_insert (
 417 				query,
 418 				g_strdup ("__evo-load-images"),
 419 				g_strdup ("true"));
 420 		}
 421 
 422 		g_free (mail_uri);
 423 
 424 		soup_uri_set_query_from_form (soup_uri, query);
 425 		g_free (new_uri);
 426 
 427 		new_uri = soup_uri_to_string (soup_uri, FALSE);
 428 		webkit_network_request_set_uri (request, new_uri);
 429 
 430 		g_free (new_uri);
 431 		soup_uri_free (soup_uri);
 432 		g_hash_table_unref (query);
 433 	}
 434 }
 435 
 436 static WebKitDOMElement *
 437 find_element_by_id (WebKitDOMDocument *document,
 438                     const gchar *id)
 439 {
 440 	WebKitDOMNodeList *frames;
 441 	WebKitDOMElement *element;
 442 	gulong ii, length;
 443 
 444 	if (!WEBKIT_DOM_IS_DOCUMENT (document))
 445 		return NULL;
 446 
 447 	/* Try to look up the element in this DOM document */
 448 	element = webkit_dom_document_get_element_by_id (document, id);
 449 	if (element != NULL)
 450 		return element;
 451 
 452 	/* If the element is not here then recursively scan all frames */
 453 	frames = webkit_dom_document_get_elements_by_tag_name (
 454 		document, "iframe");
 455 	length = webkit_dom_node_list_get_length (frames);
 456 	for (ii = 0; ii < length; ii++) {
 457 		WebKitDOMHTMLIFrameElement *iframe;
 458 		WebKitDOMDocument *frame_doc;
 459 		WebKitDOMElement *element;
 460 
 461 		iframe = WEBKIT_DOM_HTML_IFRAME_ELEMENT (
 462 			webkit_dom_node_list_item (frames, ii));
 463 
 464 		frame_doc = webkit_dom_html_iframe_element_get_content_document (iframe);
 465 
 466 		element = find_element_by_id (frame_doc, id);
 467 
 468 		if (element != NULL)
 469 			return element;
 470 	}
 471 
 472 	return NULL;
 473 }
 474 
 475 static void
 476 mail_display_plugin_widget_resize (GtkWidget *widget,
 477                                    gpointer dummy,
 478                                    EMailDisplay *display)
 479 {
 480 	WebKitDOMElement *parent_element;
 481 	gchar *dim;
 482 	gint height, width;
 483 	gfloat scale;
 484 
 485 	parent_element = g_object_get_data (G_OBJECT (widget), "parent_element");
 486 
 487 	if (!WEBKIT_DOM_IS_ELEMENT (parent_element)) {
 488 		d (
 489 			printf ("%s: %s does not have (valid) parent element!\n",
 490 			G_STRFUNC, (gchar *) g_object_get_data (object, "uri")));
 491 		return;
 492 	}
 493 
 494 	scale = webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (display));
 495 	width = gtk_widget_get_allocated_width (widget);
 496 	gtk_widget_get_preferred_height_for_width (widget, width, &height, NULL);
 497 
 498 	/* When zooming WebKit does not change dimensions of the elements, but
 499 	 * only scales them on the canvas. GtkWidget can't be scaled though
 500 	 * so we need to cope with the dimensions changes to keep the widgets
 501 	 * still the correct size. Due to inaccuracy in rounding (float -> int)
 502 	 * it still acts a bit funny, but at least it does not cause widgets in
 503 	 * WebKit to go crazy when zooming. */
 504 	height = height * (1 / scale);
 505 
 506 	/* Int -> Str */
 507 	dim = g_strdup_printf ("%d", height);
 508 
 509 	/* Set height of the containment <object> to match height of the
 510 	 * GtkWidget it contains */
 511 	webkit_dom_html_object_element_set_height (
 512 		WEBKIT_DOM_HTML_OBJECT_ELEMENT (parent_element), dim);
 513 	g_free (dim);
 514 }
 515 
 516 static void
 517 mail_display_plugin_widget_realize_cb (GtkWidget *widget,
 518                                        gpointer user_data)
 519 {
 520 	WebKitDOMHTMLElement *element;
 521 
 522 	if (GTK_IS_BOX (widget)) {
 523 		GList *children;
 524 
 525 		children = gtk_container_get_children (GTK_CONTAINER (widget));
 526 		if (children && children->data &&
 527 		    E_IS_ATTACHMENT_BAR (children->data)) {
 528 			widget = children->data;
 529 		}
 530 
 531 		g_list_free (children);
 532 	}
 533 
 534 	/* First check if we are actually supposed to be visible */
 535 	element = g_object_get_data (G_OBJECT (widget), "parent_element");
 536 	if (element == NULL || !WEBKIT_DOM_IS_HTML_ELEMENT (element)) {
 537 		g_warning ("UAAAAA");
 538 	} else {
 539 		if (webkit_dom_html_element_get_hidden (element)) {
 540 			gtk_widget_hide (widget);
 541 			return;
 542 		}
 543 	}
 544 
 545 	/* Initial resize of the <object> element when the widget
 546 	 * is displayed for the first time. */
 547 	mail_display_plugin_widget_resize (widget, NULL, user_data);
 548 }
 549 
 550 static void
 551 plugin_widget_set_parent_element (GtkWidget *widget,
 552                                   EMailDisplay *display)
 553 {
 554 	const gchar *uri;
 555 	WebKitDOMDocument *document;
 556 	WebKitDOMElement *element;
 557 
 558 	uri = g_object_get_data (G_OBJECT (widget), "uri");
 559 	if (uri == NULL || *uri == '\0')
 560 		return;
 561 
 562 	document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display));
 563 	element = find_element_by_id (document, uri);
 564 
 565 	if (!WEBKIT_DOM_IS_ELEMENT (element)) {
 566 		g_warning ("Failed to find parent <object> for '%s' - no ID set?", uri);
 567 		return;
 568 	}
 569 
 570 	/* Assign the WebKitDOMElement to "parent_element" data of the GtkWidget
 571 	 * and the GtkWidget to "widget" data of the DOM Element */
 572 	g_object_set_data (G_OBJECT (widget), "parent_element", element);
 573 	g_object_set_data (G_OBJECT (element), "widget", widget);
 574 
 575 	g_object_bind_property (
 576 		element, "hidden",
 577 		widget, "visible",
 578 		G_BINDING_SYNC_CREATE |
 579 		G_BINDING_INVERT_BOOLEAN);
 580 }
 581 
 582 static void
 583 toggle_widget_visibility (EAttachmentButton *button,
 584                           EMailDisplay *display,
 585                           WebKitDOMElement *element)
 586 {
 587 	GtkWidget *widget = NULL;
 588 	gchar *id;
 589 
 590 	id = webkit_dom_html_element_get_id (WEBKIT_DOM_HTML_ELEMENT (element));
 591 	if (id == NULL || *id == '\0')
 592 		return;
 593 
 594 	if (display->priv->widgets != NULL)
 595 		widget = g_hash_table_lookup (display->priv->widgets, id);
 596 
 597 	g_free (id);
 598 
 599 	if (widget == NULL)
 600 		return;
 601 
 602 	/* If the widget encapsulates EAttachmentBar then check, whether
 603 	 * the attachment bar is not empty. We want to display it only
 604 	 * when there's at least one attachment */
 605 	if (GTK_IS_BOX (widget)) {
 606 		GList *children;
 607 
 608 		children = gtk_container_get_children (GTK_CONTAINER (widget));
 609 		if (children && children->data && E_IS_ATTACHMENT_BAR (children->data)) {
 610 			EAttachmentStore *store;
 611 
 612 			store = e_attachment_bar_get_store (
 613 				E_ATTACHMENT_BAR (children->data));
 614 
 615 			g_list_free (children);
 616 
 617 			/* Don't allow to display such attachment bar,
 618 			 * but always allow to hide it */
 619 			if (e_attachment_button_get_expanded (button) &&
 620 			    (e_attachment_store_get_num_attachments (store) == 0))
 621 				return;
 622 		}
 623 	}
 624 
 625 	webkit_dom_html_element_set_hidden (
 626 		WEBKIT_DOM_HTML_ELEMENT (element),
 627 		!e_attachment_button_get_expanded (button));
 628 
 629 	if (e_attachment_button_get_expanded (button))
 630 		gtk_widget_show (widget);
 631 	else
 632 		gtk_widget_hide (widget);
 633 }
 634 
 635 /**
 636  * @button: An #EAttachmentButton
 637  * @iframe: An iframe element containing document with an attachment
 638  * 	    represented by the @button
 639  */
 640 static void
 641 bind_iframe_content_visibility (WebKitDOMElement *iframe,
 642                                 EMailDisplay *display,
 643                                 EAttachmentButton *button)
 644 {
 645 	WebKitDOMDocument *document;
 646 	WebKitDOMNodeList *nodes;
 647 	gulong ii, length;
 648 
 649 	if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (iframe))
 650 		return;
 651 
 652 	document = webkit_dom_html_iframe_element_get_content_document (
 653 		WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe));
 654 	if (!WEBKIT_DOM_IS_DOCUMENT (document))
 655 		return;
 656 
 657 	nodes = webkit_dom_document_get_elements_by_tag_name (document, "object");
 658 	length = webkit_dom_node_list_get_length (nodes);
 659 
 660 	d ({
 661 		gchar *name = webkit_dom_html_iframe_element_get_name (
 662 			WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe));
 663 		printf ("Found %ld objects within iframe %s\n", length, name);
 664 		g_free (name);
 665 	});
 666 
 667 	/* Iterate through all <object>s and bind visibility of their widget
 668 	 * with expanded-state of related attachment button */
 669 	for (ii = 0; ii < length; ii++) {
 670 
 671 		WebKitDOMNode *node = webkit_dom_node_list_item (nodes, ii);
 672 
 673 		/* Initial sync */
 674 		toggle_widget_visibility (
 675 			button, display, WEBKIT_DOM_ELEMENT (node));
 676 	}
 677 }
 678 
 679 static void
 680 attachment_button_expanded (GObject *object,
 681                             GParamSpec *pspec,
 682                             gpointer user_data)
 683 {
 684 	EAttachmentButton *button = E_ATTACHMENT_BUTTON (object);
 685 	EMailDisplay *display = user_data;
 686 	WebKitDOMDocument *document;
 687 	WebKitDOMElement *element;
 688 	WebKitDOMCSSStyleDeclaration *css;
 689 	gboolean expanded;
 690 	gchar *id;
 691 
 692 	d (
 693 		printf ("Attachment button %s has been %s!\n",
 694 		(gchar *) g_object_get_data (object, "uri"),
 695 		(e_attachment_button_get_expanded (button) ? "expanded" : "collapsed")));
 696 
 697 	expanded =
 698 		e_attachment_button_get_expanded (button) &&
 699 		gtk_widget_get_visible (GTK_WIDGET (button));
 700 
 701 	document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display));
 702 	element = find_element_by_id (
 703 		document, g_object_get_data (object, "attachment_id"));
 704 
 705 	if (!WEBKIT_DOM_IS_ELEMENT (element)) {
 706 		d (
 707 			printf ("%s: Content <div> of attachment %s does not exist!!\n",
 708 			G_STRFUNC, (gchar *) g_object_get_data (object, "uri")));
 709 		return;
 710 	}
 711 
 712 	/* Show or hide the DIV which contains
 713 	 * the attachment (iframe, image...). */
 714 	css = webkit_dom_element_get_style (element);
 715 	webkit_dom_css_style_declaration_set_property (
 716 		css, "display", expanded ? "block" : "none", "", NULL);
 717 
 718 	id = g_strconcat (
 719 		g_object_get_data (object, "attachment_id"),
 720 		".iframe", NULL);
 721 	element = find_element_by_id (document, id);
 722 	g_free (id);
 723 
 724 	if (!WEBKIT_DOM_IS_HTML_IFRAME_ELEMENT (element)) {
 725 		d (
 726 			printf ("%s: No <iframe> found\n",
 727 			(gchar *) g_object_get_data (object, "attachment_id")));
 728 		return;
 729 	}
 730 	bind_iframe_content_visibility (element, display, button);
 731 }
 732 
 733 static void
 734 mail_display_attachment_count_changed (EAttachmentStore *store,
 735                                        GParamSpec *pspec,
 736                                        GtkWidget *box)
 737 {
 738 	WebKitDOMHTMLElement *element;
 739 	GList *children;
 740 
 741 	children = gtk_container_get_children (GTK_CONTAINER (box));
 742 	g_return_if_fail (children  && children->data);
 743 
 744 	element = g_object_get_data (children->data, "parent_element");
 745 	g_list_free (children);
 746 
 747 	g_return_if_fail (WEBKIT_DOM_IS_HTML_ELEMENT (element));
 748 
 749 	if (e_attachment_store_get_num_attachments (store) == 0) {
 750 		gtk_widget_hide (box);
 751 		webkit_dom_html_element_set_hidden (element, TRUE);
 752 	} else {
 753 		gtk_widget_show (box);
 754 		webkit_dom_html_element_set_hidden (element, FALSE);
 755 	}
 756 }
 757 
 758 static GtkWidget *
 759 mail_display_plugin_widget_requested (WebKitWebView *web_view,
 760                                       gchar *mime_type,
 761                                       gchar *uri,
 762                                       GHashTable *param,
 763                                       gpointer user_data)
 764 {
 765 	EMailDisplay *display;
 766 	EMailExtensionRegistry *reg;
 767 	EMailFormatterExtension *extension;
 768 	GQueue *extensions;
 769 	GList *iter;
 770 	EMailPart *part;
 771 	GtkWidget *widget;
 772 	gchar *part_id, *type, *object_uri;
 773 
 774 	part_id = g_hash_table_lookup (param, "data");
 775 	if (part_id == NULL || !g_str_has_prefix (uri, "mail://"))
 776 		return NULL;
 777 
 778 	type = g_hash_table_lookup (param, "type");
 779 	if (type == NULL)
 780 		return NULL;
 781 
 782 	display = E_MAIL_DISPLAY (web_view);
 783 
 784 	widget = g_hash_table_lookup (display->priv->widgets, part_id);
 785 	if (widget != NULL) {
 786 		d (printf ("Handeled %s widget request from cache\n", part_id));
 787 		return widget;
 788 	}
 789 
 790 	/* Find the EMailPart representing the requested widget. */
 791 	part = e_mail_part_list_find_part (display->priv->part_list, part_id);
 792 	if (part == NULL)
 793 		return NULL;
 794 
 795 	reg = e_mail_formatter_get_extension_registry (display->priv->formatter);
 796 	extensions = e_mail_extension_registry_get_for_mime_type (reg, type);
 797 	if (extensions == NULL)
 798 		return NULL;
 799 
 800 	extension = NULL;
 801 	for (iter = g_queue_peek_head_link (extensions); iter; iter = iter->next) {
 802 
 803 		extension = iter->data;
 804 		if (extension == NULL)
 805 			continue;
 806 
 807 		if (e_mail_formatter_extension_has_widget (extension))
 808 			break;
 809 	}
 810 
 811 	if (extension == NULL)
 812 		return NULL;
 813 
 814 	/* Get the widget from formatter */
 815 	widget = e_mail_formatter_extension_get_widget (
 816 		extension, display->priv->part_list, part, param);
 817 	d (
 818 		printf ("Created widget %s (%p) for part %s\n",
 819 			G_OBJECT_TYPE_NAME (widget), widget, part_id));
 820 
 821 	/* Should not happen! WebKit will display an ugly 'Plug-in not
 822 	 * available' placeholder instead of hiding the <object> element. */
 823 	if (widget == NULL)
 824 		return NULL;
 825 
 826 	/* Attachment button has URI different then the actual PURI because
 827 	 * that URI identifies the attachment itself */
 828 	if (E_IS_ATTACHMENT_BUTTON (widget)) {
 829 		EMailPartAttachment *empa = (EMailPartAttachment *) part;
 830 		gchar *attachment_part_id;
 831 
 832 		if (empa->attachment_view_part_id)
 833 			attachment_part_id = empa->attachment_view_part_id;
 834 		else
 835 			attachment_part_id = part_id;
 836 
 837 		object_uri = g_strconcat (
 838 			attachment_part_id, ".attachment_button", NULL);
 839 		g_object_set_data_full (
 840 			G_OBJECT (widget), "attachment_id",
 841 			g_strdup (attachment_part_id),
 842 			(GDestroyNotify) g_free);
 843 	} else {
 844 		object_uri = g_strdup (part_id);
 845 	}
 846 
 847 	/* Store the uri as data of the widget */
 848 	g_object_set_data_full (
 849 		G_OBJECT (widget), "uri",
 850 		object_uri, (GDestroyNotify) g_free);
 851 
 852 	/* Set pointer to the <object> element as GObject data
 853 	 * "parent_element" and set pointer to the widget as GObject
 854 	 * data "widget" to the <object> element. */
 855 	plugin_widget_set_parent_element (widget, display);
 856 
 857 	/* Resizing a GtkWidget requires changing size of parent
 858 	 * <object> HTML element in DOM. */
 859 	g_signal_connect (
 860 		widget, "realize",
 861 		G_CALLBACK (mail_display_plugin_widget_realize_cb), display);
 862 	g_signal_connect (
 863 		widget, "size-allocate",
 864 		G_CALLBACK (mail_display_plugin_widget_resize), display);
 865 
 866 	if (E_IS_ATTACHMENT_BAR (widget)) {
 867 		GtkWidget *box = NULL;
 868 		EAttachmentStore *store;
 869 
 870 		/* Only when packed in box (grid does not work),
 871 		 * EAttachmentBar reports correct height */
 872 		box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
 873 		gtk_box_pack_start (GTK_BOX (box), widget, TRUE, TRUE, 0);
 874 
 875 		/* When EAttachmentBar is expanded/collapsed it does not
 876 		 * emit size-allocate signal despite it changes it's height. */
 877 		g_signal_connect (
 878 			widget, "notify::expanded",
 879 			G_CALLBACK (mail_display_plugin_widget_resize),
 880 			display);
 881 		g_signal_connect (
 882 			widget, "notify::active-view",
 883 			G_CALLBACK (mail_display_plugin_widget_resize),
 884 			display);
 885 
 886 		/* Always hide an attachment bar without attachments */
 887 		store = e_attachment_bar_get_store (E_ATTACHMENT_BAR (widget));
 888 		g_signal_connect (
 889 			store, "notify::num-attachments",
 890 			G_CALLBACK (mail_display_attachment_count_changed),
 891 			box);
 892 
 893 		gtk_widget_show (widget);
 894 		gtk_widget_show (box);
 895 
 896 		/* Initial sync */
 897 		mail_display_attachment_count_changed (store, NULL, box);
 898 
 899 		widget = box;
 900 
 901 	} else if (E_IS_ATTACHMENT_BUTTON (widget)) {
 902 
 903 		/* Bind visibility of DOM element containing related
 904 		 * attachment with 'expanded' property of this
 905 		 * attachment button. */
 906 		WebKitDOMElement *attachment;
 907 		WebKitDOMDocument *document;
 908 		EMailPartAttachment *empa = (EMailPartAttachment *) part;
 909 		gchar *attachment_part_id;
 910 
 911 		if (empa->attachment_view_part_id)
 912 			attachment_part_id = empa->attachment_view_part_id;
 913 		else
 914 			attachment_part_id = part_id;
 915 
 916 		/* Find attachment-wrapper div which contains
 917 		 * the content of the attachment (iframe). */
 918 		document = webkit_web_view_get_dom_document (
 919 			WEBKIT_WEB_VIEW (display));
 920 		attachment = find_element_by_id (document, attachment_part_id);
 921 
 922 		/* None found? Attachment cannot be expanded */
 923 		if (attachment == NULL) {
 924 			e_attachment_button_set_expandable (
 925 				E_ATTACHMENT_BUTTON (widget), FALSE);
 926 		} else {
 927 			const CamelContentDisposition *disposition;
 928 
 929 			e_attachment_button_set_expandable (
 930 				E_ATTACHMENT_BUTTON (widget), TRUE);
 931 
 932 			/* Show/hide the attachment when the EAttachmentButton
 933 			 * is expanded/collapsed or shown/hidden. */
 934 			g_signal_connect (
 935 				widget, "notify::expanded",
 936 				G_CALLBACK (attachment_button_expanded),
 937 				display);
 938 			g_signal_connect (
 939 				widget, "notify::visible",
 940 				G_CALLBACK (attachment_button_expanded),
 941 				display);
 942 
 943 			/* Automatically expand attachments that have inline
 944 			 * disposition or the EMailParts have specific
 945 			 * force_inline flag set. */
 946 			disposition =
 947 				camel_mime_part_get_content_disposition (part->part);
 948 			if (!part->force_collapse &&
 949 			    (part->force_inline ||
 950 			    (g_strcmp0 (empa->snoop_mime_type, "message/rfc822") == 0) ||
 951 			     (disposition && disposition->disposition &&
 952 				g_ascii_strncasecmp (
 953 					disposition->disposition, "inline", 6) == 0))) {
 954 
 955 				e_attachment_button_set_expanded (
 956 					E_ATTACHMENT_BUTTON (widget), TRUE);
 957 			} else {
 958 				e_attachment_button_set_expanded (
 959 					E_ATTACHMENT_BUTTON (widget), FALSE);
 960 				attachment_button_expanded (
 961 					G_OBJECT (widget), NULL, display);
 962 			}
 963 		}
 964 	}
 965 
 966 	g_hash_table_insert (
 967 		display->priv->widgets,
 968 		g_strdup (object_uri), g_object_ref (widget));
 969 
 970 	return widget;
 971 }
 972 
 973 static void
 974 toggle_headers_visibility (WebKitDOMElement *button,
 975                            WebKitDOMEvent *event,
 976                            WebKitWebView *web_view)
 977 {
 978 	WebKitDOMDocument *document;
 979 	WebKitDOMElement *short_headers, *full_headers;
 980 	WebKitDOMCSSStyleDeclaration *css_short, *css_full;
 981 	gboolean expanded;
 982 	const gchar *path;
 983 	gchar *css_value;
 984 
 985 	document = webkit_web_view_get_dom_document (web_view);
 986 
 987 	short_headers = webkit_dom_document_get_element_by_id (
 988 		document, "__evo-short-headers");
 989 	if (short_headers == NULL)
 990 		return;
 991 
 992 	css_short = webkit_dom_element_get_style (short_headers);
 993 
 994 	full_headers = webkit_dom_document_get_element_by_id (
 995 		document, "__evo-full-headers");
 996 	if (full_headers == NULL)
 997 		return;
 998 
 999 	css_full = webkit_dom_element_get_style (full_headers);
1000 	css_value = webkit_dom_css_style_declaration_get_property_value (
1001 		css_full, "display");
1002 	expanded = (g_strcmp0 (css_value, "block") == 0);
1003 	g_free (css_value);
1004 
1005 	webkit_dom_css_style_declaration_set_property (
1006 		css_full, "display",
1007 		expanded ? "none" : "block", "", NULL);
1008 	webkit_dom_css_style_declaration_set_property (
1009 		css_short, "display",
1010 		expanded ? "block" : "none", "", NULL);
1011 
1012 	if (expanded)
1013 		path = "evo-file://" EVOLUTION_IMAGESDIR "/plus.png";
1014 	else
1015 		path = "evo-file://" EVOLUTION_IMAGESDIR "/minus.png";
1016 
1017 	webkit_dom_html_image_element_set_src (
1018 		WEBKIT_DOM_HTML_IMAGE_ELEMENT (button), path);
1019 
1020 	e_mail_display_set_headers_collapsed (
1021 		E_MAIL_DISPLAY (web_view), expanded);
1022 
1023 	d (printf ("Headers %s!\n", expanded ? "collapsed" : "expanded"));
1024 }
1025 
1026 static const gchar * addresses[] = { "to", "cc", "bcc" };
1027 
1028 static void
1029 toggle_address_visibility (WebKitDOMElement *button,
1030                            WebKitDOMEvent *event,
1031                            const gchar *address)
1032 {
1033 	WebKitDOMElement *full_addr, *ellipsis;
1034 	WebKitDOMCSSStyleDeclaration *css_full, *css_ellipsis;
1035 	WebKitDOMDocument *document;
1036 	gchar *id;
1037 	const gchar *path;
1038 	gboolean expanded;
1039 
1040 	document = webkit_dom_node_get_owner_document (
1041 		WEBKIT_DOM_NODE (button));
1042 
1043 	id = g_strconcat ("__evo-moreaddr-", address, NULL);
1044 	full_addr = webkit_dom_document_get_element_by_id (document, id);
1045 	g_free (id);
1046 
1047 	if (full_addr == NULL)
1048 		return;
1049 
1050 	css_full = webkit_dom_element_get_style (full_addr);
1051 
1052 	id = g_strconcat ("__evo-moreaddr-ellipsis-", address, NULL);
1053 	ellipsis = webkit_dom_document_get_element_by_id (document, id);
1054 	g_free (id);
1055 
1056 	if (ellipsis == NULL)
1057 		return;
1058 
1059 	css_ellipsis = webkit_dom_element_get_style (ellipsis);
1060 
1061 	expanded = (g_strcmp0 (
1062 		webkit_dom_css_style_declaration_get_property_value (
1063 		css_full, "display"), "inline") == 0);
1064 
1065 	webkit_dom_css_style_declaration_set_property (
1066 		css_full, "display", (expanded ? "none" : "inline"), "", NULL);
1067 	webkit_dom_css_style_declaration_set_property (
1068 		css_ellipsis, "display", (expanded ? "inline" : "none"), "", NULL);
1069 
1070 	if (expanded)
1071 		path = "evo-file://" EVOLUTION_IMAGESDIR "/plus.png";
1072 	else
1073 		path = "evo-file://" EVOLUTION_IMAGESDIR "/minus.png";
1074 
1075 	if (!WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (button)) {
1076 		id = g_strconcat ("__evo-moreaddr-img-", address, NULL);
1077 		button = webkit_dom_document_get_element_by_id (document, id);
1078 		g_free (id);
1079 
1080 		if (button == NULL)
1081 			return;
1082 	}
1083 
1084 	webkit_dom_html_image_element_set_src (
1085 		WEBKIT_DOM_HTML_IMAGE_ELEMENT (button), path);
1086 
1087 }
1088 
1089 static void
1090 setup_DOM_bindings (GObject *object,
1091                     GParamSpec *pspec,
1092                     gpointer user_data)
1093 {
1094 	WebKitWebView *web_view;
1095 	WebKitWebFrame *frame;
1096 	WebKitLoadStatus load_status;
1097 	WebKitDOMDocument *document;
1098 	WebKitDOMElement *button;
1099 	gint ii = 0;
1100 
1101 	frame = WEBKIT_WEB_FRAME (object);
1102 	load_status = webkit_web_frame_get_load_status (frame);
1103 	if (load_status != WEBKIT_LOAD_FINISHED)
1104 		return;
1105 
1106 	web_view = webkit_web_frame_get_web_view (frame);
1107 	document = webkit_web_view_get_dom_document (web_view);
1108 
1109 	button = webkit_dom_document_get_element_by_id (
1110 		document, "__evo-collapse-headers-img");
1111 	if (button != NULL)
1112 		webkit_dom_event_target_add_event_listener (
1113 			WEBKIT_DOM_EVENT_TARGET (button), "click",
1114 			G_CALLBACK (toggle_headers_visibility),
1115 			FALSE, web_view);
1116 
1117 	for (ii = 0; ii < 3; ii++) {
1118 		gchar *id;
1119 
1120 		id = g_strconcat ("__evo-moreaddr-img-", addresses[ii], NULL);
1121 		button = webkit_dom_document_get_element_by_id (document, id);
1122 		g_free (id);
1123 
1124 		if (button == NULL)
1125 			continue;
1126 
1127 		webkit_dom_event_target_add_event_listener (
1128 			WEBKIT_DOM_EVENT_TARGET (button), "click",
1129 			G_CALLBACK (toggle_address_visibility), FALSE,
1130 			(gpointer) addresses[ii]);
1131 
1132 		id = g_strconcat (
1133 			"__evo-moreaddr-ellipsis-", addresses[ii], NULL);
1134 		button = webkit_dom_document_get_element_by_id (document, id);
1135 		g_free (id);
1136 
1137 		if (button == NULL)
1138 			continue;
1139 
1140 		webkit_dom_event_target_add_event_listener (
1141 			WEBKIT_DOM_EVENT_TARGET (button), "click",
1142 			G_CALLBACK (toggle_address_visibility), FALSE,
1143 			(gpointer) addresses[ii]);
1144 	}
1145 }
1146 
1147 static void
1148 mail_parts_bind_dom (GObject *object,
1149                      GParamSpec *pspec,
1150                      gpointer user_data)
1151 {
1152 	WebKitWebFrame *frame;
1153 	WebKitLoadStatus load_status;
1154 	WebKitWebView *web_view;
1155 	WebKitDOMDocument *document;
1156 	EMailDisplay *display;
1157 	GSList *iter;
1158 	const gchar *frame_name;
1159 
1160 	frame = WEBKIT_WEB_FRAME (object);
1161 	load_status = webkit_web_frame_get_load_status (frame);
1162 
1163 	if (load_status != WEBKIT_LOAD_FINISHED)
1164 		return;
1165 
1166 	web_view = webkit_web_frame_get_web_view (frame);
1167 	display = E_MAIL_DISPLAY (web_view);
1168 	if (display->priv->part_list == NULL)
1169 		return;
1170 
1171 	frame_name = webkit_web_frame_get_name (frame);
1172 	if (frame_name == NULL || *frame_name == '\0')
1173 		frame_name = ".message.headers";
1174 
1175 	for (iter = display->priv->part_list->list; iter; iter = iter->next) {
1176 
1177 		EMailPart *part = iter->data;
1178 
1179 		if (part == NULL)
1180 			continue;
1181 
1182 		if (g_strcmp0 (part->id, frame_name) == 0)
1183 			break;
1184 	}
1185 
1186 	document = webkit_web_view_get_dom_document (web_view);
1187 	while (iter != NULL) {
1188 		EMailPart *part = iter->data;
1189 
1190 		if (part == NULL) {
1191 			iter = iter->next;
1192 			continue;
1193 		}
1194 
1195 		/* Iterate only the parts rendered in
1196 		 * the frame and all it's subparts. */
1197 		if (!g_str_has_prefix (part->id, frame_name))
1198 			break;
1199 
1200 		if (part->bind_func != NULL) {
1201 			WebKitDOMElement *element;
1202 
1203 			element = find_element_by_id (document, part->id);
1204 			if (element != NULL) {
1205 				d (printf ("/*bind_func*/ for %s\n", part->id));
1206 				part->bind_func (part, element);
1207 			}
1208 		}
1209 
1210 		iter = iter->next;
1211 	}
1212 }
1213 
1214 static void
1215 mail_display_frame_created (WebKitWebView *web_view,
1216                             WebKitWebFrame *frame,
1217                             gpointer user_data)
1218 {
1219 	d (printf ("Frame %s created!\n", webkit_web_frame_get_name (frame)));
1220 
1221 	/* Call bind_func of all parts written in this frame */
1222 	g_signal_connect (
1223 		frame, "notify::load-status",
1224 		G_CALLBACK (mail_parts_bind_dom), NULL);
1225 }
1226 
1227 static void
1228 mail_display_uri_changed (EMailDisplay *display,
1229                           GParamSpec *pspec,
1230                           gpointer dummy)
1231 {
1232 	d (printf ("EMailDisplay URI changed, recreating widgets hashtable\n"));
1233 
1234 	if (display->priv->widgets != NULL) {
1235 		g_hash_table_foreach (
1236 			display->priv->widgets,
1237 			mail_display_plugin_widget_disconnect, display);
1238 		g_hash_table_destroy (display->priv->widgets);
1239 	}
1240 
1241 	display->priv->widgets = g_hash_table_new_full (
1242 		(GHashFunc) g_str_hash,
1243 		(GEqualFunc) g_str_equal,
1244 		(GDestroyNotify) g_free,
1245 		(GDestroyNotify) g_object_unref);
1246 }
1247 
1248 static void
1249 mail_display_set_property (GObject *object,
1250                            guint property_id,
1251                            const GValue *value,
1252                            GParamSpec *pspec)
1253 {
1254 	switch (property_id) {
1255 		case PROP_PART_LIST:
1256 			e_mail_display_set_parts_list (
1257 				E_MAIL_DISPLAY (object),
1258 				g_value_get_pointer (value));
1259 			return;
1260 		case PROP_MODE:
1261 			e_mail_display_set_mode (
1262 				E_MAIL_DISPLAY (object),
1263 				g_value_get_int (value));
1264 			return;
1265 		case PROP_HEADERS_COLLAPSABLE:
1266 			e_mail_display_set_headers_collapsable (
1267 				E_MAIL_DISPLAY (object),
1268 				g_value_get_boolean (value));
1269 			return;
1270 		case PROP_HEADERS_COLLAPSED:
1271 			e_mail_display_set_headers_collapsed (
1272 				E_MAIL_DISPLAY (object),
1273 				g_value_get_boolean (value));
1274 			return;
1275 	}
1276 
1277 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1278 }
1279 
1280 static void
1281 mail_display_get_property (GObject *object,
1282                            guint property_id,
1283                            GValue *value,
1284                            GParamSpec *pspec)
1285 {
1286 	switch (property_id) {
1287 		case PROP_FORMATTER:
1288 			g_value_set_object (
1289 				value, e_mail_display_get_formatter (
1290 				E_MAIL_DISPLAY (object)));
1291 			return;
1292 		case PROP_PART_LIST:
1293 			g_value_set_pointer (
1294 				value, e_mail_display_get_parts_list (
1295 				E_MAIL_DISPLAY (object)));
1296 			return;
1297 		case PROP_MODE:
1298 			g_value_set_int (
1299 				value, e_mail_display_get_mode (
1300 				E_MAIL_DISPLAY (object)));
1301 			return;
1302 		case PROP_HEADERS_COLLAPSABLE:
1303 			g_value_set_boolean (
1304 				value, e_mail_display_get_headers_collapsable (
1305 				E_MAIL_DISPLAY (object)));
1306 			return;
1307 		case PROP_HEADERS_COLLAPSED:
1308 			g_value_set_boolean (
1309 				value, e_mail_display_get_headers_collapsed (
1310 				E_MAIL_DISPLAY (object)));
1311 			return;
1312 	}
1313 
1314 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1315 }
1316 
1317 static void
1318 mail_display_dispose (GObject *object)
1319 {
1320 	EMailDisplayPrivate *priv;
1321 
1322 	priv = E_MAIL_DISPLAY_GET_PRIVATE (object);
1323 
1324 	if (priv->scheduled_reload > 0) {
1325 		g_source_remove (priv->scheduled_reload);
1326 		priv->scheduled_reload = 0;
1327 	}
1328 
1329 	if (priv->widgets != NULL) {
1330 		g_hash_table_foreach (
1331 			priv->widgets,
1332 			mail_display_plugin_widget_disconnect, object);
1333 		g_hash_table_destroy (priv->widgets);
1334 		priv->widgets = NULL;
1335 	}
1336 
1337 	if (priv->settings != NULL) {
1338 		g_signal_handlers_disconnect_matched (
1339 			priv->settings, G_SIGNAL_MATCH_DATA,
1340 			0, 0, NULL, NULL, object);
1341 		g_object_unref (priv->settings);
1342 		priv->settings = NULL;
1343 	}
1344 
1345 	g_clear_object (&priv->part_list);
1346 	g_clear_object (&priv->formatter);
1347 
1348 	/* Chain up to parent's dispose() method. */
1349 	G_OBJECT_CLASS (e_mail_display_parent_class)->dispose (object);
1350 }
1351 
1352 static void
1353 mail_display_constructed (GObject *object)
1354 {
1355 	e_extensible_load_extensions (E_EXTENSIBLE (object));
1356 
1357 	/* Chain up to parent's constructed() method. */
1358 	G_OBJECT_CLASS (e_mail_display_parent_class)->constructed (object);
1359 }
1360 
1361 static void
1362 mail_display_realize (GtkWidget *widget)
1363 {
1364 	/* Chain up to parent's realize() method. */
1365 	GTK_WIDGET_CLASS (e_mail_display_parent_class)->realize (widget);
1366 
1367 	mail_display_update_formatter_colors (E_MAIL_DISPLAY (widget));
1368 }
1369 
1370 static void
1371 mail_display_style_set (GtkWidget *widget,
1372                         GtkStyle *previous_style)
1373 {
1374 	EMailDisplay *display = E_MAIL_DISPLAY (widget);
1375 
1376 	mail_display_update_formatter_colors (display);
1377 
1378 	/* Chain up to parent's style_set() method. */
1379 	GTK_WIDGET_CLASS (e_mail_display_parent_class)->
1380 		style_set (widget, previous_style);
1381 }
1382 
1383 static gboolean
1384 mail_display_button_press_event (GtkWidget *widget,
1385                                  GdkEventButton *event)
1386 {
1387 	WebKitHitTestResult *hit_test;
1388 	WebKitHitTestResultContext context;
1389 	gchar *image_src;
1390 	gboolean visible;
1391 	GtkAction *action;
1392 	GList *extensions, *iter;
1393 	EWebView *web_view = E_WEB_VIEW (widget);
1394 
1395 	if (event->button != 3)
1396 		goto chainup;
1397 
1398 	hit_test = webkit_web_view_get_hit_test_result (
1399 		WEBKIT_WEB_VIEW (web_view), event);
1400 
1401 	g_object_get (
1402 		G_OBJECT (hit_test),
1403 		"context", &context,
1404 		"image-uri", &image_src,
1405 		NULL);
1406 
1407 	if ((context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE)) {
1408 		visible = image_src && g_str_has_prefix (image_src, "cid:");
1409 		if (!visible && image_src)
1410 			visible = mail_display_image_exists_in_cache (image_src);
1411 
1412 		if (image_src != NULL)
1413 			g_free (image_src);
1414 
1415 		action = e_web_view_get_action (web_view, "image-save");
1416 		if (action != NULL)
1417 			gtk_action_set_visible (action, visible);
1418 	}
1419 
1420 	extensions = e_extensible_list_extensions (
1421 		E_EXTENSIBLE (web_view), E_TYPE_EXTENSION);
1422 	for (iter = extensions; iter; iter = g_list_next (iter)) {
1423 		EExtension *extension = iter->data;
1424 
1425 		if (!E_IS_MAIL_DISPLAY_POPUP_EXTENSION (extension))
1426 			continue;
1427 
1428 		e_mail_display_popup_extension_update_actions (
1429 			E_MAIL_DISPLAY_POPUP_EXTENSION (extension), hit_test);
1430 	}
1431 	g_list_free (extensions);
1432 
1433 	g_object_unref (hit_test);
1434 
1435 chainup:
1436 	/* Chain up to parent's button_press_event() method. */
1437 	return GTK_WIDGET_CLASS (e_mail_display_parent_class)->
1438 		button_press_event (widget, event);
1439 }
1440 
1441 static void
1442 mail_display_set_fonts (EWebView *web_view,
1443                         PangoFontDescription **monospace,
1444                         PangoFontDescription **variable)
1445 {
1446 	EMailDisplay *display = E_MAIL_DISPLAY (web_view);
1447 	gboolean use_custom_font;
1448 	gchar *monospace_font;
1449 	gchar *variable_font;
1450 
1451 	use_custom_font = g_settings_get_boolean (
1452 		display->priv->settings, "use-custom-font");
1453 	if (!use_custom_font) {
1454 		*monospace = NULL;
1455 		*variable = NULL;
1456 		return;
1457 	}
1458 
1459 	monospace_font = g_settings_get_string (
1460 		display->priv->settings, "monospace-font");
1461 	variable_font = g_settings_get_string (
1462 		display->priv->settings, "variable-width-font");
1463 
1464 	*monospace = (monospace_font != NULL) ?
1465 		pango_font_description_from_string (monospace_font) : NULL;
1466 	*variable = (variable_font != NULL) ?
1467 		pango_font_description_from_string (variable_font) : NULL;
1468 
1469 	g_free (monospace_font);
1470 	g_free (variable_font);
1471 }
1472 
1473 static void
1474 e_mail_display_class_init (EMailDisplayClass *class)
1475 {
1476 	GObjectClass *object_class;
1477 	EWebViewClass *web_view_class;
1478 	GtkWidgetClass *widget_class;
1479 
1480 	g_type_class_add_private (class, sizeof (EMailDisplayPrivate));
1481 
1482 	object_class = G_OBJECT_CLASS (class);
1483 	object_class->constructed = mail_display_constructed;
1484 	object_class->set_property = mail_display_set_property;
1485 	object_class->get_property = mail_display_get_property;
1486 	object_class->dispose = mail_display_dispose;
1487 
1488 	widget_class = GTK_WIDGET_CLASS (class);
1489 	widget_class->realize = mail_display_realize;
1490 	widget_class->style_set = mail_display_style_set;
1491 	widget_class->button_press_event = mail_display_button_press_event;
1492 
1493 	web_view_class = E_WEB_VIEW_CLASS (class);
1494 	web_view_class->set_fonts = mail_display_set_fonts;
1495 
1496 	g_object_class_install_property (
1497 		object_class,
1498 		PROP_FORMATTER,
1499 		g_param_spec_pointer (
1500 			"formatter",
1501 			"Mail Formatter",
1502 			NULL,
1503 			G_PARAM_READABLE |
1504 			G_PARAM_STATIC_STRINGS));
1505 
1506 	g_object_class_install_property (
1507 		object_class,
1508 		PROP_PART_LIST,
1509 		g_param_spec_pointer (
1510 			"part-list",
1511 			"Part List",
1512 			NULL,
1513 			G_PARAM_READWRITE |
1514 			G_PARAM_STATIC_STRINGS));
1515 
1516 	g_object_class_install_property (
1517 		object_class,
1518 		PROP_MODE,
1519 		g_param_spec_int (
1520 			"mode",
1521 			"Display Mode",
1522 			NULL,
1523 			E_MAIL_FORMATTER_MODE_INVALID,
1524 			G_MAXINT,
1525 			E_MAIL_FORMATTER_MODE_NORMAL,
1526 			G_PARAM_READWRITE |
1527 			G_PARAM_STATIC_STRINGS));
1528 
1529 	g_object_class_install_property (
1530 		object_class,
1531 		PROP_HEADERS_COLLAPSABLE,
1532 		g_param_spec_boolean (
1533 			"headers-collapsable",
1534 			"Headers Collapsable",
1535 			NULL,
1536 			FALSE,
1537 			G_PARAM_READWRITE |
1538 			G_PARAM_STATIC_STRINGS));
1539 
1540 	g_object_class_install_property (
1541 		object_class,
1542 		PROP_HEADERS_COLLAPSED,
1543 		g_param_spec_boolean (
1544 			"headers-collapsed",
1545 			"Headers Collapsed",
1546 			NULL,
1547 			FALSE,
1548 			G_PARAM_READWRITE |
1549 			G_PARAM_STATIC_STRINGS));
1550 }
1551 
1552 static void
1553 e_mail_display_init (EMailDisplay *display)
1554 {
1555 	GtkUIManager *ui_manager;
1556 	const gchar *user_cache_dir;
1557 	WebKitWebSettings *settings;
1558 	WebKitWebFrame *main_frame;
1559 	GtkActionGroup *actions;
1560 
1561 	display->priv = E_MAIL_DISPLAY_GET_PRIVATE (display);
1562 
1563 	/* Set invalid mode so that MODE property initialization is run
1564 	 * completely (see e_mail_display_set_mode) */
1565 	display->priv->mode = E_MAIL_FORMATTER_MODE_INVALID;
1566 	e_mail_display_set_mode (display, E_MAIL_FORMATTER_MODE_NORMAL);
1567 	display->priv->force_image_load = FALSE;
1568 	display->priv->scheduled_reload = 0;
1569 
1570 	webkit_web_view_set_full_content_zoom (WEBKIT_WEB_VIEW (display), TRUE);
1571 
1572 	settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (display));
1573 	g_object_set (settings, "enable-frame-flattening", TRUE, NULL);
1574 
1575 	g_signal_connect (
1576 		display, "navigation-policy-decision-requested",
1577 		G_CALLBACK (mail_display_link_clicked), NULL);
1578 	g_signal_connect (
1579 		display, "resource-request-starting",
1580 		G_CALLBACK (mail_display_resource_requested), NULL);
1581 	g_signal_connect (
1582 		display, "process-mailto",
1583 		G_CALLBACK (mail_display_process_mailto), NULL);
1584 	g_signal_connect (
1585 		display, "create-plugin-widget",
1586 		G_CALLBACK (mail_display_plugin_widget_requested), NULL);
1587 	g_signal_connect (
1588 		display, "frame-created",
1589 		G_CALLBACK (mail_display_frame_created), NULL);
1590 	g_signal_connect (
1591 		display, "notify::uri",
1592 		G_CALLBACK (mail_display_uri_changed), NULL);
1593 
1594 	display->priv->settings = g_settings_new ("org.gnome.evolution.mail");
1595 	g_signal_connect_swapped (
1596 		display->priv->settings , "changed::monospace-font",
1597 		G_CALLBACK (e_web_view_update_fonts), display);
1598 	g_signal_connect_swapped (
1599 		display->priv->settings , "changed::variable-width-font",
1600 		G_CALLBACK (e_web_view_update_fonts), display);
1601 	g_signal_connect_swapped (
1602 		display->priv->settings , "changed::use-custom-font",
1603 		G_CALLBACK (e_web_view_update_fonts), display);
1604 
1605 	e_web_view_update_fonts (E_WEB_VIEW (display));
1606 
1607 	main_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (display));
1608 	g_signal_connect (
1609 		main_frame, "notify::load-status",
1610 		G_CALLBACK (setup_DOM_bindings), NULL);
1611 	g_signal_connect (
1612 		main_frame, "notify::load-status",
1613 		G_CALLBACK (mail_parts_bind_dom), NULL);
1614 
1615 	actions = e_web_view_get_action_group (E_WEB_VIEW (display), "mailto");
1616 	gtk_action_group_add_actions (
1617 		actions, mailto_entries,
1618 		G_N_ELEMENTS (mailto_entries), display);
1619 	ui_manager = e_web_view_get_ui_manager (E_WEB_VIEW (display));
1620 	gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, NULL);
1621 
1622 	actions = e_web_view_get_action_group (E_WEB_VIEW (display), "image");
1623 	gtk_action_group_add_actions (
1624 		actions, image_entries,
1625 		G_N_ELEMENTS (image_entries), display);
1626 	gtk_ui_manager_add_ui_from_string (ui_manager, image_ui, -1, NULL);
1627 
1628 	e_web_view_install_request_handler (
1629 		E_WEB_VIEW (display), E_TYPE_MAIL_REQUEST);
1630 	e_web_view_install_request_handler (
1631 		E_WEB_VIEW (display), E_TYPE_HTTP_REQUEST);
1632 	e_web_view_install_request_handler (
1633 		E_WEB_VIEW (display), E_TYPE_FILE_REQUEST);
1634 	e_web_view_install_request_handler (
1635 		E_WEB_VIEW (display), E_TYPE_STOCK_REQUEST);
1636 
1637 	if (emd_global_http_cache == NULL) {
1638 		user_cache_dir = e_get_user_cache_dir ();
1639 		emd_global_http_cache = camel_data_cache_new (user_cache_dir, NULL);
1640 
1641 		/* cache expiry - 2 hour access, 1 day max */
1642 		camel_data_cache_set_expire_age (
1643 			emd_global_http_cache, 24 * 60 * 60);
1644 		camel_data_cache_set_expire_access (
1645 			emd_global_http_cache, 2 * 60 * 60);
1646 	}
1647 }
1648 
1649 EMailFormatterMode
1650 e_mail_display_get_mode (EMailDisplay *display)
1651 {
1652 	g_return_val_if_fail (
1653 		E_IS_MAIL_DISPLAY (display),
1654 		E_MAIL_FORMATTER_MODE_NORMAL);
1655 
1656 	return display->priv->mode;
1657 }
1658 
1659 void
1660 e_mail_display_set_mode (EMailDisplay *display,
1661                          EMailFormatterMode mode)
1662 {
1663 	EMailFormatter *formatter;
1664 
1665 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1666 
1667 	if (display->priv->mode == mode)
1668 		return;
1669 
1670 	display->priv->mode = mode;
1671 
1672 	if (display->priv->mode == E_MAIL_FORMATTER_MODE_PRINTING)
1673 		formatter = e_mail_formatter_print_new ();
1674 	else
1675 		formatter = e_mail_formatter_new ();
1676 
1677 	g_clear_object (&display->priv->formatter);
1678 	display->priv->formatter = formatter;
1679 	mail_display_update_formatter_colors (display);
1680 
1681 	g_signal_connect (
1682 		formatter, "notify::image-loading-policy",
1683 		G_CALLBACK (formatter_image_loading_policy_changed_cb),
1684 		display);
1685 
1686 	g_object_connect (
1687 		formatter,
1688 		"swapped-object-signal::notify::charset",
1689 			G_CALLBACK (e_mail_display_reload), display,
1690 		"swapped-object-signal::notify::image-loading-policy",
1691 			G_CALLBACK (e_mail_display_reload), display,
1692 		"swapped-object-signal::notify::mark-citations",
1693 			G_CALLBACK (e_mail_display_reload), display,
1694 		"swapped-object-signal::notify::only-local-photos",
1695 			G_CALLBACK (e_mail_display_reload), display,
1696 		"swapped-object-signal::notify::show-sender-photo",
1697 			G_CALLBACK (e_mail_display_reload), display,
1698 		"swapped-object-signal::notify::show-real-date",
1699 			G_CALLBACK (e_mail_display_reload), display,
1700 		"swapped-object-signal::notify::animate-images",
1701 			G_CALLBACK (e_mail_display_reload), display,
1702 		"swapped-object-signal::notify::text-color",
1703 			G_CALLBACK (e_mail_display_reload), display,
1704 		"swapped-object-signal::notify::body-color",
1705 			G_CALLBACK (e_mail_display_reload), display,
1706 		"swapped-object-signal::notify::citation-color",
1707 			G_CALLBACK (e_mail_display_reload), display,
1708 		"swapped-object-signal::notify::content-color",
1709 			G_CALLBACK (e_mail_display_reload), display,
1710 		"swapped-object-signal::notify::frame-color",
1711 			G_CALLBACK (e_mail_display_reload), display,
1712 		"swapped-object-signal::notify::header-color",
1713 			G_CALLBACK (e_mail_display_reload), display,
1714 		"swapped-object-signal::need-redraw",
1715 			G_CALLBACK (e_mail_display_reload), display,
1716 		NULL);
1717 
1718 	e_mail_display_reload (display);
1719 
1720 	g_object_notify (G_OBJECT (display), "mode");
1721 }
1722 
1723 EMailFormatter *
1724 e_mail_display_get_formatter (EMailDisplay *display)
1725 {
1726 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
1727 
1728 	return display->priv->formatter;
1729 }
1730 
1731 EMailPartList *
1732 e_mail_display_get_parts_list (EMailDisplay *display)
1733 {
1734 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
1735 
1736 	return display->priv->part_list;
1737 }
1738 
1739 void
1740 e_mail_display_set_parts_list (EMailDisplay *display,
1741                                EMailPartList *part_list)
1742 {
1743 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1744 
1745 	if (display->priv->part_list == part_list)
1746 		return;
1747 
1748 	if (part_list != NULL) {
1749 		g_return_if_fail (E_IS_MAIL_PART_LIST (part_list));
1750 		g_object_ref (part_list);
1751 	}
1752 
1753 	if (display->priv->part_list != NULL)
1754 		g_object_unref (display->priv->part_list);
1755 
1756 	display->priv->part_list = part_list;
1757 
1758 	g_object_notify (G_OBJECT (display), "part-list");
1759 }
1760 
1761 gboolean
1762 e_mail_display_get_headers_collapsable (EMailDisplay *display)
1763 {
1764 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), FALSE);
1765 
1766 	return display->priv->headers_collapsable;
1767 }
1768 
1769 void
1770 e_mail_display_set_headers_collapsable (EMailDisplay *display,
1771                                         gboolean collapsable)
1772 {
1773 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1774 
1775 	if (display->priv->headers_collapsable == collapsable)
1776 		return;
1777 
1778 	display->priv->headers_collapsable = collapsable;
1779 	e_mail_display_reload (display);
1780 
1781 	g_object_notify (G_OBJECT (display), "headers-collapsable");
1782 }
1783 
1784 gboolean
1785 e_mail_display_get_headers_collapsed (EMailDisplay *display)
1786 {
1787 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), FALSE);
1788 
1789 	if (display->priv->headers_collapsable)
1790 		return display->priv->headers_collapsed;
1791 
1792 	return FALSE;
1793 }
1794 
1795 void
1796 e_mail_display_set_headers_collapsed (EMailDisplay *display,
1797                                       gboolean collapsed)
1798 {
1799 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1800 
1801 	if (display->priv->headers_collapsed == collapsed)
1802 		return;
1803 
1804 	display->priv->headers_collapsed = collapsed;
1805 
1806 	g_object_notify (G_OBJECT (display), "headers-collapsed");
1807 }
1808 
1809 void
1810 e_mail_display_load (EMailDisplay *display,
1811                      const gchar *msg_uri)
1812 {
1813 	EMailPartList *part_list;
1814 	const gchar *default_charset, *charset;
1815 	gchar *uri;
1816 
1817 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1818 
1819 	display->priv->force_image_load = FALSE;
1820 
1821 	part_list = display->priv->part_list;
1822 	if (part_list == NULL) {
1823 		e_web_view_clear (E_WEB_VIEW (display));
1824 		return;
1825 	}
1826 
1827 	default_charset = e_mail_formatter_get_default_charset (display->priv->formatter);
1828 	charset = e_mail_formatter_get_charset (display->priv->formatter);
1829 
1830 	if (!default_charset)
1831 		default_charset = "";
1832 	if (!charset)
1833 		charset = "";
1834 
1835 	uri = e_mail_part_build_uri (
1836 		part_list->folder, part_list->message_uid,
1837 		"mode", G_TYPE_INT, display->priv->mode,
1838 		"headers_collapsable", G_TYPE_BOOLEAN, display->priv->headers_collapsable,
1839 		"headers_collapsed", G_TYPE_BOOLEAN, display->priv->headers_collapsed,
1840 		"formatter_default_charset", G_TYPE_STRING, default_charset,
1841 		"formatter_charset", G_TYPE_STRING, charset,
1842 		NULL);
1843 
1844 	e_web_view_load_uri (E_WEB_VIEW (display), uri);
1845 
1846 	g_free (uri);
1847 }
1848 
1849 static gboolean
1850 do_reload_display (EMailDisplay *display)
1851 {
1852 	EWebView *web_view;
1853 	gchar *uri, *query;
1854 	GHashTable *table;
1855 	SoupURI *soup_uri;
1856 	gchar *mode, *collapsable, *collapsed;
1857 	const gchar *default_charset, *charset;
1858 
1859 	web_view = E_WEB_VIEW (display);
1860 	uri = (gchar *) e_web_view_get_uri (web_view);
1861 
1862 	display->priv->scheduled_reload = 0;
1863 
1864 	if (uri == NULL || *uri == '\0')
1865 		return FALSE;
1866 
1867 	if (strstr (uri, "?") == NULL) {
1868 		e_web_view_reload (web_view);
1869 		return FALSE;
1870 	}
1871 
1872 	soup_uri = soup_uri_new (uri);
1873 
1874 	mode = g_strdup_printf ("%d", display->priv->mode);
1875 	collapsable = g_strdup_printf ("%d", display->priv->headers_collapsable);
1876 	collapsed = g_strdup_printf ("%d", display->priv->headers_collapsed);
1877 	default_charset = e_mail_formatter_get_default_charset (display->priv->formatter);
1878 	charset = e_mail_formatter_get_charset (display->priv->formatter);
1879 
1880 	if (!default_charset)
1881 		default_charset = "";
1882 	if (!charset)
1883 		charset = "";
1884 
1885 	table = soup_form_decode (soup_uri->query);
1886 	g_hash_table_replace (
1887 		table, g_strdup ("mode"), mode);
1888 	g_hash_table_replace (
1889 		table, g_strdup ("headers_collapsable"), collapsable);
1890 	g_hash_table_replace (
1891 		table, g_strdup ("headers_collapsed"), collapsed);
1892 	g_hash_table_replace (
1893 		table, g_strdup ("formatter_default_charset"), g_strdup (default_charset));
1894 	g_hash_table_replace (
1895 		table, g_strdup ("formatter_charset"), g_strdup (charset));
1896 
1897 	query = soup_form_encode_hash (table);
1898 
1899 	/* The hash table does not free custom values upon destruction */
1900 	g_free (mode);
1901 	g_free (collapsable);
1902 	g_free (collapsed);
1903 	g_hash_table_destroy (table);
1904 
1905 	soup_uri_set_query (soup_uri, query);
1906 	g_free (query);
1907 
1908 	uri = soup_uri_to_string (soup_uri, FALSE);
1909 	e_web_view_load_uri (web_view, uri);
1910 	g_free (uri);
1911 	soup_uri_free (soup_uri);
1912 
1913 	return FALSE;
1914 }
1915 
1916 void
1917 e_mail_display_reload (EMailDisplay *display)
1918 {
1919 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1920 
1921 	if (display->priv->scheduled_reload > 0)
1922 		return;
1923 
1924 	/* Schedule reloading if neccessary.
1925 	 * Prioritize ahead of GTK+ redraws. */
1926 	display->priv->scheduled_reload = g_idle_add_full (
1927 		G_PRIORITY_HIGH_IDLE,
1928 		(GSourceFunc) do_reload_display, display, NULL);
1929 }
1930 
1931 GtkAction *
1932 e_mail_display_get_action (EMailDisplay *display,
1933                            const gchar *action_name)
1934 {
1935 	GtkAction *action;
1936 
1937 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
1938 	g_return_val_if_fail (action_name != NULL, NULL);
1939 
1940 	action = e_web_view_get_action (E_WEB_VIEW (display), action_name);
1941 
1942 	return action;
1943 }
1944 
1945 void
1946 e_mail_display_set_status (EMailDisplay *display,
1947                            const gchar *status)
1948 {
1949 	gchar *str;
1950 
1951 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
1952 
1953 	str = g_strdup_printf (
1954 		"<!DOCTYPE HTML>\n<html>\n"
1955 		"<head>\n<meta name=\"generator\" content=\"Evolution Mail Component\" />\n"
1956 		"<title>Evolution Mail Display</title>\n"
1957 		"</head>\n"
1958 		"<body bgcolor=\"#%06x\" text=\"#%06x\">"
1959 		"  <style>html, body { height: 100%%; }</style>\n"
1960 		"  <table border=\"0\" width=\"100%%\" height=\"100%%\">\n"
1961 		"    <tr height=\"100%%\" valign=\"middle\">\n"
1962 		"      <td width=\"100%%\" align=\"center\">\n"
1963 		"        <strong>%s</strong>\n"
1964 		"      </td>\n"
1965 		"    </tr>\n"
1966 		"  </table>\n"
1967 		"</body>\n"
1968 		"</html>\n",
1969 		e_color_to_value ((GdkColor *)
1970 			e_mail_formatter_get_color (
1971 				display->priv->formatter,
1972 				E_MAIL_FORMATTER_COLOR_CONTENT)),
1973 		e_color_to_value ((GdkColor *)
1974 			e_mail_formatter_get_color (
1975 				display->priv->formatter,
1976 				E_MAIL_FORMATTER_COLOR_TEXT)),
1977 		status);
1978 
1979 	e_web_view_load_string (E_WEB_VIEW (display), str);
1980 
1981 	g_free (str);
1982 }
1983 
1984 static gchar *
1985 mail_display_get_frame_selection_text (WebKitDOMElement *iframe)
1986 {
1987 	WebKitDOMDocument *document;
1988 	WebKitDOMDOMWindow *window;
1989 	WebKitDOMDOMSelection *selection;
1990 	WebKitDOMNodeList *frames;
1991 	gulong ii, length;
1992 
1993 	document = webkit_dom_html_iframe_element_get_content_document (
1994 			WEBKIT_DOM_HTML_IFRAME_ELEMENT (iframe));
1995 	window = webkit_dom_document_get_default_view (document);
1996 	selection = webkit_dom_dom_window_get_selection (window);
1997 	if (selection && (webkit_dom_dom_selection_get_range_count (selection) > 0)) {
1998 		WebKitDOMRange *range;
1999 
2000 		range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
2001 		if (range != NULL)
2002 			return webkit_dom_range_to_string (range, NULL);
2003 	}
2004 
2005 	frames = webkit_dom_document_get_elements_by_tag_name (
2006 		document, "IFRAME");
2007 	length = webkit_dom_node_list_get_length (frames);
2008 	for (ii = 0; ii < length; ii++) {
2009 		WebKitDOMNode *node;
2010 		gchar *text;
2011 
2012 		node = webkit_dom_node_list_item (frames, ii);
2013 
2014 		text = mail_display_get_frame_selection_text (
2015 			WEBKIT_DOM_ELEMENT (node));
2016 
2017 		if (text != NULL)
2018 			return text;
2019 	}
2020 
2021 	return NULL;
2022 }
2023 
2024 gchar *
2025 e_mail_display_get_selection_plain_text (EMailDisplay *display)
2026 {
2027 	WebKitDOMDocument *document;
2028 	WebKitDOMNodeList *frames;
2029 	gulong ii, length;
2030 
2031 	g_return_val_if_fail (E_IS_MAIL_DISPLAY (display), NULL);
2032 
2033 	if (!webkit_web_view_has_selection (WEBKIT_WEB_VIEW (display)))
2034 		return NULL;
2035 
2036 	document = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (display));
2037 	frames = webkit_dom_document_get_elements_by_tag_name (document, "IFRAME");
2038 	length = webkit_dom_node_list_get_length (frames);
2039 
2040 	for (ii = 0; ii < length; ii++) {
2041 		gchar *text;
2042 		WebKitDOMNode *node;
2043 
2044 		node = webkit_dom_node_list_item (frames, ii);
2045 
2046 		text = mail_display_get_frame_selection_text (
2047 			WEBKIT_DOM_ELEMENT (node));
2048 
2049 		if (text != NULL)
2050 			return text;
2051 	}
2052 
2053 	return NULL;
2054 }
2055 
2056 void
2057 e_mail_display_load_images (EMailDisplay *display)
2058 {
2059 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2060 
2061 	display->priv->force_image_load = TRUE;
2062 	e_web_view_reload (E_WEB_VIEW (display));
2063 }
2064 
2065 void
2066 e_mail_display_set_force_load_images (EMailDisplay *display,
2067                                       gboolean force_load_images)
2068 {
2069 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2070 
2071 	display->priv->force_image_load = force_load_images;
2072 }
2073 
2074 void
2075 e_mail_display_set_charset (EMailDisplay *display,
2076                             const gchar *charset)
2077 {
2078 	g_return_if_fail (E_IS_MAIL_DISPLAY (display));
2079 
2080 	if (display->priv->formatter != NULL)
2081 		e_mail_formatter_set_charset (
2082 			display->priv->formatter, charset);
2083 }