gecko-dev/testing/web-platform/tests/native-file-system/resources/message-target.js
Steve Becker ba435d06a4 Bug 1579753 [wpt PR 18921] - [NativeFileSystem] Make FileSystemHandle cloneable, a=testonly
Automatic update from web-platform-tests
[NativeFileSystem] Make FileSystemHandle cloneable

Updates postMessage() to clone FileSystemFileHandle and
FileSystemDirectoryHandle objects for same origin targets.  Including
a FileSystemHandle object with a cross origin message fails by
dispatching a 'messageerror' event instead of 'message' event.

The change consists of four parts:

(1) Updates V8ScriptValueSerializerForModules to serialize
FileSystemFileHandle and FileSystemDirectoryHandle into
blink::SerializedScriptValue, by following these steps:

 * Write a tag for the handle type (file or directory).

 * Write the name of the file or directory.

 * Creates a mojom::blink::NativeFileSystemTransferTokenPtr  by calling
   blink:NativeFileSystemHandle::Transfer().  This token informs the
   storage::NativeFileSystemManagerImpl that a transfer is in progress.
   The NativeFileSystemManagerImpl creates a
   NativeFileSystemTransferTokenImpl to store the information required
   to clone the handle.

 * Stores the token in
   blink::SerializedScriptValue::native_file_system_tokens_.  This
   array tracks all cloned FileSystemFileHandle. The
   blink::mojom::CloneableMessage struct is also updated to hold this
   array for MessagePort and BroadcastChannels.

 * Write the index of the token in the native_file_system_tokens_
   array.

(2) Updates V8ScriptValueDeserializerForModules to deserialize
FileSystemFileHandle objects when creating clones for the message
targets.  This is the inverse of (1).  Deserializing uses
mojom::blink::NativeFileSystemManager to redeem the token, which
creates the mojom::blink::NativeFileSystemFileHandlePtr or
mojom::blink::NativeFileSystemDirectoryHandlePtr using the info
stored by NativeFileSystemTransferTokenImpl.

(3) Updates content::NativeFileSystemManagerImpl to support token
transfers.  To redeem a token, NativeFileSystemManagerImpl receives
a mojo message that includes the token as well as a request for a
handle interface like mojom::blink::NativeFileSystemFileHandlePtr.
NativeFileSystemManagerImpl finds the token and then binds the request.
Token redemption does not return any results.  Token redemption should
never fail, unless a render process is misbehaving.
NativeFileSystemManagerImpl performs a few sanity checks before binding
the mojo request, including a token existence check, a handle type
check and an origin check.  If any of the sanity checks fail,
NativeFileSystemManagerImpl silently fails closing the redeemed
FileHandle's pipe.

(4) Adds a cross origin check to window and message port messaging.
Most message targets, like dedicated workers, are same origin only.
However, both windows and message port messages can go cross origin.
When a cross origin message includes a FileSystemHandle, the message
must fail with a 'messageerror' event to prevent cross origin access
to the FileSystemHandle.

Messaging between windows already included origin information before
this change. This change adds a NativeFileSystem origin check before
dispatching a message event to a window.  The message event is
replaced with  a message error when a cross origin NativeFileSystem
object exists in the message data.

For message ports, no sender origin information existed before this
change.  This change updates the CloneableMessage structs to
include a 'sender_origin' url::Origin property.  Message ports use
this property to perform the same cross origin NativeFileSystem
check as the window.

The NativeFileSystemManagerImpl performs an additional origin check
before binding the FileSystemHandle mojo request.  The
NativeFileSystemManagerImpl cannot trust the postMessage() origin
check performed in the render process.

Bug: 955192
Change-Id: Ieeb76bd8102067d70c5d7719622ecd4930c3a88f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1791942
Commit-Queue: Steve Becker <stevebe@microsoft.com>
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
Reviewed-by: Yuki Shiino <yukishiino@chromium.org>
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
Reviewed-by: Marijn Kruisselbrink <mek@chromium.org>
Reviewed-by: Olivier Yiptong <oyiptong@chromium.org>
Cr-Commit-Position: refs/heads/master@{#709407}

--

wpt-commits: 474923949524b5c05a9e6f28ec082fdca87078de
wpt-pr: 18921

Differential Revision: https://phabricator.services.mozilla.com/D53468
2019-11-20 11:18:36 +00:00

145 lines
No EOL
5.9 KiB
JavaScript

'use strict';
// This script depends on the following scripts:
// /native-file-system/resources/messaging-helpers.js
// /native-file-system/resources/test-helpers.js
// add_message_event_handlers() is the helper function used to setup all
// message targets, including iframes and workers.
//
// Adds a message event handler and a message error handler to |receiver|.
// The 'data' property from received MessageEvents must include a 'type'
// property. The 'type' selects the test logic to run. Most message type
// handlers use postMessage() to respond to the sender with test results.
// The sender then validates the test results after receiving the response.
//
// Both |target| and |target_origin| are optional. |target| is used
// to send message responses back to the sender. When omitted, the
// 'source' from received MessageEvents is used instead.
//
// For window messaging, |target_origin| specifies the origin to receive
// responses. Most window tests use '*' for the |target_origin|. Worker
// and message port tests must use undefined for |target_origin| to avoid
// exceptions.
function add_message_event_handlers(receiver, target, target_origin) {
receiver.addEventListener('message', async function (message_event) {
const message_data = message_event.data;
// Reply to the sender using the 'source' from the received MessageEvent.
let message_source = message_event.source;
if (message_source === null) {
// However, some message senders, like DedicatedWorkers, don't include
// a source. Fallback to the target when the source is null.
message_source = target;
}
try {
switch (message_data.type) {
case 'receive-message-port':
// Receive a MessagePort to use as a message target for testing.
add_message_event_handlers(
/*receiver=*/message_data.message_port,
/*target=*/message_data.message_port);
message_data.message_port.start();
break;
case 'create-broadcast-channel':
// Create a BroadcastChannel to use as a message target for testing.
const broadcast_channel =
new BroadcastChannel(message_data.broadcast_channel_name);
add_message_event_handlers(
/*receiver=*/broadcast_channel,
/*target=*/broadcast_channel);
message_source.postMessage(
{ type: 'broadcast-channel-created' },
{ targetOrigin: target_origin });
break;
case 'receive-file-system-handles':
// Receive a list of cloned FileSystemFileHandles. Access the
// properties of each FileSystemFileHandle by serializing the
// handle to a JavaScript object. Then respond with the serialized
// results, enabling the sender to verify that the cloned handle
// produced the expected property values from this execution context.
const serialized_handles = [];
const cloned_handles = message_data.cloned_handles;
for (let i = 0; i < cloned_handles.length; ++i) {
const serialized = await serialize_handle(cloned_handles[i]);
serialized_handles.push(serialized);
}
message_source.postMessage({
type: 'receive-serialized-file-system-handles',
serialized_handles,
// Respond with the cloned handles to create new clones for
// the sender to verify.
cloned_handles,
}, { targetOrigin: target_origin });
break;
case 'receive-serialized-file-system-handles':
// Do nothing. This message is meant for test runner validation.
// Other message targets may receive this message while testing
// broadcast channels.
break;
case 'create-file':
// Create a new file and then respond to the sender with it.
const directory =
await FileSystemDirectoryHandle.getSystemDirectory(
{ type: 'sandbox' });
const file_handle =
await directory.getFile('temp-file', { create: true });
message_source.postMessage(
{ type: 'receive-file', file_handle },
{ targetOrigin: target_origin });
break;
case 'create-directory':
// Create a new directory and then respond to the sender with it.
const parent_directory =
await FileSystemDirectoryHandle.getSystemDirectory(
{ type: 'sandbox' });
const directory_handle =
await parent_directory.getDirectory('temp-directory',
{ create: true });
message_source.postMessage(
{ type: 'receive-directory', directory_handle },
{ targetOrigin: target_origin });
break;
default:
throw `Unknown message type: '${message_data.type}'`;
}
} catch (error) {
// Respond with an error to trigger a failure in the sender's
// test runner.
message_source.postMessage(`ERROR: ${error}`,
{ targetOrigin: target_origin });
}
});
receiver.addEventListener('messageerror', async function (message_event) {
// Select the target for message responses (see comment in 'message' event
// listener above).
let message_source = message_event.source;
if (message_source === null) {
message_source = target;
}
try {
// Respond with the MessageEvent's property values, enabling the sender
// to verify results.
const serialized_message_error_event =
serialize_message_error_event(message_event);
message_source.postMessage({
type: 'serialized-message-error',
serialized_message_error_event
}, { targetOrigin: target_origin });
} catch (error) {
// Respond with an error to trigger a failure in the sender's
// test runner.
message_source.postMessage(`ERROR: ${error}`,
{ targetOrigin: target_origin });
}
});
}