// Copyright (c) 2012, 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 postmessage_js_test; import 'dart:async'; import 'dart:html'; import 'dart:collection' show HashMap, SplayTreeMap; import 'dart:typed_data'; import 'package:expect/legacy/minitest.dart'; // ignore: deprecated_member_use_from_same_package import 'utils.dart'; final isMap = predicate((v) => v is Map); void injectSource(String code) { final script = new ScriptElement(); script.type = 'text/javascript'; script.innerHtml = code; document.body!.append(script); } Future go(String name, dynamic value) { // Round-trip graph from Dart to JavaScript and back. final JS_CODE = """ window.addEventListener('message', handler); function handler(e) { var data = e.data; if (typeof data == 'string') return; if (data.recipient != 'JS') return; window.console.log(data.data); var response = {recipient: 'DART', data: data.data}; window.removeEventListener('message', handler); window.postMessage(response, '*'); } """; final done = new Completer(); var subscription; subscription = window.onMessage.listen((e) { var data = e.data; if (data is String) return; // Messages from unit test protocol. if (data['recipient'] != 'DART') return; // Not for me. try { subscription.cancel(); expect(data, isMap); var returnedValue = data['data']; expect(returnedValue, notEquals(value)); verifyGraph(value, returnedValue); done.complete(); } catch (e) { done.completeError('$name failed: $e'); } }); injectSource(JS_CODE); window.postMessage({'recipient': 'JS', 'data': value}, '*'); return done.future; } Future primitives() async { await testJsToDartPostmessage(); var obj1 = {'a': 100, 'b': 's'}; var obj2 = {'x': obj1, 'y': obj1}; // DAG. var obj3 = {}; obj3['a'] = 100; obj3['b'] = obj3; // Cycle. var obj4 = new SplayTreeMap(); // Different implementation. obj4['a'] = 100; obj4['b'] = 's'; var cyclic_list = [1, 2, 3]; cyclic_list[1] = cyclic_list; await go('test_simple_list', [1, 2, 3]); await go('test_map', obj1); await go('test_DAG', obj2); await go('test_cycle', obj3); await go('test_simple_splay', obj4); await go('const_array_1', const [ const [1], const [2], ]); await go('const_array_dag', const [ const [1], const [1], ]); await go('array_deferred_copy', [1, 2, 3, obj3, obj3, 6]); await go('array_deferred_copy_2', [ 1, 2, 3, [4, 5, obj3], [obj3, 6], ]); await go('cyclic_list', cyclic_list); } Future testJsToDartPostmessage() { // Pass an object literal from JavaScript. It should be seen as a Dart Map. final JS_CODE = """ window.postMessage({eggs: 3}, '*'); """; final done = new Completer(); var subscription = null; subscription = window.onMessage.listen((e) { var data = e.data; if (data is String) return; // Messages from unit test protocol. try { subscription.cancel(); expect(data, isMap); expect(data['eggs'], equals(3)); done.complete(); } catch (e) { done.completeError(e); } }); injectSource(JS_CODE); return done.future; } Future morePrimitives() async { await testJsToDartNullPrototypeEventdata(); } Future testJsToDartNullPrototypeEventdata() { // Pass an object with a null prototype from JavaScript. // It should be seen as a Dart Map. final JS_CODE = """ // Call anonymous function to create a local scope. (function() { var o = Object.create(null); o.eggs = 3; var foo = new MessageEvent('stuff', {data: o}); window.dispatchEvent(foo); })(); """; final done = new Completer(); var subscription = null; subscription = window.on['stuff'].listen((e) { var data = (e as MessageEvent).data; if (data is String) return; // Messages from unit test protocol. try { subscription.cancel(); expect(data, isMap); expect(data['eggs'], equals(3)); done.complete(); } catch (e) { done.completeError(e); } }); injectSource(JS_CODE); return done.future; } Future typedArrays() async { var array_buffer = new Uint8List(16).buffer; var view_a = new Float32List.view(array_buffer, 0, 4); var view_b = new Uint8List.view(array_buffer, 1, 13); var typed_arrays_list = [view_a, array_buffer, view_b]; // Note that FF is failing this test because in the sent message: // view_a.buffer == array_buffer // But in the response: // view_a.buffer != array_buffer await go('typed_arrays_list', typed_arrays_list); } Future iframe() async { await postMessageClonesData(); } Future postMessageClonesData() { var iframe = new IFrameElement(); var future = iframe.onLoad.first.then((_) { iframe.contentWindow!.postMessage(new HashMap(), '*'); }); iframe.src = 'about:blank'; document.body!.append(iframe); return future; } main() async { await primitives(); await morePrimitives(); await typedArrays(); await iframe(); }