众所周知,我们可以使用 WKContentRuleList 来阻止 url 请求/cookie 或在 WKWebView 中执行其他操作。有什么方法可以让我们根据 WKContentRuleList 知道 WKWebView 阻止了什么?
3 回答
我有一定程度的把握,没有一种简单的方法可以使用公共 API 检索这些信息。因此,我已经为我的目的整理了一个“足够好”的解决方案。它尝试从解析的 html 中提取资源,然后将它们与从 window.performance 模块获得的加载资源进行比较。主要的警告是,一些资源类型根本没有被处理,而另一些可能被遗漏了。
显然,它应该在页面完全加载它要加载的内容后调用。通常,这将通过“webViewDidFinishNavigation”委托方法完成。提供的完成参数是闭包,它以被阻塞资源的数组作为单个参数调用。
第一部分是构建 javascript 以从页面中提取资源的函数。Stackoverflow 似乎通过这种拆分来更好地格式化事情。
private static func buildResourceInfoJavascript() -> String {
let script = """
function extractUrls( fromCss ) {
let matches = fromCss.match(/url\\(.+?\\)/g);
if( !matches ) {
return [] ;
}
let urls = matches.map(url => url.replace(/url\\(['\\"]?(.+?)['\\"]?\\)/g, "$1"));
return urls;
}
function getPageResources() {
let pageResources = [...document.images].map(x => x.src);
pageResources = [...pageResources, ...[...document.scripts].map(x => x.src) ] ;
pageResources = [...pageResources, ...[...document.getElementsByTagName("link")].map(x => x.href) ];
[...document.styleSheets].forEach(sheet => {
if( !sheet.cssRules ) {
return ;
}
[...sheet.cssRules].forEach(rule => {
pageResources = [...pageResources, ...extractUrls( rule.cssText )];
} );
});
let inlineStyles = document.querySelectorAll( '*[style]') ;
[...inlineStyles].forEach(x => {
pageResources = [...pageResources, ...extractUrls( x.getAttributeNode("style").value )];
}) ;
let backgrounds = document.querySelectorAll( 'td[background], tr[background], table[background]') ;
[...backgrounds].forEach(x => {
pageResources.push( x.getAttributeNode("background").value );
}) ;
return pageResources.filter(x => (x != null && x != '') );
}
let pageResources = getPageResources() ;
let loadedResources = window.performance.getEntriesByType('resource').map(x => x.name );
let resourceInfo = {
'pageResources' : pageResources,
'loadedResources' : loadedResources.filter(x => (x != null && x != '') ),
};
JSON.stringify(resourceInfo);
"""
return script
}
下一部分是从 didFinishNavigation 委托调用的函数。
public static func getBlockedResourcesAsync( fromWebView:WKWebView, completion:@escaping (([String]) -> Void)) {
let script = buildResourceInfoJavascript()
fromWebView.evaluateJavaScript(script) { (results, error) in
guard let resultsData = (results as? String)?.data(using: .utf8) else {
NSLog("No results for getBlockedResources" )
completion( [] )
return
}
do {
let resourceInfo = try JSONSerialization.jsonObject(with: resultsData) as? [String:[String]] ?? [:]
let pageResources = Array(Set(resourceInfo["pageResources"] ?? []) )
let loadedResources = Array(Set( resourceInfo["loadedResources"] ?? []) )
let blockedResources = pageResources.filter { !loadedResources.contains($0) }
let unrecognizedResources = loadedResources.filter { !pageResources.contains($0) }
if unrecognizedResources.count > 0 {
NSLog("Didn't recognized resources \(unrecognizedResources)" )
}
completion( blockedResources )
}
catch let err {
NSLog("JSON decoding failed: \(err.localizedDescription)" )
completion([])
return
}
}
}
您可以使用 WebKit SDK 来完成。使用 WKScriptMessageHandler : https://developer.apple.com/documentation/webkit/wkscriptmessagehandler/1396222-usercontentcontroller
您可以在开源项目 iOS 浏览器 DuckDuckGo 中找到该技术的示例: https ://github.com/duckduckgo/iOS
看看这个文件: https ://github.com/duckduckgo/iOS/blob/develop/Core/ContentBlockerRulesUserScript.swift
您的意思是要获取列表中的所有规则还是要查找规则列表?然后你可以使用下面的 WKContentRuleListStore 的 api https://developer.apple.com/documentation/webkit/wkcontentruleliststore
// Gets the identifiers for all rules lists in the store.
func getAvailableContentRuleListIdentifiers((([String]?) -> Void)!)
// Searches for a specific rules list in the store.
func lookUpContentRuleList(forIdentifier: String!, completionHandler:
((WKContentRuleList?, Error?) -> Void)!)
如果您需要更多详细信息,您还可以参考以下问题的答案: Block ads from url loaded in WKWebView
更新:检查此线程是否有帮助 如何在基于 WKWebView 的桌面应用程序中显示检查器?