diff --git a/src/engine/rfc822/rfc822-gmime-filter-blockquotes.vala b/src/engine/rfc822/rfc822-gmime-filter-blockquotes.vala index 8cf7563d..d2a941c0 100644 --- a/src/engine/rfc822/rfc822-gmime-filter-blockquotes.vala +++ b/src/engine/rfc822/rfc822-gmime-filter-blockquotes.vala @@ -59,7 +59,7 @@ private class Geary.RFC822.FilterBlockquotes : GMime.Filter { if (!initial_element) { // We set the style explicitly so it will be set in HTML emails. We also give it a // class so users can customize the style in the viewer. - insert_string("
", ref out_index); + insert_string("
", ref out_index); initial_element = true; } diff --git a/test/engine/rfc822/rfc822-message-test.vala b/test/engine/rfc822/rfc822-message-test.vala index b7697dda..57976cca 100644 --- a/test/engine/rfc822/rfc822-message-test.vala +++ b/test/engine/rfc822/rfc822-message-test.vala @@ -15,7 +15,7 @@ class Geary.RFC822.MessageTest : TestCase { private const string BASIC_MULTIPART_TNEF = "basic-multipart-tnef.eml"; private const string HTML_CONVERSION_TEMPLATE = - "
%s
"; + "
%s
"; private const string BASIC_PLAIN_BODY = """This is the first line. diff --git a/ui/components-web-view.js b/ui/components-web-view.js index d0998a67..9b026dcd 100644 --- a/ui/components-web-view.js +++ b/ui/components-web-view.js @@ -26,77 +26,31 @@ PageState.prototype = { this._commandStackChanged = MessageSender("command_stack_changed"); this._documentModified = MessageSender("document_modified"); - let state = this; - // Set up an observer to keep track of modifications made to // the document when editing. let modifiedId = null; - this.bodyObserver = new MutationObserver(function(records) { + this.bodyObserver = new MutationObserver((records) => { if (modifiedId == null) { - modifiedId = window.setTimeout(function() { - state.documentModified(); - state.checkCommandStack(); + modifiedId = window.setTimeout(() => { + this.documentModified(); + this.checkCommandStack(); modifiedId = null; }, 1000); } }); - document.addEventListener("DOMContentLoaded", function(e) { - state.loaded(); + this.heightObserver = new ResizeObserver((entries) => { + this.updatePreferredHeight(); }); - document.addEventListener("selectionchange", function(e) { - state.selectionChanged(); + document.addEventListener("DOMContentLoaded", (e) => { + this.heightObserver.observe(window.document.documentElement); + this.loaded(); }); - // Coalesce multiple calls to updatePreferredHeight using a - // timeout to avoid the overhead of multiple JS messages sent - // to the app and hence view multiple resizes being queued. - let queueTimeout = null; - let queuePreferredHeightUpdate = function() { - if (queueTimeout != null) { - clearTimeout(queueTimeout); - } - queueTimeout = setTimeout( - function() { state.updatePreferredHeight(); }, 100 - ); - }; - - // Queues an update when the complete document is loaded. - // - // Note also that the delay introduced here by this last call - // to queuePreferredHeightUpdate when the complete document is - // loaded seems to be important to get an accurate idea of the - // final document size. - window.addEventListener("load", function(e) { - queuePreferredHeightUpdate(); - }, true); // load does not bubble - - // Queues updates for any STYLE, IMG and other loaded - // elements, hence handles resizing when the user later - // requests remote images loading. - document.addEventListener("load", function(e) { - queuePreferredHeightUpdate(); - }, true); // load does not bubble - - // Queues an update if the window changes size, e.g. if the - // user resized the window. Only trigger when the width has - // changed however since the height should only change as the - // body is being loaded. - let width = window.innerWidth; - window.addEventListener("resize", function(e) { - let currentWidth = window.innerWidth; - if (width != currentWidth) { - width = currentWidth; - queuePreferredHeightUpdate(); - } - }, false); // load does not bubble - - // Queues an update when a transition has completed, e.g. if the - // user resized the window - window.addEventListener("transitionend", function(e) { - queuePreferredHeightUpdate(); - }, false); // load does not bubble + document.addEventListener("selectionchange", (e) => { + this.selectionChanged(); + }); this.testResult = null; }, diff --git a/ui/composer-web-view.css b/ui/composer-web-view.css index 07ae6869..462d5876 100644 --- a/ui/composer-web-view.css +++ b/ui/composer-web-view.css @@ -60,6 +60,6 @@ blockquote { } pre { - white-space: pre-wrap; + white-space: break-spaces; margin: 0; } diff --git a/ui/composer-web-view.js b/ui/composer-web-view.js index 5ee4105e..bd010b6c 100644 --- a/ui/composer-web-view.js +++ b/ui/composer-web-view.js @@ -264,7 +264,7 @@ ComposerPageState.prototype = { }, tabOut: function() { document.execCommand( - "inserthtml", false, "\t" + "inserthtml", false, "\t" ); }, tabIn: function() { diff --git a/ui/conversation-web-view.css b/ui/conversation-web-view.css index d4786b4a..d1da2eae 100644 --- a/ui/conversation-web-view.css +++ b/ui/conversation-web-view.css @@ -1,5 +1,8 @@ /** * Style that is inserted into the message after it is loaded. + * + * Copyright © 2016 Software Freedom Conservancy Inc. + * Copyright © 2020 Michael Gratton */ /* @@ -14,17 +17,24 @@ html { color: black; background-color: white; - /* Trigger CSS 2.1 § 10.6.7 to get a shrink-wrapped height. */ - position: absolute !important; - top: 0 !important; - bottom: auto !important; - height: auto !important; + /* Width must always be defined by the viewport so content doesn't + overflow inside the WebView, height must always be defined by the + content so the WebView can be sized to fit exactly. */ + width: 100vw !important; + height: max-content !important; - /* Fix up the width after going to absolute positioning above. */ - width: 100%; + /* Despite the fact that the width must always be defined by the + viewport, the viewport width will be 0 if the email is loaded before + its WebView is laid out in the widget hierarchy. As a workaround, to + prevent this causing the email being squished down to is minimum + width and hence being stretched right out in height, set a + reasonable minimum width. See + https://gitlab.gnome.org/GNOME/geary/-/issues/283 */ + min-width: 400px !important; - /* Lock down the box just enough so we don't get an incrementally - expanding web view */ + /* Lock down the box sizing just enough so that the width and height + constraints above work as expected, and so the element's + scrollHeight is accurate. */ box-sizing: border-box !important; margin: 0 !important; border-width: 0 !important; @@ -67,7 +77,7 @@ blockquote { } pre { - white-space: pre-wrap; + white-space: break-spaces; } /** diff --git a/ui/conversation-web-view.js b/ui/conversation-web-view.js index 1d730d47..7b3a1c89 100644 --- a/ui/conversation-web-view.js +++ b/ui/conversation-web-view.js @@ -213,7 +213,7 @@ ConversationPageState.prototype = { if (ConversationPageState.isDescendantOf( ancestor, "DIV", "plaintext", false)) { dummy.classList.add("plaintext"); - dummy.setAttribute("style", "white-space: pre-wrap;"); + dummy.setAttribute("style", "white-space: break-spaces;"); includeDummy = true; } dummy.appendChild(range.cloneContents());