chromium/third_party/blink/web_tests/http/tests/notifications/instrumentation-service-worker.js

// Deep-copies the attributes of |notification|. Note that the
// robustness of this function (and also |assert_object_equals| in
// testharness.js) affects the types of possible testing can be done.
// TODO(peter): change this to a structured clone algorithm.
function cloneNotification(notification) {
    function deepCopy(src) {
        if (typeof src !== 'object' || src === null)
            return src;
        var dst = Array.isArray(src) ? [] : {};
        for (var property in src) {
            if (typeof src[property] === 'function')
                continue;
            dst[property] = deepCopy(src[property]);
        }
        return dst;
    }

    return deepCopy(notification);
}

// Deserializes a trigger object sent via postMessage.
function deserializeTrigger(trigger) {
    if (trigger && trigger.timestamp)
        return new TimestampTrigger(trigger.timestamp);
    return trigger;
}

// Deserializes notification options sent via postMessage.
function deserializeOptions(options) {
    return {
        ...options,
        showTrigger: deserializeTrigger(options.showTrigger),
    };
}

// Allows a document to exercise the Notifications API within a service worker by sending commands.
var messagePort = null;

// All urls of requests that have been routed through the fetch event handler.
var fetchHistory = [];

addEventListener('install', event => {
    event.waitUntil(skipWaiting());
});

addEventListener('activate', event => {
    event.waitUntil(clients.claim());
});

addEventListener('message', workerEvent => {
    messagePort = workerEvent.data;

    // Listen to incoming commands on the message port.
    messagePort.onmessage = event => {
        if (typeof event.data != 'object' || !event.data.command)
            return;

        switch (event.data.command) {
            case 'permission':
                messagePort.postMessage({ command: event.data.command,
                                          value: Notification.permission });
                break;

            case 'show':
                registration.showNotification(event.data.title, deserializeOptions(event.data.options)).then(() => {
                    messagePort.postMessage({ command: event.data.command,
                                              success: true });
                }, error => {
                    messagePort.postMessage({ command: event.data.command,
                                              success: false,
                                              message: error.message });
                });
                break;

            case 'get-fetch-history':
                messagePort.postMessage({ command: event.data.command,
                                          fetchHistory: fetchHistory });
                break;

            case 'get':
                var filter = {};
                if (typeof (event.data.filter) !== 'undefined')
                    filter = event.data.filter;

                registration.getNotifications(filter).then(notifications => {
                    var clonedNotifications = [];
                    for (var notification of notifications)
                        clonedNotifications.push(cloneNotification(notification));

                    messagePort.postMessage({ command: event.data.command,
                                              success: true,
                                              notifications: clonedNotifications });
                }, error => {
                    messagePort.postMessage({ command: event.data.command,
                                              success: false,
                                              message: error.message });
                });
                break;

            case 'request-permission-exists':
                messagePort.postMessage({ command: event.data.command,
                                          value: 'requestPermission' in Notification });
                break;

            default:
                messagePort.postMessage({ command: 'error', message: 'Invalid command: ' + event.data.command });
                break;
        }
    };

    // Notify the controller that the worker is now available.
    messagePort.postMessage('ready');
});

addEventListener('notificationclick', event => {
    var notificationCopy = cloneNotification(event.notification);

    // Notifications containing "ACTION:CLOSE" in their message will be closed
    // immediately by the Service Worker.
    if (event.notification.body.indexOf('ACTION:CLOSE') != -1)
        event.notification.close();

    // Notifications containing "ACTION:OPENWINDOW" in their message will attempt
    // to open a new window for an example URL.
    if (event.notification.body.indexOf('ACTION:OPENWINDOW') != -1)
        event.waitUntil(clients.openWindow('https://example.com/'));

    messagePort.postMessage({ command: 'click',
                              notification: notificationCopy,
                              action: event.action,
                              reply: event.reply });
});

addEventListener('notificationclose', event => {
    var notificationCopy = cloneNotification(event.notification);
    messagePort.postMessage({ command: 'close',
                              notification: notificationCopy });
});

addEventListener('fetch', event => {
    fetchHistory.push(event.request.url);
    event.respondWith(fetch(event.request));
});