Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify if ProgressEvent.loaded should indicate the size of compress, or uncompressed, data #388

Open
Zwyx opened this issue Mar 22, 2024 · 2 comments

Comments

@Zwyx
Copy link

Zwyx commented Mar 22, 2024

What is the issue with the XMLHttpRequest Standard?

When a ProgressEvent fires, ProgressEvent.loaded contains the size of the data already transmitted.

If the data is compressed and its total size is unknown, then, depending on the browser, ProgressEvent.loaded can contain either the size of the compressed data, or the size of the decompressed data.

It would be great if the spec could clarify which of these two values should be the one provided by ProgressEvent.loaded.

Additional context

With a response containing compressed data and unknown total size (the Content-Length header is not set), ProgressEvent.lengthComputable is false and ProgressEvent.total is not usable, preventing us from displaying a progress indicator.

In this case, a common technique is to set a custom response header X-Uncompressed-Content-Length containing the size of the uncompressed data being transmitted.

We can then, on the client side, use the value of this header as a substitue of ProgressEvent.total, and display a progress indicator with the value of ProgressEvent.loaded divided by the value of this header.

However, depending on the browser, ProgressEvent.loaded can be either the size of the compressed data already transmitted (the case in Firefox), or, the size of the decompressed data already transmitted (the case in Chrome).

For example:

  • In Chrome, downloading a content of 1.3MB (1,345,276 bytes), while logging the value of loaded, shows a total of 1345276 at the end.
  • In Firefox, downloading the same content of 1,345,276 bytes, while logging the value of loaded, shows a total of 1017454 at the end.
@annevk
Copy link
Member

annevk commented Mar 23, 2024

Firefox is correct. This is actually defined in detail through the interaction with Fetch. Content codings are removed before the creation of a response stream.

@Zwyx
Copy link
Author

Zwyx commented Mar 23, 2024

Thank you for replying. Would you be able to provide a link to where this is defined? I searched the Fetch spec for progress, leaded, compress, etc. but didn't find anything.

I'm also surprised the Fetch API has anything to do in XMLHttpRequest 🤔, considering Fetch came around more than a decade later?

Zwyx added a commit to Zwyx/PrivateBin that referenced this issue Mar 24, 2024
Because the response from the API is PHP output, the usual `Content-Length` header is absent.

This [custom header technique](https://stackoverflow.com/questions/15097712/how-can-i-use-deflated-gzipped-content-with-an-xhr-onprogress-function/32799706#32799706) allows the client to know the total length of the data being received, in order to display a progress indicator.

Here's a code example with `XMLHttpRequest`:


```
xhr.addEventListener("progress", (e) => {
	if (e.lengthComputable) {
		onDownloadProgress({
			loaded: e.loaded,
			total: e.total,
		});
	} else {
		const uncompressedContentLength = xhr.getResponseHeader(
			"X-Uncompressed-Content-Length",
		);

		if (uncompressedContentLength) {
			onDownloadProgress({
				loaded: e.loaded,
				total: Number(uncompressedContentLength),
			});
		}
	}
});
```

Notes:
- `Fetch` can be used as well, however it doesn't allow to track the progress of uploaded data (when creating a paste); whereas `XMLHttpRequest` does.
- `e.loaded` can be different between browsers; Firefox reports the length of the compressed data, Chrome reports the length of uncompressed data (see whatwg/xhr#388). A workaround for this is to manually set our progress indicator to 100% when the request finishes.
Zwyx added a commit to Zwyx/PrivateBin that referenced this issue Mar 24, 2024
Because the response from the API is PHP output, the usual `Content-Length` header is absent.

This [custom header technique](https://stackoverflow.com/questions/15097712/how-can-i-use-deflated-gzipped-content-with-an-xhr-onprogress-function/32799706#32799706) allows the client to know the total length of the data being received, in order to display a progress indicator.

Here's a code example with `XMLHttpRequest`:


```
xhr.addEventListener("progress", (e) => {
	if (e.lengthComputable) {
		onDownloadProgress({
			loaded: e.loaded,
			total: e.total,
		});
	} else {
		const uncompressedContentLength = xhr.getResponseHeader(
			"X-Uncompressed-Content-Length",
		);

		if (uncompressedContentLength) {
			onDownloadProgress({
				loaded: e.loaded,
				total: Number(uncompressedContentLength),
			});
		}
	}
});
```

Notes:
- `Fetch` can be used as well (only reason I use `XMLHttpRequest` is because `fetch` doesn't allow to track the progress of uploaded data (when creating a paste); whereas `XMLHttpRequest` does).
- `e.loaded` can be different between browsers; Firefox reports the length of the compressed data, Chrome reports the length of uncompressed data (see whatwg/xhr#388). A workaround for this is to manually set our progress indicator to 100% when the request finishes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants