Coder Social home page Coder Social logo

Comments (21)

jakearchibald avatar jakearchibald commented on May 27, 2024

In general I like the idea of introducing a deferred event queue to service workers.

The tricky part here is:

addEventListener('backgroundfetchsuccess', (bgFetchEvent) => {
  bgFetchEvent.updateUI({ title: 'Download complete!' });
});

I guess we could get developers to provide the 'fail' and 'success' titles/icons ahead of time. I'd need to think more about the capabilities we'd lose as a result. If we did this, we'd also need to think about backwards compatibility.

from background-fetch.

jakearchibald avatar jakearchibald commented on May 27, 2024

This is problematic as this might trigger execution of arbitrary JavaScript in the background, without the user being potentially aware of any of this.

Can you summarise the kind of JavaScript you're worried about here? Would an aggressively short amount of execution time be acceptable?

It doesn't seem like a tracking vector, because the download itself can do that.

from background-fetch.

youennf avatar youennf commented on May 27, 2024

Executing JavaScript, thus spinning an origin-specific process, is expensive. This goes against the goal of bg fetch to improve battery life.

This also extends the time the user gets tracked. And there might be some time between the download ends and the actual JS execution.

If user is aborting a fetch, maybe for privacy reasons, it does not seem great to execute some JavaScript. In fact, chances are pretty high in that case that the user may never get back to the page, so there is clearly no need to notify the page proactively.

If user allows the web site to start bg fetch in service workers in the background, this becomes a very powerful tracking tool and can be abused similarly to bg periodic sync.

The user is not deciding when the download fails/finishes, the server can control that.
User should be the one in control of when JS executes.

from background-fetch.

wanderview avatar wanderview commented on May 27, 2024

If we end up deferring events we should be careful not to create a storm of activity right in the middle of the next load of the site. Seems like a potential perf footgun.

from background-fetch.

wanderview avatar wanderview commented on May 27, 2024

Of course, if you defer the events too long then the site cannot render UI indicating the downloaded resources are available.

In regards to battery, it seems some amount of deferring without requiring a full delay to next window open would be adequate.

from background-fetch.

youennf avatar youennf commented on May 27, 2024

Of course, if you defer the events too long then the site cannot render UI indicating the downloaded resources are available.

Are you thinking of something like the badging API?

from background-fetch.

youennf avatar youennf commented on May 27, 2024

Also, is Chrome planning to start service workers periodically to fire update events, or just the final events?

from background-fetch.

jakearchibald avatar jakearchibald commented on May 27, 2024

Of course, if you defer the events too long then the site cannot render UI indicating the downloaded resources are available.

Are you thinking of something like the badging API?

I think @wanderview is referring to something like:

  1. User clicks 'download complete' notification.
  2. This fires a backgroundfetchclick event in the service worker:
    1. JS in this event calls client.openWindow('/my-downloads'), creating a navigation to podcasts.example.com/my-downloads.
    2. A fetch event fires for the navigation, which serves the page.
  3. In parallel with the previous step, a pending backgroundfetchsuccess event fires, which updates indexeddb, and the cache API with details from the download.

Due to the events happening in parallel, it's likely that the user will see the my-downloads page without the completed download. The developer will need to use postMessage to tell pages to update, although this will, at best, cause a jump in layout.

Also, is Chrome planning to start service workers periodically to fire update events, or just the final events?

The service worker can only be started by events on the service worker global. The "update" event is on the individual background fetch registration, so the service worker isn't woken for these, and even if it did, there wouldn't be an object there to dispatch the event on.

There isn't a top level 'background fetch update' event.

from background-fetch.

youennf avatar youennf commented on May 27, 2024

Due to the events happening in parallel, it's likely that the user will see the my-downloads page without the completed download.

Agreed the sequencing of events is to be carefully studied.

AIUI, the 'download complete' is displayed so the fetch succeeded so there is a backgroundfetchsuccess event in the service worker event queue but the service worker is not started just for this event.
When the backgroundfetchclick event is enqueued, the service worker will start and will process the two events, first backgroundfetchsuccess and second backgroundfetchclick.

from background-fetch.

jakearchibald avatar jakearchibald commented on May 27, 2024

Are you suggesting that the deferred event queue must be exhausted (plus any waitUntils) until regular events like backgroundfetchclick / fetch can fire? That would at least avoid the race, but would delay navigation & click response.

from background-fetch.

youennf avatar youennf commented on May 27, 2024

backgroundfetchclick and backgroundfetchsuccess are enqueued in the same functional event task source so there should be no race in the firing of these events. The navigation fetch will only happen after the service worker decides to do a navigation. At this point, I would hope the service worker has all necessary information to suppress any potential reflow.

Currently, there is one task source for all functional events, maybe there should be one for BackgroundFetchEvent specifically. In that case, backgroundfetchsuccess might not always trigger running the service worker, but backgroundfetchclick would. This would then trigger firing events in the order of the task source event queue.

That said, this exhibits the issue that backgroundfetchsuccess and backgroundfetchclick are redundant in that scenario. This requires careful handling from the web application which is a bit unfortunate. Not sure how we can improve the model here.

from background-fetch.

jakearchibald avatar jakearchibald commented on May 27, 2024

backgroundfetchclick and backgroundfetchsuccess are enqueued in the same functional event task source so there should be no race in the firing of these events

Event queues aren't held back by waitUntil, so if you do anything async in these events, there'll be a race in those async things completing.

That said, this exhibits the issue that backgroundfetchsuccess and backgroundfetchclick are redundant in that scenario.

backgroundfetchsuccess provides the developer with the responses from the background fetch, so I'm not sure how we can get rid of it. Same of backgroundfetchclick - that allows the developer to open a window, or focus an existing window, which we only want to allow after an interaction.

I can't think of a way around this other than making the deferred event queue special in some way, in that it must be emptied (including async stuff) before functional events can be called.

However, if the developer does anything potentially long-running in these events (like a fetch), they could lock up their app.

The closest thing we have to this is the activate event, which delays events like fetch. This is why docs stress that devs should make their activate event as short as possible - usually just deleting caches & migrating database schemas, rather than performing fetches.

from background-fetch.

jakearchibald avatar jakearchibald commented on May 27, 2024

A slightly less-worse alternative is to create an API like waitForTheseSpecialEventThings(), which returns a promise that's resolved once the events in this special event queue (and their related waitUntils) are processed.

This allows the developer to be smarter about when they wait, although of course it requires them to get it right. Oh, and not get themselves in a deadlock.

from background-fetch.

youennf avatar youennf commented on May 27, 2024

Right, it is also true we want to have backgroundfetchclick be processed as fast as possible.
For that reason, we would like something like marking the response as success, fire the backgroundfetchclick event and fire the backgroundfetchsuccess event once backgroundfetchclick is fully handled.
Maybe this is not as illogical as it looks.

from background-fetch.

jakearchibald avatar jakearchibald commented on May 27, 2024

Hm, but in the case of a photo album upload, or a video upload, the responses to the background fetch could contain important information, like the URL to the newly created album, or the URL to the published video.

That's why I landed on something like waitForTheseSpecialEventThings(), so the developer could choose to wait or not. Waiting delays the response, which is bad, but a quick response with bad data is also bad.

Although, if something like waitForTheseSpecialEventThings() lacks granularity, then there's a risk of blocking on unrelated things.

from background-fetch.

youennf avatar youennf commented on May 27, 2024

but in the case of a photo album upload, or a video upload, the responses to the background fetch could contain important information, like the URL to the newly created album, or the URL to the published video.

Right, the question I am asking is whether it is bad to have the service worker know that the bgfetch is a success before the success event is fired, for instance in a message/fetch/bgfetchclick event handler. AIUI, the spec is not prescribing that so service worker scripts would need to deal with it anyway.

We might be digressing a bit though. Are we fine with the following requirements?

  • The User Agent should be able to delay the firing of success/abort/fail events. This may be handled either in the bgfetch spec or in the service worker spec.
  • The User Agent should be able to update the download UI without having to run the service worker.

from background-fetch.

jakearchibald avatar jakearchibald commented on May 27, 2024

the question I am asking is whether it is bad to have the service worker know that the bgfetch is a success before the success event is fired, for instance in a message/fetch/bgfetchclick event handler. AIUI, the spec is not prescribing that so service worker scripts would need to deal with it anyway.

The spec currently updates any bgfetch instances before dispatching the success event, which seems like the right way around. But I'm not sure what they'd have to deal with.

Here's the order of things in the current spec:

  1. Bgfetch completes.
  2. Success event is dispatched in the service worker. The rest of the steps are performed by the developer in the event listener.
  3. Process the complete responses (either, put them in the cache API, or update something in indexeddb so the app code knows where the new video/album is).
  4. Update the download UI to reflect that the download is complete.

If the click event happens before step 3, then app storage will not contain info for the complete bgfetch. So, maybe the app will choose to open a 'downloads in progress' page. It's possible that the bgfetch success event has fired by this point, but the processing hasn't completed. But that's fine, the download UI isn't showing 'complete' yet, so sending the user to a 'downloads in progress' page is fine.

Whereas, as far as I can tell, what you're proposing would move the UI update (step 4) before step 2. This means the UI is showing complete, when the data hasn't yet been processed. If a click event happens before that processing, then it may act on outdated info. It seems bad if the user clicks a 'download complete' UI, and they're taken to a 'downloads in progress' page.

That's why I've been suggesting something like waitForTheseSpecialEventThings(), so the developer could choose to wait for the success event before completing the click event. Not all click events will need data processed in the success event, but some will.

The User Agent should be able to delay the firing of success/abort/fail events. This may be handled either in the bgfetch spec or in the service worker spec.

I get the privacy implications of firing these events sooner, so yeah I'd like to find another way that works. I don't think we've found a great option yet, though.

The User Agent should be able to update the download UI without having to run the service worker.

It can already update without running the service worker, in reaction to progress events. But, I guess you mean switching state from "in progress" to "complete".

I don't think this is a requirement, but it may be something we need to order to make the other requirement work.

Right now, the UI is updated in the success event. If we end up delaying the success event, we need some other way to update the UI. But that seems to mean updating the UI before the responses are processed by the app, which is where we get the race with things like click events.

from background-fetch.

jakearchibald avatar jakearchibald commented on May 27, 2024

@annevk in this thread we're looking at alternatives to starting the service worker in the background. I think Mozilla are interested in this too, right? Maybe even for push? If there's enough interested I can organise a call around this.

from background-fetch.

annevk avatar annevk commented on May 27, 2024

Yes, Mozilla would like to avoid introducing new opportunities for starting service workers or keeping them alive when there are no fully active documents.

cc @asutherland

from background-fetch.

jakearchibald avatar jakearchibald commented on May 27, 2024

The more I think about this, the more I think an API like this is useful:

await swRegistration.backgroundFetch.pendingEventsDone();

Where pendingEventsDone() returns a promise that resolves once any queued reaction events are 'processed' (events fired, and their waitUntils settle).

Developers could use this in a page, or as part of a service worker event like fetch. For example, how it could be used on an offline media page:

displayLoadingSpinnerAfterDelay();
await swRegistration.backgroundFetch.pendingEventsDone();
const data = await getOfflineMediaDataFromIDB();
displayOfflineMedia(data);
cancelLoadingSpinner();

It feels like the same mechanism could be used in push:

await swRegistration.pushManager.pendingEventsDone();

…if we wanted to explore the same 'delayed events' pattern there.

I'm going to talk to some folks who are experimenting with background fetch, to see if this works for them. Shout up if you think I'm heading in the wrong direction.

from background-fetch.

youennf avatar youennf commented on May 27, 2024

This seems fine to me.
I just want to mention that this API may or may not be useful whether browser is delaying events or depending on how the browser is firing concurrent events. Web developers may assume a particular order and have a sub-par or even broken UX for browsers that would delay events.

from background-fetch.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.