diff options
Diffstat (limited to 'ext/js/background/request-builder.js')
| -rw-r--r-- | ext/js/background/request-builder.js | 90 | 
1 files changed, 89 insertions, 1 deletions
| diff --git a/ext/js/background/request-builder.js b/ext/js/background/request-builder.js index ad1536f1..2cdd6f0e 100644 --- a/ext/js/background/request-builder.js +++ b/ext/js/background/request-builder.js @@ -42,6 +42,58 @@ class RequestBuilder {          return await this._fetchInternal(url, init, headerModifications);      } +    static async readFetchResponseArrayBuffer(response, onProgress) { +        let reader; +        try { +            if (typeof onProgress === 'function') { +                reader = response.body.getReader(); +            } +        } catch (e) { +            // Not supported +        } + +        if (typeof reader === 'undefined') { +            const result = await response.arrayBuffer(); +            if (typeof onProgress === 'function') { +                onProgress(true); +            } +            return result; +        } + +        const contentLengthString = response.headers.get('Content-Length'); +        const contentLength = contentLengthString !== null ? Number.parseInt(contentLengthString, 10) : null; +        let target = Number.isFinite(contentLength) ? new Uint8Array(contentLength) : null; +        let targetPosition = 0; +        let totalLength = 0; +        const targets = []; + +        while (true) { +            const {done, value} = await reader.read(); +            if (done) { break; } +            onProgress(false); +            if (target === null) { +                targets.push({array: value, length: value.length}); +            } else if (targetPosition + value.length > target.length) { +                targets.push({array: target, length: targetPosition}); +                target = null; +            } else { +                target.set(value, targetPosition); +                targetPosition += value.length; +            } +            totalLength += value.length; +        } + +        if (target === null) { +            target = this._joinUint8Arrays(targets, totalLength); +        } else if (totalLength < target.length) { +            target = target.slice(0, totalLength); +        } + +        onProgress(true); + +        return target; +    } +      // Private      async _fetchInternal(url, init, headerModifications) { @@ -92,7 +144,10 @@ class RequestBuilder {                  }, 100);              }              const details = await errorDetailsPromise; -            e.data = {details}; +            if (details !== null) { +                const data = {details}; +                this._assignErrorData(e, data); +            }              throw e;          } finally {              this._removeWebRequestEventListeners(eventListeners); @@ -295,4 +350,37 @@ class RequestBuilder {          }          return result;      } + +    _assignErrorData(error, data) { +        try { +            error.data = data; +        } catch (e) { +            // On Firefox, assigning DOMException.data can fail in certain contexts. +            // https://bugzilla.mozilla.org/show_bug.cgi?id=1776555 +            try { +                Object.defineProperty(error, 'data', { +                    configurable: true, +                    enumerable: true, +                    writable: true, +                    value: data +                }); +            } catch (e2) { +                // NOP +            } +        } +    } + +    static _joinUint8Arrays(items, totalLength) { +        if (items.length === 1) { +            const {array, length} = items[0]; +            if (array.length === length) { return array; } +        } +        const result = new Uint8Array(totalLength); +        let position = 0; +        for (const {array, length} of items) { +            result.set(array, position); +            position += length; +        } +        return result; +    }  } |