Notification with buttons and service worker lify-cicle

418 views
Skip to first unread message

Robbi

unread,
Sep 14, 2022, 8:48:34 PM9/14/22
to Chromium Extensions
I noticed another thing  that worried me not a little.
I will try to be brief and if necessary I will provide the details later.

Scenario:
  1. I create a notification with 2 buttons from inside the sw.
  2. I press the 1st button. It begin a process which reads an indexedDB of about 50 Mbytes serializes it into a JSON file, compresses it (gzip) and finally downloads it. This process takes about 20 seconds.

Doing some test, I noticed that, once the notification was created...
  • . ...If I wait for the sw to go to sleep and then click on the button, I see the sw wake up, doing its duty until the end (the file is downloaded).
  •  ...With the sw still active, if I wait for a certain amount of time (however not enough to send the sw to sleep, i.e 10 seconds) and then I click on the button the procedure does not end because the sw in the meantime becomes inactive.

My question is:
Shouldn't pressing the button restart the s.w countdown?
I mean, if the user doesn't see the notification because he has not been in front of the PC for some time and when he returns he presses the button everything is fine (because the sw start from inactivity), but if he gets distracted for a few seconds and then presses the button, everything falls apart.
Is this a normal behavior?

Thank you in advance

wOxxOm

unread,
Sep 15, 2022, 4:30:06 AM9/15/22
to Chromium Extensions, Robbi
Shouldn't pressing the button restart the s.w countdown?

Assuming it's chrome.notifications.onClicked, it certainly should, just like any other chrome event that wakes the worker, so apparently it's a bug.

Anyway, the only reliable workaround would be to open a new window that reads IndexedDB and downloads the file because on a slower computer gzipping of a large database may take more than the nonsensically inflexible timeout of 30 seconds enforced on ManifestV3 extensions.

That said, if you use a js library for gzipping, try the new CompressionStream instead. Also, since IndexedDB can store Blob or ArrayBuffer directly, maybe you can reorganize the storage accordingly to avoid JSON serialization completely.

zip('foo').then(blob => download(blob, 'foo.txt.gz', 'application/gzip'))

/** @returns {Promise<Blob>} */
function zip(str) {
  return new Response(new Response(str).body.pipeThrough(new CompressionStream('gzip'))).blob();
}
/** @returns {Promise<String>} */
function unzip(blob) {
  return new Response(blob.stream().pipeThrough(new DecompressionStream('gzip'))).text();
}

Robbi

unread,
Sep 15, 2022, 7:24:16 AM9/15/22
to Chromium Extensions, wOxxOm, Robbi
Hi @wOxxOm, thank you for your reply.
I am using the browser's native compression method for the first time and so I don't know if I got things right. Part of the code I got from a post of yours on SO.
I believe that I already follow most of your advice. The only thing is that in my DB there are no blobs or arrayBuffers but json objects.

Now I'm going to create a small extension to try to understand if and how the countdown starts again after pressing the button.
Creating a function that resolves a promise after 10-20 seconds should be enough to do this test.
If the countdown does not restart then it is clear that there is a bug somewhere.

This is the code from reading the db to dowload the archive file:
Do you see something strange?

//sw script
opt = {
    type: "basic",
    title: "Do you want to backup your data?",
    message: "bla bla bla...",
    iconUrl: 'img/warning.png',
    buttons: [{
            title: "Proceed"
        }, {
            title: "Not now"
        }
    ],
    requireInteraction: true
}
chrome.notifications.create('BS', opt);

chrome.notifications.onButtonClicked.addListener((id, btnIdx) => {
    if (id == 'BS') {
        if (btnIdx == 0) {
            chrome.notifications.clear(id, _ => {
                exportBusy = true;
                var req = indexedDB.open('foobar');   //opening the db
                req.addEventListener('success', e => {
                    db = e.target.result;
                    var objExport = {    /* This is the skeleton of the object I'm going to backup */
                        'name': db.name,
                        'version': db.version,
                        'objectStores': [{
                                name: 'mainNetworkData ',
                                data: []    /* it will be populated later */
                            }, {
                                name: 'otherStuff',
                                data: []    /* it will be populated later */
                            }
                        ]
                    };
                    var arrProm = [];
                    var arrOs = objExport.objectStores;
                    for (let i = 0; i < arrOs.length; i++)
                        arrProm[i] = queryKey(db, arrOs[i].name, null, null);    //pulling out the objectStores from db (asynchronously)
                   
                    Promise.all(arrProm).then(results => {
                        results.forEach((r, i) => {
                            arrOs[i].data = r.slice()    //populating the big object...
                        });
                       
                        chrome.windows.create({url: 'downloader.html', state: 'minimized'});
                       
                        self.addEventListener('message', function onMsg(e) {
                            if (e.data === 'sendBlob') {
                                async function send(dst) {
                                    //all heavy stuff is done after the window is created (not too stylish!)
                                    var content = JSON.stringify(objExport);
                                    const stream = new Response(content).body.pipeThrough(new CompressionStream('gzip'));
                                    blob = await new Response(stream).blob();
                                    dst.postMessage({blob}, [await blob.arrayBuffer()])
                                };
                                self.removeEventListener('message', onMsg);
                                send(e.source)
                            } else if (e.data === 'done')
                                exportBusy = false
                        })
                    }).catch(err => {})
                })
            })
        } else
            chrome.notifications.clear(id)
    }
});

//downloader.html
<html>
    <head> <script src="./script/downloader.js"></script> </head>
    <body>Don't close this window.<br> It will close automatically when the download is complete. </body>
</html>

//downloader.js
navigator.serviceWorker.ready.then(swr => swr.active.postMessage('sendBlob'));
navigator.serviceWorker.onmessage = async e => {
    if (e.data.blob) {
        let url = URL.createObjectURL(e.data.blob);
        let downloadAnchorNode = document.createElement("a");
        downloadAnchorNode.download = "foobar.gz";
        downloadAnchorNode.href = url;
        downloadAnchorNode.addEventListener("click", _ => setTimeout(_ => {
                URL.revokeObjectURL(url);
                e.source.postMessage('done');
                window.close()
            }, 500));
        downloadAnchorNode.click();
        downloadAnchorNode.remove()

wOxxOm

unread,
Sep 15, 2022, 7:41:35 AM9/15/22
to Chromium Extensions, Robbi, wOxxOm
Well, the code is fine but since you're opening a new window anyway, you should just move it all inside downloader.js. This way, the service worker's listener will only call chrome.windows.create.

Robbi

unread,
Sep 15, 2022, 8:04:19 AM9/15/22
to Chromium Extensions, wOxxOm, Robbi
Yes you are right. Without asking too many questions, the sure solution would be just this.

Robbi

unread,
Sep 15, 2022, 8:53:12 AM9/15/22
to Chromium Extensions, Robbi, wOxxOm
A simple extension like this below confirms my hypothesis.
The button wakes up the sw but if the latter is already awake it does not restart the countdown.

//manifest.json
{
    "manifest_version": 3,
    "name": "foobar",
    "description": "foo bar",
    "version": "0.1",
    "background": { "service_worker": "sw.js" },
    "permissions": [ "notifications" ]
}

//sw.js
chrome.notifications.onButtonClicked.addListener((id, btnIdx) => {
    function wait(sec) {
        return new Promise(ok => setTimeout(ok, 1e3 * sec))
    }
    chrome.notifications.clear(id);
    if (btnIdx == 0) {
        wait(20)
        .then(_ => {
            chrome.notifications.create('DONE', {
                type: "basic",
                title: "Done!",
                message: "Done!",
                iconUrl: './warning.png',
                requireInteraction: true
            })    
        })
    }
});
chrome.runtime.onInstalled.addListener(details => {
    chrome.notifications.create('BCK', {

        type: "basic",
        title: "Do you want to backup your data?",
        message: "The operation takes 20 seconds",
        iconUrl: './warning.png',
        buttons: [
            { title: "YES" },
            { title: "NO" }
        ],                            
        requireInteraction: true
    })    
})

wOxxOm

unread,
Sep 15, 2022, 9:40:21 AM9/15/22
to Chromium Extensions, Robbi, wOxxOm
Looks like this is matching the standard web platform behavior. Reusing it for extensions is completely nonsensical, though, precisely for the reason that it breaks any complex operation such as zipping or processing video etc. Yet another evidence out of dozens or hundreds why service worker idea was a mistake. It's also a regression compared to behavior of a ManifestV2 event page. Will you open a bug report on https://crbug.com?

Here's an MV2 demo that shows that the kill timer (15 seconds) is prolonged as expected:

  • manifest.json

    {
      "name": "test MV2",
      "version": "1.0",
      "manifest_version": 2,
      "background": {
        "scripts": ["bg.js"],
        "persistent": false
      },
      "permissions": [
        "notifications"
      ]
    }

  • bg.js

    chrome.notifications.onButtonClicked.addListener(console.log);
    chrome.notifications.getAll(n => {
      if (!n.BCK) {
        chrome.notifications.create('BCK', {
          title: 'The background script should run while',
          message: 'you click a button below every 5 seconds',
          type: 'basic',
          iconUrl: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==',

  •       buttons: [{title: 'YES'}, {title: 'NO'}],
  •       requireInteraction: true,
        });
      }
    });

Robbi

unread,
Sep 15, 2022, 10:01:56 AM9/15/22
to Chromium Extensions, wOxxOm, Robbi
Yes, I was thinking of opening one. I have already opened 3 reports for different things and two of them have yet to go through the triage phase.
I sadly found that if the bug is not starred by many people after about a year it is closed because anybody don't give a f**k . Bad thoughts?
Anyway, as soon as I have reported the bug I place a link here so that you can add information or impressions (if needed).

wOxxOm

unread,
Sep 15, 2022, 10:18:41 AM9/15/22
to Chromium Extensions, Robbi, wOxxOm
I opened 219 issues of which about a half was fixed so don't give up.

Robbi

unread,
Sep 15, 2022, 10:40:23 AM9/15/22
to Chromium Extensions, wOxxOm, Robbi
Wow, you will have spent a good chunk of your life on it  :-)

Robbi

unread,
Sep 15, 2022, 11:26:13 AM9/15/22
to Chromium Extensions, Robbi, wOxxOm
Et voilà:   bug#1364120
Anyone who thinks I have forgotten something or have not explained well enough is welcome for additional considerations or information.

wOxxOm

unread,
Sep 17, 2022, 5:09:32 AM9/17/22
to Chromium Extensions, Robbi, wOxxOm
You probably should note that MV2 correctly restarted the timer.

Robbi

unread,
Sep 17, 2022, 7:54:19 AM9/17/22
to Chromium Extensions, wOxxOm, Robbi
Ok good idea, I'm going to add a new comment with your code snippet.
I was thinking maybe I should also test the onclick and onclose event of the notification.
Thanks

Robbi

unread,
Sep 17, 2022, 8:20:20 AM9/17/22
to Chromium Extensions, Robbi, wOxxOm
Just checked; the other two events also behave exactly like onButtonClicked
Message has been deleted

Robbi

unread,
Sep 18, 2022, 6:53:20 AM9/18/22
to Chromium Extensions, Robbi, wOxxOm
  • The onClick event of the ACTION,
  • the onCommand event,
  • all omnibox events
  • ...
does not restart the timer as well.
Please someone tell me I'm crazy.

wOxxOm

unread,
Sep 18, 2022, 10:08:37 AM9/18/22
to Chromium Extensions, Robbi, wOxxOm
It's likely that no chrome events restart the timer because this simply wasn't implemented when the support was wired up, so the standard service worker behavior for web sites is being used instead. The idea to use service workers for extensions itself is pretty crazy because this is the most site-oriented kind of workers with tons of limits and quirks that only make sense for sites with their typical reaction times of 100-1000ms, whereas extensions typically react in 1ms. Anyway since Chromium team won't be able to admit they made a mistake in choosing service workers, let's hope at least this problem will be fixed soon, although I won't be surprised if they'll try to dismiss its importance in favor of maintaining web compatibility, even though compatibility is entirely inapplicable for the use case of chrome events.

Robbi

unread,
Sep 18, 2022, 11:52:11 AM9/18/22
to Chromium Extensions, wOxxOm, Robbi
> Anyway since Chromium team won't be able to admit they made a mistake in choosing service workers, let's hope at least this problem will be fixed soon, although I won't be surprised if they'll try to dismiss its importance in favor of maintaining web compatibility

Yes, generalizing it could be said that perhaps one of the biggest problems in the world is that it is infested with people who never admit they were wrong even in the face of the evidence. Personally I prefer to give credit to humble people who have some doubts rather than those who are full of certainties. I believe that if many thought this way we would soon get rid of these " weight" that are dragging us into the abyss every day more and more.
I apologize for the OT

Robbi

unread,
Sep 19, 2022, 3:53:36 AM9/19/22
to Chromium Extensions, Robbi, wOxxOm
Hi @wOxxOm,
a bugs report team member commented that the MV2 extension throws out an error and does not reload the notification after reloading the extension. Would you add your own comment to this bug report?
I would really appreciate it and this would give even more strength to that bug report.

wOxxOm

unread,
Sep 19, 2022, 4:01:21 AM9/19/22
to Chromium Extensions, Robbi, wOxxOm
As you can see in their video they didn't follow the instruction, which is in the original description. Instead they installed an MV2 extension and apparently they did it incorrectly.

You need to understand that the triage team are essentially average users, expect them to understand nothing at all and always butcher up everything. This is how support teams generally are.

Be patient and precise, as you would be with a person who just received a severe head blow, don't berate them, walk them through the steps again until they can reproduce the problem. Only then an actual developer will be assigned.

wOxxOm

unread,
Sep 19, 2022, 4:03:54 AM9/19/22
to Chromium Extensions, wOxxOm, Robbi
Er, "botch up", not butcher up :-)

Robbi

unread,
Sep 19, 2022, 4:11:53 AM9/19/22
to Chromium Extensions, wOxxOm, Robbi
Ok,  anyway apart from the error that refers to any unpacked mv2 extension, in fact the notification is not always recreated at the extension reload, and this is actually very strange. I checked it right now with Canary.

Robbi

unread,
Sep 19, 2022, 4:54:53 AM9/19/22
to Chromium Extensions, Robbi, wOxxOm
I still don't understand why the notification is not recreated upon extension reload (it would seem that BCK remains persistent, at least for a while, beyond the notification closure), but I can for sure say that the notification is closed after clicking on each of the buttons whether onButtonClicked is set or not.
At least this is the default behavior on Win 10, but I don't know if the same can be said for Linux.
So the statement: "The background script should run while ', you click a button below every 5 seconds'" may not always be consistent.
Never mind, the real problem is another, I hope they notice and do not close the bug report superficially.

wOxxOm

unread,
Sep 19, 2022, 5:12:57 AM9/19/22
to Chromium Extensions, Robbi, wOxxOm
Indeed, here's a fixed MV2 version, but don't add it yet, wait for them to confirm the bug itself.

chrome.notifications.onButtonClicked.addListener(console.log);
chrome.notifications.onClosed.addListener(create);
chrome.notifications.getAll(n => !n.BCK && create());

function create() {

  chrome.notifications.create('BCK', {
    title: 'The background script should run while',
    message: 'you click a button below every 5 seconds',
    type: 'basic',
    iconUrl: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==',
    buttons: [{title: 'YES'}, {title: 'NO'}],
    requireInteraction: true,
  });
}
Message has been deleted

Robbi

unread,
Sep 19, 2022, 5:20:00 AM9/19/22
to Chromium Extensions, wOxxOm, Robbi
err.png
Ok,
what I said is true if the notification you see is like the one I saw in Chrome Dev or Chrome Canary (the below one).
This is not the case with another browser such as SRWare Iron which I believe uses another graphics engine and another way to handle the notifications.
Here I could also write a book about it...........

Robbi

unread,
Sep 20, 2022, 7:26:27 AM9/20/22
to Chromium Extensions, Robbi, wOxxOm
I removed the mv2 version from the comments and added 2 new videos to the bug report.
I thought it best not to give him too many distractions.
If they can figure out the core of the problem, I'll tell them about the regression later.

Robbi

unread,
Sep 21, 2022, 6:55:52 AM9/21/22
to Chromium Extensions, Robbi, wOxxOm
Hi @wOxxOm,
As Galileo said: "Yet it moves!"
I have re-tested your new MV2 version and it doesn't work with Win10 (at least)
They consider this issue as Non-Regression issue.
Did you notice how this thread became a P2P chat?
Evidently nobody cares about this "very small" problem.

wOxxOm

unread,
Sep 21, 2022, 7:46:14 AM9/21/22
to Chromium Extensions, Robbi, wOxxOm
It works for me in Windows 10.
My Chrome uses its own notification UI because I've disabled Windows notification center.
Actually, you should just make a different example that doesn't depend on notifications altogether e.g. just use chrome.action.onClicked.

Robbi

unread,
Sep 21, 2022, 7:59:08 AM9/21/22
to Chromium Extensions, wOxxOm, Robbi
I deduced it; However, if I post your code, the team does not believe that it is true to close the report and celebrate :-) 

wOxxOm

unread,
Sep 21, 2022, 8:14:29 AM9/21/22
to Chromium Extensions, Robbi, wOxxOm
I'm not interfering with the issue yet, because I'm interested in seeing how they handle it, whether they "still got it".
I'll open as many reports as necessary myself if they close it.

Robbi

unread,
Oct 5, 2022, 8:23:56 AM10/5/22
to Chromium Extensions, wOxxOm, Robbi
In my opinion they "still NOT got it yet" or they pretend not to understand.
We can deduce it from how they labeled the problem (Components: UI> Notifications).
We would have to change the title of the bug report to something like this: "Any event doesn't restart the internal service worker timer", but I think is too late...
In the meanwhile I have been able to verify that not even a resource fetch will never restart the internal SW timer.
I was wondering how many bug reports are due to the same bug.
Something like: "Sometime, but not always, when I click on action, it doesn't work this... but no errors appears in console".

wOxxOm

unread,
Oct 5, 2022, 10:16:06 AM10/5/22
to Chromium Extensions, Robbi, wOxxOm
Well, they don't have people available to check all bug reports, the extensions team still has just a few people working fulltime, judging by the frequency of commits in the source code. It might be worth opening a new report for all chrome events indeed.

Robbi

unread,
Oct 6, 2022, 9:19:40 AM10/6/22
to Chromium Extensions, wOxxOm, Robbi
I just opened a new one: bug #1371876
If they want to see the finger instead of the moon, that's their business.
I don't waste time on it anymore

Robbi

unread,
Oct 11, 2022, 4:29:32 PM10/11/22
to Chromium Extensions, Robbi, wOxxOm
This one reminds me of something I've just seen : bug #130536
It's from last May
Reply all
Reply to author
Forward
0 new messages