diff --git a/THANKS b/THANKS index 3115a5b8aa35ac61a256a206ff480a57531a8fc8..b5a2d94c21126d3b94d5c87f531619142210128d 100644 --- a/THANKS +++ b/THANKS @@ -2,6 +2,7 @@ The Geary team would like to thank the following contributors: Robert Ancell Jürg Billeter +Andrea Corbellini Sergey Shnatsel Davidoff Joanmarie Diggs Christian Dywan diff --git a/src/client/util/util-webkit.vala b/src/client/util/util-webkit.vala index a50056fb609fa36c9632c20c22a6f060b3c23534..d73dfd2f501348efadebff9513486a07e8301451 100644 --- a/src/client/util/util-webkit.vala +++ b/src/client/util/util-webkit.vala @@ -192,19 +192,6 @@ public WebKit.DOM.HTMLElement? closest_ancestor(WebKit.DOM.Element element, stri } } -public bool is_image(string? uri) { - if (uri == null) - return false; - - try { - Regex regex = new Regex("(?:jpe?g|gif|png)$", RegexCompileFlags.CASELESS); - return regex.match(uri); - } catch (RegexError err) { - debug("Error creating image-matching regex: %s", err.message); - return false; - } -} - public string decorate_quotes(string text) throws Error { int level = 0; string outtext = ""; diff --git a/src/client/views/conversation-viewer.vala b/src/client/views/conversation-viewer.vala index 942657de84b43a747b28c5e3cd3f79aa85512fd6..c677bbc779163952d356a33a35f1284bed91e5a8 100644 --- a/src/client/views/conversation-viewer.vala +++ b/src/client/views/conversation-viewer.vala @@ -110,7 +110,7 @@ public class ConversationViewer : Gtk.Box { } private void on_external_images_info_bar_response(Gtk.InfoBar sender, int response_id) { - web_view.set_load_external_images(response_id == Gtk.ResponseType.OK); + web_view.apply_load_external_images(response_id == Gtk.ResponseType.OK); sender.hide(); } @@ -162,7 +162,7 @@ public class ConversationViewer : Gtk.Box { } public void add_message(Geary.Email email) { - web_view.set_load_external_images(false); + web_view.apply_load_external_images(false); // Make sure the message container is showing and the multi-message counter hidden. try { @@ -871,25 +871,39 @@ public class ConversationViewer : Gtk.Box { // Now look for the signature. wrap_html_signature(ref container); - // Then get all inline images and replace them with data URLs. - WebKit.DOM.NodeList inline_list = container.query_selector_all("img[src^=\"cid:\"]"); - for (int i = 0; i < inline_list.length; ++i) { + // Then look for all tags. Inline images are replaced with + // data URLs, while external images are added to + // external_images_uri (to be used later by is_image()). + Gee.ArrayList external_images_uri = new Gee.ArrayList(); + WebKit.DOM.NodeList inline_list = container.query_selector_all("img"); + for (ulong i = 0; i < inline_list.length; ++i) { // Get the MIME content for the image. WebKit.DOM.HTMLImageElement img = (WebKit.DOM.HTMLImageElement) inline_list.item(i); - string mime_id = img.get_attribute("src").substring(4); - Geary.Memory.AbstractBuffer image_content = - email.get_message().get_content_by_mime_id(mime_id); - uint8[] image_data = image_content.get_array(); - - // Get the content type. - bool uncertain_content_type; - string mimetype = ContentType.get_mime_type(ContentType.guess(null, image_data, - out uncertain_content_type)); - - // Then set the source to a data url. - web_view.set_data_url(img, mimetype, image_data); + string? src = img.get_attribute("src"); + if (Geary.String.is_empty(src)) { + continue; + } else if (src.has_prefix("cid:")) { + string mime_id = src.substring(4); + Geary.Memory.AbstractBuffer image_content = + email.get_message().get_content_by_mime_id(mime_id); + uint8[] image_data = image_content.get_array(); + + // Get the content type. + bool uncertain_content_type; + string mimetype = ContentType.get_mime_type(ContentType.guess(null, image_data, + out uncertain_content_type)); + + // Then set the source to a data url. + web_view.set_data_url(img, mimetype, image_data); + } else if (!src.has_prefix("data:")) { + external_images_uri.add(src); + if (!web_view.load_external_images) + external_images_info_bar.show(); + } } + web_view.set_external_images_uris(external_images_uri); + // Now return the whole message. return set_up_quotes(container.get_inner_html()); } catch (Error e) { diff --git a/src/client/views/conversation-web-view.vala b/src/client/views/conversation-web-view.vala index 19a785915d325802dcfc9413b8cbebc1f73d3138..1e2ddcf2e55ac4d149472c691fe8c761820ebfc9 100644 --- a/src/client/views/conversation-web-view.vala +++ b/src/client/views/conversation-web-view.vala @@ -12,16 +12,19 @@ public class ConversationWebView : WebKit.WebView { private const string USER_CSS = "user-message.css"; private const string STYLE_NAME = "STYLE"; - - public signal void image_load_requested(); - public signal void link_selected(string link); - - private bool load_external_images = false; - private FileMonitor? user_style_monitor = null; - + + public bool load_external_images { get; private set; default = false; } + // HTML element that contains message DIVs. public WebKit.DOM.HTMLDivElement? container { get; private set; default = null; } - + + private Gee.ArrayList? external_images_uri = null; + private FileMonitor? user_style_monitor = null; + + public signal void image_load_requested(); + + public signal void link_selected(string link); + public ConversationWebView() { // Set defaults. set_border_width(0); @@ -79,15 +82,31 @@ public class ConversationWebView : WebKit.WebView { private void on_resource_request_starting(WebKit.WebFrame web_frame, WebKit.WebResource web_resource, WebKit.NetworkRequest request, WebKit.NetworkResponse? response) { - + if (response != null) { + // A request that was previously approved resulted in a redirect. + return; + } + string? uri = request.get_uri(); - bool uri_is_image = is_image(uri); - if (uri_is_image && !load_external_images) - image_load_requested(); - if (!is_always_loaded(uri) && !(uri_is_image && load_external_images)) + if (!is_always_loaded(uri) && !(is_image(uri) && load_external_images)) request.set_uri("about:blank"); } - + + public void set_external_images_uris(Gee.ArrayList uris) { + external_images_uri = uris; + } + + public bool is_image(string? uri) { + if (Geary.String.is_empty_or_whitespace(uri)) + return false; + + if (uri.has_prefix("data:image/")) + return true; + + // check if external_images_uri is null in case this is called before a page is loaded + return (external_images_uri != null) ? (uri in external_images_uri) : false; + } + private bool is_always_loaded(string? uri) { if (uri == null) return false; @@ -100,7 +119,7 @@ public class ConversationWebView : WebKit.WebView { return false; } - public void set_load_external_images(bool load_external_images) { + public void apply_load_external_images(bool load_external_images) { this.load_external_images = load_external_images; // Refreshing the images would do nothing in this case--the resource has already been