* @param {GMReq.BG} req * @param {GMReq.EventTypeMap[]} events * @param {boolean} blobbed * @param {boolean} chunked * @param {boolean} isJson
(req, events, blobbed, chunked, isJson)
| 96 | * @param {boolean} isJson |
| 97 | */ |
| 98 | function xhrCallbackWrapper(req, events, blobbed, chunked, isJson) { |
| 99 | let lastPromise = Promise.resolve(); |
| 100 | let contentType; |
| 101 | let dataSize; |
| 102 | let numChunks = 0; |
| 103 | let chunkSize; |
| 104 | let getChunk; |
| 105 | let fullResponse = null; |
| 106 | let response; |
| 107 | let responseHeaders; |
| 108 | let sent = true; |
| 109 | let sentTextLength = 0; |
| 110 | let sentReadyState4; |
| 111 | let tmp; |
| 112 | const { id, xhr } = req; |
| 113 | const getResponseHeaders = () => req[kResponseHeaders] || xhr.getAllResponseHeaders(); |
| 114 | const eventQueue = []; |
| 115 | const sequentialize = async () => { |
| 116 | const evt = eventQueue.shift(); |
| 117 | const upload = evt.target === xhr ? 0 : 1; |
| 118 | const { type } = evt; |
| 119 | const shouldNotify = events[upload][type]; |
| 120 | const isEnd = !upload && type === 'loadend'; |
| 121 | const readyState4 = xhr.readyState === 4 || (sentReadyState4 = false); // reset on redirection |
| 122 | if (!shouldNotify && !isEnd && type !== ERROR) { |
| 123 | return; |
| 124 | } |
| 125 | // Firefox duplicates readystatechange for state=4 randomly, #1862 |
| 126 | if (readyState4 && type === 'readystatechange') { |
| 127 | if (sentReadyState4) return; |
| 128 | sentReadyState4 = true; |
| 129 | } |
| 130 | if (!contentType) { |
| 131 | contentType = xhr.getResponseHeader('Content-Type') || ''; |
| 132 | } |
| 133 | if (!upload && fullResponse !== xhr[kResponse]) { |
| 134 | fullResponse = response = xhr[kResponse]; |
| 135 | sent = false; |
| 136 | if (response) { |
| 137 | if ((tmp = response.length - sentTextLength)) { // a non-empty text response has `length` |
| 138 | chunked = tmp > TEXT_CHUNK_SIZE; |
| 139 | chunkSize = TEXT_CHUNK_SIZE; |
| 140 | dataSize = tmp; |
| 141 | getChunk = text2chunk; |
| 142 | response = sentTextLength ? response.slice(sentTextLength) : response; |
| 143 | sentTextLength += dataSize; |
| 144 | } else { |
| 145 | chunkSize = CHUNK_SIZE; |
| 146 | dataSize = response.size; |
| 147 | getChunk = blobbed ? blob2objectUrl : blob2chunk; |
| 148 | } |
| 149 | numChunks = chunked ? Math.ceil(dataSize / chunkSize) || 1 |
| 150 | : blobbed ? 1 : 0; |
| 151 | } |
| 152 | } |
| 153 | if (response && isEnd && req[kFileName]) { |
| 154 | downloadBlob(response, req[kFileName]); |
| 155 | } |