mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2024-12-18 12:28:37 +01:00
Change article rendering to use loadHTMLString instead of JavaScript. Issue #1923
This commit is contained in:
parent
37cf7fae1d
commit
36841602a5
@ -8,6 +8,7 @@
|
||||
|
||||
import AppKit
|
||||
import WebKit
|
||||
import RSCore
|
||||
import RSWeb
|
||||
import Articles
|
||||
|
||||
@ -118,8 +119,7 @@ final class DetailWebViewController: NSViewController, WKUIDelegate {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(avatarDidBecomeAvailable(_:)), name: .AvatarDidBecomeAvailable, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
|
||||
|
||||
webView.loadFileURL(ArticleRenderer.page.url, allowingReadAccessTo: ArticleRenderer.page.baseURL)
|
||||
|
||||
webView.loadFileURL(ArticleRenderer.blank.url, allowingReadAccessTo: ArticleRenderer.blank.baseURL)
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
@ -196,12 +196,6 @@ extension DetailWebViewController: WKNavigationDelegate {
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
struct TemplateData: Codable {
|
||||
let style: String
|
||||
let body: String
|
||||
let title: String
|
||||
let baseURL: String
|
||||
}
|
||||
|
||||
private extension DetailWebViewController {
|
||||
|
||||
@ -236,16 +230,15 @@ private extension DetailWebViewController {
|
||||
rendering = ArticleRenderer.articleHTML(article: article, extractedArticle: extractedArticle, style: style)
|
||||
}
|
||||
|
||||
let templateData = TemplateData(style: rendering.style, body: rendering.html, title: rendering.title, baseURL: rendering.baseURL)
|
||||
let substitutions = [
|
||||
"title": rendering.title,
|
||||
"baseURL": rendering.baseURL,
|
||||
"style": rendering.style,
|
||||
"body": rendering.html
|
||||
]
|
||||
|
||||
let encoder = JSONEncoder()
|
||||
var render = "error();"
|
||||
if let data = try? encoder.encode(templateData) {
|
||||
let json = String(data: data, encoding: .utf8)!
|
||||
render = "render(\(json), 0);"
|
||||
}
|
||||
|
||||
webView.evaluateJavaScript(render)
|
||||
let html = try! MacroProcessor.renderedText(withTemplate: ArticleRenderer.page.html, substitutions: substitutions)
|
||||
webView.loadHTMLString(html, baseURL: ArticleRenderer.page.baseURL)
|
||||
}
|
||||
|
||||
func fetchScrollInfo(_ completion: @escaping (ScrollInfo?) -> Void) {
|
||||
|
6
Mac/MainWindow/Detail/blank.html
Normal file
6
Mac/MainWindow/Detail/blank.html
Normal file
@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
@ -1,13 +1,20 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<base href="">
|
||||
<title>[[title]]</title>
|
||||
<style>
|
||||
[[style]]
|
||||
</style>
|
||||
<script src="main.js"></script>
|
||||
<script src="main_mac.js"></script>
|
||||
<script src="newsfoot.js" async="async"></script>
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
processPage();
|
||||
})
|
||||
</script>
|
||||
<base href="[[baseURL]]">
|
||||
</head>
|
||||
<body>
|
||||
[[body]]
|
||||
</body>
|
||||
</html>
|
||||
|
@ -14,6 +14,9 @@
|
||||
3B826DCE2385C89600FC1ADB /* AccountsFeedWranglerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */; };
|
||||
49F40DF82335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; };
|
||||
49F40DF92335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; };
|
||||
5103A9982421643300410853 /* blank.html in Resources */ = {isa = PBXBuildFile; fileRef = 5103A9972421643300410853 /* blank.html */; };
|
||||
5103A9992421643300410853 /* blank.html in Resources */ = {isa = PBXBuildFile; fileRef = 5103A9972421643300410853 /* blank.html */; };
|
||||
5103A9B424216A4200410853 /* blank.html in Resources */ = {isa = PBXBuildFile; fileRef = 5103A9B324216A4200410853 /* blank.html */; };
|
||||
5108F6B62375E612001ABC45 /* CacheCleaner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5108F6B52375E612001ABC45 /* CacheCleaner.swift */; };
|
||||
5108F6B72375E612001ABC45 /* CacheCleaner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5108F6B52375E612001ABC45 /* CacheCleaner.swift */; };
|
||||
5108F6D22375EED2001ABC45 /* TimelineCustomizerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5108F6D12375EED2001ABC45 /* TimelineCustomizerViewController.swift */; };
|
||||
@ -1250,6 +1253,8 @@
|
||||
3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsFeedWrangler.xib; sourceTree = "<group>"; };
|
||||
3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsFeedWranglerWindowController.swift; sourceTree = "<group>"; };
|
||||
49F40DEF2335B71000552BF4 /* newsfoot.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = newsfoot.js; sourceTree = "<group>"; };
|
||||
5103A9972421643300410853 /* blank.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = blank.html; sourceTree = "<group>"; };
|
||||
5103A9B324216A4200410853 /* blank.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = blank.html; sourceTree = "<group>"; };
|
||||
5108F6B52375E612001ABC45 /* CacheCleaner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheCleaner.swift; sourceTree = "<group>"; };
|
||||
5108F6D12375EED2001ABC45 /* TimelineCustomizerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineCustomizerViewController.swift; sourceTree = "<group>"; };
|
||||
5108F6D32375EEEF001ABC45 /* TimelinePreviewTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelinePreviewTableViewController.swift; sourceTree = "<group>"; };
|
||||
@ -2370,6 +2375,7 @@
|
||||
84E8E0EA202F693600562D8F /* DetailWebView.swift */,
|
||||
84D52E941FE588BB00D14F5B /* DetailStatusBarView.swift */,
|
||||
5141E7552374A2890013FF27 /* DetailIconSchemeHandler.swift */,
|
||||
5103A9972421643300410853 /* blank.html */,
|
||||
B528F81D23333C7E00E735DD /* page.html */,
|
||||
5142194A2353C1CF00E07E2C /* main_mac.js */,
|
||||
848362FC2262A30800DA1D35 /* styleSheet.css */,
|
||||
@ -2677,6 +2683,7 @@
|
||||
51F85BF02272524100C787DC /* Credits.rtf */,
|
||||
51F85BEE2272520B00C787DC /* Thanks.rtf */,
|
||||
51F85BF22272531500C787DC /* Dedication.rtf */,
|
||||
5103A9B324216A4200410853 /* blank.html */,
|
||||
51BB7C302335ACDE008E8144 /* page.html */,
|
||||
514219572353C28900E07E2C /* main_ios.js */,
|
||||
51C452B72265178500C03939 /* styleSheet.css */,
|
||||
@ -3475,6 +3482,7 @@
|
||||
3B826DCD2385C89600FC1ADB /* AccountsFeedWrangler.xib in Resources */,
|
||||
65ED4069235DEF6C0081F399 /* AccountsReaderAPI.xib in Resources */,
|
||||
65ED406A235DEF6C0081F399 /* newsfoot.js in Resources */,
|
||||
5103A9992421643300410853 /* blank.html in Resources */,
|
||||
65ED406B235DEF6C0081F399 /* CrashReporterWindow.xib in Resources */,
|
||||
65ED406C235DEF6C0081F399 /* Credits.rtf in Resources */,
|
||||
65ED406D235DEF6C0081F399 /* Inspector.storyboard in Resources */,
|
||||
@ -3507,6 +3515,7 @@
|
||||
516A093723609A3600EAE89B /* SettingsAccountTableViewCell.xib in Resources */,
|
||||
51F85BF32272531500C787DC /* Dedication.rtf in Resources */,
|
||||
516A09422361248000EAE89B /* Inspector.storyboard in Resources */,
|
||||
5103A9B424216A4200410853 /* blank.html in Resources */,
|
||||
84C9FCA42262A1B800D921D6 /* LaunchScreenPhone.storyboard in Resources */,
|
||||
51F85BEB22724CB600C787DC /* About.rtf in Resources */,
|
||||
516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */,
|
||||
@ -3561,6 +3570,7 @@
|
||||
3B826DCB2385C84800FC1ADB /* AccountsFeedWrangler.xib in Resources */,
|
||||
55E15BCB229D65A900D6602A /* AccountsReaderAPI.xib in Resources */,
|
||||
49F40DF82335B71000552BF4 /* newsfoot.js in Resources */,
|
||||
5103A9982421643300410853 /* blank.html in Resources */,
|
||||
84BAE64921CEDAF20046DB56 /* CrashReporterWindow.xib in Resources */,
|
||||
84C9FC8E22629E8F00D921D6 /* Credits.rtf in Resources */,
|
||||
84BBB12D20142A4700F054F5 /* Inspector.storyboard in Resources */,
|
||||
|
@ -17,15 +17,23 @@ import Account
|
||||
struct ArticleRenderer {
|
||||
|
||||
typealias Rendering = (style: String, html: String, title: String, baseURL: String)
|
||||
typealias Page = (url: URL, baseURL: URL)
|
||||
|
||||
struct Page {
|
||||
let url: URL
|
||||
let baseURL: URL
|
||||
let html: String
|
||||
|
||||
init(name: String) {
|
||||
url = Bundle.main.url(forResource: name, withExtension: "html")!
|
||||
baseURL = url.deletingLastPathComponent()
|
||||
html = try! NSString(contentsOfFile: url.path, encoding: String.Encoding.utf8.rawValue) as String
|
||||
}
|
||||
}
|
||||
|
||||
static var imageIconScheme = "nnwImageIcon"
|
||||
|
||||
static var page: Page = {
|
||||
let url = Bundle.main.url(forResource: "page", withExtension: "html")!
|
||||
let baseURL = url.deletingLastPathComponent()
|
||||
return Page(url: url, baseURL: baseURL)
|
||||
}()
|
||||
static var blank = Page(name: "blank")
|
||||
static var page = Page(name: "page")
|
||||
|
||||
private let article: Article?
|
||||
private let extractedArticle: ExtractedArticle?
|
||||
|
@ -106,25 +106,12 @@ function styleLocalFootnotes() {
|
||||
}
|
||||
}
|
||||
|
||||
function render(data, scrollY) {
|
||||
document.getElementsByTagName("style")[0].innerHTML = data.style;
|
||||
|
||||
let title = document.getElementsByTagName("title")[0];
|
||||
title.textContent = data.title
|
||||
|
||||
let base = document.getElementsByTagName("base")[0];
|
||||
base.href = data.baseURL
|
||||
|
||||
document.body.innerHTML = data.body;
|
||||
|
||||
window.scrollTo(0, scrollY);
|
||||
|
||||
wrapFrames()
|
||||
wrapTables()
|
||||
stripStyles()
|
||||
convertImgSrc()
|
||||
flattenPreElements()
|
||||
styleLocalFootnotes()
|
||||
|
||||
postRenderProcessing()
|
||||
function processPage() {
|
||||
wrapFrames();
|
||||
wrapTables();
|
||||
stripStyles();
|
||||
convertImgSrc();
|
||||
flattenPreElements();
|
||||
styleLocalFootnotes();
|
||||
postRenderProcessing();
|
||||
}
|
||||
|
@ -11,10 +11,6 @@ import WebKit
|
||||
|
||||
class PreloadedWebView: WKWebView {
|
||||
|
||||
private struct MessageName {
|
||||
static let domContentLoaded = "domContentLoaded"
|
||||
}
|
||||
|
||||
private var isReady: Bool = false
|
||||
private var readyCompletion: ((PreloadedWebView) -> Void)?
|
||||
|
||||
@ -38,8 +34,8 @@ class PreloadedWebView: WKWebView {
|
||||
}
|
||||
|
||||
func preload() {
|
||||
configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.domContentLoaded)
|
||||
loadFileURL(ArticleRenderer.page.url, allowingReadAccessTo: ArticleRenderer.page.baseURL)
|
||||
navigationDelegate = self
|
||||
loadFileURL(ArticleRenderer.blank.url, allowingReadAccessTo: ArticleRenderer.blank.baseURL)
|
||||
}
|
||||
|
||||
func ready(completion: @escaping (PreloadedWebView) -> Void) {
|
||||
@ -54,18 +50,16 @@ class PreloadedWebView: WKWebView {
|
||||
|
||||
// MARK: WKScriptMessageHandler
|
||||
|
||||
extension PreloadedWebView: WKScriptMessageHandler {
|
||||
extension PreloadedWebView: WKNavigationDelegate {
|
||||
|
||||
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
||||
if message.name == MessageName.domContentLoaded {
|
||||
isReady = true
|
||||
if let completion = readyCompletion {
|
||||
completeRequest(completion: completion)
|
||||
readyCompletion = nil
|
||||
}
|
||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||
isReady = true
|
||||
if let completion = readyCompletion {
|
||||
completeRequest(completion: completion)
|
||||
readyCompletion = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
@ -74,7 +68,7 @@ private extension PreloadedWebView {
|
||||
|
||||
func completeRequest(completion: @escaping (PreloadedWebView) -> Void) {
|
||||
isReady = false
|
||||
configuration.userContentController.removeScriptMessageHandler(forName: MessageName.domContentLoaded)
|
||||
navigationDelegate = nil
|
||||
completion(self)
|
||||
}
|
||||
|
||||
|
@ -325,7 +325,7 @@ extension WebViewController: WKNavigationDelegate {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// MARK: WKUIDelegate
|
||||
@ -393,13 +393,6 @@ extension WebViewController: UIScrollViewDelegate {
|
||||
|
||||
// MARK: JSON
|
||||
|
||||
private struct TemplateData: Codable {
|
||||
let style: String
|
||||
let body: String
|
||||
let title: String
|
||||
let baseURL: String
|
||||
}
|
||||
|
||||
private struct ImageClickMessage: Codable {
|
||||
let x: Float
|
||||
let y: Float
|
||||
@ -416,11 +409,13 @@ private extension WebViewController {
|
||||
func loadWebView() {
|
||||
guard isViewLoaded else { return }
|
||||
|
||||
if let webView = webView {
|
||||
self.renderPage(webView)
|
||||
return
|
||||
}
|
||||
|
||||
coordinator.webViewProvider.dequeueWebView() { webView in
|
||||
|
||||
let webViewToRecycle = self.webView
|
||||
self.renderPage(webViewToRecycle)
|
||||
|
||||
// Add the webview
|
||||
webView.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.view.insertSubview(webView, at: 0)
|
||||
@ -451,9 +446,6 @@ private extension WebViewController {
|
||||
webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasShown)
|
||||
|
||||
self.renderPage(webView)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
||||
self.recycleWebView(webViewToRecycle)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -498,16 +490,16 @@ private extension WebViewController {
|
||||
rendering = ArticleRenderer.noSelectionHTML(style: style)
|
||||
}
|
||||
|
||||
let templateData = TemplateData(style: rendering.style, body: rendering.html, title: rendering.title, baseURL: rendering.baseURL)
|
||||
|
||||
let encoder = JSONEncoder()
|
||||
var render = "error();"
|
||||
if let data = try? encoder.encode(templateData) {
|
||||
let json = String(data: data, encoding: .utf8)!
|
||||
render = "render(\(json), \(windowScrollY));"
|
||||
}
|
||||
let substitutions = [
|
||||
"title": rendering.title,
|
||||
"baseURL": rendering.baseURL,
|
||||
"style": rendering.style,
|
||||
"body": rendering.html,
|
||||
"windowScrollY": String(windowScrollY)
|
||||
]
|
||||
|
||||
webView.evaluateJavaScript(render)
|
||||
let html = try! MacroProcessor.renderedText(withTemplate: ArticleRenderer.page.html, substitutions: substitutions)
|
||||
webView.loadHTMLString(html, baseURL: ArticleRenderer.page.baseURL)
|
||||
|
||||
}
|
||||
|
||||
|
11
iOS/Resources/blank.html
Normal file
11
iOS/Resources/blank.html
Normal file
@ -0,0 +1,11 @@
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
@ -1,17 +1,22 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<base href="">
|
||||
<title>[[title]]</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
[[style]]
|
||||
</style>
|
||||
<script src="main.js"></script>
|
||||
<script src="main_ios.js"></script>
|
||||
<script src="newsfoot.js" async="async"></script>
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
window.scrollTo(0, [[windowScrollY]]);
|
||||
processPage();
|
||||
})
|
||||
</script>
|
||||
<base href="[[baseURL]]">
|
||||
</head>
|
||||
<body>
|
||||
[[body]]
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,4 +1,5 @@
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
font: -apple-system-body;
|
||||
font-size: [[font-size]]px;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user