blob: fbb6cfc8be9646eeeab758770882643165a5ff03 [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library test.src.runner.browser.post_message_channel;
import 'dart:js_util';
import 'package:js/js.dart';
import 'package:stream_channel/stream_channel.dart';
import 'dom.dart' as dom;
// Avoid using this from dart:html to work around dart-lang/sdk#32113.
@JS('window.parent.postMessage')
external void _postParentMessage(Object message, String targetOrigin);
/// Constructs a [StreamChannel] wrapping [MessageChannel] communication with
/// the host page.
StreamChannel<Object?> postMessageChannel() {
var controller = StreamChannelController<Object?>(sync: true);
// Listen for a message from the host that transfers a message port, then
// cancel the subscription. This is important to prevent multiple
// subscriptions if the test is ever hot restarted.
late final dom.Subscription subscription;
subscription =
dom.Subscription(dom.window, 'message', allowInterop((dom.Event event) {
// A message on the Window can theoretically come from any website. It's
// very unlikely that a malicious site would care about hacking someone's
// unit tests, let alone be able to find the test server while it's
// running, but it's good practice to check the origin anyway.
final message = event as dom.MessageEvent;
if (message.origin == dom.window.location.origin &&
message.data == 'port') {
subscription.cancel();
var port = message.ports.first;
port.start();
var portSubscription =
dom.Subscription(port, 'message', allowInterop((dom.Event event) {
controller.local.sink.add((event as dom.MessageEvent).data);
}));
controller.local.stream.listen((data) {
port.postMessage({'data': data});
}, onDone: () {
port.postMessage({'event': 'done'});
portSubscription.cancel();
});
}
}));
// Send a ready message once we're listening so the host knows it's safe to
// start sending events.
// TODO(nweiz): Stop manually adding href here once issue 22554 is fixed.
_postParentMessage(
jsify({'href': dom.window.location.href, 'ready': true}) as Object,
dom.window.location.origin);
return controller.foreign;
}