tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PresentationReceiver_create_receiving-ua.html (15449B)


      1 <!DOCTYPE html>
      2 
      3 <meta charset="utf-8">
      4 <title>Creating a receiving browsing context</title>
      5 <link rel="author" title="Tomoyuki Shimizu" href="https://github.com/tomoyukilabs/">
      6 <link rel="help" href="https://w3c.github.io/presentation-api/#creating-a-receiving-browsing-context">
      7 <script src="/resources/testharness.js"></script>
      8 <script src="/resources/testharnessreport.js"></script>
      9 <script src="../common.js"></script>
     10 <script src="stash.js"></script>
     11 <style>
     12 a { visibility: hidden; }
     13 iframe { width: 100%; }
     14 </style>
     15 
     16 <iframe id="child" src="PresentationReceiver_create_receiving-ua_child.html"></iframe>
     17 <p id="notice">Checking <code id="modal"></code>: if you see this message, please wait for test to time out.</p>
     18 <a href="PresentationReceiver_unreached_receiving-ua.html">do not navigate</a>
     19 <script>
     20 let finished = false;
     21 
     22 const sendResult = (test, status) => {
     23    if(!finished) {
     24        finished = true;
     25        const stash = new Stash(stashIds.toReceiver, stashIds.toController);
     26        stash.send(JSON.stringify({ test: test, status: status }));
     27    }
     28 };
     29 
     30 add_completion_callback((tests, status) => {
     31    // note: a single test result is supposed to appear here.
     32    sendResult(tests[0], status);
     33 });
     34 
     35 const child = document.getElementById('child');
     36 child.addEventListener('load', () => {
     37    const notice = document.getElementById('notice');
     38    const modal = document.getElementById('modal');
     39 
     40    const dbName = {
     41        controller: 'db-presentation-api-controlling-ua',
     42        receiver: 'db-presentation-api-receiving-ua'
     43    };
     44 
     45    promise_test(t => {
     46        t.add_cleanup(() => {
     47            document.cookie = cookieName + '=False;Expires=' + new Date().toUTCString();
     48            document.cookie = cookieNameChild + '=False;Expires=' + new Date().toUTCString();
     49            sessionStorage.removeItem(storageName);
     50            localStorage.removeItem(storageName);
     51            sessionStorage.removeItem(storageNameChild);
     52            localStorage.removeItem(storageNameChild);
     53 
     54            Object.values(dbName).forEach(name => {
     55                indexedDB.deleteDatabase(name);
     56            });
     57 
     58            if ('serviceWorker' in navigator) {
     59                navigator.serviceWorker.getRegistrations().then(registrations => {
     60                    return Promise.all(registrations.map(reg => reg.unregister()));
     61                });
     62            }
     63            if ('caches' in window) {
     64                caches.keys().then(keys => {
     65                    return Promise.all(keys.map(key => caches.delete(key)));
     66                });
     67            }
     68        });
     69 
     70        // Session history
     71        assert_equals(window.history.length, 1, 'Session history consists of the current page only.');
     72 
     73        // The Sandboxed auxiliary navigation browsing context flag
     74        assert_equals(window.open('PresentationReceiver_unreached_receiving-ua.html'), null, 'Sandboxed auxiliary navigation browsing context flag is set.');
     75 
     76        // The sandboxed top-level navigation browsing context flag (codes below are expected to be ignored)
     77        window.close();
     78        document.querySelector('a').click();
     79        location.href = 'PresentationReceiver_unreached_receiving-ua.html';
     80 
     81        // The sandboxed modals flag (codes below are expected to be ignored):
     82        // If user agent prompts user, a timeout will occur and the test will eventually fail
     83        let message = 'If you see this prompt, do not dismiss it and wait for test to time out.';
     84        notice.style.display = 'block';
     85        modal.textContent = 'alert()';
     86        alert(message);
     87        modal.textContent = 'confirm()';
     88        confirm(message);
     89        modal.textContent = 'print()';
     90        print();
     91        modal.textContent = 'prompt()';
     92        prompt(message);
     93        notice.style.display = 'none';
     94 
     95        // Permissions
     96        const checkPermission = query => {
     97            return navigator.permissions ? navigator.permissions.query(query).then(status => {
     98                assert_equals(status.state, 'denied', 'The state of the "' + query.name + '" permission is set to "denied".');
     99            }, () => { /* skip this assertion if the specified permission is not implemented */ }) : Promise.resolve();
    100        }
    101 
    102        // Cookie
    103        assert_equals(document.cookie, '', 'A cookie store is set to an empty store.')
    104 
    105        // Indexed Database
    106        const checkIndexedDB = () => {
    107            if ('indexedDB' in window) {
    108                // The test would fail when the database is already created by the controlling UA
    109                const req = indexedDB.open(dbName.controller);
    110                const upgradeneededWatcher = new EventWatcher(t, req, 'upgradeneeded');
    111                const successWatcher = new EventWatcher(t, req, 'success');
    112                return Promise.race([
    113                    upgradeneededWatcher.wait_for('upgradeneeded').then(evt => {
    114                        evt.target.result.close();
    115                        const req = indexedDB.open(dbName.receiver, 2);
    116                        const eventWatcher = new EventWatcher(t, req, 'upgradeneeded');
    117                        return eventWatcher.wait_for('upgradeneeded');
    118                    }).then(evt => {
    119                        evt.target.result.close();
    120                    }),
    121                    successWatcher.wait_for('success').then(evt => {
    122                        evt.target.result.close();
    123                        // This would fail if the database created by the controlling UA is visible to the receiving UA
    124                        assert_unreached('Indexed Database is set to an empty storage.');
    125                    })
    126                ]);
    127            }
    128            else
    129                return Promise.resolve();
    130        };
    131 
    132        // Web Storage
    133        assert_equals(sessionStorage.length, 0, 'Session storage is set to an empty storage.');
    134        assert_equals(localStorage.length, 0, 'Local storage is set to an empty storage.');
    135 
    136        // Service Workers
    137        const checkServiceWorkers = () => {
    138            return 'serviceWorker' in navigator ? navigator.serviceWorker.getRegistrations().then(registrations => {
    139                assert_equals(registrations.length, 0, 'List of registered service worker registrations is empty.');
    140            }) : Promise.resolve();
    141        };
    142        const checkCaches = () => {
    143            return 'caches' in window ? caches.keys().then(keys => {
    144                assert_equals(keys.length, 0, 'Cache storage is empty.')
    145            }) : Promise.resolve();
    146        };
    147 
    148        // Navigation
    149        const checkNavigation = () => {
    150            return navigator.presentation.receiver.connectionList.then(connectionList => {
    151                assert_equals(connectionList.connections.length, 1, 'The initial number of presentation connections is one');
    152                assert_equals(location.href, connectionList.connections[0].url, 'A receiving browsing context is navigated to the presentation URL.');
    153            });
    154        };
    155 
    156        // Update storages and service workers shared with the top-level brosing context
    157        const cookieName = 'PresentationApiTest';
    158        const cookieValue = 'Receiving-UA';
    159        const storageName = 'presentation_api_test';
    160        const storageValue = 'receiving-ua';
    161        document.cookie = cookieName + '=' + cookieValue;
    162        sessionStorage.setItem(storageName, storageValue);
    163        localStorage.setItem(storageName, storageValue);
    164 
    165        // Service Workers and Caches
    166        const cacheName = 'receiving-ua';
    167        const getClientUrls = () => {
    168            return new Promise(resolve => {
    169                navigator.serviceWorker.getRegistration().then(reg => {
    170                    const channel = new MessageChannel();
    171                    channel.port1.onmessage = event => {
    172                        resolve(event.data);
    173                    };
    174                    reg.active.postMessage('test', [channel.port2]);
    175                });
    176            });
    177        };
    178 
    179        const registerServiceWorker = () => {
    180            return ('serviceWorker' in navigator ?
    181                navigator.serviceWorker.register('../serviceworker.js').then(registration => {
    182                    return new Promise((resolve, reject) => {
    183                        if (registration.installing) {
    184                            registration.installing.addEventListener('statechange', event => {
    185                                if(event.target.state === 'installed')
    186                                    resolve();
    187                            });
    188                        }
    189                        else
    190                            resolve();
    191                    });
    192                }) : Promise.resolve()).then(getClientUrls).then(urls => {
    193                    assert_true(urls.every(url => { return url !== new Request('../PresentationReceiver_create-manual.https.html').url }),
    194                                'A window client in a controlling user agent is not accessible to a service worker on a receiving user agent.');
    195                });
    196        };
    197 
    198        const openCaches = () => {
    199            return 'caches' in window ? caches.open(cacheName).then(cache => cache.add('../cache.txt')) : Promise.resolve();
    200        };
    201 
    202        const getChildFrameResult = () => {
    203            return new Promise(resolve => {
    204                window.addEventListener('message', t.step_func(event => {
    205                    const result = event.data;
    206                    if (result.type === 'presentation-api') {
    207                        // if the test in iframe failed, report the result and abort the test
    208                        if (result.test.status === 0)
    209                            resolve();
    210                        else {
    211                            sendResult(result.test, result.status);
    212                            assert_unreached('A test for a nested browsing context failed.');
    213                        }
    214                    }
    215                }));
    216                child.contentWindow.postMessage('start', location.origin);
    217            });
    218        };
    219 
    220        // check the results from updates by iframe
    221        const cookieNameChild = 'NestedBrowsingContext';
    222        const cookieValueChild = 'True';
    223        const storageNameChild = 'nested_browsing_context';
    224        const storageValueChild = 'yes';
    225 
    226        const checkUpdatedResult = () => {
    227            // cookie
    228            const cookies = document.cookie.split(/;\s*/).reduce((result, item) => {
    229                const t = item.split('=');
    230                result[t[0]] = t[1];
    231                return result;
    232            }, {});
    233            message = 'A cookie store is shared by top-level and nested browsing contexts.'
    234            assert_equals(Object.keys(cookies).length, 2, message);
    235            assert_equals(cookies[cookieName], cookieValue, message);
    236            assert_equals(cookies[cookieNameChild], cookieValueChild, message);
    237 
    238            // Web Storage
    239            message = 'Session storage is shared by top-level and nested browsing contexts.';
    240            assert_equals(sessionStorage.length, 2, message);
    241            assert_equals(sessionStorage.getItem(storageName), storageValue, message);
    242            assert_equals(sessionStorage.getItem(storageNameChild), storageValueChild, message);
    243            message = 'Local storage is shared by top-level and nested browsing contexts.';
    244            assert_equals(localStorage.length, 2, message);
    245            assert_equals(localStorage.getItem(storageName), storageValue, message);
    246            assert_equals(localStorage.getItem(storageNameChild), storageValueChild, message);
    247        };
    248 
    249        // Indexed Database
    250        const checkUpdatedIndexedDB = () => {
    251            if ('indexedDB' in window) {
    252                message = 'Indexed Database is shared by top-level and nested browsing contexts.';
    253                const req = indexedDB.open(dbName.receiver);
    254                const upgradeneededWatcher = new EventWatcher(t, req, 'upgradeneeded');
    255                const successWatcher = new EventWatcher(t, req, 'success');
    256                return Promise.race([
    257                    upgradeneededWatcher.wait_for('upgradeneeded').then(evt => {
    258                        evt.target.result.close();
    259                        // Check if the version of the database is upgraded to 3 by the nested browsing context
    260                        assert_unreached(message);
    261                    }),
    262                    successWatcher.wait_for('success').then(evt => {
    263                        const db = evt.target.result;
    264                        const version = db.version;
    265                        db.close();
    266                        // Check if the version of the database is upgraded to 3 by the nested browsing context
    267                        assert_equals(version, 3, message);
    268                    })
    269                ]);
    270            }
    271            else
    272                return Promise.resolve();
    273        };
    274 
    275        // Service Workers
    276        const checkUpdatedServiceWorkers = () => {
    277            return 'serviceWorker' in window ? navigator.serviceWorker.getRegistrations().then(registrations => {
    278                message = 'List of registered service worker registrations is shared by top-level and nested browsing contexts.';
    279                assert_equals(registrations.length, 2, message);
    280                const scriptURLs = registrations.map(reg => { return reg.active.scriptURL; }).sort();
    281                assert_equals(scriptURLs[0], new Request('../serviceworker.js').url, message);
    282                assert_equals(scriptURLs[1], new Request('serviceworker.js').url, message);
    283            }) : Promise.resolve();
    284        };
    285        const cacheNameChild = 'nested-browsing-context';
    286        const checkUpdatedCaches = () => {
    287            message = 'Cache storage is shared by top-level and nested browsing contexts.';
    288            return 'caches' in window ? caches.keys().then(keys => {
    289                assert_equals(keys.length, 2, message);
    290                const cacheKeys = keys.sort();
    291                assert_equals(cacheKeys[0], cacheNameChild, message);
    292                assert_equals(cacheKeys[1], cacheName, message);
    293                return Promise.all(keys.map(
    294                    key => caches.open(key)
    295                        .then(cache => cache.matchAll())
    296                        .then(responses => {
    297                            assert_equals(responses.length, 1, message);
    298                            assert_equals(responses[0].url, new Request('../cache.txt').url, message);
    299                        })));
    300            }) : Promise.resolve();
    301        };
    302 
    303        // Asynchronous tests
    304        const permissionList = [
    305            { name: 'geolocation' },
    306            { name: 'notifications' },
    307            { name: 'push', userVisibleOnly: true },
    308            { name: 'midi' },
    309            { name: 'camera' },
    310            { name: 'microphone' },
    311            { name: 'speaker' },
    312            { name: 'background-sync' }
    313        ];
    314        return Promise.all(permissionList.map(perm => { return checkPermission(perm); }))
    315            .then(checkIndexedDB)
    316            .then(checkServiceWorkers)
    317            .then(checkCaches)
    318            .then(checkNavigation)
    319            .then(registerServiceWorker)
    320            .then(openCaches)
    321            .then(getChildFrameResult)
    322            .then(checkUpdatedResult)
    323            .then(checkUpdatedIndexedDB)
    324            .then(checkUpdatedServiceWorkers)
    325            .then(checkUpdatedCaches);
    326    });
    327 });
    328 </script>