Closed Bug 1538761 Opened 6 years ago Closed 6 years ago

Download via ReadableStream : browser doesn't release lock after cancel

Categories

(Core :: JavaScript Engine, defect)

66 Branch
defect
Not set
normal

Tracking

()

RESOLVED INVALID
Tracking Status
firefox66 --- affected
firefox67 --- affected
firefox68 --- affected

People

(Reporter: zurldan, Unassigned)

Details

Attachments

(1 file)

Attached file stream-lock.zip

User Agent: Mozilla/5.0 (X11; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0

Steps to reproduce:

In my index.html I register a service worker and I display 2 links to download a txt file and a png file. The txt file will be generated via a ReadableStream in the service worker. This stream has a CountQueuingStrategy with a highWaterMark of 3. In the cancel() method in the object representing the underlying source, in a setTimeout with a 5s delay, I put :

console.log('Stream Locked : ', readableStream.locked);

I open the console and then click on the txt file link. The browser shows a popup asking if I want to open / save / cancel the file. I click cancel right away or accept and cancel the download later.

(I attached a zip archive containing the index.html and service worker in order to reproduce)

Actual results:

In the console we can see that the stream is locked. So more than 5s after the browser called the stream's cancel() method, it didn't call releaseLock() on its reader. I tried 15s later and same result so we can assume the browser never releases the lock.

Expected results:

When the browser calls cancel() and the stream, it should also release the lock by calling releaseLock() on its reader.

Has STR: --- → yes

I've tried to test this report on Windows 10 and Ubuntu 18.04 using the latest Nightly and Firefox release build. When I load the provided index.html file, open the console and click on the txt file link, I get "File not found". I don't get any prompt to open/save/cancel the file. If I create empty files then the link is displaying a blank page. If possible please provide more explicit steps in order to test it properly.

Component: Untriaged → JavaScript Engine
Flags: needinfo?(zurldan)
Product: Firefox → Core

Hello, this requires a web server in order to work, if opened directly the service worker registration fails with this message in the console :

Registration failed with SecurityError: The operation is insecure.

If you need a simple web server to test it, you can go into the case directory, open a terminal there and run one of those commands :

// python 2.7
python -m SimpleHTTPServer

or

// python 3
python -m http.server

or

// php
php -S localhost:8000

Then you can open your browser and go to http://localhost:8000 and it should work.

Flags: needinfo?(zurldan)

Since I submitted multiple bugs / test cases and you responded to all of them : There can be only one sw.js service worker active at one time for the domain (localhost:8000), meaning that for each test case of these bugs, to be safe you will need to close the page of the test case, then go to about:serviceworkers and unregister the service worker for Origin: http://localhost:8000. Then you can open the next test case and the service worker for that test case will be fetched and registered.

Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0 (20190325095153)

Thanks. I was able to reproduce the mentioned behavior using the provided steps. I can see the "Stream Locked = true" even after the stream is canceled.

Status: UNCONFIRMED → NEW
Ever confirmed: true

What a great bug report.

I don't have the spec in front of me but I'm guessing the bug is in the browser where we consume this stream. (It could also be something that the spec says the stream itself is supposed to do when it's canceled. I haven't checked.)

baku, could you take a look?

Flags: needinfo?(amarchesini)

moving NI back to Jason.

Flags: needinfo?(amarchesini) → needinfo?(jorendorff)

OK, having looked at the spec a little, I don't think this is a bug!

Marking this INVALID, which just means there's nothing to fix. Please reopen it if this is wrong.

Spec technical details:

The only places where .[[reader]] is set are InitializeReadableStream, ReadableStreamReaderGenericInitialize, and ReadableStreamReaderGenericRelease. The last one is the one that's called from defaultReader.releaseLock(). But it isn't called from defaultReader.cancel() anywhere that I could find. We do end up here, which performs the .[[cancelAlgorithm]], which is what calls the underlying source object's cancel method.

Spec design intent: This is harder to pin down. The spec says,

A reader or writer [can] release its lock, which makes it no longer active, and allows further readers or writers to be acquired. This is done via the defaultReader.releaseLock(), byobReader.releaseLock(), or writer.releaseLock() method, as appropriate.

The cancel method isn't listed. I guess since the intent with .cancel() is not to allow further readers to be acquired, it doesn't unlock the stream. It just closes it.

There might be a better reason for .cancel() to leave the stream locked, but I don't know what it would be.

Status: NEW → RESOLVED
Closed: 6 years ago
Flags: needinfo?(jorendorff)
Resolution: --- → INVALID

Out of curiosity, I tried this in Google Chrome. It doesn't seem to call the underlying source object's .cancel() method at all.

You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: