PresentationReceiver_create-manual.https.html (12645B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"> 3 <title>Creating a receiving browsing context</title> 4 <link rel="author" title="Tomoyuki Shimizu" href="https://github.com/tomoyukilabs"> 5 <link rel="help" href="https://w3c.github.io/presentation-api/#creating-a-receiving-browsing-context"> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 <script src="common.js"></script> 9 <script src="support/stash.js"></script> 10 11 <p id="notice"> 12 Click the button below and select the available presentation display, to start the manual test. The test passes if a "PASS" result appears.<br> 13 This test asks you to click the button twice, unless the test fails.<br> 14 <button id="presentBtn">Start Presentation Test</button> 15 </p> 16 17 <script> 18 setup({explicit_timeout: true}); 19 20 let receiverStack; 21 add_completion_callback(() => { 22 // overwrite a stack written in the test result 23 if (receiverStack) { 24 document.querySelector('#log pre').textContent = receiverStack; 25 } 26 }); 27 28 let connection; 29 const presentBtn = document.getElementById('presentBtn'); 30 31 const dbName = { 32 controller: 'db-presentation-api-controlling-ua', 33 receiver: 'db-presentation-api-receiving-ua' 34 }; 35 36 const main = () => { 37 promise_test(t => { 38 presentBtn.disabled = true; 39 const stash = new Stash(stashIds.toController, stashIds.toReceiver); 40 const request = new PresentationRequest('support/PresentationReceiver_create_receiving-ua.html'); 41 42 t.add_cleanup(() => { 43 const notice = document.getElementById('notice'); 44 notice.parentNode.removeChild(notice); 45 stash.stop(); 46 47 // history.back(); 48 document.cookie = 'PresentationApiTest=true; Expires=' + new Date().toUTCString(); 49 sessionStorage.removeItem('presentation_api_test'); 50 localStorage.removeItem('presentation_api_test'); 51 52 Object.values(dbName).forEach(name => { 53 indexedDB.deleteDatabase(name); 54 }); 55 56 if (connection) { 57 connection.onconnect = () => { connection.terminate(); }; 58 if (connection.state === 'closed') 59 request.reconnect(connection.id); 60 else 61 connection.terminate(); 62 } 63 64 if ('serviceWorker' in navigator) { 65 navigator.serviceWorker.getRegistrations().then(registrations => { 66 return Promise.all(registrations.map(reg => reg.unregister())); 67 }); 68 } 69 if ('caches' in window) { 70 caches.keys().then(keys => { 71 return Promise.all(keys.map(key => caches.delete(key))); 72 }); 73 } 74 }); 75 76 history.pushState(null, 'test', 'PresentationReceiver_create-manual.https.html'); 77 document.cookie = 'PresentationApiTest=Controlling-UA'; 78 79 const storageName = 'presentation_api_test'; 80 const storageValue = 'receiving-ua'; 81 sessionStorage.setItem(storageName, storageValue); 82 localStorage.setItem(storageName, storageValue); 83 84 const openIndexedDB = () => { 85 if ('indexedDB' in window) { 86 const req = indexedDB.open(dbName.controller, 1); 87 const eventWatcher = new EventWatcher(t, req, 'upgradeneeded'); 88 return eventWatcher.wait_for('upgradeneeded').then(evt => { 89 evt.target.result.close(); 90 }); 91 } 92 else 93 return Promise.resolve(); 94 }; 95 96 const cacheName = 'controlling-ua'; 97 let clientUrls; 98 const getClientUrls = () => { 99 return new Promise(resolve => { 100 navigator.serviceWorker.getRegistration().then(reg => { 101 if (reg) { 102 const channel = new MessageChannel(); 103 channel.port1.onmessage = event => { 104 resolve(event.data); 105 }; 106 reg.active.postMessage('', [channel.port2]); 107 } 108 else 109 resolve([]); 110 }); 111 }); 112 }; 113 const registerServiceWorker = () => { 114 return ('serviceWorker' in navigator ? 115 navigator.serviceWorker.register('serviceworker.js').then(registration => { 116 return new Promise((resolve, reject) => { 117 if (registration.installing) { 118 registration.installing.addEventListener('statechange', event => { 119 if(event.target.state === 'installed') 120 resolve(); 121 }); 122 } 123 else 124 resolve(); 125 }); 126 }) : Promise.resolve()).then(getClientUrls).then(urls => { 127 clientUrls = urls; 128 }); 129 }; 130 const openCaches = () => { 131 return 'caches' in window ? caches.open(cacheName).then(cache => cache.add('cache.txt')) : Promise.resolve(); 132 }; 133 134 const checkUpdates = () => { 135 // Cookie 136 assert_equals(document.cookie, 'PresentationApiTest=Controlling-UA', 'A cookie store is not shared with a receiving user agent.'); 137 138 // Web Storage 139 assert_equals(sessionStorage.length, 1, 'Session storage is not shared with a receiving user agent.'); 140 assert_equals(sessionStorage.getItem(storageName), storageValue, 'Session storage is not shared with a receiving user agent.'); 141 assert_equals(localStorage.length, 1, 'Local storage is not shared with a receiving user agent.'); 142 assert_equals(localStorage.getItem(storageName), storageValue, 'Local storage is not shared with a receiving user agent.'); 143 }; 144 145 // Indexed Database 146 const checkIndexedDB = t => { 147 if ('indexedDB' in window) { 148 const req = indexedDB.open(dbName.receiver); 149 const upgradeneededWatcher = new EventWatcher(t, req, 'upgradeneeded'); 150 const successWatcher = new EventWatcher(t, req, 'success'); 151 return Promise.race([ 152 upgradeneededWatcher.wait_for('upgradeneeded').then(evt => { 153 evt.target.result.close(); 154 }), 155 successWatcher.wait_for('success').then(evt => { 156 evt.target.result.close(); 157 // This would fail if the database created by the receiving UA is visible to the controlling UA 158 assert_unreached('Indexed Database is not shared with a receiving user agent.'); 159 }) 160 ]); 161 } 162 else 163 return Promise.resolve(); 164 }; 165 166 // Service Workers 167 const checkServiceWorkers = () => { 168 return 'serviceWorker' in navigator ? navigator.serviceWorker.getRegistrations().then(registrations => { 169 const message = 'List of registered service worker registrations is not shared with a receiving user agent.'; 170 assert_equals(registrations.length, 1, message); 171 assert_equals(registrations[0].active.scriptURL, new Request('serviceworker.js').url, message); 172 }) : Promise.resolve(); 173 }; 174 const checkCaches = () => { 175 const message = 'Cache storage is not shared with a receiving user agent.'; 176 return 'caches' in window ? caches.keys().then(keys => { 177 assert_equals(keys.length, 1, message); 178 assert_equals(keys[0], cacheName, message); 179 return caches.open(keys[0]); 180 }).then(cache => cache.matchAll()) 181 .then(responses => { 182 assert_equals(responses.length, 1, message); 183 assert_equals(responses[0].url, new Request('cache.txt').url, message); 184 }) : Promise.resolve(); 185 }; 186 187 let timeout; 188 const enableTimeout = () => { 189 timeout = t.step_timeout(() => { 190 t.force_timeout(); 191 t.done(); 192 }, 5000); 193 }; 194 const disableTimeout = () => { 195 clearTimeout(timeout); 196 }; 197 const cancelWait = () => { 198 connection.removeEventListener('close', onTerminate); 199 connection.removeEventListener('terminate', onTerminate); 200 }; 201 const onTerminate = (reject, event) => { 202 cancelWait(); 203 reject('A receiving user agent unexpectedly ' + event.type + 'd a presentation. '); 204 }; 205 const waitForTerminate = () => { 206 return new Promise((resolve, reject) => { 207 connection.addEventListener('close', onTerminate.bind(this, reject)); 208 connection.addEventListener('terminate', onTerminate.bind(this, reject)); 209 }); 210 }; 211 212 // Start a presentation for receiving user agent tests 213 return request.start().then(c => { 214 connection = c; 215 enableTimeout(); 216 217 // This Promise.race will be rejected if a receiving side terminates/closes the connection when window.close() is invoked 218 return Promise.race([ 219 openIndexedDB() 220 .then(registerServiceWorker) 221 .then(openCaches) 222 .then(() => { return stash.init(); }) 223 .then(() => { return stash.receive(); }), 224 waitForTerminate()]); 225 }).then(result => { 226 // terminate and connect again if the result is PASS 227 cancelWait(); 228 const json = JSON.parse(result); 229 if (json.test.status !== 0) 230 return json; 231 232 // Check accessibility to window clients before terminating a presentation 233 return getClientUrls().then(urls => { 234 assert_true(urls.length === clientUrls.length && urls.every((value, index) => { return clientUrls[index] === value}), 235 'A window client in a receiving user agent is not accessible to a service worker on a controlling user agent.'); 236 const eventWatcher = new EventWatcher(t, connection, 'terminate'); 237 connection.terminate(); 238 return eventWatcher.wait_for('terminate'); 239 }).then(() => { 240 connection = null; 241 disableTimeout(); 242 presentBtn.removeEventListener('click', main); 243 presentBtn.textContent = 'Continue Presentation Test'; 244 presentBtn.disabled = false; 245 const eventWatcher = new EventWatcher(t, presentBtn, 'click'); 246 return eventWatcher.wait_for('click'); 247 }).then(() => { 248 presentBtn.disabled = true; 249 return request.start(); 250 }).then(c => { 251 connection = c; 252 enableTimeout(); 253 return stash.receive(); 254 }).then(result => { 255 return JSON.parse(result); 256 }); 257 }).then(json => { 258 if (json.test.status === 0) { 259 checkUpdates(); 260 return checkIndexedDB(t) 261 .then(checkServiceWorkers) 262 .then(checkCaches); 263 } 264 else { 265 receiverStack = json.test.stack; 266 parseResult(json.test.message); 267 } 268 }); 269 }); 270 }; 271 presentBtn.addEventListener('click', main); 272 </script>