[geary/bug/728002-webkit2: 115/140] Reenable converting plain text URLs to links in HTML documents.
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/bug/728002-webkit2: 115/140] Reenable converting plain text URLs to links in HTML documents.
- Date: Tue, 31 Jan 2017 23:08:13 +0000 (UTC)
commit 848558f368fb31e0ca50127b220671b9ec59075f
Author: Michael James Gratton <mike vee net>
Date: Thu Jan 26 16:31:03 2017 +1100
Reenable converting plain text URLs to links in HTML documents.
* src/client/composer/composer-web-view.vala (ComposerWebView): Rename
::linkify_document since it really only applies to editor content. Make
an asyc method so we can wait until its finished. Update call
sites. Thunk call to JS.
* src/client/web-process/util-composer.vala,
src/client/web-process/util-webkit.vala: Remove unused code.
* ui/composer-web-view.js (ComposerPageState): Add ::linkifyContent
method and ::linkify static method. Add unit tests.
src/CMakeLists.txt | 1 -
src/client/composer/composer-web-view.vala | 14 ++--
src/client/composer/composer-widget.vala | 7 +-
src/client/web-process/util-composer.vala | 5 -
src/client/web-process/util-webkit.vala | 124 ----------------------------
test/js/composer-page-state-test.vala | 43 ++++++++++-
ui/composer-web-view.js | 60 +++++++++++++-
7 files changed, 108 insertions(+), 146 deletions(-)
---
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index bb0ad05..d0a04e0 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -413,7 +413,6 @@ client/util/util-webkit.vala
set(WEB_PROCESS_SRC
client/web-process/web-process-extension.vala
client/web-process/util-composer.vala
-client/web-process/util-webkit.vala
)
set(CONSOLE_SRC
diff --git a/src/client/composer/composer-web-view.vala b/src/client/composer/composer-web-view.vala
index 5356696..8cc1c30 100644
--- a/src/client/composer/composer-web-view.vala
+++ b/src/client/composer/composer-web-view.vala
@@ -408,6 +408,13 @@ public class ComposerWebView : ClientWebView {
}
/**
+ * Converts plain text URLs in the editor content into links.
+ */
+ public async void linkify_content() throws Error {
+ yield run_javascript("geary.linkifyContent();", null);
+ }
+
+ /**
* Returns the editor content as an HTML string.
*/
public async string? get_html() throws Error {
@@ -491,13 +498,6 @@ public class ComposerWebView : ClientWebView {
return flowed.str;
}
- /**
- * ???
- */
- public void linkify_document() {
- // XXX
- }
-
public override bool button_release_event(Gdk.EventButton event) {
// WebView seems to unconditionally consume button events, so
// to show a link popopver after the view has processed one,
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index cb9fc2e..0cb3e94 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -1260,16 +1260,15 @@ public class ComposerWidget : Gtk.EventBox {
private async void on_send_async() {
this.container.vanish();
this.is_closing = true;
-
- this.editor.linkify_document();
-
+
// Perform send.
try {
+ yield this.editor.linkify_content();
yield this.account.send_email_async(yield get_composed_email());
} catch (Error e) {
GLib.message("Error sending email: %s", e.message);
}
-
+
Geary.Nonblocking.Semaphore? semaphore = discard_draft();
if (semaphore != null) {
try {
diff --git a/src/client/web-process/util-composer.vala b/src/client/web-process/util-composer.vala
index ac340ff..6ef334c 100644
--- a/src/client/web-process/util-composer.vala
+++ b/src/client/web-process/util-composer.vala
@@ -19,11 +19,6 @@ namespace Util.Composer {
private const string EDITING_DELETE_CONTAINER_ID = "WebKit-Editing-Delete-Container";
- public void linkify_document(WebKit.WebPage page) {
- Util.DOM.linkify_document(page.get_dom_document());
- }
-
-
/////////////////////// From WebEditorFixer ///////////////////////
public bool on_should_insert_text(WebKit.WebPage page,
diff --git a/test/js/composer-page-state-test.vala b/test/js/composer-page-state-test.vala
index f792984..debb739 100644
--- a/test/js/composer-page-state-test.vala
+++ b/test/js/composer-page-state-test.vala
@@ -13,13 +13,15 @@ class ComposerPageStateTest : ClientWebViewTestCase<ComposerWebView> {
add_test("edit_context_link", edit_context_link);
add_test("indent_line", indent_line);
add_test("contains_attachment_keywords", contains_attachment_keywords);
+ add_test("linkify_content", linkify_content);
add_test("get_html", get_html);
add_test("get_text", get_text);
add_test("get_text_with_quote", get_text_with_quote);
add_test("get_text_with_nested_quote", get_text_with_nested_quote);
- add_test("resolve_nesting", resolve_nesting);
+
add_test("contains_keywords", contains_keywords);
add_test("quote_lines", quote_lines);
+ add_test("resolve_nesting", resolve_nesting);
add_test("replace_non_breaking_space", replace_non_breaking_space);
}
@@ -109,6 +111,45 @@ some text
}
}
+ public void linkify_content() {
+ // 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
+""");
+
+ 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
+<br><br>""";
+
+ try {
+ run_javascript("geary.linkifyContent();");
+ assert(WebKitUtil.to_string(run_javascript("geary.messageBody.innerHTML;")) ==
+ expected);
+ } catch (Geary.JS.Error err) {
+ print("Geary.JS.Error: %s\n", err.message);
+ assert_not_reached();
+ } catch (Error err) {
+ print("WKError: %s\n", err.message);
+ assert_not_reached();
+ }
+ }
+
public void get_html() {
string html = "<p>para</p>";
load_body_fixture(html);
diff --git a/ui/composer-web-view.js b/ui/composer-web-view.js
index 8207f2e..da55842 100644
--- a/ui/composer-web-view.js
+++ b/ui/composer-web-view.js
@@ -14,12 +14,13 @@ let ComposerPageState = function() {
};
ComposerPageState.BODY_ID = "message-body";
ComposerPageState.KEYWORD_SPLIT_REGEX = /[\s]+/g;
-ComposerPageState.QUOTE_START = "";
-ComposerPageState.QUOTE_END = "";
-ComposerPageState.QUOTE_MARKER = "\x7f";
+ComposerPageState.QUOTE_START = "\x91"; // private use one
+ComposerPageState.QUOTE_END = "\x92"; // private use two
+ComposerPageState.QUOTE_MARKER = "\x7f"; // delete
+ComposerPageState.PROTOCOL_REGEX =
/^(aim|apt|bitcoin|cvs|ed2k|ftp|file|finger|git|gtalk|http|https|irc|ircs|irc6|lastfm|ldap|ldaps|magnet|news|nntp|rsync|sftp|skype|smb|sms|svn|telnet|tftp|ssh|webcal|xmpp):/i;
// Taken from Geary.HTML.URL_REGEX, without the inline modifier (?x)
// at the start, which is unsupported in JS
-ComposerPageState.URL_REGEX = new
RegExp("\\b((?:[a-z][\\w-]+:(?:/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:'\".,<>?«»“”‘’]))",
"i");
+ComposerPageState.URL_REGEX = new
RegExp("\\b((?:[a-z][\\w-]+:(?:/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:'\".,<>?«»“”‘’]))",
"gi");
ComposerPageState.prototype = {
__proto__: PageState.prototype,
@@ -264,6 +265,9 @@ ComposerPageState.prototype = {
}
}
},
+ linkifyContent: function() {
+ ComposerPageState.linkify(this.messageBody);
+ },
getHtml: function() {
return this.messageBody.innerHTML;
},
@@ -403,6 +407,54 @@ ComposerPageState.htmlToQuotedText = function(root) {
return ComposerPageState.replaceNonBreakingSpace(text);
};
+// Linkifies "plain text" link
+ComposerPageState.linkify = function(node) {
+ if (node.nodeType == Node.TEXT_NODE) {
+ // Examine text node for something that looks like a URL
+ let input = node.nodeValue;
+ if (input != null) {
+ let output = input.replace(ComposerPageState.URL_REGEX, function(url) {
+ if (url.match(ComposerPageState.PROTOCOL_REGEX) != null) {
+ url = "\x01" + url + "\x01";
+ }
+ return url;
+ });
+
+ if (input != output) {
+ // We got one! Now split the text and swap in a new anchor.
+ let parent = node.parentNode;
+ let sibling = node.nextSibling;
+ for (let part of output.split("\x01")) {
+ let newNode = null;
+ if (part.match(ComposerPageState.URL_REGEX) != null) {
+ newNode = document.createElement("A");
+ newNode.href = part;
+ newNode.innerText = part;
+ } else {
+ newNode = document.createTextNode(part);
+ }
+ parent.insertBefore(newNode, sibling);
+ }
+ parent.removeChild(node);
+ }
+ }
+ } else {
+ // Recurse
+ let child = node.firstChild;
+ while (child != null) {
+ // Save the child and get its next sibling early since if
+ // it does actually contain a URL, it will be removed from
+ // the tree
+ let target = child;
+ child = child.nextSibling;
+ // Don't attempt to linkify existing links
+ if (target.nodeName != "A") {
+ ComposerPageState.linkify(target);
+ }
+ }
+ }
+};
+
ComposerPageState.resolveNesting = function(text, values) {
let tokenregex = new RegExp(
"(.?)" +
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]