@@ -1003,6 +1003,47 @@ static Maybe<bool> ReadIterable(Environment* env,
10031003 return Just (true );
10041004}
10051005
1006+ bool GetTransferList (Environment* env,
1007+ Local<Context> context,
1008+ Local<Value> transfer_list_v,
1009+ TransferList* transfer_list_out) {
1010+ if (transfer_list_v->IsNullOrUndefined ()) {
1011+ // Browsers ignore null or undefined, and otherwise accept an array or an
1012+ // options object.
1013+ return true ;
1014+ }
1015+
1016+ if (!transfer_list_v->IsObject ()) {
1017+ THROW_ERR_INVALID_ARG_TYPE (
1018+ env, " Optional transferList argument must be an iterable" );
1019+ return false ;
1020+ }
1021+
1022+ bool was_iterable;
1023+ if (!ReadIterable (env, context, *transfer_list_out, transfer_list_v)
1024+ .To (&was_iterable))
1025+ return false ;
1026+ if (!was_iterable) {
1027+ Local<Value> transfer_option;
1028+ if (!transfer_list_v.As <Object>()
1029+ ->Get (context, env->transfer_string ())
1030+ .ToLocal (&transfer_option))
1031+ return false ;
1032+ if (!transfer_option->IsUndefined ()) {
1033+ if (!ReadIterable (env, context, *transfer_list_out, transfer_option)
1034+ .To (&was_iterable))
1035+ return false ;
1036+ if (!was_iterable) {
1037+ THROW_ERR_INVALID_ARG_TYPE (
1038+ env, " Optional options.transfer argument must be an iterable" );
1039+ return false ;
1040+ }
1041+ }
1042+ }
1043+
1044+ return true ;
1045+ }
1046+
10061047void MessagePort::PostMessage (const FunctionCallbackInfo<Value>& args) {
10071048 Environment* env = Environment::GetCurrent (args);
10081049 Local<Object> obj = args.This ();
@@ -1013,33 +1054,10 @@ void MessagePort::PostMessage(const FunctionCallbackInfo<Value>& args) {
10131054 " MessagePort.postMessage" );
10141055 }
10151056
1016- if (!args[1 ]->IsNullOrUndefined () && !args[1 ]->IsObject ()) {
1017- // Browsers ignore null or undefined, and otherwise accept an array or an
1018- // options object.
1019- return THROW_ERR_INVALID_ARG_TYPE (env,
1020- " Optional transferList argument must be an iterable" );
1021- }
1022-
10231057 TransferList transfer_list;
1024- if (args[1 ]->IsObject ()) {
1025- bool was_iterable;
1026- if (!ReadIterable (env, context, transfer_list, args[1 ]).To (&was_iterable))
1027- return ;
1028- if (!was_iterable) {
1029- Local<Value> transfer_option;
1030- if (!args[1 ].As <Object>()->Get (context, env->transfer_string ())
1031- .ToLocal (&transfer_option)) return ;
1032- if (!transfer_option->IsUndefined ()) {
1033- if (!ReadIterable (env, context, transfer_list, transfer_option)
1034- .To (&was_iterable)) return ;
1035- if (!was_iterable) {
1036- return THROW_ERR_INVALID_ARG_TYPE (env,
1037- " Optional options.transfer argument must be an iterable" );
1038- }
1039- }
1040- }
1058+ if (!GetTransferList (env, context, args[1 ], &transfer_list)) {
1059+ return ;
10411060 }
1042-
10431061 MessagePort* port = Unwrap<MessagePort>(args.This ());
10441062 // Even if the backing MessagePort object has already been deleted, we still
10451063 // want to serialize the message to ensure spec-compliant behavior w.r.t.
@@ -1531,6 +1549,56 @@ static void SetDeserializerCreateObjectFunction(
15311549 env->set_messaging_deserialize_create_object (args[0 ].As <Function>());
15321550}
15331551
1552+ static void StructuredClone (const FunctionCallbackInfo<Value>& args) {
1553+ Isolate* isolate = args.GetIsolate ();
1554+ Local<Context> context = isolate->GetCurrentContext ();
1555+ Realm* realm = Realm::GetCurrent (context);
1556+ Environment* env = realm->env ();
1557+
1558+ if (args.Length () == 0 ) {
1559+ return THROW_ERR_MISSING_ARGS (env, " The value argument must be specified" );
1560+ }
1561+
1562+ Local<Value> value = args[0 ];
1563+
1564+ TransferList transfer_list;
1565+ if (!args[1 ]->IsUndefined ()) {
1566+ if (!args[1 ]->IsObject ()) {
1567+ return THROW_ERR_INVALID_ARG_TYPE (
1568+ env, " The options argument must be either an object or undefined" );
1569+ }
1570+ Local<Object> options = args[1 ].As <Object>();
1571+ Local<Value> transfer_list_v;
1572+ if (!options->Get (context, FIXED_ONE_BYTE_STRING (isolate, " transfer" ))
1573+ .ToLocal (&transfer_list_v)) {
1574+ return ;
1575+ }
1576+
1577+ if (!GetTransferList (env, context, transfer_list_v, &transfer_list)) {
1578+ return ;
1579+ }
1580+ }
1581+
1582+ // TODO(joyeecheung): refactor and use V8 serialization/deserialization
1583+ // directly instead of going through message ports.
1584+ BindingData* binding_data = realm->GetBindingData <BindingData>(context);
1585+ MessagePort* port1;
1586+ MessagePort* port2;
1587+ std::tie (port1, port2) =
1588+ binding_data->GetOrCreatePortsForStructuredClone (context);
1589+ if (port1 == nullptr || port2 == nullptr ) {
1590+ return ;
1591+ }
1592+
1593+ Maybe<bool > res = port1->PostMessage (env, context, value, transfer_list);
1594+ if (res.IsNothing ()) {
1595+ return ;
1596+ }
1597+ MaybeLocal<Value> payload = port2->ReceiveMessage (
1598+ context, MessagePort::MessageProcessingMode::kForceReadMessages );
1599+ if (!payload.IsEmpty ()) args.GetReturnValue ().Set (payload.ToLocalChecked ());
1600+ }
1601+
15341602static void MessageChannel (const FunctionCallbackInfo<Value>& args) {
15351603 Environment* env = Environment::GetCurrent (args);
15361604 if (!args.IsConstructCall ()) {
@@ -1569,6 +1637,83 @@ static void BroadcastChannel(const FunctionCallbackInfo<Value>& args) {
15691637 }
15701638}
15711639
1640+ void BindingData::MemoryInfo (MemoryTracker* tracker) const {
1641+ tracker->TrackField (" port1" , port1_);
1642+ tracker->TrackField (" port2" , port2_);
1643+ }
1644+
1645+ BindingData::BindingData (Realm* realm, v8::Local<v8::Object> object)
1646+ : SnapshotableObject(realm, object, type_int) {}
1647+
1648+ std::pair<MessagePort*, MessagePort*>
1649+ BindingData::GetOrCreatePortsForStructuredClone (Local<Context> context) {
1650+ if (port1_ != nullptr ) {
1651+ DCHECK_NOT_NULL (port2_);
1652+ return std::make_pair (port1_, port2_);
1653+ }
1654+
1655+ port1_ = MessagePort::New (env (), context);
1656+
1657+ if (port1_ != nullptr ) {
1658+ port2_ = MessagePort::New (env (), context);
1659+ }
1660+
1661+ if (port1_ == nullptr || port2_ == nullptr ) {
1662+ ThrowDataCloneException (context,
1663+ FIXED_ONE_BYTE_STRING (context->GetIsolate (),
1664+ " Cannot create MessagePort" ));
1665+ if (port1_ != nullptr ) {
1666+ port1_->Close ();
1667+ port1_ = nullptr ;
1668+ }
1669+ }
1670+
1671+ uv_unref (port1_->GetHandle ());
1672+ uv_unref (port2_->GetHandle ());
1673+ MessagePort::Entangle (port1_, port2_);
1674+
1675+ return std::make_pair (port1_, port2_);
1676+ }
1677+
1678+ bool BindingData::PrepareForSerialization (v8::Local<v8::Context> context,
1679+ v8::SnapshotCreator* creator) {
1680+ // We'll just re-initialize them when structuredClone is called again.
1681+ // TODO(joyeecheung): currently this is not enough to clean up the ports
1682+ // because their shutdown is async. Either add a special path to shut
1683+ // them down synchronously, or make it possible for the the snapshot
1684+ // process to deal with async shutdown, or just don't use ports and
1685+ // serialize/deserialize the data directly. Until then, structuredClone
1686+ // is not supported in custom snapshots.
1687+ if (port1_ != nullptr ) {
1688+ DCHECK_NOT_NULL (port2_);
1689+ port1_->Close ();
1690+ port1_ = nullptr ;
1691+ port2_->Close ();
1692+ port2_ = nullptr ;
1693+ }
1694+ // Return true because we need to maintain the reference to the binding from
1695+ // JS land.
1696+ return true ;
1697+ }
1698+
1699+ InternalFieldInfoBase* BindingData::Serialize (int index) {
1700+ DCHECK_IS_SNAPSHOT_SLOT (index);
1701+ InternalFieldInfo* info =
1702+ InternalFieldInfoBase::New<InternalFieldInfo>(type ());
1703+ return info;
1704+ }
1705+
1706+ void BindingData::Deserialize (v8::Local<v8::Context> context,
1707+ v8::Local<v8::Object> holder,
1708+ int index,
1709+ InternalFieldInfoBase* info) {
1710+ DCHECK_IS_SNAPSHOT_SLOT (index);
1711+ v8::HandleScope scope (context->GetIsolate ());
1712+ Realm* realm = Realm::GetCurrent (context);
1713+ BindingData* binding = realm->AddBindingData <BindingData>(holder);
1714+ CHECK_NOT_NULL (binding);
1715+ }
1716+
15721717static void CreatePerIsolateProperties (IsolateData* isolate_data,
15731718 Local<ObjectTemplate> target) {
15741719 Isolate* isolate = isolate_data->isolate ();
@@ -1608,18 +1753,21 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
16081753 " setDeserializerCreateObjectFunction" ,
16091754 SetDeserializerCreateObjectFunction);
16101755 SetMethod (isolate, target, " broadcastChannel" , BroadcastChannel);
1756+ SetMethod (isolate, target, " structuredClone" , StructuredClone);
16111757}
16121758
16131759static void CreatePerContextProperties (Local<Object> target,
16141760 Local<Value> unused,
16151761 Local<Context> context,
16161762 void * priv) {
1763+ Realm* realm = Realm::GetCurrent (context);
16171764 Isolate* isolate = context->GetIsolate ();
16181765 Local<Function> domexception = GetDOMException (context).ToLocalChecked ();
16191766 target
16201767 ->Set (
16211768 context, FIXED_ONE_BYTE_STRING (isolate, " DOMException" ), domexception)
16221769 .Check ();
1770+ realm->AddBindingData <BindingData>(target);
16231771}
16241772
16251773static void RegisterExternalReferences (ExternalReferenceRegistry* registry) {
@@ -1634,6 +1782,7 @@ static void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
16341782 registry->Register (MessagePort::ReceiveMessage);
16351783 registry->Register (MessagePort::MoveToContext);
16361784 registry->Register (SetDeserializerCreateObjectFunction);
1785+ registry->Register (StructuredClone);
16371786}
16381787
16391788} // namespace messaging
0 commit comments