3

众所周知,我们可以使用 WKContentRuleList 来阻止 url 请求/cookie 或在 WKWebView 中执行其他操作。有什么方法可以让我们根据 WKContentRuleList 知道 WKWebView 阻止了什么?

4

3 回答 3

1

我有一定程度的把握,没有一种简单的方法可以使用公共 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
        }
    }
}

于 2020-08-07T21:15:25.800 回答
0

您可以使用 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

于 2021-03-26T13:44:20.253 回答
0

您的意思是要获取列表中的所有规则还是要查找规则列表?然后你可以使用下面的 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 的桌面应用程序中显示检查器?

于 2020-08-01T22:09:00.783 回答