@@ -999,6 +999,47 @@ static Maybe<bool> ReadIterable(Environment* env,
999999 return Just (true );
10001000}
10011001
1002+ bool GetTransferList (Environment* env,
1003+ Local<Context> context,
1004+ Local<Value> transfer_list_v,
1005+ TransferList* transfer_list_out) {
1006+ if (transfer_list_v->IsNullOrUndefined ()) {
1007+ // Browsers ignore null or undefined, and otherwise accept an array or an
1008+ // options object.
1009+ return true ;
1010+ }
1011+
1012+ if (!transfer_list_v->IsObject ()) {
1013+ THROW_ERR_INVALID_ARG_TYPE (
1014+ env, " Optional transferList argument must be an iterable" );
1015+ return false ;
1016+ }
1017+
1018+ bool was_iterable;
1019+ if (!ReadIterable (env, context, *transfer_list_out, transfer_list_v)
1020+ .To (&was_iterable))
1021+ return false ;
1022+ if (!was_iterable) {
1023+ Local<Value> transfer_option;
1024+ if (!transfer_list_v.As <Object>()
1025+ ->Get (context, env->transfer_string ())
1026+ .ToLocal (&transfer_option))
1027+ return false ;
1028+ if (!transfer_option->IsUndefined ()) {
1029+ if (!ReadIterable (env, context, *transfer_list_out, transfer_option)
1030+ .To (&was_iterable))
1031+ return false ;
1032+ if (!was_iterable) {
1033+ THROW_ERR_INVALID_ARG_TYPE (
1034+ env, " Optional options.transfer argument must be an iterable" );
1035+ return false ;
1036+ }
1037+ }
1038+ }
1039+
1040+ return true ;
1041+ }
1042+
10021043void MessagePort::PostMessage (const FunctionCallbackInfo<Value>& args) {
10031044 Environment* env = Environment::GetCurrent (args);
10041045 Local<Object> obj = args.This ();
@@ -1009,33 +1050,10 @@ void MessagePort::PostMessage(const FunctionCallbackInfo<Value>& args) {
10091050 " MessagePort.postMessage" );
10101051 }
10111052
1012- if (!args[1 ]->IsNullOrUndefined () && !args[1 ]->IsObject ()) {
1013- // Browsers ignore null or undefined, and otherwise accept an array or an
1014- // options object.
1015- return THROW_ERR_INVALID_ARG_TYPE (env,
1016- " Optional transferList argument must be an iterable" );
1017- }
1018-
10191053 TransferList transfer_list;
1020- if (args[1 ]->IsObject ()) {
1021- bool was_iterable;
1022- if (!ReadIterable (env, context, transfer_list, args[1 ]).To (&was_iterable))
1023- return ;
1024- if (!was_iterable) {
1025- Local<Value> transfer_option;
1026- if (!args[1 ].As <Object>()->Get (context, env->transfer_string ())
1027- .ToLocal (&transfer_option)) return ;
1028- if (!transfer_option->IsUndefined ()) {
1029- if (!ReadIterable (env, context, transfer_list, transfer_option)
1030- .To (&was_iterable)) return ;
1031- if (!was_iterable) {
1032- return THROW_ERR_INVALID_ARG_TYPE (env,
1033- " Optional options.transfer argument must be an iterable" );
1034- }
1035- }
1036- }
1054+ if (!GetTransferList (env, context, args[1 ], &transfer_list)) {
1055+ return ;
10371056 }
1038-
10391057 MessagePort* port = Unwrap<MessagePort>(args.This ());
10401058 // Even if the backing MessagePort object has already been deleted, we still
10411059 // want to serialize the message to ensure spec-compliant behavior w.r.t.
@@ -1472,6 +1490,48 @@ static void SetDeserializerCreateObjectFunction(
14721490 env->set_messaging_deserialize_create_object (args[0 ].As <Function>());
14731491}
14741492
1493+ static void StructuredClone (const FunctionCallbackInfo<Value>& args) {
1494+ Isolate* isolate = args.GetIsolate ();
1495+ Local<Context> context = isolate->GetCurrentContext ();
1496+ Realm* realm = Realm::GetCurrent (context);
1497+ Environment* env = realm->env ();
1498+
1499+ if (args.Length () == 0 ) {
1500+ return THROW_ERR_MISSING_ARGS (env, " The value argument must be specified" );
1501+ }
1502+
1503+ Local<Value> value = args[0 ];
1504+
1505+ TransferList transfer_list;
1506+ if (!args[1 ]->IsNullOrUndefined ()) {
1507+ if (!args[1 ]->IsObject ()) {
1508+ return THROW_ERR_INVALID_ARG_TYPE (
1509+ env, " The options argument must be either an object or undefined" );
1510+ }
1511+ Local<Object> options = args[1 ].As <Object>();
1512+ Local<Value> transfer_list_v;
1513+ if (!options->Get (context, env->transfer_string ())
1514+ .ToLocal (&transfer_list_v)) {
1515+ return ;
1516+ }
1517+
1518+ // TODO(joyeecheung): implement this in JS land to avoid the C++ -> JS
1519+ // cost to convert a sequence into an array.
1520+ if (!GetTransferList (env, context, transfer_list_v, &transfer_list)) {
1521+ return ;
1522+ }
1523+ }
1524+
1525+ std::shared_ptr<Message> msg = std::make_shared<Message>();
1526+ Local<Value> result;
1527+ if (msg->Serialize (env, context, value, transfer_list, Local<Object>())
1528+ .IsNothing () ||
1529+ !msg->Deserialize (env, context, nullptr ).ToLocal (&result)) {
1530+ return ;
1531+ }
1532+ args.GetReturnValue ().Set (result);
1533+ }
1534+
14751535static void MessageChannel (const FunctionCallbackInfo<Value>& args) {
14761536 Environment* env = Environment::GetCurrent (args);
14771537 if (!args.IsConstructCall ()) {
@@ -1554,6 +1614,7 @@ static void InitMessaging(Local<Object> target,
15541614 " setDeserializerCreateObjectFunction" ,
15551615 SetDeserializerCreateObjectFunction);
15561616 SetMethod (context, target, " broadcastChannel" , BroadcastChannel);
1617+ SetMethod (context, target, " structuredClone" , StructuredClone);
15571618
15581619 {
15591620 Local<Function> domexception = GetDOMException (context).ToLocalChecked ();
@@ -1578,6 +1639,7 @@ static void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
15781639 registry->Register (MessagePort::ReceiveMessage);
15791640 registry->Register (MessagePort::MoveToContext);
15801641 registry->Register (SetDeserializerCreateObjectFunction);
1642+ registry->Register (StructuredClone);
15811643}
15821644
15831645} // anonymous namespace
0 commit comments