Conversation
2b45158 to
e52d4be
Compare
404 時需取
|
|
这个好了吗?如果没有就先转为 draft,好了再转换回来 |
好吧。先改成draft |
你说得对。要找一下问题在哪 |
src/app/service/content/gm_api.ts
Outdated
| if (data.readyState === 4 && data.ok) { | ||
| if (resultType === 1) { | ||
| // stream type | ||
| controller = undefined; // GC用 | ||
| } else if (resultType === 2) { | ||
| // buffer type | ||
| responseText = false; // 设为false 表示需要更新。在 get setter 中更新 | ||
| response = false; // 设为false 表示需要更新。在 get setter 中更新 | ||
| responseXML = false; // 设为false 表示需要更新。在 get setter 中更新 |
There was a problem hiding this comment.
我调试了一下,大概是这一段代码和下面这个导致的,我觉得不应该处理statusCode,而且我看还有很多地方处理 3xx 的,3xx 实际上也可以不做重定向操作,用来传数据
大概是因为404,的ok变为了false,这里没进入处理 responseText = false; 在 makeXHRCallbackParam_ 中的responseText处理出了问题
responseText=null,然后就不会处理responseText的值了
There was a problem hiding this comment.
而且我看还有很多地方处理 3xx 的,3xx 实际上也可以不做重定向操作,用来传数据
你指的是哪里?
我记得没有特别做什么重定向的处理
https://developer.mozilla.org/en-US/docs/Web/API/Response/ok
ok 的定义是 200-299
虽然没实际传回 GM API 结果,暂时先保留吧
83f96f4 to
fb61035
Compare
fb61035 to
1fc53ea
Compare
| }; | ||
|
|
||
| // stackAsyncTask 确保 nwReqIdCollects{"stdUrl"} 单一执行 | ||
| await stackAsyncTask(`nwReqIdCollects::${stdUrl}`, async () => { |
There was a problem hiding this comment.
通过x-sc-request-marker关联,还要这个防止并发吗?而且怎么还加上了sleep
There was a problem hiding this comment.
用 GM_xhr API 发出时,加这个在 onBeforeRequest 阶段关联一下
避免无法在 onBeforeSendHeaders 阶段关联。除了modifier header DNR 次序问题,还可能会有 onBeforeRequest -> onErrorOccurred 而没有 onBeforeSendHeaders 的可能性
stackAsyncTask( ``nwReqIdCollects::${stdUrl}`` , async () => { 只会等一下GM API 的执行
请求本身没有被锁
因为是用 url 判断。所以 GM API 同一个网址时,要等上一个网址发出请求 & 尝试关联。 未能关联也没所谓,因为有 x-sc-request-marker,在 onBeforeSendHeaders 也会关联
两次尝试关联失败就没办法了哈哈
(
sleep(1) 是用来等一下 onBeforeRequest 的执行。 期间的请求有只有一个匹对就能关联。 正常情况 (DNR次序OK & 没有ERROR),在 onBeforeSendHeaders 时会再用 x-sc-request-marker 关联。
sleep(1) 是为了
假如在GM API执行期间,扩展有其他对同一网址的网络请求, onBeforeRequest阶段的关联就会被视作失败(因为多于一个判断不了)
p.s. sleep(1) 是1ms 不是1秒
There was a problem hiding this comment.
验证了一下,下面这种情况会直接走到 onErrorOccurred 没有 onBeforeSendHeaders:
chrome.webRequest.onBeforeRequest.addListener(
(details) => {console.log("onBeforeRequest",details);}, {
urls: ["<all_urls>"],
types: ["xmlhttprequest"],
tabId: -1
});
chrome.webRequest.onBeforeSendHeaders.addListener(
(details) => {console.log(details);}, {
urls: ["<all_urls>"],
types: ["xmlhttprequest"],
tabId: -1
},["requestHeaders","extraHeaders"]);
const controller = new AbortController()
const signal = controller.signal;
fetch("https://example.com",{headers:{"x-sc-dnr-test-header":"1234"},signal});
controller.abort();我觉得这块的实现逻辑还是有点复杂了,如果abort/失败了,走不到onBeforeSendHeaders和之后的东西,关联不关联这个id也无所谓了吧,我觉得像之前,只用在onBeforeSendHeaders里面关联markerID和requestId就好了呀,什么锁,什么sleep都不用考虑的。
我理解这块的目的就是为了准确的关联 markerID和requestId 吧,如果abort/失败了,走不到onBeforeSendHeaders和之后的东西,关联不关联这个id也无所谓了(因为没有信息需要处理),如果成功了走到了onBeforeSendHeaders,那么就能通过 x-sc-request-marker 准确的关联到 requestId
There was a problem hiding this comment.
我觉得像之前,只用在onBeforeSendHeaders里面关联markerID和requestId就好了呀,什么锁,什么sleep都不用考虑的。
好吧好吧。你不想的话,那就这样算了。
只好等失效或出问题再改回来
There was a problem hiding this comment.
直接走到 onErrorOccurred 没有 onBeforeSendHeaders
也可以针对这个改一下,不会锁死的
不过你喜欢吧。是我想多了
There was a problem hiding this comment.
如果成功了走到了onBeforeSendHeaders,那么就能通过 x-sc-request-marker 准确的关联到 requestId
问题是 DNR 顺序真的有可能在 onBeforeSendHeaders 前发生
所以才要加这块
不过你认为现在没有这问题就不用理会也没办法
There was a problem hiding this comment.
问题是 DNR 顺序真的有可能在 onBeforeSendHeaders 前发生
但是和顺序没有关系呀,onBeforeSendHeaders 是获取原始的header,始终可以获取到 x-sc-request-marker 然后关联到requestId 的
There was a problem hiding this comment.
问题是 DNR 顺序真的有可能在 onBeforeSendHeaders 前发生
但是和顺序没有关系呀,onBeforeSendHeaders 是获取原始的header,始终可以获取到 x-sc-request-marker 然后关联到requestId 的
上面解释过了。官方文档从没有说明过两者的次序前后
只是Chrome 是这样做
Vivaldi 用Servo,可能又不一样
Firefox的MV3又更不一样
不过算了。这个PR不搞这个。不然没完没了
There was a problem hiding this comment.
这个倒是确实,我只考虑了Chrome,其它平台不好说,不过在还没发生前先这样吧,我喜欢这样 🫠
0fd3343 to
4ac2789
Compare
|
勉强无幸福,只好退回 698d762 |
辛苦了🫡 |
27bd319 to
de41130
Compare
There was a problem hiding this comment.
Pull Request Overview
这是一个对 GM_xmlhttpRequest 及相关代码的重大重构,旨在提高性能、减少对 DNR 的依赖,并改进架构设计。主要改进包括使用 onBeforeRequest 替代 modifyHeaders 进行 requestId 关联、支持 fetch API、优化内存管理(通过 chunk 传输避免在 service worker 中缓存大数据)以及新增 FetchXHR 类以支持流式传输。
主要变更:
- 新增核心模块:
xhr_data.ts(数据编解码)、bg_gm_xhr.ts(后台 XHR 处理)、fetch_xhr.ts(Fetch-based XHR 实现)、gm_xhr.ts(GM API XHR 策略与辅助) - 重构 GM API 层:
gm_api.ts、gm_xhr.ts(content)、offscreen 和 service worker 的 GM API - 更新类型定义、测试框架和 mock 系统
- 优化
GM_download实现,支持 native/browser 两种模式
Reviewed Changes
Copilot reviewed 35 out of 37 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
src/pkg/utils/xhr/xhr_data.ts |
新增数据编解码模块,支持多种数据类型的序列化与反序列化 |
src/pkg/utils/xhr/bg_gm_xhr.ts |
新增后台 GM XHR 核心逻辑,处理请求执行与 chunk 传输 |
src/pkg/utils/xhr/fetch_xhr.ts |
新增 FetchXHR 类,提供类 XMLHttpRequest 的 fetch 封装 |
src/pkg/utils/datatype.ts |
新增数据类型转换工具(base64、Uint8Array、chunk 等) |
src/pkg/utils/uuid.ts |
新增 UUID 生成工具 |
src/pkg/utils/utils.ts |
添加 URL 规范化工具 |
src/app/service/service_worker/gm_api/gm_api.ts |
重构 service worker GM API,优化 XHR 和 download 实现 |
src/app/service/service_worker/gm_api/gm_xhr.ts |
新增 XHR 策略类和辅助映射 |
src/app/service/service_worker/gm_api/gm_api.test.ts |
更新测试以适配新的 getConnectMatched API |
src/app/service/content/gm_api/gm_xhr.ts |
新增 content 端 XHR 处理逻辑 |
src/app/service/content/gm_api/gm_api.ts |
重构 content GM API,整合新的 XHR 和 download 实现 |
src/app/service/offscreen/gm_api.ts |
简化 offscreen GM API,复用 BgGMXhr |
src/types/scriptcat.d.ts |
更新类型定义,添加新参数和泛型支持 |
src/types/main.d.ts |
更新 GMSend 类型定义 |
tests/ |
新增和更新测试文件及 mock 系统 |
packages/chrome-extension-mock/web_reqeuest.ts |
更新 webRequest mock 支持 |
| if (value) { | ||
| progressEvents++; | ||
| loaded += value.length; | ||
| if (loaded != null) lastLoaded = loaded; |
There was a problem hiding this comment.
This guard always evaluates to true.
| if (loaded != null) lastLoaded = loaded; | |
| lastLoaded = loaded; |
| "fail" | ||
| ); | ||
| } | ||
| function skip(msg) { |
There was a problem hiding this comment.
Unused function skip.
| name: 'anonymous TEST - set cookie "abc"', | ||
| async run(fetch) { | ||
| // httpbin echoes Cookie header in headers | ||
| const { res } = await gmRequest({ |
There was a problem hiding this comment.
Unused variable res.
| const { res } = await gmRequest({ | |
| await gmRequest({ |
| name: "anonymous TEST - delete cookies", | ||
| async run(fetch) { | ||
| // httpbin echoes Cookie header in headers | ||
| const { res } = await gmRequest({ |
There was a problem hiding this comment.
Unused variable res.
| const { res } = await gmRequest({ | |
| await gmRequest({ |
| name: 'anonymous: true[2] - set cookie "def"', | ||
| async run(fetch) { | ||
| // httpbin echoes Cookie header in headers | ||
| const { res } = await gmRequest({ |
There was a problem hiding this comment.
Unused variable res.
| const { res } = await gmRequest({ | |
| await gmRequest({ |
| name: "anonymous TEST - delete cookies", | ||
| async run(fetch) { | ||
| // httpbin echoes Cookie header in headers | ||
| const { res } = await gmRequest({ |
There was a problem hiding this comment.
Unused variable res.
| const { res } = await gmRequest({ | |
| await gmRequest({ |
| function getHeader(headersStr, key) { | ||
| const lines = (headersStr || "").split(/\r?\n/); | ||
| const line = lines.find((l) => l.toLowerCase().startsWith(key.toLowerCase() + ":")); | ||
| return line ? line.split(":").slice(1).join(":").trim() : ""; | ||
| } |
There was a problem hiding this comment.
Unused function getHeader.
| function getHeader(headersStr, key) { | |
| const lines = (headersStr || "").split(/\r?\n/); | |
| const line = lines.find((l) => l.toLowerCase().startsWith(key.toLowerCase() + ":")); | |
| return line ? line.split(":").slice(1).join(":").trim() : ""; | |
| } |
| res = await Promise.race([ | ||
| gmRequest({ | ||
| method: "GET", | ||
| url, | ||
| redirect: "error", | ||
| fetch, | ||
| }), | ||
| new Promise((resolve) => setTimeout(resolve, 4000)), | ||
| ]); |
There was a problem hiding this comment.
The value assigned to res here is unused.
src/pkg/utils/xhr/bg_gm_xhr.ts
Outdated
| rawData instanceof Uint8Array | ||
| ) { | ||
| // | ||
| } else if (rawData && typeof rawData === "object" && !(rawData instanceof ArrayBuffer)) { |
There was a problem hiding this comment.
This use of variable 'rawData' always evaluates to true.
| } else if (rawData && typeof rawData === "object" && !(rawData instanceof ArrayBuffer)) { | |
| } else if (typeof rawData === "object" && !(rawData instanceof ArrayBuffer)) { |

PR 901 — 重构
GM_xmlhttpRequest及相关代码pr-reconstruct-xhrAPI4→main相关:
GM_xmlhttpRequest的实作 #862GM_xmlhttpRequest在连接@connect以外的网域下,是弹出询问用户批准,而不是跟TM一样返回error? #884 (日后增加选项)日后要增加的选项
@connect没指定时的行为 - 用户可在扩充设置重点改善
不使用
modifyHeaders关联 requestIdmodifyHeaders关联 requestId 有隐藏风险。API说明没有解释modifyHeaders和onBeforeSendHeaders的次序问题。GM_xmlhttpRequest无法使用 (modifyHeaders先執行的話,onBeforeSendHeaders不能关联 requestId)onBeforeRequest确保 xhr/fetch 的 requestId 必能取得减少依赖 DNR
modifyHeaders,但不再是必须 (生成 DNR Rule 时要执行一下 cacheInstance 的chrome.storage.session.get和chrome.storage.session.set)useFetch
fetch: trueredirect: "follow"|"manual"|"error"(跟随TM。fetch判断。虽然用 xhr 应该也可以做到)anonymous: true(保留旧写法的modifyHeaders。但加入真正的anonymous处理:credentials = "omit"。不包括referrerPolicy = "no-referrer")responseType: "stream"(xhr只能在最后载入完成才能取结果)GM_xmlhttpRequestcontent/page
GM_xmlhttpRequest(C) <-> service_workerGM_xmlhttpRequest(S) <-> service_workerbgXhrInterface/ offscreenbgXhrInterface(BI) ------ bgXhrRequestFn (BR) --- XMLHttpRequest / FetchXHR(BI) 会在 service_worker / offscreen 处理 MessageConnect 和 bgXhrRequestFn 之问的资讯传递
(BR) 会在 service_worker / offscreen 操作 XMLHttpRequest / FetchXHR
FetchXHR -> 不单是stream, 每抓到一点资讯都会传到 (C),service_worker 不储存实际 chunk
XMLHttpRequest -> 歴史原因,资讯只在最后完成后能抓到,然后分成 2mb chunk 传回 content (完成前,记忆储在 xhr 内部)
最终 (C) 取得所有 chunk,然后结合
因为记忆都在 content/page, tab关掉就能释放
实测操作,每个测试,速度都比TM快100~200ms
(BI) / (BR) 只处理 text (需要考虑 charset encoding) / byte (arraybuffer)
(C) 接收 text / byte 后再转化成需要的 response / responseText / responseXML
GM_downloadnativeGM_xmlhttpRequest(C) 取得完整资料,在 content/page 建立 BlobURL,传到 service_worker 呼叫 API 下戴browserchrome.downloadsAPI 系列限制,不支持 onprogress)概要
本 PR 重构了
GM_xmlhttpRequest的实现及其相关基础设施。引入了新的后台/核心模块,更新了 Service Worker 接口,修改了多个上下文中的 GM API(content / offscreen / service worker),并扩展了测试与 mock 以验证网络行为。
重点包括内部结构重命名、类型更明确、以及后台中 XHR 的执行与路由方式调整。
变更统计
提交数: 2
294a04b— “重构GM_xmlhttpRequest及相关代码”(主要重构)5397a85— “删除无用代码”修改文件: 30(29 个
.ts+ 1 个.json)净变动行数: +3,674 / −786
变更范围(按模块分类)
新增/更新的 XHR 核心与数据层
src/pkg/utils/xhr_bg_core.ts— 后台核心逻辑,负责GM_xmlhttpRequest的执行与路由。src/pkg/utils/xhr_data.ts— 数据与类型定义模块,为 XHR 流程提供共享 DTO 类型。src/app/service/service_worker/xhr_interface.ts— Service Worker 接口定义,规定 GM XHR 与后台的通信协议。GM API 接口(多上下文)
src/app/service/content/gm_api.ts— 内容脚本的 GM API 更新。src/app/service/offscreen/gm_api.ts— Offscreen 上下文 GM API 更新。src/app/service/service_worker/gm_api.ts— Service Worker 上下文 GM API 更新。src/app/service/service_worker/gm_api.test.ts— 测试调整以匹配新的 XHR 行为。Service Worker 与系统层调整
src/app/service/service_worker/{index,permission_verify,resource,script_update_check,system,types}.ts—更新以支持新的 XHR 架构和类型。
公共工具函数更新
src/pkg/utils/{script,sw_fetch,utils,utils_datatype,uuid}.ts— 工具方法和类型调整,适配新的 XHR 流程。类型定义文件
src/types/{main.d.ts,scriptcat.d.ts}— 更新或扩展类型定义以反映新的 GM API 结构。测试与测试基础设施
tests/runtime/gm_api.test.ts,tests/{shared.ts,utils.test.ts,utils.ts,vitest.setup.ts}—增加或更新测试用例以覆盖 GM XHR 的行为。
Mocks(模拟层):
packages/chrome-extension-mock/runtime.ts— 小幅修改(监听器、ID 等)。packages/chrome-extension-mock/web_reqeuest.ts— 大幅调整(+49/−30),模拟 Chrome 的 webRequest 层供 GM XHR 使用。packages/network-mock.ts— 新的网络请求模拟工具。工具链
package.json— 测试命令调整:增加--test-timeout=500,缩短单测超时时间。开发者注记与附件
PR 描述中嵌入了一个完整的 用户脚本测试工具(Exhaustive Test Harness v2),
用于验证
GM_xmlhttpRequest的以下行为:responseType('' | 'json' | 'arraybuffer' | 'blob')overrideMimeType、超时、ontimeout、onprogressonload(包括非 2xx 状态)onerror(DNS/被拦截主机)onabort、anonymous模式测试脚本使用
httpbun.com接口以保证响应的确定性。设计动机(从结构推断)
xhr_bg_core.ts与xhr_interface.ts将后台执行逻辑与消息通信协议分离,降低耦合度。xhr_data.ts与.d.ts文件提供更严格的类型约束,保证请求/响应结构一致。chrome-extension-mock、network-mock)使 XHR 行为可在单元测试层面稳定验证。潜在兼容性与行为变更(审查重点)
onload与onerror对非 2xx 响应、DNS 失败、被阻止主机的处理是否符合 Greasemonkey/Tampermonkey 行为。finalUrl: 验证多重跳转时finalUrl的正确性(测试涵盖/redirect-to)。responseType一致性: 确认二进制类型(arraybuffer/blob)与 JSON 解码逻辑正确。onprogress和ontimeout。@connect域名检查 / 匿名模式: 验证拒绝连接时的错误路径与无 Cookie 模式的行为。xhr_interface.ts与 SW 层在消息序列化、错误传递、abort 信号处理方面的更改。手动测试清单(建议)
response、responseText、status、finalUrl。arraybuffer/blob大小匹配。finalUrl更新且 header 持续有效。ontimeout,且不会触发onload。onerror回调内容。.abort(),验证仅触发onabort。anonymous模式下不应携带 Cookie;基本认证请求应成功附带认证头。(这些用例对应 PR 中的测试脚本内容。)
受影响文件(总览)
根目录/工具链:
package.jsonMocks:
packages/chrome-extension-mock/{runtime.ts, web_reqeuest.ts}packages/network-mock.tsGM API:
src/app/service/content/gm_api.tssrc/app/service/offscreen/gm_api.tssrc/app/service/service_worker/{gm_api.ts, gm_api.test.ts, index.ts, permission_verify.ts, resource.ts, script_update_check.ts, system.ts, types.ts, xhr_interface.ts}工具/核心:
src/pkg/utils/{script.ts, sw_fetch.ts, utils.ts, utils_datatype.ts, uuid.ts, xhr_bg_core.ts, xhr_data.ts}类型定义:
src/types/{main.d.ts, scriptcat.d.ts}测试:
tests/runtime/gm_api.test.ts,tests/{shared.ts, utils.test.ts, utils.ts, vitest.setup.ts}建议的审查流程
.d.ts与gm_api*.ts中的GM_xmlhttpRequest参数与回调定义。xhr_interface.ts(消息格式)、xhr_bg_core.ts(请求生命周期、错误/中止逻辑)、xhr_data.ts(序列化)。web_reqeuest.ts、network-mock.ts,确认测试文件确实覆盖新的行为路径。迁移 / 发布说明(草案)
xhr_bg_core.ts、xhr_data.ts、xhr_interface.ts)。测试代码