@@ -1008,6 +1008,47 @@ static Maybe<bool> ReadIterable(Environment* env,
10081008 return Just (true );
10091009}
10101010
1011+ bool GetTransferList (Environment* env,
1012+ Local<Context> context,
1013+ Local<Value> transfer_list_v,
1014+ TransferList* transfer_list_out) {
1015+ if (transfer_list_v->IsNullOrUndefined ()) {
1016+ // Browsers ignore null or undefined, and otherwise accept an array or an
1017+ // options object.
1018+ return true ;
1019+ }
1020+
1021+ if (!transfer_list_v->IsObject ()) {
1022+ THROW_ERR_INVALID_ARG_TYPE (
1023+ env, " Optional transferList argument must be an iterable" );
1024+ return false ;
1025+ }
1026+
1027+ bool was_iterable;
1028+ if (!ReadIterable (env, context, *transfer_list_out, transfer_list_v)
1029+ .To (&was_iterable))
1030+ return false ;
1031+ if (!was_iterable) {
1032+ Local<Value> transfer_option;
1033+ if (!transfer_list_v.As <Object>()
1034+ ->Get (context, env->transfer_string ())
1035+ .ToLocal (&transfer_option))
1036+ return false ;
1037+ if (!transfer_option->IsUndefined ()) {
1038+ if (!ReadIterable (env, context, *transfer_list_out, transfer_option)
1039+ .To (&was_iterable))
1040+ return false ;
1041+ if (!was_iterable) {
1042+ THROW_ERR_INVALID_ARG_TYPE (
1043+ env, " Optional options.transfer argument must be an iterable" );
1044+ return false ;
1045+ }
1046+ }
1047+ }
1048+
1049+ return true ;
1050+ }
1051+
10111052void MessagePort::PostMessage (const FunctionCallbackInfo<Value>& args) {
10121053 Environment* env = Environment::GetCurrent (args);
10131054 Local<Object> obj = args.This ();
@@ -1018,33 +1059,10 @@ void MessagePort::PostMessage(const FunctionCallbackInfo<Value>& args) {
10181059 " MessagePort.postMessage" );
10191060 }
10201061
1021- if (!args[1 ]->IsNullOrUndefined () && !args[1 ]->IsObject ()) {
1022- // Browsers ignore null or undefined, and otherwise accept an array or an
1023- // options object.
1024- return THROW_ERR_INVALID_ARG_TYPE (env,
1025- " Optional transferList argument must be an iterable" );
1026- }
1027-
10281062 TransferList transfer_list;
1029- if (args[1 ]->IsObject ()) {
1030- bool was_iterable;
1031- if (!ReadIterable (env, context, transfer_list, args[1 ]).To (&was_iterable))
1032- return ;
1033- if (!was_iterable) {
1034- Local<Value> transfer_option;
1035- if (!args[1 ].As <Object>()->Get (context, env->transfer_string ())
1036- .ToLocal (&transfer_option)) return ;
1037- if (!transfer_option->IsUndefined ()) {
1038- if (!ReadIterable (env, context, transfer_list, transfer_option)
1039- .To (&was_iterable)) return ;
1040- if (!was_iterable) {
1041- return THROW_ERR_INVALID_ARG_TYPE (env,
1042- " Optional options.transfer argument must be an iterable" );
1043- }
1044- }
1045- }
1063+ if (!GetTransferList (env, context, args[1 ], &transfer_list)) {
1064+ return ;
10461065 }
1047-
10481066 MessagePort* port = Unwrap<MessagePort>(args.This ());
10491067 // Even if the backing MessagePort object has already been deleted, we still
10501068 // want to serialize the message to ensure spec-compliant behavior w.r.t.
@@ -1535,6 +1553,48 @@ static void SetDeserializerCreateObjectFunction(
15351553 env->set_messaging_deserialize_create_object (args[0 ].As <Function>());
15361554}
15371555
1556+ static void StructuredClone (const FunctionCallbackInfo<Value>& args) {
1557+ Isolate* isolate = args.GetIsolate ();
1558+ Local<Context> context = isolate->GetCurrentContext ();
1559+ Realm* realm = Realm::GetCurrent (context);
1560+ Environment* env = realm->env ();
1561+
1562+ if (args.Length () == 0 ) {
1563+ return THROW_ERR_MISSING_ARGS (env, " The value argument must be specified" );
1564+ }
1565+
1566+ Local<Value> value = args[0 ];
1567+
1568+ TransferList transfer_list;
1569+ if (!args[1 ]->IsNullOrUndefined ()) {
1570+ if (!args[1 ]->IsObject ()) {
1571+ return THROW_ERR_INVALID_ARG_TYPE (
1572+ env, " The options argument must be either an object or undefined" );
1573+ }
1574+ Local<Object> options = args[1 ].As <Object>();
1575+ Local<Value> transfer_list_v;
1576+ if (!options->Get (context, env->transfer_string ())
1577+ .ToLocal (&transfer_list_v)) {
1578+ return ;
1579+ }
1580+
1581+ // TODO(joyeecheung): implement this in JS land to avoid the C++ -> JS
1582+ // cost to convert a sequence into an array.
1583+ if (!GetTransferList (env, context, transfer_list_v, &transfer_list)) {
1584+ return ;
1585+ }
1586+ }
1587+
1588+ std::shared_ptr<Message> msg = std::make_shared<Message>();
1589+ Local<Value> result;
1590+ if (msg->Serialize (env, context, value, transfer_list, Local<Object>())
1591+ .IsNothing () ||
1592+ !msg->Deserialize (env, context, nullptr ).ToLocal (&result)) {
1593+ return ;
1594+ }
1595+ args.GetReturnValue ().Set (result);
1596+ }
1597+
15381598static void MessageChannel (const FunctionCallbackInfo<Value>& args) {
15391599 Environment* env = Environment::GetCurrent (args);
15401600 if (!args.IsConstructCall ()) {
@@ -1615,6 +1675,7 @@ static void InitMessaging(Local<Object> target,
16151675 " setDeserializerCreateObjectFunction" ,
16161676 SetDeserializerCreateObjectFunction);
16171677 SetMethod (context, target, " broadcastChannel" , BroadcastChannel);
1678+ SetMethod (context, target, " structuredClone" , StructuredClone);
16181679
16191680 {
16201681 Local<Function> domexception = GetDOMException (context).ToLocalChecked ();
@@ -1638,6 +1699,7 @@ static void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
16381699 registry->Register (MessagePort::ReceiveMessage);
16391700 registry->Register (MessagePort::MoveToContext);
16401701 registry->Register (SetDeserializerCreateObjectFunction);
1702+ registry->Register (StructuredClone);
16411703}
16421704
16431705} // anonymous namespace
0 commit comments