2017-01-01 12:37:08 +11:00
|
|
|
|
/*
|
|
|
|
|
|
* Copyright 2016 Michael Gratton <mike@vee.net>
|
|
|
|
|
|
*
|
|
|
|
|
|
* This software is licensed under the GNU Lesser General Public License
|
|
|
|
|
|
* (version 2.1 or later). See the COPYING file in this distribution.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2020-02-12 16:48:24 +11:00
|
|
|
|
class Composer.PageStateTest : ClientWebViewTestCase<Composer.WebView> {
|
2017-01-01 12:37:08 +11:00
|
|
|
|
|
2019-03-11 11:59:11 +11:00
|
|
|
|
public const string COMPLETE_BODY_TEMPLATE =
|
|
|
|
|
|
"""<div id="geary-body" dir="auto">%s<div><br></div><div><br></div></div><div id="geary-signature" dir="auto"></div>""";
|
2019-11-08 10:39:46 +11:00
|
|
|
|
public const string DIRTY_BODY_TEMPLATE =
|
|
|
|
|
|
"""
|
|
|
|
|
|
<div id="geary-body" dir="auto" class="geary-focus" contenteditable="true">%s<div><br></div><div><br></div></div>
|
|
|
|
|
|
<div id="geary-signature" class="geary-no-display" dir="auto" contenteditable="true"></div>
|
|
|
|
|
|
""";
|
|
|
|
|
|
public const string CLEAN_BODY_TEMPLATE = """<div id="geary-body" dir="auto">%s<div><br></div><div><br></div></div>""";
|
Split composer web view up into multiple parts.
This lets us implement changing signatures and deleting bottom-quoted
messages without having to reload the whole view, and makes it possible
to target only the user's content when modifying for send, etc.
* src/client/composer/composer-web-view.vala (ComposerWebView): Move
composer CSS into composer-web-view.css resource file, load it when
loading JS resource and add it to the view's user content manager.
(ComposerWebView::load_html): Split up body, signature and quote into a
DIV container for each.
(ComposerWebView::linkify_content): Replaced with ::clean_content,
which will also tidy up internal markup before sending. Update call
site and unit test.
* src/engine/rfc822/rfc822-utils.vala (Geary.RFC822.Utils): Remove some
more obtrusive white space when sending replies/forwards.
* test/client/composer/composer-web-view-test.vala,
test/js/composer-page-state-test.vala: Update tests to expect new HTML
and text output from ComposerWebView and use of individual parts for
composer markup.
* ui/composer-web-view.js (ComposerPageState): Replace messageBody
property and uses with bodyPart, signaturePart and quotePart. Set these
content-editable on load. Move listeners from messageBody back to the
document.body so they also listen for events on the additional
parts. Keep track of text cursor location within the parts and set a
class if so, to work around the lack of :focus-inside support.
(ComposerPageState::updateSignature): Implement by updating the inner
content of the signature part.
(ComposerPageState::deleteQuotedMessage): Implement by removing the
quote part from the DOM tree.
(ComposerPageState::containsAttachmentKeyword): Consider only the
bodyPart when scanning for attachments, remove hacks for ignoring the
signature any any quoted message.
(ComposerPageState::linkifyContent): Mirror ClientWebView change and
replace with ::cleanContent. Ensure existing parts have contenteditable
and focus class removed, remove signature and quote parts if empty.
(ComposerPageState::getHtml): Generate HTML using clones of the three
parts, so we can rmeove contenteditable and focus classes without
modifying the actual DOM.
(ComposerPageState::selectionChanged): Update focus class on parts as
needed.
2017-01-31 23:55:44 +11:00
|
|
|
|
|
2019-11-10 09:35:58 +11:00
|
|
|
|
public PageStateTest() {
|
|
|
|
|
|
base("Composer.PageStateTest");
|
2019-10-07 23:42:49 +11:00
|
|
|
|
add_test("html_to_text", html_to_text);
|
|
|
|
|
|
add_test("html_to_text_with_quote", html_to_text_with_quote);
|
|
|
|
|
|
add_test("html_to_text_with_nested_quote", html_to_text_with_nested_quote);
|
|
|
|
|
|
add_test("html_to_text_with_blacklist", html_to_text_with_blacklist);
|
2017-01-19 01:48:03 +11:00
|
|
|
|
add_test("edit_context_font", edit_context_font);
|
Replace composer link dialog with a popover.
* src/client/composer/composer-link-popover.vala: New GtkPopover subclass
for creating/editing links.
* src/client/composer/composer-web-view.vala (EditContext): Add is_link
and link_uri properties, decode them from the message string, add
decoding tests.
(ComposerWebView): Add some as-yet un-implemented methods for
inserting/deleting links.
* src/client/composer/composer-widget.vala (ComposerWidget): Add
cursor_url for storing current text cursor link, update it from the
cursor_context_changed signal param, rename hover_url to pointer_url to
match. Add link_activated signal to let user's open links they are
adding, hook that up in the controller. Rename
::update_selection_actions to ::update_cursor_actions, since that's a
little more apt now, also enable insert link action if there is a
cursor_url set as well as a selection. Remove ::link_dialog, replace
with ::new_link_popover, hook up the new popover's signals there as
appropriate.
(ComposerWidget::on_insert_link): Create and show a lin popover instead
of a dialog.
* ui/composer-web-view.js: Take note of whther the context node is a link
and if so, also it's href. Include both when serialsing for the
cursorContextChanged message. Add serialisation tests.
* ui/composer-link-popover.ui: New UI for link popover.
2017-01-19 02:23:57 +11:00
|
|
|
|
add_test("edit_context_link", edit_context_link);
|
2017-01-26 14:06:36 +11:00
|
|
|
|
add_test("indent_line", indent_line);
|
Split composer web view up into multiple parts.
This lets us implement changing signatures and deleting bottom-quoted
messages without having to reload the whole view, and makes it possible
to target only the user's content when modifying for send, etc.
* src/client/composer/composer-web-view.vala (ComposerWebView): Move
composer CSS into composer-web-view.css resource file, load it when
loading JS resource and add it to the view's user content manager.
(ComposerWebView::load_html): Split up body, signature and quote into a
DIV container for each.
(ComposerWebView::linkify_content): Replaced with ::clean_content,
which will also tidy up internal markup before sending. Update call
site and unit test.
* src/engine/rfc822/rfc822-utils.vala (Geary.RFC822.Utils): Remove some
more obtrusive white space when sending replies/forwards.
* test/client/composer/composer-web-view-test.vala,
test/js/composer-page-state-test.vala: Update tests to expect new HTML
and text output from ComposerWebView and use of individual parts for
composer markup.
* ui/composer-web-view.js (ComposerPageState): Replace messageBody
property and uses with bodyPart, signaturePart and quotePart. Set these
content-editable on load. Move listeners from messageBody back to the
document.body so they also listen for events on the additional
parts. Keep track of text cursor location within the parts and set a
class if so, to work around the lack of :focus-inside support.
(ComposerPageState::updateSignature): Implement by updating the inner
content of the signature part.
(ComposerPageState::deleteQuotedMessage): Implement by removing the
quote part from the DOM tree.
(ComposerPageState::containsAttachmentKeyword): Consider only the
bodyPart when scanning for attachments, remove hacks for ignoring the
signature any any quoted message.
(ComposerPageState::linkifyContent): Mirror ClientWebView change and
replace with ::cleanContent. Ensure existing parts have contenteditable
and focus class removed, remove signature and quote parts if empty.
(ComposerPageState::getHtml): Generate HTML using clones of the three
parts, so we can rmeove contenteditable and focus classes without
modifying the actual DOM.
(ComposerPageState::selectionChanged): Update focus class on parts as
needed.
2017-01-31 23:55:44 +11:00
|
|
|
|
add_test("clean_content", clean_content);
|
2017-01-01 12:37:08 +11:00
|
|
|
|
add_test("get_html", get_html);
|
|
|
|
|
|
add_test("get_text", get_text);
|
2017-01-26 13:31:08 +11:00
|
|
|
|
add_test("contains_keywords", contains_keywords);
|
2019-10-07 23:49:12 +11:00
|
|
|
|
// Depends contains_keywords and html_to_text_with_blacklist
|
|
|
|
|
|
add_test("contains_attachment_keywords", contains_attachment_keywords);
|
2017-01-02 10:23:35 +11:00
|
|
|
|
add_test("replace_non_breaking_space", replace_non_breaking_space);
|
2017-01-30 23:09:02 +11:00
|
|
|
|
|
|
|
|
|
|
try {
|
2019-11-10 09:35:58 +11:00
|
|
|
|
WebView.load_resources();
|
2017-01-30 23:09:02 +11:00
|
|
|
|
} catch (Error err) {
|
2020-05-09 16:04:22 +10:00
|
|
|
|
GLib.assert_not_reached();
|
2017-01-30 23:09:02 +11:00
|
|
|
|
}
|
2017-01-01 12:37:08 +11:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-10-07 23:42:49 +11:00
|
|
|
|
public void html_to_text() throws Error {
|
|
|
|
|
|
load_body_fixture("<p>para</p>");
|
|
|
|
|
|
try {
|
|
|
|
|
|
assert(
|
|
|
|
|
|
Util.JS.to_string(
|
|
|
|
|
|
run_javascript(
|
|
|
|
|
|
@"ComposerPageState.htmlToText(window.document.body);"
|
|
|
|
|
|
).get_js_value()
|
|
|
|
|
|
) == "para\n\n\n\n"
|
|
|
|
|
|
);
|
|
|
|
|
|
} catch (Util.JS.Error err) {
|
|
|
|
|
|
print("Util.JS.Error: %s\n", err.message);
|
|
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
} catch (Error err) {
|
|
|
|
|
|
print("WKError: %s\n", err.message);
|
|
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void html_to_text_with_quote() throws Error {
|
|
|
|
|
|
unichar q_marker = Geary.RFC822.Utils.QUOTE_MARKER;
|
|
|
|
|
|
load_body_fixture("<p>pre</p> <blockquote><p>quote</p></blockquote> <p>post</p>");
|
|
|
|
|
|
try {
|
|
|
|
|
|
assert(
|
|
|
|
|
|
Util.JS.to_string(
|
|
|
|
|
|
run_javascript(
|
|
|
|
|
|
"ComposerPageState.htmlToText(window.document.body);"
|
|
|
|
|
|
).get_js_value()
|
|
|
|
|
|
) == @"pre\n\n$(q_marker)quote\n$(q_marker)\npost\n\n\n\n"
|
|
|
|
|
|
);
|
|
|
|
|
|
} catch (Util.JS.Error err) {
|
|
|
|
|
|
print("Util.JS.Error: %s", err.message);
|
|
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
} catch (Error err) {
|
|
|
|
|
|
print("WKError: %s", err.message);
|
|
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void html_to_text_with_nested_quote() throws Error {
|
|
|
|
|
|
unichar q_marker = Geary.RFC822.Utils.QUOTE_MARKER;
|
|
|
|
|
|
load_body_fixture("<p>pre</p> <blockquote><p>quote1</p> <blockquote><p>quote2</p></blockquote></blockquote> <p>post</p>");
|
|
|
|
|
|
try {
|
|
|
|
|
|
assert(
|
|
|
|
|
|
Util.JS.to_string(
|
|
|
|
|
|
run_javascript(
|
|
|
|
|
|
"ComposerPageState.htmlToText(window.document.body)"
|
|
|
|
|
|
).get_js_value()
|
|
|
|
|
|
) == @"pre\n\n$(q_marker)quote1\n$(q_marker)\n$(q_marker)$(q_marker)quote2\n$(q_marker)$(q_marker)\npost\n\n\n\n"
|
|
|
|
|
|
);
|
|
|
|
|
|
} catch (Util.JS.Error err) {
|
|
|
|
|
|
print("Util.JS.Error: %s\n", err.message);
|
|
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
} catch (Error err) {
|
|
|
|
|
|
print("WKError: %s\n", err.message);
|
|
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void html_to_text_with_blacklist() throws Error {
|
|
|
|
|
|
load_body_fixture("<p>pre</p> <blockquote><p>quote1</p> <blockquote><p>quote2</p></blockquote></blockquote> <p>post</p>");
|
|
|
|
|
|
try {
|
|
|
|
|
|
assert(
|
|
|
|
|
|
Util.JS.to_string(
|
|
|
|
|
|
run_javascript(
|
|
|
|
|
|
"ComposerPageState.htmlToText(window.document.body, [\"blockquote\"])"
|
|
|
|
|
|
).get_js_value()
|
|
|
|
|
|
) == @"pre\n\npost\n\n\n\n"
|
|
|
|
|
|
);
|
|
|
|
|
|
} catch (Util.JS.Error err) {
|
|
|
|
|
|
print("Util.JS.Error: %s\n", err.message);
|
|
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
} catch (Error err) {
|
|
|
|
|
|
print("WKError: %s\n", err.message);
|
|
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-07 17:30:14 +11:00
|
|
|
|
public void edit_context_link() throws Error {
|
Replace composer link dialog with a popover.
* src/client/composer/composer-link-popover.vala: New GtkPopover subclass
for creating/editing links.
* src/client/composer/composer-web-view.vala (EditContext): Add is_link
and link_uri properties, decode them from the message string, add
decoding tests.
(ComposerWebView): Add some as-yet un-implemented methods for
inserting/deleting links.
* src/client/composer/composer-widget.vala (ComposerWidget): Add
cursor_url for storing current text cursor link, update it from the
cursor_context_changed signal param, rename hover_url to pointer_url to
match. Add link_activated signal to let user's open links they are
adding, hook that up in the controller. Rename
::update_selection_actions to ::update_cursor_actions, since that's a
little more apt now, also enable insert link action if there is a
cursor_url set as well as a selection. Remove ::link_dialog, replace
with ::new_link_popover, hook up the new popover's signals there as
appropriate.
(ComposerWidget::on_insert_link): Create and show a lin popover instead
of a dialog.
* ui/composer-web-view.js: Take note of whther the context node is a link
and if so, also it's href. Include both when serialsing for the
cursorContextChanged message. Add serialisation tests.
* ui/composer-link-popover.ui: New UI for link popover.
2017-01-19 02:23:57 +11:00
|
|
|
|
string html = "<a id=\"test\" href=\"url\">para</a>";
|
|
|
|
|
|
load_body_fixture(html);
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
2019-07-21 10:46:42 +10:00
|
|
|
|
assert(
|
|
|
|
|
|
Util.JS.to_string(
|
|
|
|
|
|
run_javascript(@"new EditContext(document.getElementById('test')).encode()")
|
|
|
|
|
|
.get_js_value()
|
2020-01-14 22:35:18 -06:00
|
|
|
|
).has_prefix("1;url;"));
|
2019-07-21 10:00:32 +10:00
|
|
|
|
} catch (Util.JS.Error err) {
|
|
|
|
|
|
print("Util.JS.Error: %s\n", err.message);
|
Replace composer link dialog with a popover.
* src/client/composer/composer-link-popover.vala: New GtkPopover subclass
for creating/editing links.
* src/client/composer/composer-web-view.vala (EditContext): Add is_link
and link_uri properties, decode them from the message string, add
decoding tests.
(ComposerWebView): Add some as-yet un-implemented methods for
inserting/deleting links.
* src/client/composer/composer-widget.vala (ComposerWidget): Add
cursor_url for storing current text cursor link, update it from the
cursor_context_changed signal param, rename hover_url to pointer_url to
match. Add link_activated signal to let user's open links they are
adding, hook that up in the controller. Rename
::update_selection_actions to ::update_cursor_actions, since that's a
little more apt now, also enable insert link action if there is a
cursor_url set as well as a selection. Remove ::link_dialog, replace
with ::new_link_popover, hook up the new popover's signals there as
appropriate.
(ComposerWidget::on_insert_link): Create and show a lin popover instead
of a dialog.
* ui/composer-web-view.js: Take note of whther the context node is a link
and if so, also it's href. Include both when serialsing for the
cursorContextChanged message. Add serialisation tests.
* ui/composer-link-popover.ui: New UI for link popover.
2017-01-19 02:23:57 +11:00
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
} catch (Error err) {
|
|
|
|
|
|
print("WKError: %s\n", err.message);
|
|
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-07 17:30:14 +11:00
|
|
|
|
public void edit_context_font() throws Error {
|
2020-01-14 22:35:18 -06:00
|
|
|
|
string html = "<p id=\"test\" style=\"font-family: Comic Sans; font-size: 144; color: #FF7F01\">para</p>";
|
2017-01-19 01:48:03 +11:00
|
|
|
|
load_body_fixture(html);
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
2019-07-21 10:46:42 +10:00
|
|
|
|
assert(
|
|
|
|
|
|
Util.JS.to_string(
|
|
|
|
|
|
run_javascript(@"new EditContext(document.getElementById('test')).encode()")
|
|
|
|
|
|
.get_js_value()
|
2020-01-14 22:35:18 -06:00
|
|
|
|
) == "0;;Comic Sans;144;rgb(255, 127, 1)");
|
2019-07-21 10:00:32 +10:00
|
|
|
|
} catch (Util.JS.Error err) {
|
|
|
|
|
|
print("Util.JS.Error: %s\n", err.message);
|
2017-01-19 01:48:03 +11:00
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
} catch (Error err) {
|
|
|
|
|
|
print("WKError: %s\n", err.message);
|
|
|
|
|
|
assert_not_reached();
|
2017-01-26 14:06:36 +11:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-07 17:30:14 +11:00
|
|
|
|
public void indent_line() throws Error {
|
2017-01-26 14:06:36 +11:00
|
|
|
|
load_body_fixture("""<span id="test">some text</span>""");
|
|
|
|
|
|
try {
|
|
|
|
|
|
run_javascript(@"SelectionUtil.selectNode(document.getElementById('test'))");
|
|
|
|
|
|
run_javascript(@"geary.indentLine()");
|
2019-07-21 10:46:42 +10:00
|
|
|
|
assert(
|
|
|
|
|
|
Util.JS.to_int32(
|
|
|
|
|
|
run_javascript(@"document.querySelectorAll('blockquote[type=cite]').length")
|
|
|
|
|
|
.get_js_value()
|
|
|
|
|
|
) == 1
|
|
|
|
|
|
);
|
|
|
|
|
|
assert(
|
|
|
|
|
|
Util.JS.to_string(
|
|
|
|
|
|
run_javascript(@"document.querySelectorAll('blockquote[type=cite]').item(0).innerText")
|
|
|
|
|
|
.get_js_value()
|
|
|
|
|
|
) == "some text"
|
|
|
|
|
|
);
|
2019-07-21 10:00:32 +10:00
|
|
|
|
} catch (Util.JS.Error err) {
|
|
|
|
|
|
print("Util.JS.Error: %s\n", err.message);
|
2017-01-26 14:06:36 +11:00
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
} catch (Error err) {
|
|
|
|
|
|
print("WKError: %s\n", err.message);
|
|
|
|
|
|
assert_not_reached();
|
2017-01-19 01:48:03 +11:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-07 17:30:14 +11:00
|
|
|
|
public void contains_attachment_keywords() throws Error {
|
Split composer web view up into multiple parts.
This lets us implement changing signatures and deleting bottom-quoted
messages without having to reload the whole view, and makes it possible
to target only the user's content when modifying for send, etc.
* src/client/composer/composer-web-view.vala (ComposerWebView): Move
composer CSS into composer-web-view.css resource file, load it when
loading JS resource and add it to the view's user content manager.
(ComposerWebView::load_html): Split up body, signature and quote into a
DIV container for each.
(ComposerWebView::linkify_content): Replaced with ::clean_content,
which will also tidy up internal markup before sending. Update call
site and unit test.
* src/engine/rfc822/rfc822-utils.vala (Geary.RFC822.Utils): Remove some
more obtrusive white space when sending replies/forwards.
* test/client/composer/composer-web-view-test.vala,
test/js/composer-page-state-test.vala: Update tests to expect new HTML
and text output from ComposerWebView and use of individual parts for
composer markup.
* ui/composer-web-view.js (ComposerPageState): Replace messageBody
property and uses with bodyPart, signaturePart and quotePart. Set these
content-editable on load. Move listeners from messageBody back to the
document.body so they also listen for events on the additional
parts. Keep track of text cursor location within the parts and set a
class if so, to work around the lack of :focus-inside support.
(ComposerPageState::updateSignature): Implement by updating the inner
content of the signature part.
(ComposerPageState::deleteQuotedMessage): Implement by removing the
quote part from the DOM tree.
(ComposerPageState::containsAttachmentKeyword): Consider only the
bodyPart when scanning for attachments, remove hacks for ignoring the
signature any any quoted message.
(ComposerPageState::linkifyContent): Mirror ClientWebView change and
replace with ::cleanContent. Ensure existing parts have contenteditable
and focus class removed, remove signature and quote parts if empty.
(ComposerPageState::getHtml): Generate HTML using clones of the three
parts, so we can rmeove contenteditable and focus classes without
modifying the actual DOM.
(ComposerPageState::selectionChanged): Update focus class on parts as
needed.
2017-01-31 23:55:44 +11:00
|
|
|
|
load_body_fixture_full("""
|
|
|
|
|
|
<blockquote>innerquote</blockquote>
|
2017-01-26 13:31:08 +11:00
|
|
|
|
|
|
|
|
|
|
<p>some text</p>
|
|
|
|
|
|
|
|
|
|
|
|
some text
|
Split composer web view up into multiple parts.
This lets us implement changing signatures and deleting bottom-quoted
messages without having to reload the whole view, and makes it possible
to target only the user's content when modifying for send, etc.
* src/client/composer/composer-web-view.vala (ComposerWebView): Move
composer CSS into composer-web-view.css resource file, load it when
loading JS resource and add it to the view's user content manager.
(ComposerWebView::load_html): Split up body, signature and quote into a
DIV container for each.
(ComposerWebView::linkify_content): Replaced with ::clean_content,
which will also tidy up internal markup before sending. Update call
site and unit test.
* src/engine/rfc822/rfc822-utils.vala (Geary.RFC822.Utils): Remove some
more obtrusive white space when sending replies/forwards.
* test/client/composer/composer-web-view-test.vala,
test/js/composer-page-state-test.vala: Update tests to expect new HTML
and text output from ComposerWebView and use of individual parts for
composer markup.
* ui/composer-web-view.js (ComposerPageState): Replace messageBody
property and uses with bodyPart, signaturePart and quotePart. Set these
content-editable on load. Move listeners from messageBody back to the
document.body so they also listen for events on the additional
parts. Keep track of text cursor location within the parts and set a
class if so, to work around the lack of :focus-inside support.
(ComposerPageState::updateSignature): Implement by updating the inner
content of the signature part.
(ComposerPageState::deleteQuotedMessage): Implement by removing the
quote part from the DOM tree.
(ComposerPageState::containsAttachmentKeyword): Consider only the
bodyPart when scanning for attachments, remove hacks for ignoring the
signature any any quoted message.
(ComposerPageState::linkifyContent): Mirror ClientWebView change and
replace with ::cleanContent. Ensure existing parts have contenteditable
and focus class removed, remove signature and quote parts if empty.
(ComposerPageState::getHtml): Generate HTML using clones of the three
parts, so we can rmeove contenteditable and focus classes without
modifying the actual DOM.
(ComposerPageState::selectionChanged): Update focus class on parts as
needed.
2017-01-31 23:55:44 +11:00
|
|
|
|
""",
|
|
|
|
|
|
"<p>outerquote text</p>",
|
|
|
|
|
|
true
|
|
|
|
|
|
);
|
2017-01-26 13:31:08 +11:00
|
|
|
|
try {
|
2019-07-21 10:46:42 +10:00
|
|
|
|
assert(
|
|
|
|
|
|
Util.JS.to_bool(
|
|
|
|
|
|
run_javascript(@"geary.containsAttachmentKeyword(\"some\", \"subject text\");")
|
|
|
|
|
|
.get_js_value()
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
|
|
|
|
|
assert(
|
|
|
|
|
|
Util.JS.to_bool(
|
|
|
|
|
|
run_javascript(@"geary.containsAttachmentKeyword(\"subject\", \"subject text\");")
|
|
|
|
|
|
.get_js_value()
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
|
|
|
|
|
assert(
|
|
|
|
|
|
!Util.JS.to_bool(
|
|
|
|
|
|
run_javascript(@"geary.containsAttachmentKeyword(\"innerquote\", \"subject text\");")
|
|
|
|
|
|
.get_js_value()
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
|
|
|
|
|
assert(
|
|
|
|
|
|
!Util.JS.to_bool(
|
|
|
|
|
|
run_javascript(@"geary.containsAttachmentKeyword(\"outerquote\", \"subject text\");")
|
|
|
|
|
|
.get_js_value()
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
2019-07-21 10:00:32 +10:00
|
|
|
|
} catch (Util.JS.Error err) {
|
|
|
|
|
|
print("Util.JS.Error: %s\n", err.message);
|
2017-01-26 16:31:03 +11:00
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
} catch (Error err) {
|
|
|
|
|
|
print("WKError: %s\n", err.message);
|
|
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-07 17:30:14 +11:00
|
|
|
|
public void clean_content() throws Error {
|
2017-01-26 16:31:03 +11:00
|
|
|
|
// XXX split these up into multiple tests
|
|
|
|
|
|
load_body_fixture("""
|
|
|
|
|
|
http://example1.com
|
|
|
|
|
|
|
|
|
|
|
|
<p>http://example2.com</p>
|
|
|
|
|
|
|
|
|
|
|
|
<p>http://example3.com http://example4.com</p>
|
|
|
|
|
|
|
|
|
|
|
|
<a href="blarg">http://example5.com</a>
|
|
|
|
|
|
|
|
|
|
|
|
unknown://example6.com
|
2019-10-22 18:53:18 -06:00
|
|
|
|
|
|
|
|
|
|
I can send email through smtp.gmail.com:587 or through https://www.gmail.com/
|
2017-01-26 16:31:03 +11:00
|
|
|
|
""");
|
|
|
|
|
|
|
|
|
|
|
|
string expected = """
|
|
|
|
|
|
<a href="http://example1.com">http://example1.com</a>
|
|
|
|
|
|
|
|
|
|
|
|
<p><a href="http://example2.com">http://example2.com</a></p>
|
|
|
|
|
|
|
|
|
|
|
|
<p><a href="http://example3.com">http://example3.com</a> <a href="http://example4.com">http://example4.com</a></p>
|
|
|
|
|
|
|
|
|
|
|
|
<a href="blarg">http://example5.com</a>
|
|
|
|
|
|
|
|
|
|
|
|
unknown://example6.com
|
2019-10-22 18:53:18 -06:00
|
|
|
|
|
|
|
|
|
|
I can send email through smtp.gmail.com:587 or through <a href="https://www.gmail.com/">https://www.gmail.com/</a>
|
Split composer web view up into multiple parts.
This lets us implement changing signatures and deleting bottom-quoted
messages without having to reload the whole view, and makes it possible
to target only the user's content when modifying for send, etc.
* src/client/composer/composer-web-view.vala (ComposerWebView): Move
composer CSS into composer-web-view.css resource file, load it when
loading JS resource and add it to the view's user content manager.
(ComposerWebView::load_html): Split up body, signature and quote into a
DIV container for each.
(ComposerWebView::linkify_content): Replaced with ::clean_content,
which will also tidy up internal markup before sending. Update call
site and unit test.
* src/engine/rfc822/rfc822-utils.vala (Geary.RFC822.Utils): Remove some
more obtrusive white space when sending replies/forwards.
* test/client/composer/composer-web-view-test.vala,
test/js/composer-page-state-test.vala: Update tests to expect new HTML
and text output from ComposerWebView and use of individual parts for
composer markup.
* ui/composer-web-view.js (ComposerPageState): Replace messageBody
property and uses with bodyPart, signaturePart and quotePart. Set these
content-editable on load. Move listeners from messageBody back to the
document.body so they also listen for events on the additional
parts. Keep track of text cursor location within the parts and set a
class if so, to work around the lack of :focus-inside support.
(ComposerPageState::updateSignature): Implement by updating the inner
content of the signature part.
(ComposerPageState::deleteQuotedMessage): Implement by removing the
quote part from the DOM tree.
(ComposerPageState::containsAttachmentKeyword): Consider only the
bodyPart when scanning for attachments, remove hacks for ignoring the
signature any any quoted message.
(ComposerPageState::linkifyContent): Mirror ClientWebView change and
replace with ::cleanContent. Ensure existing parts have contenteditable
and focus class removed, remove signature and quote parts if empty.
(ComposerPageState::getHtml): Generate HTML using clones of the three
parts, so we can rmeove contenteditable and focus classes without
modifying the actual DOM.
(ComposerPageState::selectionChanged): Update focus class on parts as
needed.
2017-01-31 23:55:44 +11:00
|
|
|
|
""";
|
2017-01-26 16:31:03 +11:00
|
|
|
|
|
|
|
|
|
|
try {
|
Split composer web view up into multiple parts.
This lets us implement changing signatures and deleting bottom-quoted
messages without having to reload the whole view, and makes it possible
to target only the user's content when modifying for send, etc.
* src/client/composer/composer-web-view.vala (ComposerWebView): Move
composer CSS into composer-web-view.css resource file, load it when
loading JS resource and add it to the view's user content manager.
(ComposerWebView::load_html): Split up body, signature and quote into a
DIV container for each.
(ComposerWebView::linkify_content): Replaced with ::clean_content,
which will also tidy up internal markup before sending. Update call
site and unit test.
* src/engine/rfc822/rfc822-utils.vala (Geary.RFC822.Utils): Remove some
more obtrusive white space when sending replies/forwards.
* test/client/composer/composer-web-view-test.vala,
test/js/composer-page-state-test.vala: Update tests to expect new HTML
and text output from ComposerWebView and use of individual parts for
composer markup.
* ui/composer-web-view.js (ComposerPageState): Replace messageBody
property and uses with bodyPart, signaturePart and quotePart. Set these
content-editable on load. Move listeners from messageBody back to the
document.body so they also listen for events on the additional
parts. Keep track of text cursor location within the parts and set a
class if so, to work around the lack of :focus-inside support.
(ComposerPageState::updateSignature): Implement by updating the inner
content of the signature part.
(ComposerPageState::deleteQuotedMessage): Implement by removing the
quote part from the DOM tree.
(ComposerPageState::containsAttachmentKeyword): Consider only the
bodyPart when scanning for attachments, remove hacks for ignoring the
signature any any quoted message.
(ComposerPageState::linkifyContent): Mirror ClientWebView change and
replace with ::cleanContent. Ensure existing parts have contenteditable
and focus class removed, remove signature and quote parts if empty.
(ComposerPageState::getHtml): Generate HTML using clones of the three
parts, so we can rmeove contenteditable and focus classes without
modifying the actual DOM.
(ComposerPageState::selectionChanged): Update focus class on parts as
needed.
2017-01-31 23:55:44 +11:00
|
|
|
|
run_javascript("geary.cleanContent();");
|
2019-11-08 10:39:46 +11:00
|
|
|
|
string result = Util.JS.to_string(
|
|
|
|
|
|
run_javascript("window.document.body.innerHTML;")
|
|
|
|
|
|
.get_js_value()
|
2019-07-21 10:46:42 +10:00
|
|
|
|
);
|
2019-11-08 10:39:46 +11:00
|
|
|
|
assert(result == DIRTY_BODY_TEMPLATE.printf(expected));
|
2019-07-21 10:00:32 +10:00
|
|
|
|
} catch (Util.JS.Error err) {
|
|
|
|
|
|
print("Util.JS.Error: %s\n", err.message);
|
2017-01-26 13:31:08 +11:00
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
} catch (Error err) {
|
|
|
|
|
|
print("WKError: %s\n", err.message);
|
|
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-07 17:30:14 +11:00
|
|
|
|
public void get_html() throws Error {
|
2017-01-01 12:37:08 +11:00
|
|
|
|
string html = "<p>para</p>";
|
|
|
|
|
|
load_body_fixture(html);
|
|
|
|
|
|
try {
|
2019-11-08 10:39:46 +11:00
|
|
|
|
string result = Util.JS.to_string(
|
|
|
|
|
|
run_javascript(@"window.geary.getHtml();").get_js_value()
|
2019-07-21 10:46:42 +10:00
|
|
|
|
);
|
2019-11-08 10:39:46 +11:00
|
|
|
|
assert(result == CLEAN_BODY_TEMPLATE.printf(html));
|
2019-07-21 10:00:32 +10:00
|
|
|
|
} catch (Util.JS.Error err) {
|
|
|
|
|
|
print("Util.JS.Error: %s\n", err.message);
|
2017-01-01 12:37:08 +11:00
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
} catch (Error err) {
|
2017-01-02 10:51:26 +11:00
|
|
|
|
print("WKError: %s\n", err.message);
|
2017-01-01 12:37:08 +11:00
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-07 17:30:14 +11:00
|
|
|
|
public void get_text() throws Error {
|
2017-01-01 12:37:08 +11:00
|
|
|
|
load_body_fixture("<p>para</p>");
|
|
|
|
|
|
try {
|
2019-07-21 10:46:42 +10:00
|
|
|
|
assert(
|
|
|
|
|
|
Util.JS.to_string(
|
|
|
|
|
|
run_javascript(@"window.geary.getText();")
|
|
|
|
|
|
.get_js_value()
|
|
|
|
|
|
) == "para\n\n\n\n"
|
|
|
|
|
|
);
|
2019-07-21 10:00:32 +10:00
|
|
|
|
} catch (Util.JS.Error err) {
|
|
|
|
|
|
print("Util.JS.Error: %s\n", err.message);
|
2017-01-01 12:37:08 +11:00
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
} catch (Error err) {
|
2017-01-02 10:51:26 +11:00
|
|
|
|
print("WKError: %s\n", err.message);
|
2017-01-01 12:37:08 +11:00
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-07 17:30:14 +11:00
|
|
|
|
public void contains_keywords() throws Error {
|
2017-01-26 13:31:08 +11:00
|
|
|
|
load_body_fixture();
|
|
|
|
|
|
string complete_keys = """new Set(["keyword1", "keyword2"])""";
|
|
|
|
|
|
string suffix_keys = """new Set(["sf1", "sf2"])""";
|
|
|
|
|
|
try {
|
|
|
|
|
|
// Doesn't contain
|
2019-07-21 10:46:42 +10:00
|
|
|
|
assert(!Util.JS.to_bool(run_javascript(
|
2017-01-26 13:31:08 +11:00
|
|
|
|
@"ComposerPageState.containsKeywords('notcontained', $complete_keys, $suffix_keys);"
|
2019-07-21 10:46:42 +10:00
|
|
|
|
).get_js_value()
|
|
|
|
|
|
));
|
|
|
|
|
|
assert(!Util.JS.to_bool(run_javascript(
|
2017-01-26 13:31:08 +11:00
|
|
|
|
@"ComposerPageState.containsKeywords('not contained', $complete_keys, $suffix_keys);"
|
2019-07-21 10:46:42 +10:00
|
|
|
|
).get_js_value()
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
assert(!Util.JS.to_bool(run_javascript(
|
2017-01-26 13:31:08 +11:00
|
|
|
|
@"ComposerPageState.containsKeywords('not\tcontained', $complete_keys, $suffix_keys);"
|
2019-07-21 10:46:42 +10:00
|
|
|
|
).get_js_value()
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
assert(!Util.JS.to_bool(run_javascript(
|
2017-01-26 13:31:08 +11:00
|
|
|
|
@"ComposerPageState.containsKeywords('http://www.keyword1.com', $complete_keys, $suffix_keys);"
|
2019-07-21 10:46:42 +10:00
|
|
|
|
).get_js_value()
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
assert(!Util.JS.to_bool(run_javascript(
|
2017-01-26 13:31:08 +11:00
|
|
|
|
@"ComposerPageState.containsKeywords('http://www.something.com/something.sf1', $complete_keys, $suffix_keys);"
|
2019-07-21 10:46:42 +10:00
|
|
|
|
).get_js_value()
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
assert(!Util.JS.to_bool(run_javascript(
|
2017-01-26 13:31:08 +11:00
|
|
|
|
@"ComposerPageState.containsKeywords('sf1', $complete_keys, $suffix_keys);"
|
2019-07-21 10:46:42 +10:00
|
|
|
|
).get_js_value()
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
assert(!Util.JS.to_bool(run_javascript(
|
2017-01-26 13:31:08 +11:00
|
|
|
|
@"ComposerPageState.containsKeywords('.sf1', $complete_keys, $suffix_keys);"
|
2019-07-21 10:46:42 +10:00
|
|
|
|
).get_js_value()
|
|
|
|
|
|
));
|
|
|
|
|
|
|
2017-01-26 13:31:08 +11:00
|
|
|
|
|
|
|
|
|
|
// Does contain
|
2019-07-21 10:46:42 +10:00
|
|
|
|
assert(Util.JS.to_bool(run_javascript(
|
2017-01-26 13:31:08 +11:00
|
|
|
|
@"ComposerPageState.containsKeywords('keyword1', $complete_keys, $suffix_keys);"
|
2019-07-21 10:46:42 +10:00
|
|
|
|
).get_js_value()
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
assert(Util.JS.to_bool(run_javascript(
|
2017-01-26 13:31:08 +11:00
|
|
|
|
@"ComposerPageState.containsKeywords('keyword2 contained', $complete_keys, $suffix_keys);"
|
2019-07-21 10:46:42 +10:00
|
|
|
|
).get_js_value()
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
assert(Util.JS.to_bool(run_javascript(
|
2017-01-26 13:31:08 +11:00
|
|
|
|
@"ComposerPageState.containsKeywords('keyword2\tcontained', $complete_keys, $suffix_keys);"
|
2019-07-21 10:46:42 +10:00
|
|
|
|
).get_js_value()
|
|
|
|
|
|
));
|
|
|
|
|
|
|
2019-10-07 23:46:33 +11:00
|
|
|
|
assert(Util.JS.to_bool(run_javascript(
|
|
|
|
|
|
@"ComposerPageState.containsKeywords('keyword1.', $complete_keys, $suffix_keys);"
|
|
|
|
|
|
).get_js_value()
|
|
|
|
|
|
));
|
|
|
|
|
|
|
2019-07-21 10:46:42 +10:00
|
|
|
|
assert(Util.JS.to_bool(run_javascript(
|
2017-01-26 13:31:08 +11:00
|
|
|
|
@"ComposerPageState.containsKeywords('something.sf1', $complete_keys, $suffix_keys);"
|
2019-07-21 10:46:42 +10:00
|
|
|
|
).get_js_value()
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
assert(Util.JS.to_bool(run_javascript(
|
2017-01-26 13:31:08 +11:00
|
|
|
|
@"ComposerPageState.containsKeywords('something.something.sf2', $complete_keys, $suffix_keys);"
|
2019-07-21 10:46:42 +10:00
|
|
|
|
).get_js_value()
|
|
|
|
|
|
));
|
2019-10-07 23:46:33 +11:00
|
|
|
|
|
|
|
|
|
|
assert(!Util.JS.to_bool(run_javascript(
|
|
|
|
|
|
@"ComposerPageState.containsKeywords('http://something/esle.sf2', $complete_keys, $suffix_keys);"
|
|
|
|
|
|
).get_js_value()
|
|
|
|
|
|
));
|
|
|
|
|
|
|
2019-07-21 10:00:32 +10:00
|
|
|
|
} catch (Util.JS.Error err) {
|
|
|
|
|
|
print("Util.JS.Error: %s\n", err.message);
|
2017-01-26 13:31:08 +11:00
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
} catch (Error err) {
|
|
|
|
|
|
print("WKError: %s\n", err.message);
|
2017-01-01 12:37:08 +11:00
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-07 17:30:14 +11:00
|
|
|
|
public void replace_non_breaking_space() throws Error {
|
2017-01-02 10:23:35 +11:00
|
|
|
|
load_body_fixture();
|
|
|
|
|
|
string single_nbsp = "a b";
|
|
|
|
|
|
string multiple_nbsp = "a b c";
|
|
|
|
|
|
try {
|
2019-07-21 10:46:42 +10:00
|
|
|
|
assert(
|
|
|
|
|
|
Util.JS.to_string(
|
|
|
|
|
|
run_javascript(@"ComposerPageState.replaceNonBreakingSpace('$(single_nbsp)');")
|
|
|
|
|
|
.get_js_value()
|
|
|
|
|
|
) == "a b");
|
|
|
|
|
|
assert(
|
|
|
|
|
|
Util.JS.to_string(
|
|
|
|
|
|
run_javascript(@"ComposerPageState.replaceNonBreakingSpace('$(multiple_nbsp)');")
|
|
|
|
|
|
.get_js_value()
|
|
|
|
|
|
) == "a b c");
|
2019-07-21 10:00:32 +10:00
|
|
|
|
} catch (Util.JS.Error err) {
|
|
|
|
|
|
print("Util.JS.Error: %s\n", err.message);
|
2017-01-02 10:23:35 +11:00
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
} catch (Error err) {
|
|
|
|
|
|
print("WKError: %s\n", err.message);
|
|
|
|
|
|
assert_not_reached();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-11-10 09:35:58 +11:00
|
|
|
|
protected override Composer.WebView set_up_test_view() {
|
|
|
|
|
|
return new Composer.WebView(this.config);
|
2017-01-02 10:23:35 +11:00
|
|
|
|
}
|
|
|
|
|
|
|
Split composer web view up into multiple parts.
This lets us implement changing signatures and deleting bottom-quoted
messages without having to reload the whole view, and makes it possible
to target only the user's content when modifying for send, etc.
* src/client/composer/composer-web-view.vala (ComposerWebView): Move
composer CSS into composer-web-view.css resource file, load it when
loading JS resource and add it to the view's user content manager.
(ComposerWebView::load_html): Split up body, signature and quote into a
DIV container for each.
(ComposerWebView::linkify_content): Replaced with ::clean_content,
which will also tidy up internal markup before sending. Update call
site and unit test.
* src/engine/rfc822/rfc822-utils.vala (Geary.RFC822.Utils): Remove some
more obtrusive white space when sending replies/forwards.
* test/client/composer/composer-web-view-test.vala,
test/js/composer-page-state-test.vala: Update tests to expect new HTML
and text output from ComposerWebView and use of individual parts for
composer markup.
* ui/composer-web-view.js (ComposerPageState): Replace messageBody
property and uses with bodyPart, signaturePart and quotePart. Set these
content-editable on load. Move listeners from messageBody back to the
document.body so they also listen for events on the additional
parts. Keep track of text cursor location within the parts and set a
class if so, to work around the lack of :focus-inside support.
(ComposerPageState::updateSignature): Implement by updating the inner
content of the signature part.
(ComposerPageState::deleteQuotedMessage): Implement by removing the
quote part from the DOM tree.
(ComposerPageState::containsAttachmentKeyword): Consider only the
bodyPart when scanning for attachments, remove hacks for ignoring the
signature any any quoted message.
(ComposerPageState::linkifyContent): Mirror ClientWebView change and
replace with ::cleanContent. Ensure existing parts have contenteditable
and focus class removed, remove signature and quote parts if empty.
(ComposerPageState::getHtml): Generate HTML using clones of the three
parts, so we can rmeove contenteditable and focus classes without
modifying the actual DOM.
(ComposerPageState::selectionChanged): Update focus class on parts as
needed.
2017-01-31 23:55:44 +11:00
|
|
|
|
protected override void load_body_fixture(string body = "") {
|
2019-03-11 11:59:11 +11:00
|
|
|
|
load_body_fixture_full(body, "", true);
|
Split composer web view up into multiple parts.
This lets us implement changing signatures and deleting bottom-quoted
messages without having to reload the whole view, and makes it possible
to target only the user's content when modifying for send, etc.
* src/client/composer/composer-web-view.vala (ComposerWebView): Move
composer CSS into composer-web-view.css resource file, load it when
loading JS resource and add it to the view's user content manager.
(ComposerWebView::load_html): Split up body, signature and quote into a
DIV container for each.
(ComposerWebView::linkify_content): Replaced with ::clean_content,
which will also tidy up internal markup before sending. Update call
site and unit test.
* src/engine/rfc822/rfc822-utils.vala (Geary.RFC822.Utils): Remove some
more obtrusive white space when sending replies/forwards.
* test/client/composer/composer-web-view-test.vala,
test/js/composer-page-state-test.vala: Update tests to expect new HTML
and text output from ComposerWebView and use of individual parts for
composer markup.
* ui/composer-web-view.js (ComposerPageState): Replace messageBody
property and uses with bodyPart, signaturePart and quotePart. Set these
content-editable on load. Move listeners from messageBody back to the
document.body so they also listen for events on the additional
parts. Keep track of text cursor location within the parts and set a
class if so, to work around the lack of :focus-inside support.
(ComposerPageState::updateSignature): Implement by updating the inner
content of the signature part.
(ComposerPageState::deleteQuotedMessage): Implement by removing the
quote part from the DOM tree.
(ComposerPageState::containsAttachmentKeyword): Consider only the
bodyPart when scanning for attachments, remove hacks for ignoring the
signature any any quoted message.
(ComposerPageState::linkifyContent): Mirror ClientWebView change and
replace with ::cleanContent. Ensure existing parts have contenteditable
and focus class removed, remove signature and quote parts if empty.
(ComposerPageState::getHtml): Generate HTML using clones of the three
parts, so we can rmeove contenteditable and focus classes without
modifying the actual DOM.
(ComposerPageState::selectionChanged): Update focus class on parts as
needed.
2017-01-31 23:55:44 +11:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected void load_body_fixture_full(string body,
|
|
|
|
|
|
string quote,
|
|
|
|
|
|
bool top_posting) {
|
2019-03-11 11:59:11 +11:00
|
|
|
|
this.test_view.load_html(body, quote, top_posting, false);
|
2017-01-01 12:37:08 +11:00
|
|
|
|
while (this.test_view.is_loading) {
|
|
|
|
|
|
Gtk.main_iteration();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|