Firefox自定义广告拦截插件中异步阻止请求的实现问题
解决Firefox广告拦截插件中webRequest.filterResponseData与blocking模式的异步冲突问题
问题核心
你当前的矛盾点在于:onBeforeRequest的blocking监听器返回Promise时,浏览器会挂起请求等待Promise决议,但此时请求尚未进入响应阶段,导致filterResponseData无法初始化并触发事件;而不返回Promise的话,无法在响应分析完成后动态决定是否拦截请求。
可行解决思路
1. 拆分拦截逻辑到不同webRequest阶段
将URL预分析和响应分析拆分到两个监听阶段:
onBeforeRequest:仅做URL特征匹配,对明确符合拦截规则的请求直接返回{cancel: true}阻止,无需进入响应分析流程。onHeadersReceived:处理需要响应分析的请求,此时服务器已返回响应头,响应流可被正常拦截,适合初始化filterResponseData。
2. 调整响应分析后的拦截方式
响应分析完成后,无需通过返回{cancel: true}阻止请求(此时请求已进入响应阶段),而是通过截断响应流或返回空白内容达到拦截效果。
修改后的代码示例
background.js
// URL预分析:判断是否需要进入响应分析 function preCheckUrl(details) { // 替换为你的URL正则检查逻辑 const shouldAnalyzeResponse = true; // 示例:假设需要分析响应 if (!shouldAnalyzeResponse) { // 直接拦截URL匹配的请求 return {cancel: true}; } // 无需拦截,继续请求 return {cancel: false}; } // 响应分析与处理 async function analyzeResponse(details) { let responseChunks = []; const filter = browser.webRequest.filterResponseData(details.requestId); // 收集响应数据块 filter.ondata = (event) => { responseChunks.push(event.data); }; // 等待响应流结束 await new Promise((resolve) => { filter.onstop = resolve; filter.onerror = resolve; }); // 合并数据并执行响应特征检查 const combinedBlob = new Blob(responseChunks); const responseText = await combinedBlob.text(); const shouldBlock = /广告特征正则/.test(responseText); // 替换为你的响应特征正则 if (shouldBlock) { // 截断响应,不给页面返回内容 filter.disconnect(); } else { // 将收集到的内容重新写入流,保证页面正常加载 responseChunks.forEach(chunk => filter.write(chunk)); filter.disconnect(); } return {}; } // 注册监听器 browser.webRequest.onBeforeRequest.addListener( preCheckUrl, {urls: blocked_domains}, ["blocking"] ); browser.webRequest.onHeadersReceived.addListener( analyzeResponse, {urls: blocked_domains, types: ["script", "image", "stylesheet", "xmlhttprequest"]}, // 按需指定拦截资源类型 ["blocking"] );
关键说明
- 阶段选择原因:
onHeadersReceived是第一个可访问响应流的blocking阶段,此时filterResponseData能正常绑定事件并捕获响应内容,不会出现Promise挂起导致的流初始化失败问题。 - 响应拦截逻辑:当分析后需要拦截时,调用
filter.disconnect()截断响应流,页面无法获取完整内容;若无需拦截,则将收集到的内容重新写入流,保证页面正常加载。 - 性能优化建议:57000个域名的数组匹配可先做预编译,或使用
URL对象提取域名后再匹配,避免数组遍历或正则匹配的性能损耗。
内容的提问来源于stack exchange,提问作者Martynas Jurkėnas
相关产品推荐
相关产品推荐

