query-ordering.https.html (5453B)
1 <!DOCTYPE html> 2 <meta charset=utf-8> 3 <title>Web Locks API: navigator.locks.query ordering</title> 4 <link rel=help href="https://w3c.github.io/web-locks/"> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="resources/helpers.js"></script> 8 <style>iframe { display: none; }</style> 9 <script> 10 'use strict'; 11 12 // Grab a lock and hold until a release function is called. Resolves 13 // to a release function. 14 function getLockAndHoldUntilReleased(name, options) { 15 let release; 16 const promise = new Promise(resolve => { release = resolve; }); 17 return new Promise(resolve => { 18 navigator.locks.request(name, options || {}, lock => { 19 resolve(release); 20 return promise; 21 }).catch(_ => {}); 22 }); 23 } 24 25 // Returns a promise resolved by the next message event. 26 function nextMessage() { 27 return new Promise(resolve => { 28 window.addEventListener('message', event => { 29 resolve(event.data); 30 }, {once: true}); 31 }); 32 } 33 34 // Tests the ordering constraints on the requested lock state returned by 35 // navigator.locks.query(). Three separate iframes are instantiated to make 36 // lock requests on the same resource, first in one order and then in another, 37 // different order. For each set of requests, it is verified that the requests 38 // appear in the result of navigator.locks.query() in the same order in which 39 // they were made. 40 // 41 // It is necessary to use separate iframes here so that the lock requests have 42 // distinguishable client_ids (otherwise it would not be possible to 43 // distinguish the requests and thus impossible to verify ordering). 44 promise_test(async testCase => { 45 assert_implements(navigator.locks); 46 const resourceName = uniqueName(testCase); 47 48 // Set up clients. 49 const frame1 = await iframe('resources/iframe.html'); 50 const frame2 = await iframe('resources/iframe.html'); 51 const frame3 = await iframe('resources/iframe.html'); 52 testCase.add_cleanup(() => { frame1.remove(); }); 53 testCase.add_cleanup(() => { frame2.remove(); }); 54 testCase.add_cleanup(() => { frame3.remove(); }); 55 56 // Collect the client ids. 57 const clientId1 = 58 (await postToFrameAndWait(frame1, {op: 'client_id', 59 name: resourceName})).client_id; 60 const clientId2 = 61 (await postToFrameAndWait(frame2, {op: 'client_id', 62 name: resourceName})).client_id; 63 const clientId3 = 64 (await postToFrameAndWait(frame3, {op: 'client_id', 65 name: resourceName})).client_id; 66 67 // Preemptively take the lock. 68 const firstRequestGroupReleaseFunction = 69 await getLockAndHoldUntilReleased(resourceName); 70 71 // Queue the first group of lock requests from the different clients. These 72 // will be blocked until firstRequestGroupReleaseFunction() is called. 73 let lockId1; 74 let lockId2; 75 const lockPromise1 = 76 postToFrameAndWait(frame1, {op: 'request', name: resourceName}) 77 .then(val => {lockId1 = val.lock_id;}); 78 const lockPromise2 = 79 postToFrameAndWait(frame2, {op: 'request', name: resourceName}) 80 .then(val => {lockId2 = val.lock_id;}); 81 82 // This third request will later be granted and held in order to block a 83 // second group of requests to test a different client ordering. It is not 84 // meant to be released. 85 postToFrameAndWait(frame3, {op: 'request', name: resourceName}); 86 87 // Request and wait for the release of a separate lock to ensure all previous 88 // requests are processed. 89 const checkpointName = uniqueName(testCase, 'checkpoint'); 90 const checkpointId = (await postToFrameAndWait( 91 frame3, 92 {op: 'request', name: checkpointName})).lock_id; 93 await postToFrameAndWait(frame3, {op: 'release', lock_id: checkpointId}); 94 95 // Query the state and test the ordering of requested locks. 96 const state = await navigator.locks.query(); 97 const relevant_pending_ids = state.pending 98 .filter(lock => [clientId1, clientId2, clientId3].includes(lock.clientId)) 99 .map(lock => lock.clientId); 100 assert_array_equals( 101 [clientId1, clientId2, clientId3], 102 relevant_pending_ids, 103 'Querying the state should return requested locks in the order they were ' 104 + 'requested.'); 105 106 // Add the second group of requests from the clients in a new order. 107 postToFrameAndWait(frame3, {op: 'request', name: resourceName}); 108 postToFrameAndWait(frame1, {op: 'request', name: resourceName}); 109 postToFrameAndWait(frame2, {op: 'request', name: resourceName}); 110 111 // Release locks such that only the newly added locks are requested. This 112 // acts like a checkpoint for the newly queued requests. 113 firstRequestGroupReleaseFunction(); 114 await lockPromise1; 115 await postToFrameAndWait(frame1, {op: 'release', lock_id: lockId1}); 116 await lockPromise2; 117 await postToFrameAndWait(frame2, {op: 'release', lock_id: lockId2}); 118 119 // Query the state and test the new ordering. 120 const state2 = await navigator.locks.query(); 121 const relevant_pending_ids2 = state2.pending 122 .filter(lock => [clientId1, clientId2, clientId3].includes(lock.clientId)) 123 .map(lock => lock.clientId); 124 assert_array_equals( 125 [clientId3, clientId1, clientId2], 126 relevant_pending_ids2, 127 'Querying the state should return requested locks in the order they were ' 128 + 'requested.'); 129 130 }, 'Requests appear in state in order made.'); 131 </script>