Ver Fonte

fix: text chunks mixed with plain messages

* chunked (big), plain (small), plain, chunked, ...
* unbork c59b46b8 for loadend
tophf há 2 anos atrás
pai
commit
96653c8922

+ 1 - 11
src/background/utils/requests.js

@@ -92,7 +92,6 @@ function xhrCallbackWrapper(req, events, blobbed, chunked, isJson) {
   let getChunk;
   let fullResponse = null;
   let response;
-  let responseText;
   let responseHeaders;
   let sent = true;
   let sentTextLength = 0;
@@ -107,7 +106,7 @@ function xhrCallbackWrapper(req, events, blobbed, chunked, isJson) {
     const shouldNotify = events.includes(type);
     const isEnd = type === 'loadend';
     const readyState4 = xhr.readyState === 4;
-    if (!shouldNotify && !isEnd || readyState4 && sentReadyState4) {
+    if ((!shouldNotify || readyState4 && sentReadyState4) && !isEnd) {
       return;
     }
     if (readyState4) { // Firefox duplicates it randomly, #1862
@@ -119,12 +118,6 @@ function xhrCallbackWrapper(req, events, blobbed, chunked, isJson) {
     if (fullResponse !== xhr[kResponse]) {
       fullResponse = response = xhr[kResponse];
       sent = false;
-      try {
-        responseText = xhr[kResponseText];
-        if (responseText === response) responseText = ['same'];
-      } catch (e) {
-        // ignore if responseText is unreachable
-      }
       if (response) {
         if ((tmp = response.length - sentTextLength)) { // a non-empty text response has `length`
           chunked = tmp > TEXT_CHUNK_SIZE;
@@ -174,9 +167,6 @@ function xhrCallbackWrapper(req, events, blobbed, chunked, isJson) {
         [kResponseHeaders]: responseHeaders !== (tmp = getResponseHeaders())
           ? (responseHeaders = tmp)
           : null,
-        [kResponseText]: shouldSendResponse
-          ? responseText
-          : null,
       } : null,
     });
     if (isEnd) {

+ 7 - 25
src/injected/content/requests.js

@@ -68,10 +68,13 @@ addBackgroundHandlers({
         response = await importBlob(req, response);
       } else if (msg.chunked) {
         processChunk(req, response);
-        response = combineChunks(req, msg);
-      } else if (!req[kXhrType]) {
-        if (req[kResponse]) response = (req[kResponse] += response);
-        else req[kResponse] = response;
+        response = req[CHUNKS];
+        delete req[CHUNKS];
+        if (req[kXhrType]) {
+          response = req[kXhrType] === 'blob'
+            ? new SafeBlob([response], { type: msg.contentType })
+            : response::getTypedArrayBuffer();
+        } // else: sending text chunks as-is to avoid memory overflow due to frequent concatenation
       }
       data[kResponse] = response;
     }
@@ -138,27 +141,6 @@ function processChunk(req, data, msg) {
   }
 }
 
-/**
- * @param {GMReq.Content} req
- * @param {GMReq.Message.BG} msg
- * @returns {Blob|Uint8Array|string}
- */
-function combineChunks(req, msg) {
-  let res;
-  if (req[kXhrType]) {
-    res = req[CHUNKS];
-    res = req[kXhrType] === 'blob'
-      ? new SafeBlob([res], { type: msg.contentType })
-      : res::getTypedArrayBuffer();
-  } else {
-    res = req[kResponse] || '';
-    req[CHUNKS]::forEach(str => res += str);
-    req[kResponse] = res;
-  }
-  delete req[CHUNKS];
-  return res;
-}
-
 /** Doing it here because vault's SafeResponse+blob() doesn't work in injected-web */
 async function encodeBody(body, mode) {
   if (mode === 'fd') {

+ 30 - 11
src/injected/web/requests.js

@@ -6,6 +6,7 @@ const kContentTextHtml = 'text/html';
 const kResponse = 'response';
 const kResponseXML = 'responseXML';
 const kDocument = 'document';
+const kRaw = 'raw';
 const EVENTS_TO_NOTIFY = [
   'abort',
   'error',
@@ -69,20 +70,31 @@ addHandlers({
     const {
       [kResponse]: response,
       [kResponseHeaders]: headers,
-      [kResponseText]: text,
     } = data;
     if (response != null) {
-      req.raw = response;
+      if (req[kXhrType]) {
+        req[kRaw] = response;
+        setOwnProp(data, kResponseText, null);
+      } else {
+        const raw = req[kRaw] || (req[kRaw] = []);
+        if (isString(response)) {
+          safePush(raw, response);
+        } else {
+          for (const chunk of response) safePush(raw, chunk);
+        }
+        if (raw.length === 1) {
+          setOwnProp(data, kResponseText, raw[0]);
+        } else {
+          setOwnProp(data, kResponseText, safeBind(parseRaw, data, req, msg, kResponseText),
+            true, 'get');
+        }
+      }
     }
     if (headers != null) {
       req[kResponseHeaders] = headers;
     }
-    if (text != null) {
-      req[kResponseText] = getOwnProp(text, 0) === 'same' ? response : text;
-    }
     setOwnProp(data, 'context', req.context);
     setOwnProp(data, kResponseHeaders, req[kResponseHeaders]);
-    setOwnProp(data, kResponseText, req[kResponseText]);
     setOwnProp(data, kResponseXML, safeBind(parseRaw, data, req, msg, kResponseXML), true, 'get');
     setOwnProp(data, kResponse, safeBind(parseRaw, data, req, msg, kResponse), true, 'get');
     cb(data);
@@ -100,9 +112,16 @@ addHandlers({
  */
 function parseRaw(req, msg, propName) {
   const { [kResponseType]: responseType } = req;
-  let res, ct;
-  if ('raw' in req) {
-    res = req.raw;
+  let res, ct, tmp;
+  if (kRaw in req) {
+    res = req[kRaw];
+    if (!req[kXhrType]) {
+      tmp = res;
+      res = '';
+      tmp::forEach(chunk => res += chunk);
+      req[kRaw] = [res];
+      req[kResponseText] = res;
+    }
     if (responseType === kDocument && (ct = kContentTextHtml)
     || !responseType
       && propName === kResponseXML
@@ -121,7 +140,7 @@ function parseRaw(req, msg, propName) {
     //   throw new SafeError('responseXML failed: responseType must be "document" or "" or absent.');
     // }
     if (responseType) {
-      delete req.raw;
+      delete req[kRaw];
     }
     req[propName] = res;
   } else {
@@ -191,7 +210,7 @@ export function onRequestCreate(opts, context, fileName) {
     url,
     [kFileName]: fileName,
     [kResponseType]: type,
-    [kXhrType]: XHR_TYPES[type] ? type : '',
+    [kXhrType]: req[kXhrType] = XHR_TYPES[type] ? type : '',
     events: EVENTS_TO_NOTIFY::filter(key => isFunction(cb[key] = opts[`on${key}`])),
   }, opts, OPTS_TO_PASS));
   return {