serviceworkers focus tab: clients is empty on notificationclick - google-chrome

I have a common serviceworker escenario, where I want catch a notification click and focus the tab where the notification has come from. However, clients variable is always empty, its lenght is 0
console.log("sw startup");
self.addEventListener('install', function (event) {
console.log("SW installed");
});
self.addEventListener('activate', function (event) {
console.log("SW activated");
});
self.addEventListener("notificationclick", function (e) {
// Android doesn't automatically close notifications on click
console.log(e);
e.notification.close();
// Focus tab if open
e.waitUntil(clients.matchAll({
type: 'window'
}).then(function (clientList) {
console.log("clients:" + clientList.length);
for (var i = 0; i < clientList.length; ++i) {
var client = clientList[i];
if (client.url === '/' && 'focus' in client) {
return client.focus();
}
}
if (clients.openWindow) {
return clients.openWindow('/');
}
}));
});
And the registration is this one:
this.doNotify = function (notification) {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js').then(function (reg) {
requestCreateNotification(notification, reg);
}, function (err) {
console.log('sw reg error:' + err);
});
}
...
}
chrome://serviceworker-internals/ output shows that registration and installation are fine. However, when a notification is pushed, clientList is empty. I have tried removing the filter type:'window' but the result is still the same. As clients are empty, a new window is always opened. What am I doing wrong?

The suspicion in your own comment is correct. A page is controlled by a service worker on navigation to an origin that the service worker is registered for. So the original page load that actually initializes the service worker is not itself controlled. That's why the worker only finds your tab once you visit with a new tab or do a refresh.
However (as Jeff Posnick points out in the comments) you can get uncontrolled pages as follows: ServiceWorkerClients.matchAll({includeUncontrolled: true, type: 'window'}).

Try making the service worker immediately claim the page.
E.g.:
self.addEventListener('install', event => event.waitUntil(self.skipWaiting()));
self.addEventListener('activate', event => event.waitUntil(self.clients.claim()));
For a more complex example, see https://serviceworke.rs/immediate-claim.html.

Related

Transform Google apps script webapp into progressive wa

i want to get more accessible my webabb deployed on Google Apps Script.
I tried to do this adding an embedded manifest:
<link rel="manifest" href='data:application/manifest+json,
{
"name":"SharePoint Title",
"short_name":"SP Title",
"description":"description",
"start_url":"get_script_url",
"icons":[
{
"src":"icon_url",
"sizes":"144x144","type":"image/png"
}
],
"background":"rgb(255,0,0)",
"theme_color":"rgb(255,255,255)",
"display":"fullscreen"
}'>
and this js code (as shown in https://developer.mozilla.org/:
let deferredPrompt;
const addBtn = document.querySelector('.add-button');
addBtn.style.display = 'none';
window.addEventListener('beforeinstallprompt', (e) => {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
// Stash the event so it can be triggered later.
deferredPrompt = e;
// Update UI to notify the user they can add to home screen
addBtn.style.display = 'block';
addBtn.addEventListener('click', (e) => {
// hide our user interface that shows our A2HS button
addBtn.style.display = 'none';
// Show the prompt
deferredPrompt.prompt();
// Wait for the user to respond to the prompt
deferredPrompt.userChoice.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the A2HS prompt');
} else {
console.log('User dismissed the A2HS prompt');
}
deferredPrompt = null;
});
});
});
But when I can't load the page from any mobile device: Google returns an error.
Does somebody know how could I solve the problem?
Thanks a lot

chrome ext many loads on update page

In app use content script for all pages, and send message to active page on complete loaded page, but I have many calls of script sometimes 2 and more:
You can see that here
Code implimentation:
chrome.tabs.onCreated.addListener(function (tabs) {
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if(changeInfo.status === "complete") {
let tabid = tab.id;
console.log("Site is valid: url -> " + tab.url)
chrome.tabs.executeScript(tab.id, {
file: '/injections/mobile.bet365.com.js',
});
console.log(tab);
setTimeout(function () {
console.log("timeout was set")
chrome.tabs.query({}, function (tabs) {
let countOpenedTabsFrom = tabs.length;
let opener = 1;
// на целевой вкладке
chrome.tabs.sendMessage(tabid, {
message: "start_app",
opener: opener,
queuenumber: countOpenedTabsFrom
}, function (response) {
console.log(response);
});
});
}, 500);
}
And executed script have many queries too.
Why is this happen?
Every time onCreated event fires, you're adding a new onUpdated listener.
When, after that, onUpdated event fires, all of them are executed, leading to the behavior you're seeing.
You either need to de-register the handlers when they are done, or register the handler only once. See chrome.events docs (which describe common points of all event objects in other APIs) for ideas on how to implement that.
Note that the code inside chrome.tabs.onCreated listener does not use the tabs parameter at all, so it's not clear why do you even need to listen to onCreated.

Notify new application version with browser service workers

I build an html/js application (a progressive web app) with Polymer and polymer-cli and the well generated service-worker for caching and offline.
I wonder how to notify the user when a new version of the application is available and invite him to restart browser.
any ideas ?
Edit
a talk at IO2016 where Eric Bidel talk about service worker and notify user about new version of an application :
https://youtu.be/__KvYxcIIm8?list=PLOU2XLYxmsILe6_eGvDN3GyiodoV3qNSC&t=1510
Need to check the google IO Web source code
References:
https://developers.google.com/web/fundamentals/instant-and-offline/service-worker/lifecycle
https://classroom.udacity.com/courses/ud899
// page script
document.addEventListener('DOMContentLoaded', function(){
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('/sw.js')
.then(function(registration) {
console.info('ServiceWorker registration successful with scope:', registration.scope);
// if there's no controller, this page wasn't loaded
// via a service worker, so they're looking at the latest version.
// In that case, exit early
if (!navigator.serviceWorker.controller) return;
// if there's an updated worker already waiting, update
if (registration.waiting) {
console.info('show toast and upon click update...');
registration.waiting.postMessage({ updateSw: true });
return;
}
// if there's an updated worker installing, track its
// progress. If it becomes "installed", update
if (registration.installing) {
registration.addEventListener('statechange', function(){
if (registration.installing.state == 'installed'){
console.info('show toast and upon click update...');
registration.installing.postMessage({ updateSw: true });
return;
}
});
}
// otherwise, listen for new installing workers arriving.
// If one arrives, track its progress.
// If it becomes "installed", update
registration.addEventListener('updatefound', function(){
let newServiceWorker = registration.installing;
newServiceWorker.addEventListener('statechange', function() {
if (newServiceWorker.state == 'installed') {
console.info('show toast and upon click update...');
newServiceWorker.postMessage({ updateSw: true });
}
});
});
})
.catch(function(error) {
console.info('ServiceWorker registration failed:', error);
});
navigator.serviceWorker.addEventListener('controllerchange', function() {
window.location.reload();
});
}
});
// sw script
self.addEventListener('message', function(e) {
if (e.data.updateSw){
self.skipWaiting();
}
});
Thanks to IO team .. we need to check if the current service-worker becomes redundant
// Check to see if the service worker controlling the page at initial load
// has become redundant, since this implies there's a new service worker with fresh content.
if (navigator.serviceWorker && navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.onstatechange = function(event) {
if (event.target.state === 'redundant') {
// Define a handler that will be used for the next io-toast tap, at which point it
// be automatically removed.
const tapHandler = function() {
window.location.reload();
};
if (IOWA.Elements && IOWA.Elements.Toast &&
IOWA.Elements.Toast.showMessage) {
IOWA.Elements.Toast.showMessage(
'A new version of this app is available.', tapHandler, 'Refresh',
null, 0); // duration 0 indications shows the toast indefinitely.
} else {
tapHandler(); // Force reload if user never was shown the toast.
}
}
};
}

gcm push notification: not showing actual message reopen the browser

Gcm push notification message is sending properly to endpoints when browser is open :Notification messages which are in json file.
serviceWorker.js
'use strict';
self.addEventListener('install', function(event) {
self.skipWaiting();
console.log('Installed', event);
});
self.addEventListener('activate', function(event) {
console.log('Activated', event);
});
self.addEventListener('push', function(event) {
console.log('Started', self);
self.addEventListener('install', function(event) {
self.skipWaiting();
});
self.addEventListener('activate', function(event) {
console.log('Activated', event);
});
self.addEventListener('push', function(event) {
var url = "http://localhost/pntest/gmpush1.json?param="+Math.random();
event.waitUntil(
fetch(url).then(function(response) {
if (response.status !== 200) {
console.log('Problem. Status Code: ' + response.status);
throw new Error();
}
// Examine the text in the response
return response.json().then(function(data) {
if (data.error || !data.notification) {
console.error('The API returned an error.', data.error);
throw new Error();
}
var promises = [];
for(var i=0; data.notification && i < data.notification.length; i++) {
promises.push(self.registration.showNotification(data.notification[i].title, {
body: data.notification[i].body,
'renotify': true,
icon: data.notification[i].icon
//tag: notification.tag
}));
}
return Promise.all( promises );
});
})
);
});
self.addEventListener('notificationclick', function(event) {
console.log('Notification click: tag ', event.notification.tag);
event.notification.close();
var newurl = event.notification.data.newurl;
console.log(newurl.updatedurl);
var url = newurl.updatedurl;
event.waitUntil(
clients.matchAll({
type: 'window'
})
.then(function(windowClients) {
console.log(url);
for (var i = 0; i < windowClients.length; i++) {
var client = windowClients[i];
if (client.url === url && 'focus' in client) {
return client.focus();
}
}
if (clients.openWindow) {
return clients.openWindow(url);
}
})
);
});
});
gcmpush1.json
{"notification": [{"body": "Test data", "url": "https://www.google.com/", "icon": "http://www.wired.com/wp-content/uploads/2015/09/google-logo-1200x630.jpg", "title": "Test Notification"}]}
When browser is open, It's showing original message
Test Notification
If client browser is in offline(not opened) while my curl trigger. When reopening the client browser i suppose to get original message but what i'm getting is
site has been updated in the background
In my curl call, I have used 'time_to_live' = 2419200.
Whenever notification failed to load data to show on chrome notification window and 'PUSH' event generate successfully. It will show "site has been updated in the background". (Nothing to do with notification delivery from Curl. it may be fine)
Couple of observations from you service worker code:
1). You are using localhost path to fetch data, will create problem to load notification data while localhost will be offline.
var url = "http://localhost/pntest/gmpush1.json?param="+Math.random();
2). You are using two 'PUSH' event code in your SW. can wrap work in one function.
self.addEventListener('push', function(event) {...
You can refer below URL for creating simple service worker to get dynamic data for push notification.
https://developers.google.com/web/updates/2015/03/push-notifications-on-the-open-web?hl=en

web notifications not working

I'm tring to use the web notifications API like in this example:
http://www.inserthtml.com/2013/10/notification-api/?utm_source=html5weekly&utm_medium=email
When i'm in this website, everything is working great, in the console i'm writing "Notification.permission" and gets "granted".
But if i'm trying to do the same in my website, i'm getting error about the Notification object and when i'm trying to print "Notification.permission" i've noticed that the Notification object doesn't have this property and other properties like "requestPermition".
This happening in all the browsers and they all updated to the last version.
i've tried to open console in other websites, like cnn.com for example, and inspect the Notifications object, and also there are missing properties.
Any idea why?? and how its working the website above??
thanks.
this is my code:
window.addEventListener('load', function(){
var button = document.getElementById( "notifications" );
function theNotification() {
var n = new Notification("Hi!", {
});
}
// When the button is clicked
button.addEventListener('click', function () {
// If they are not denied (i.e. default)
if (Notification && Notification.permission !== "denied") {
// Request permission
Notification.requestPermission( function( status ){
// Change based on user's decision
if (Notification.permission !== status)
Notification.permission = status;
});
}
});
$(button).click();
var socket = io.connect('http://localhost:3000', {query : "user=343"});
socket.on('notification', function (data) {
console.log( data );
if (Notification && Notification.permission === "granted") {
theNotification();
} else {
alert(data);
}
});
});