Render with evaluateJavaScript.

Replaced loadHTMLString based rendering for improved performance.
This commit is contained in:
Ben Ubois 2019-09-18 22:03:23 -07:00
parent 3b5a6f2576
commit 0b6683d073
4 changed files with 236 additions and 80 deletions

View File

@ -100,7 +100,13 @@ final class DetailWebViewController: NSViewController, WKUIDelegate {
NotificationCenter.default.addObserver(self, selector: #selector(webInspectorEnabledDidChange(_:)), name: .WebInspectorEnabledDidChange, object: nil)
reloadHTML()
webView.loadHTMLString(template(), baseURL: nil)
}
func template() -> String {
let path = Bundle.main.path(forResource: "page", ofType: "html")!
let s = try! NSString(contentsOfFile: path, encoding: String.Encoding.utf8.rawValue)
return s as String
}
// MARK: Scrolling
@ -162,6 +168,9 @@ extension DetailWebViewController: WKNavigationDelegate {
}
// MARK: - Private
struct TemplateData: Codable {
let body: String
}
private extension DetailWebViewController {
@ -179,8 +188,17 @@ private extension DetailWebViewController {
case .extracted(let article, let extractedArticle):
html = ArticleRenderer.articleHTML(article: article, extractedArticle: extractedArticle, style: style)
}
let templateData = TemplateData(body: html)
let encoder = JSONEncoder()
var render = "error();"
if let data = try? encoder.encode(templateData) {
let json = String(data: data, encoding: .utf8)!
render = "render(\(json));"
}
webView.loadHTMLString(html, baseURL: nil)
webView.evaluateJavaScript(render)
}
func fetchScrollInfo(_ callback: @escaping (ScrollInfo?) -> Void) {

View File

@ -372,6 +372,8 @@
84FF69B11FC3793300DC198E /* FaviconURLFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */; };
9EA33BB92318F8C10097B644 /* AccountsFeedlyWebWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EA33BB72318F8C10097B644 /* AccountsFeedlyWebWindowController.swift */; };
9EA33BBA2318F8C10097B644 /* AccountsFeedlyWeb.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9EA33BB82318F8C10097B644 /* AccountsFeedlyWeb.xib */; };
B528F81E23333C7E00E735DD /* page.html in Resources */ = {isa = PBXBuildFile; fileRef = B528F81D23333C7E00E735DD /* page.html */; };
B528F81F23333C7E00E735DD /* page.html in Resources */ = {isa = PBXBuildFile; fileRef = B528F81D23333C7E00E735DD /* page.html */; };
D553738B20186C20006D8857 /* Article+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D553737C20186C1F006D8857 /* Article+Scriptability.swift */; };
D57BE6E0204CD35F00D11AAC /* NSScriptCommand+NetNewsWire.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57BE6DF204CD35F00D11AAC /* NSScriptCommand+NetNewsWire.swift */; };
D5907D7F2004AC00005947E5 /* NSApplication+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5907D7E2004AC00005947E5 /* NSApplication+Scriptability.swift */; };
@ -1051,10 +1053,11 @@
84F9EAE4213660A100CF2DE4 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = Frameworks/Vendor/Sparkle.framework; sourceTree = SOURCE_ROOT; };
84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconURLFinder.swift; sourceTree = "<group>"; };
B24EFD482330FF99006C6242 /* NetNewsWire-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NetNewsWire-Bridging-Header.h"; sourceTree = "<group>"; };
B24EFD5923310109006C6242 /* WKPreferencesPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKPreferencesPrivate.h; sourceTree = "<group>"; };
9EA33BB72318F8C10097B644 /* AccountsFeedlyWebWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsFeedlyWebWindowController.swift; sourceTree = "<group>"; };
9EA33BB82318F8C10097B644 /* AccountsFeedlyWeb.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountsFeedlyWeb.xib; sourceTree = "<group>"; };
B24EFD482330FF99006C6242 /* NetNewsWire-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NetNewsWire-Bridging-Header.h"; sourceTree = "<group>"; };
B24EFD5923310109006C6242 /* WKPreferencesPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKPreferencesPrivate.h; sourceTree = "<group>"; };
B528F81D23333C7E00E735DD /* page.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = page.html; sourceTree = "<group>"; };
D519E74722EE553300923F27 /* NetNewsWire_safariextension_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_safariextension_target.xcconfig; sourceTree = "<group>"; };
D553737C20186C1F006D8857 /* Article+Scriptability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Article+Scriptability.swift"; sourceTree = "<group>"; };
D57BE6DF204CD35F00D11AAC /* NSScriptCommand+NetNewsWire.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSScriptCommand+NetNewsWire.swift"; sourceTree = "<group>"; };
@ -1381,6 +1384,7 @@
isa = PBXGroup;
children = (
849A977D1ED9EC42007D329B /* ArticleRenderer.swift */,
B528F81D23333C7E00E735DD /* page.html */,
848362FE2262A30E00DA1D35 /* template.html */,
);
path = "Article Rendering";
@ -2223,16 +2227,16 @@
TargetAttributes = {
513C5CE5232571C2003D4054 = {
CreatedOnToolsVersion = 11.0;
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = DY2XQRVWN9;
ProvisioningStyle = Automatic;
};
6581C73220CED60000F4AD34 = {
DevelopmentTeam = SHJK2V3AJG;
ProvisioningStyle = Automatic;
ProvisioningStyle = Manual;
};
840D617B2029031C009BC708 = {
CreatedOnToolsVersion = 9.3;
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = DY2XQRVWN9;
ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.BackgroundModes = {
@ -2243,7 +2247,7 @@
849C645F1ED37A5D003D8FC0 = {
CreatedOnToolsVersion = 8.2.1;
DevelopmentTeam = SHJK2V3AJG;
ProvisioningStyle = Automatic;
ProvisioningStyle = Manual;
SystemCapabilities = {
com.apple.HardenedRuntime = {
enabled = 1;
@ -2252,7 +2256,7 @@
};
849C64701ED37A5D003D8FC0 = {
CreatedOnToolsVersion = 8.2.1;
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = 9C84TZ7Q6Z;
ProvisioningStyle = Automatic;
TestTargetID = 849C645F1ED37A5D003D8FC0;
};
@ -2491,6 +2495,7 @@
511D43EF231FBDE900FB1562 /* LaunchScreenPad.storyboard in Resources */,
511D43D2231FA62C00FB1562 /* GlobalKeyboardShortcuts.plist in Resources */,
84C9FCA12262A1B300D921D6 /* Main.storyboard in Resources */,
B528F81F23333C7E00E735DD /* page.html in Resources */,
51F85BF32272531500C787DC /* Dedication.rtf in Resources */,
84C9FCA42262A1B800D921D6 /* LaunchScreenPhone.storyboard in Resources */,
51F85BEB22724CB600C787DC /* About.rtf in Resources */,
@ -2532,6 +2537,7 @@
848363052262A3CC00DA1D35 /* AddFolderSheet.xib in Resources */,
5144EA52227B8E4500D19003 /* AccountsFeedbin.xib in Resources */,
8405DDA222168920008CE1BF /* TimelineTableView.xib in Resources */,
B528F81E23333C7E00E735DD /* page.html in Resources */,
8483630E2262A3FE00DA1D35 /* MainWindow.storyboard in Resources */,
55E15BCB229D65A900D6602A /* AccountsReaderAPI.xib in Resources */,
84BAE64921CEDAF20046DB56 /* CrashReporterWindow.xib in Resources */,

View File

@ -328,87 +328,17 @@ private extension ArticleRenderer {
return dateFormatter.string(from: date)
}
#if os(macOS)
func renderHTML(withBody body: String) -> String {
var s = "<!DOCTYPE html><html><head>\n\n"
var s = ""
if let baseURL = baseURL {
s += ("<base href=\"" + baseURL + "\"\n>")
}
s += title.htmlBySurroundingWithTag("title")
s += styleString().htmlBySurroundingWithTag("style")
s += """
<script type="text/javascript">
function startup() {
var anchors = document.getElementsByTagName("a");
for (var i = 0; i < anchors.length; i++) {
anchors[i].addEventListener("mouseenter", function() { mouseDidEnterLink(this) });
anchors[i].addEventListener("mouseleave", function() { mouseDidExitLink(this) });
}
document.getElementsByTagName("body")[0].querySelectorAll("style, link[rel=stylesheet]").forEach(element => element.remove());
document.getElementsByTagName("body")[0].querySelectorAll("[style]").forEach(element => element.removeAttribute("style"));
}
function mouseDidEnterLink(anchor) {
window.webkit.messageHandlers.mouseDidEnter.postMessage(anchor.href);
}
function mouseDidExitLink(anchor) {
window.webkit.messageHandlers.mouseDidExit.postMessage(anchor.href);
}
</script>
"""
s += "\n\n</head><body onload='startup()'>\n\n"
s += body
s += "\n\n</body></html>"
//print(s)
return s
}
#else
func renderHTML(withBody body: String) -> String {
var s = "<!DOCTYPE html><html><head>\n"
if let baseURL = baseURL {
s += ("<base href=\"" + baseURL + "\"\n>")
}
s += "<meta name=\"viewport\" content=\"width=device-width\">\n"
s += title.htmlBySurroundingWithTag("title")
s += styleString().htmlBySurroundingWithTag("style")
s += """
<script type="text/javascript">
function startup() {
document.getElementsByTagName("body")[0].querySelectorAll("style, link[rel=stylesheet]").forEach(element => element.remove());
document.getElementsByTagName("body")[0].querySelectorAll("[style]").forEach(element => element.removeAttribute("style"));
}
</script>
"""
s += "\n\n</head><body onload='startup()'>\n\n"
s += body
s += "\n\n</body></html>"
return s
}
#endif
}
// MARK: - Article extension

View File

@ -0,0 +1,202 @@
<html>
<head>
<style>
body {
margin-top: 20px;
margin-bottom: 64px;
margin-left: 64px;
margin-right: 64px;
font-family: -apple-system;
font-size: 18px;
word-wrap: break-word; /* break long words or URLs */
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.feedlink {
font-weight: bold;
}
.headerTable {
width: 100%;
height: 68px;
}
.systemMessage {
position: absolute;
top: 45%;
left: 50%;
transform: translateX(-55%) translateY(-50%);
}
:root {
--body-color: #444;
--body-background-color: -apple-system-text-background;
--link-color: hsla(215, 99%, 43%, 1);
--header-table-border-color: rgba(0, 0, 0, 0.1);
--header-color: rgba(0, 0, 0, 0.3);
--header-link-color: rgba(0, 0, 0, 0.3);
--body-code-color: #666;
--system-message-color: #cbcbcb;
--feedlink-color: rgba(0, 0, 0, 0.6);
}
@media(prefers-color-scheme: dark) {
:root {
--body-color: #d2d2d2;
--body-background-color: #2d2d2d;
--link-color: #4490e2;
--header-table-border-color: rgba(255, 255, 255, 0.1);
--header-color: #d2d2d2;
--header-link-color: #4490e2;
--body-code-color: #b2b2b2;
--system-message-color: #5f5f5f
}
}
body {
color: var(--body-color);
background-color: var(--body-background-color);
}
body a, body a:link, body a:visited {
color: var(--link-color);
}
body .headerTable {
border-bottom: 1px solid var(--header-table-border-color);
}
body .header {
color: var(--header-color);
}
body .header a:link, body .header a:visited {
color: var(--header-link-color);
}
body .articleDateline, body .articleDateLine.a:link, body .articleDateline a:visited {
color: var(--header-color);
}
body code, body pre {
color: var(--body-code-color);
}
body > .systemMessage {
color: var(--system-message-color);
}
.feedlink a:link, .feedlink a:visited {
color: var(--feed-link-color);
}
.avatar img {
border-radius: 4px;
}
.feedIcon {
border-radius: 4px;
}
.rightAlign {
text-align: right;
}
.leftAlign {
text-align: left;
}
.articleTitle {
margin-top: 26px;
}
.articleDateline {
margin-bottom: 25px;
font-weight: bold;
}
.articleBody {
line-height: 1.6em;
}
h1 {
line-height: 1.15em;
font-weight: bold;
}
code, pre {
font-family: "SF Mono", Menlo, "Courier New", Courier, monospace;
font-size: 14px;
}
pre {
white-space: pre-wrap;
}
img, figure, video, iframe, div {
max-width: 100%;
height: auto !important;
margin: 0 auto;
}
figcaption {
font-size: 14px;
line-height: 1.3em;
}
/*Block ads and junk*/
iframe[src*="feedads"],
iframe[src*="doubleclick"],
iframe[src*="plusone.google"] {
display: none !important;
}
a[href*=".ads."],
a[href*="feedads"],
a[href*="doubleclick"],
a[href*="//ads."],
a[href*="api.tweetmeme"],
a[href*="delicious.com/post?"],
a[href*="digg.com/submit?"],
a[href*="google.com/bookmarks/mark?"],
a[href*="posterous.com/share?"],
a[href*="tumblr.com/share?"],
a[href*="linkedin.com/shareArticle?"],
a[href*="facebook.com/share.php?"],
a[href*="http://twitter.com/home?"],
a[href*="addtoany.com/share_save"] {
display: none !important;
}
img[src*=".ads."],
img[src*="//ads."],
img[src*="doubleclick"],
img[src*="feedads"],
img[src*="feedburner"],
img[src*="feedblitz"],
img[src*="share-buttons"] {
display: none !important;
}
</style>
<script>
function mouseDidEnterLink(anchor) {
window.webkit.messageHandlers.mouseDidEnter.postMessage(anchor.href);
}
function mouseDidExitLink(anchor) {
window.webkit.messageHandlers.mouseDidExit.postMessage(anchor.href);
}
function render(data) {
document.body.innerHTML = data.body;
var anchors = document.getElementsByTagName("a");
for (var i = 0; i < anchors.length; i++) {
anchors[i].addEventListener("mouseenter", function() { mouseDidEnterLink(this) });
anchors[i].addEventListener("mouseleave", function() { mouseDidExitLink(this) });
}
document.getElementsByTagName("body")[0].querySelectorAll("style, link[rel=stylesheet]").forEach(element => element.remove());
document.getElementsByTagName("body")[0].querySelectorAll("[style]").forEach(element => element.removeAttribute("style"));
}
function error() {
document.body.innerHTML = "error";
}
</script>
</head>
<body>
</body>
</html>