在adblockplus的广告过滤中实际上包括广告过滤和广告隐藏,前者是通过extensions机制提供的网络接口回调根据过滤规则判断对应的资源是否需要下载,如果此资源URL符合过滤规则则屏蔽此资源的下载,从而达到过滤的目的;而广告隐藏则是根据主页的host在网页中注入一段css样式,将某些id选择器和类选择器的样式设置为{ display: none !important; },从而将符合规则的元素在网页上不显示;此外,需要做的另外一件事情就是讲某些被过滤掉资源的网页元素隐藏,当然,并不是所有的这些元素都能被隐藏。
在adblockplus.js文件中主要实现了规则文件的下载、规则归类解析、规则判定等核心工作;与这些规则操作相关的几个类关系大体上如下图所示(通过原型链prototype):
1)规则解析:直接从FilterStorage.loadFromDisk()方法入手,读取读取规则文件,并按行调用Filter.fromText方法进行解析:
上述流程中将每行规则文本解析成相应的有效规则条目或无效规则条目InvalidFilter,最终通过addFilter加入到规则列表中。其中几个重要的正则表达式如下:
Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/; //用于将隐藏规则各个字段分割出来
Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/; //用于探测是否符合过滤规则
Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/; //用于将过滤规则$符号后面的各个字段分割开出来
2)过滤规则匹配:由于ext.webRequest.onBeforeRequest.addListener(ext.webRequest.onBeforeRequest.addListener(onBeforeRequest););注册了onBeforeRequest监听器,当有资源请求时会触发此监听器:通过matchesAny将资源url按照/[a-z0-9%]{3,}/g分割成各个字符串,然后在规则关键字列表中查看是否存在这些子字符串,并依次查找匹配这些关键字对应的规则:this.regexp.test(location) && (RegExpFilter.typeMap[contentType] & this.contentType) != 0 && (this.thirdParty == null || this.thirdParty == thirdParty) && this.isActiveOnDomain(docDomain, sitekey);此方法最终返回匹配的规则(如果有的话,无则返回null),通过 !(filter instanceof BlockingFilter);判定此url是否需要被过滤。
3)过滤规则匹配:在include.preload.js文件中通过document.createElement("shadow")在网页上创建shadow节点(如果可以),通过document.createElement("style");创建style元素(将此元素添加到shadow节点或head节点中),并发送"get-selectors"到后台消息处理监听器上获取可用的css样式:通过ElemHide.getSelectorsForDomain方法依次查找每条过滤规则并根据每条规则的约定构造最终的样式列表;在获得了样式列表后通过回调方法setElemhideCSSRules将样式分组后通过style.sheet.insertRule(selector + " { display: none !important; }", i);方法添加到上面创建的style元素上;最终通过chrome的渲染机制使得相应的元素得以隐藏。
以上简单的分析了广告过滤的原理,很多细节没有提及,其机制看起来也没有多复杂,但要真正看懂每一个细节还是需要花点功夫的,以后慢慢品味。其中涉及的类如下: