现有简单Firefox扩展是否可以改造为Greasemonkey脚本?
结论
你的这款扩展可以改造为Greasemonkey类用户脚本,不过因为底层API权限差异,无法100%复现原扩展的拦截时机和覆盖范围,优先推荐保留扩展形式使用,如果一定要转脚本可以参考下方的改造方案。
核心差异说明
原扩展的核心逻辑依赖Firefox扩展专属的browser.webRequest API,这个API可以在请求响应头返回的第一时间完成拦截、取消操作,拦截覆盖所有网页发起的请求,优先级很高。
而Greasemonkey类用户脚本的运行时机是在页面DOM加载前后,没有直接调用webRequest API的权限,只能通过页面层的拦截手段实现类似效果,拦截范围和及时性都弱于原生扩展。
改造后的用户脚本参考实现
// ==UserScript== // @name MIME类型下载拦截 // @namespace http://tampermonkey.net/ // @version 0.1 // @description 拦截非页面类资源下载弹窗、跳转 // @match *://*/* // @exclude *://somesite.com/* // @grant none // @run-at document-start // ==/UserScript== 'use strict'; // 拦截带download属性的a标签跳转下载 document.addEventListener('click', e => { const target = e.target.closest('a'); if (target && target.hasAttribute('download')) { e.preventDefault(); console.log('拦截到download属性的下载请求', target.href); return; } }, true); // 拦截window.open触发的跳转 const originalOpen = window.open; window.open = function(url) { console.log('拦截到window.open请求', url); return null; }; // 拦截fetch请求 const originalFetch = window.fetch; window.fetch = async function(...args) { const response = await originalFetch.apply(this, args); const contentType = response.headers.get('content-type'); const contentDisposition = response.headers.get('content-disposition'); // 复用原扩展的MIME匹配规则 const needBlock = () => { if (contentDisposition) return true; if (!contentType) return false; if (contentType.startsWith('image')) return false; if (contentType.startsWith('text')) return false; if (contentType.startsWith('font')) return false; if (contentType.startsWith('application/json')) return false; if (contentType.startsWith('application/javascript')) return false; return true; }; if (needBlock()) { console.log('拦截到符合规则的fetch请求', args[0]); return new Response(null, {status: 204}); } return response; }; // 拦截XMLHttpRequest请求 const originalXHROpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function(method, url) { this._url = url; this.addEventListener('readystatechange', () => { if (this.readyState === 2) { const contentType = this.getResponseHeader('content-type'); const contentDisposition = this.getResponseHeader('content-disposition'); const needBlock = () => { if (contentDisposition) return true; if (!contentType) return false; if (contentType.startsWith('image')) return false; if (contentType.startsWith('text')) return false; if (contentType.startsWith('font')) return false; if (contentType.startsWith('application/json')) return false; if (contentType.startsWith('application/javascript')) return false; return true; }; if (needBlock()) { console.log('拦截到符合规则的XHR请求', this._url); this.abort(); } } }); return originalXHROpen.apply(this, arguments); };
两种方案对比
- 原扩展方案:拦截范围全覆盖、响应速度快、不会漏拦截,唯一缺点是需要单独安装扩展
- 用户脚本方案:无法拦截浏览器直接跳转的非页面资源请求、部分特殊场景的请求无法覆盖,但是无需单独安装扩展,可以直接在油猴管理器中导入使用
内容的提问来源于stack exchange,提问作者Scorpys
相关产品推荐
相关产品推荐

