From d559b91ca1e176beb74cf2b74a00485ca7280e55 Mon Sep 17 00:00:00 2001 From: cyfung1031 <44498510+cyfung1031@users.noreply.github.com> Date: Mon, 20 Oct 2025 03:35:35 +0900 Subject: [PATCH] xhr --- src/app/service/offscreen/gm_api.ts | 5 +-- src/app/service/service_worker/gm_api.ts | 44 ++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/app/service/offscreen/gm_api.ts b/src/app/service/offscreen/gm_api.ts index d63f105f6..fc68c4675 100644 --- a/src/app/service/offscreen/gm_api.ts +++ b/src/app/service/offscreen/gm_api.ts @@ -24,11 +24,12 @@ export default class GMApi { statusText: xhr.statusText, // header由service_worker处理,但是存在特殊域名(例如:edge.microsoft.com)无法获取的情况,在这里增加一个默认值 responseHeaders: xhr.getAllResponseHeaders(), - responseType: details.responseType, + responseType: details.responseType, // **** 概念错误. Xhr的responseType是用来在最终内容判断时做相应的处理,不是拿来直接当 xhr的responseType. 假如 response status 不是 2xx, response type 不是 basic, responseType 就不会是 Xhr 的要求 responseType **** }; if (xhr.readyState === 4) { - const responseType = details.responseType?.toLowerCase(); + const responseType = details.responseType?.toLowerCase(); // **** 概念错误 **** if (responseType === "arraybuffer" || responseType === "blob") { + // **** 概念错误. Blob型 (包括stream) 的response都应该用progressive body getReader 不断 read. 而不用考虑是progressive与否 **** const xhrResponse = xhr.response; if (xhrResponse === null) { response.response = null; diff --git a/src/app/service/service_worker/gm_api.ts b/src/app/service/service_worker/gm_api.ts index 5e1e22e44..de91ec641 100644 --- a/src/app/service/service_worker/gm_api.ts +++ b/src/app/service/service_worker/gm_api.ts @@ -629,7 +629,7 @@ export default class GMApi { status: response.status, statusText: response.statusText, responseHeaders: respHeader, - responseType: config.responseType, + responseType: config.responseType, // **** 概念错误. Xhr的responseType是用来在最终内容判断时做相应的处理,不是拿来直接当 xhr的responseType. 假如 response status 不是 2xx, response type 不是 basic, responseType 就不会是 Xhr 的要求 responseType **** }; if (resultParam) { respond.status = respond.status || resultParam.statusCode; @@ -683,10 +683,50 @@ export default class GMApi { if (!reader) { throw new Error("read is not found"); } - const readData = ({ done, value }: { done: boolean; value?: Uint8Array }) => { + const chunks = [] as Uint8Array[]; + let receivedLength = 0; + const readData = async ({ done, value }: { done: boolean; value?: Uint8Array }) => { + if (value?.length) { + chunks.push(value); + receivedLength += value.length; + } if (done) { const data = this.dealFetch(config, resp, 4, resultParam); data.responseHeaders = resultParam.responseHeader || data.responseHeaders; + + // 真正的 responseType 在这里处理! + + if (resp.type === "basic" && resp.ok) { + if (!data.responseType || data.responseText === "text") { + // 因为现在用了 body.getReader().read(), 所以 response body 被 consume了,不能用 await response.text() + + // 检查 Content-Type 中的 charset + const contentType = resp.headers.get("content-type") || ""; + const charsetMatch = contentType.match(/charset=([^;]+)/i); + const charset = charsetMatch ? charsetMatch[1].toLowerCase() : "utf-8"; + + // 合并分片(chunks) + const chunksAll = new Uint8Array(receivedLength); + let position = 0; + for (const chunk of chunks) { + chunksAll.set(chunk, position); + position += chunk.length; + } + + // 使用检测到的 charset 解码 + let textDecoded; + try { + textDecoded = new TextDecoder(charset).decode(chunksAll); + } catch (e: any) { + throw new Error(`Failed to decode response with charset ${charset}: ${e.message}`); + } + if (typeof textDecoded === "string" && textDecoded.length > 0) { + data.response = textDecoded; + data.responseType = "text"; + } + } + // 还要处理其他类型 + } msgConn.sendMessage({ action: "onreadystatechange", data: data,