summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/file-system-access/resources/messaging-helpers.js
blob: 55fc04ab817aeac2ccf13e716030264666b79d97 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
'use strict';

// This script depends on the following script:
//    /file-system-access/resources/test-helpers.js
//    /service-workers/service-worker/resources/test-helpers.sub.js

// Define the URL constants used for each type of message target, including
// iframes and workers.
const kDocumentMessageTarget = 'resources/message-target.html';
const kSharedWorkerMessageTarget = 'resources/message-target-shared-worker.js';
const kServiceWorkerMessageTarget =
  'resources/message-target-service-worker.js';
const kDedicatedWorkerMessageTarget =
  'resources/message-target-dedicated-worker.js';

function create_dedicated_worker(test, url) {
  const dedicated_worker = new Worker(url);
  test.add_cleanup(() => {
    dedicated_worker.terminate();
  });
  return dedicated_worker;
}

async function create_service_worker(test, script_url, scope) {
  const registration = await service_worker_unregister_and_register(
    test, script_url, scope);
  test.add_cleanup(() => {
    return registration.unregister();
  });
  return registration;
}

// Creates an iframe and waits to receive a message from the iframe.
// Valid |options| include src, srcdoc and sandbox, which mirror the
// corresponding iframe element properties.
async function add_iframe(test, options) {
  const iframe = document.createElement('iframe');

  if (options.sandbox !== undefined) {
    iframe.sandbox = options.sandbox;
  }

  if (options.src !== undefined) {
    iframe.src = options.src;
  }

  if (options.srcdoc !== undefined) {
    iframe.srcdoc = options.srcdoc;
  }

  document.body.appendChild(iframe);
  test.add_cleanup(() => {
    iframe.remove();
  });

  await wait_for_loaded_message(self);
  return iframe;
}

// Creates a child window using window.open() and waits to receive a message
// from the child window.
async function open_window(test, url) {
  const child_window = window.open(url);
  test.add_cleanup(() => {
    child_window.close();
  });
  await wait_for_loaded_message(self);
  return child_window;
}

// Wait until |receiver| gets a message event with the data set to 'LOADED'.
// The postMessage() tests use messaging instead of the loaded event because
// cross-origin child windows from window.open() do not dispatch the loaded
// event to the parent window.
async function wait_for_loaded_message(receiver) {
  const message_promise = new Promise((resolve, reject) => {
    receiver.addEventListener('message', message_event => {
      if (message_event.data === 'LOADED') {
        resolve();
      } else {
        reject('The message target must receive a "LOADED" message response.');
      }
    });
  });
  await message_promise;
}

// Sets up a new message channel. Sends one port to |target| and then returns
// the other port.
function create_message_channel(target, target_origin) {
  const message_channel = new MessageChannel();

  const message_data =
    { type: 'receive-message-port', message_port: message_channel.port2 };
  target.postMessage(
    message_data,
    {
      transfer: [message_channel.port2],
      targetOrigin: target_origin
    });
  message_channel.port1.start();
  return message_channel.port1;
}

// Creates a variety of different FileSystemFileHandles for testing.
async function create_file_system_handles(test, root) {
  // Create some files to use with postMessage().
  const empty_file = await createEmptyFile(test, 'empty-file', root);
  const first_file = await createFileWithContents(
    test, 'first-file-with-contents', 'first-text-content', root);
  const second_file = await createFileWithContents(
    test, 'second-file-with-contents', 'second-text-content', root);

  // Create an empty directory to use with postMessage().
  const empty_directory = await createDirectory(test, 'empty-directory', root);

  // Create a directory containing both files and subdirectories to use
  // with postMessage().
  const directory_with_files =
    await createDirectory(test, 'directory-with-files', root);
  await createFileWithContents(test, 'first-file-in-directory',
    'first-directory-text-content', directory_with_files);
  await createFileWithContents(test, 'second-file-in-directory',
    'second-directory-text-content', directory_with_files);
  const subdirectory =
    await createDirectory(test, 'subdirectory', directory_with_files);
  await createFileWithContents(test, 'first-file-in-subdirectory',
    'first-subdirectory-text-content', subdirectory);

  return [
    empty_file,
    first_file,
    second_file,
    // Include the same FileSystemFileHandle twice.
    second_file,
    empty_directory,
    // Include the Same FileSystemDirectoryHandle object twice.
    empty_directory,
    directory_with_files
  ];
}

// Tests sending an array of FileSystemHandles to |target| with postMessage().
// The array includes both FileSystemFileHandles and FileSystemDirectoryHandles.
// After receiving the message, |target| accesses all cloned handles by
// serializing the properties of each handle to a JavaScript object.
//
// |target| then responds with the resulting array of serialized handles. The
// response also includes the array of cloned handles, which creates more
// clones. After receiving the response, this test runner verifies that both
// the serialized handles and the cloned handles contain the expected properties.
async function do_post_message_test(
  test, root_dir, receiver, target, target_origin) {
  // Create and send the handles to |target|.
  const handles =
    await create_file_system_handles(test, root_dir, target, target_origin);
  target.postMessage(
    { type: 'receive-file-system-handles', cloned_handles: handles },
    { targetOrigin: target_origin });

  // Wait for |target| to respond with results.
  const event_watcher = new EventWatcher(test, receiver, 'message');
  const message_event = await event_watcher.wait_for('message');
  const response = message_event.data;

  assert_equals(response.type, 'receive-serialized-file-system-handles',
    'The test runner must receive a "serialized-file-system-handles" ' +
    `message response. Actual response: ${response}`);

  // Verify the results.
  const expected_serialized_handles = await serialize_handles(handles);

  assert_equals_serialized_handles(
    response.serialized_handles, expected_serialized_handles);

  await assert_equals_cloned_handles(response.cloned_handles, handles);
}

// Runs the same test as do_post_message_test(), but uses a MessagePort.
// This test starts by establishing a message channel between the test runner
// and |target|. Afterwards, the test sends FileSystemHandles through the
// message port channel.
async function do_message_port_test(test, root_dir, target, target_origin) {
  const message_port = create_message_channel(target, target_origin);
  await do_post_message_test(
      test, root_dir, /*receiver=*/ message_port, /*target=*/ message_port);
}