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>