messaging-helpers.js (6840B)
1 'use strict'; 2 3 // This script depends on the following script: 4 // /fs/resources/test-helpers.js 5 // /service-workers/service-worker/resources/test-helpers.sub.js 6 7 // Define the URL constants used for each type of message target, including 8 // iframes and workers. 9 const kDocumentMessageTarget = '../fs/resources/message-target.html'; 10 const kSharedWorkerMessageTarget = 11 '../fs/resources/message-target-shared-worker.js'; 12 const kServiceWorkerMessageTarget = 13 '../fs/resources/message-target-service-worker.js'; 14 const kDedicatedWorkerMessageTarget = 15 '../fs/resources/message-target-dedicated-worker.js'; 16 17 function create_dedicated_worker(test, url) { 18 const dedicated_worker = new Worker(url); 19 test.add_cleanup(() => { 20 dedicated_worker.terminate(); 21 }); 22 return dedicated_worker; 23 } 24 25 async function create_service_worker(test, script_url, scope) { 26 const registration = await service_worker_unregister_and_register( 27 test, script_url, scope); 28 test.add_cleanup(() => { 29 return registration.unregister(); 30 }); 31 return registration; 32 } 33 34 // Creates an iframe and waits to receive a message from the iframe. 35 // Valid |options| include src, srcdoc and sandbox, which mirror the 36 // corresponding iframe element properties. 37 async function add_iframe(test, options) { 38 const iframe = document.createElement('iframe'); 39 40 if (options.sandbox !== undefined) { 41 iframe.sandbox = options.sandbox; 42 } 43 44 if (options.src !== undefined) { 45 iframe.src = options.src; 46 } 47 48 if (options.srcdoc !== undefined) { 49 iframe.srcdoc = options.srcdoc; 50 } 51 52 document.body.appendChild(iframe); 53 test.add_cleanup(() => { 54 iframe.remove(); 55 }); 56 57 await wait_for_loaded_message(self); 58 return iframe; 59 } 60 61 // Creates a child window using window.open() and waits to receive a message 62 // from the child window. 63 async function open_window(test, url) { 64 const child_window = window.open(url); 65 test.add_cleanup(() => { 66 child_window.close(); 67 }); 68 await wait_for_loaded_message(self); 69 return child_window; 70 } 71 72 // Wait until |receiver| gets a message event with the data set to 'LOADED'. 73 // The postMessage() tests use messaging instead of the loaded event because 74 // cross-origin child windows from window.open() do not dispatch the loaded 75 // event to the parent window. 76 async function wait_for_loaded_message(receiver) { 77 const message_promise = new Promise((resolve, reject) => { 78 receiver.addEventListener('message', message_event => { 79 if (message_event.data === 'LOADED') { 80 resolve(); 81 } else { 82 reject('The message target must receive a "LOADED" message response.'); 83 } 84 }); 85 }); 86 await message_promise; 87 } 88 89 // Sets up a new message channel. Sends one port to |target| and then returns 90 // the other port. 91 function create_message_channel(target, target_origin) { 92 const message_channel = new MessageChannel(); 93 94 const message_data = 95 { type: 'receive-message-port', message_port: message_channel.port2 }; 96 target.postMessage( 97 message_data, 98 { 99 transfer: [message_channel.port2], 100 targetOrigin: target_origin 101 }); 102 message_channel.port1.start(); 103 return message_channel.port1; 104 } 105 106 // Creates a variety of different FileSystemFileHandles for testing. 107 async function create_file_system_handles(root) { 108 // Create some files to use with postMessage(). 109 const empty_file = await createEmptyFile('empty-file', root); 110 const first_file = await createFileWithContents( 111 'first-file-with-contents', 'first-text-content', root); 112 const second_file = await createFileWithContents( 113 'second-file-with-contents', 'second-text-content', root); 114 115 // Create an empty directory to use with postMessage(). 116 const empty_directory = await createDirectory('empty-directory', root); 117 118 // Create a directory containing both files and subdirectories to use 119 // with postMessage(). 120 const directory_with_files = 121 await createDirectory('directory-with-files', root); 122 await createFileWithContents( 123 'first-file-in-directory', 'first-directory-text-content', 124 directory_with_files); 125 await createFileWithContents( 126 'second-file-in-directory', 'second-directory-text-content', 127 directory_with_files); 128 const subdirectory = 129 await createDirectory('subdirectory', directory_with_files); 130 await createFileWithContents( 131 'first-file-in-subdirectory', 'first-subdirectory-text-content', 132 subdirectory); 133 134 return [ 135 empty_file, 136 first_file, 137 second_file, 138 // Include the same FileSystemFileHandle twice. 139 second_file, 140 empty_directory, 141 // Include the Same FileSystemDirectoryHandle object twice. 142 empty_directory, 143 directory_with_files 144 ]; 145 } 146 147 // Tests sending an array of FileSystemHandles to |target| with postMessage(). 148 // The array includes both FileSystemFileHandles and FileSystemDirectoryHandles. 149 // After receiving the message, |target| accesses all cloned handles by 150 // serializing the properties of each handle to a JavaScript object. 151 // 152 // |target| then responds with the resulting array of serialized handles. The 153 // response also includes the array of cloned handles, which creates more 154 // clones. After receiving the response, this test runner verifies that both 155 // the serialized handles and the cloned handles contain the expected properties. 156 async function do_post_message_test( 157 test, root_dir, receiver, target, target_origin) { 158 // Create and send the handles to |target|. 159 const handles = await create_file_system_handles(root_dir); 160 target.postMessage( 161 { type: 'receive-file-system-handles', cloned_handles: handles }, 162 { targetOrigin: target_origin }); 163 164 // Wait for |target| to respond with results. 165 const event_watcher = new EventWatcher(test, receiver, 'message'); 166 const message_event = await event_watcher.wait_for('message'); 167 const response = message_event.data; 168 169 assert_equals(response.type, 'receive-serialized-file-system-handles', 170 'The test runner must receive a "serialized-file-system-handles" ' + 171 `message response. Actual response: ${response}`); 172 173 // Verify the results. 174 const expected_serialized_handles = await serialize_handles(handles); 175 176 assert_equals_serialized_handles( 177 response.serialized_handles, expected_serialized_handles); 178 179 await assert_equals_cloned_handles(response.cloned_handles, handles); 180 } 181 182 // Runs the same test as do_post_message_test(), but uses a MessagePort. 183 // This test starts by establishing a message channel between the test runner 184 // and |target|. Afterwards, the test sends FileSystemHandles through the 185 // message port channel. 186 async function do_message_port_test(test, root_dir, target, target_origin) { 187 const message_port = create_message_channel(target, target_origin); 188 await do_post_message_test( 189 test, root_dir, /*receiver=*/ message_port, /*target=*/ message_port); 190 }