mirror of
https://github.com/servo/servo.git
synced 2025-07-12 09:53:40 +01:00
272 lines
12 KiB
HTML
272 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<meta charset="utf-8">
|
|
<title>Creating a receiving browsing context</title>
|
|
<link rel="author" title="Tomoyuki Shimizu" href="https://github.com/tomoyukilabs">
|
|
<link rel="help" href="https://w3c.github.io/presentation-api/#creating-a-receiving-browsing-context">
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="common.js"></script>
|
|
<script src="support/stash.js"></script>
|
|
|
|
<p id="notice">
|
|
Click the button below and select the available presentation display, to start the manual test. The test passes if a "PASS" result appears.<br>
|
|
This test asks you to click the button twice, unless the test fails.<br>
|
|
<button id="presentBtn">Start Presentation Test</button>
|
|
</p>
|
|
|
|
<script>
|
|
setup({explicit_timeout: true});
|
|
|
|
let receiverStack;
|
|
add_completion_callback(() => {
|
|
// overwrite a stack written in the test result
|
|
if (receiverStack) {
|
|
document.querySelector('#log pre').textContent = receiverStack;
|
|
}
|
|
});
|
|
|
|
let connection;
|
|
const presentBtn = document.getElementById('presentBtn');
|
|
|
|
const dbName = {
|
|
controller: 'db-presentation-api-controlling-ua',
|
|
receiver: 'db-presentation-api-receiving-ua'
|
|
};
|
|
|
|
const main = () => {
|
|
promise_test(t => {
|
|
presentBtn.disabled = true;
|
|
const stash = new Stash(stashIds.toController, stashIds.toReceiver);
|
|
const request = new PresentationRequest('support/PresentationReceiver_create_receiving-ua.html');
|
|
|
|
t.add_cleanup(() => {
|
|
const notice = document.getElementById('notice');
|
|
notice.parentNode.removeChild(notice);
|
|
stash.stop();
|
|
|
|
// history.back();
|
|
document.cookie = 'PresentationApiTest=true; Expires=' + new Date().toUTCString();
|
|
sessionStorage.removeItem('presentation_api_test');
|
|
localStorage.removeItem('presentation_api_test');
|
|
|
|
Object.values(dbName).forEach(name => {
|
|
indexedDB.deleteDatabase(name);
|
|
});
|
|
|
|
if (connection) {
|
|
connection.onconnect = () => { connection.terminate(); };
|
|
if (connection.state === 'closed')
|
|
request.reconnect(connection.id);
|
|
else
|
|
connection.terminate();
|
|
}
|
|
|
|
if ('serviceWorker' in navigator) {
|
|
navigator.serviceWorker.getRegistrations().then(registrations => {
|
|
return Promise.all(registrations.map(reg => reg.unregister()));
|
|
});
|
|
}
|
|
if ('caches' in window) {
|
|
caches.keys().then(keys => {
|
|
return Promise.all(keys.map(key => caches.delete(key)));
|
|
});
|
|
}
|
|
});
|
|
|
|
history.pushState(null, 'test', 'PresentationReceiver_create-manual.https.html');
|
|
document.cookie = 'PresentationApiTest=Controlling-UA';
|
|
|
|
const storageName = 'presentation_api_test';
|
|
const storageValue = 'receiving-ua';
|
|
sessionStorage.setItem(storageName, storageValue);
|
|
localStorage.setItem(storageName, storageValue);
|
|
|
|
const openIndexedDB = () => {
|
|
if ('indexedDB' in window) {
|
|
const req = indexedDB.open(dbName.controller, 1);
|
|
const eventWatcher = new EventWatcher(t, req, 'upgradeneeded');
|
|
return eventWatcher.wait_for('upgradeneeded').then(evt => {
|
|
evt.target.result.close();
|
|
});
|
|
}
|
|
else
|
|
return Promise.resolve();
|
|
};
|
|
|
|
const cacheName = 'controlling-ua';
|
|
let clientUrls;
|
|
const getClientUrls = () => {
|
|
return new Promise(resolve => {
|
|
navigator.serviceWorker.getRegistration().then(reg => {
|
|
if (reg) {
|
|
const channel = new MessageChannel();
|
|
channel.port1.onmessage = event => {
|
|
resolve(event.data);
|
|
};
|
|
reg.active.postMessage('', [channel.port2]);
|
|
}
|
|
else
|
|
resolve([]);
|
|
});
|
|
});
|
|
};
|
|
const registerServiceWorker = () => {
|
|
return ('serviceWorker' in navigator ?
|
|
navigator.serviceWorker.register('serviceworker.js').then(registration => {
|
|
return new Promise((resolve, reject) => {
|
|
if (registration.installing) {
|
|
registration.installing.addEventListener('statechange', event => {
|
|
if(event.target.state === 'installed')
|
|
resolve();
|
|
});
|
|
}
|
|
else
|
|
resolve();
|
|
});
|
|
}) : Promise.resolve()).then(getClientUrls).then(urls => {
|
|
clientUrls = urls;
|
|
});
|
|
};
|
|
const openCaches = () => {
|
|
return 'caches' in window ? caches.open(cacheName).then(cache => cache.add('cache.txt')) : Promise.resolve();
|
|
};
|
|
|
|
const checkUpdates = () => {
|
|
// Cookie
|
|
assert_equals(document.cookie, 'PresentationApiTest=Controlling-UA', 'A cookie store is not shared with a receiving user agent.');
|
|
|
|
// Web Storage
|
|
assert_equals(sessionStorage.length, 1, 'Session storage is not shared with a receiving user agent.');
|
|
assert_equals(sessionStorage.getItem(storageName), storageValue, 'Session storage is not shared with a receiving user agent.');
|
|
assert_equals(localStorage.length, 1, 'Local storage is not shared with a receiving user agent.');
|
|
assert_equals(localStorage.getItem(storageName), storageValue, 'Local storage is not shared with a receiving user agent.');
|
|
};
|
|
|
|
// Indexed Database
|
|
const checkIndexedDB = t => {
|
|
if ('indexedDB' in window) {
|
|
const req = indexedDB.open(dbName.receiver);
|
|
const upgradeneededWatcher = new EventWatcher(t, req, 'upgradeneeded');
|
|
const successWatcher = new EventWatcher(t, req, 'success');
|
|
return Promise.race([
|
|
upgradeneededWatcher.wait_for('upgradeneeded').then(evt => {
|
|
evt.target.result.close();
|
|
}),
|
|
successWatcher.wait_for('success').then(evt => {
|
|
evt.target.result.close();
|
|
// This would fail if the database created by the receiving UA is visible to the controlling UA
|
|
assert_unreached('Indexed Database is not shared with a receiving user agent.');
|
|
})
|
|
]);
|
|
}
|
|
else
|
|
return Promise.resolve();
|
|
};
|
|
|
|
// Service Workers
|
|
const checkServiceWorkers = () => {
|
|
return 'serviceWorker' in navigator ? navigator.serviceWorker.getRegistrations().then(registrations => {
|
|
const message = 'List of registered service worker registrations is not shared with a receiving user agent.';
|
|
assert_equals(registrations.length, 1, message);
|
|
assert_equals(registrations[0].active.scriptURL, new Request('serviceworker.js').url, message);
|
|
}) : Promise.resolve();
|
|
};
|
|
const checkCaches = () => {
|
|
const message = 'Cache storage is not shared with a receiving user agent.';
|
|
return 'caches' in window ? caches.keys().then(keys => {
|
|
assert_equals(keys.length, 1, message);
|
|
assert_equals(keys[0], cacheName, message);
|
|
return caches.open(keys[0]);
|
|
}).then(cache => cache.matchAll())
|
|
.then(responses => {
|
|
assert_equals(responses.length, 1, message);
|
|
assert_equals(responses[0].url, new Request('cache.txt').url, message);
|
|
}) : Promise.resolve();
|
|
};
|
|
|
|
let timeout;
|
|
const enableTimeout = () => {
|
|
timeout = t.step_timeout(() => {
|
|
t.force_timeout();
|
|
t.done();
|
|
}, 5000);
|
|
};
|
|
const disableTimeout = () => {
|
|
clearTimeout(timeout);
|
|
};
|
|
const cancelWait = () => {
|
|
connection.removeEventListener('close', onTerminate);
|
|
connection.removeEventListener('terminate', onTerminate);
|
|
};
|
|
const onTerminate = (reject, event) => {
|
|
cancelWait();
|
|
reject('A receiving user agent unexpectedly ' + event.type + 'd a presentation. ');
|
|
};
|
|
const waitForTerminate = () => {
|
|
return new Promise((resolve, reject) => {
|
|
connection.addEventListener('close', onTerminate.bind(this, reject));
|
|
connection.addEventListener('terminate', onTerminate.bind(this, reject));
|
|
});
|
|
};
|
|
|
|
// Start a presentation for receiving user agent tests
|
|
return request.start().then(c => {
|
|
connection = c;
|
|
enableTimeout();
|
|
|
|
// This Promise.race will be rejected if a receiving side terminates/closes the connection when window.close() is invoked
|
|
return Promise.race([
|
|
openIndexedDB()
|
|
.then(registerServiceWorker)
|
|
.then(openCaches)
|
|
.then(() => { return stash.init(); })
|
|
.then(() => { return stash.receive(); }),
|
|
waitForTerminate()]);
|
|
}).then(result => {
|
|
// terminate and connect again if the result is PASS
|
|
cancelWait();
|
|
const json = JSON.parse(result);
|
|
if (json.test.status !== 0)
|
|
return json;
|
|
|
|
// Check accessibility to window clients before terminating a presentation
|
|
return getClientUrls().then(urls => {
|
|
assert_true(urls.length === clientUrls.length && urls.every((value, index) => { return clientUrls[index] === value}),
|
|
'A window client in a receiving user agent is not accessible to a service worker on a controlling user agent.');
|
|
const eventWatcher = new EventWatcher(t, connection, 'terminate');
|
|
connection.terminate();
|
|
return eventWatcher.wait_for('terminate');
|
|
}).then(() => {
|
|
connection = null;
|
|
disableTimeout();
|
|
presentBtn.removeEventListener('click', main);
|
|
presentBtn.textContent = 'Continue Presentation Test';
|
|
presentBtn.disabled = false;
|
|
const eventWatcher = new EventWatcher(t, presentBtn, 'click');
|
|
return eventWatcher.wait_for('click');
|
|
}).then(() => {
|
|
presentBtn.disabled = true;
|
|
return request.start();
|
|
}).then(c => {
|
|
connection = c;
|
|
enableTimeout();
|
|
return stash.receive();
|
|
}).then(result => {
|
|
return JSON.parse(result);
|
|
});
|
|
}).then(json => {
|
|
if (json.test.status === 0) {
|
|
checkUpdates();
|
|
return checkIndexedDB(t)
|
|
.then(checkServiceWorkers)
|
|
.then(checkCaches);
|
|
}
|
|
else {
|
|
receiverStack = json.test.stack;
|
|
parseResult(json.test.message);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
presentBtn.addEventListener('click', main);
|
|
</script>
|