@@ -1003,6 +1003,47 @@ static Maybe<bool> ReadIterable(Environment* env,
1003
1003
return Just (true );
1004
1004
}
1005
1005
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
+
1006
1047
void MessagePort::PostMessage (const FunctionCallbackInfo<Value>& args) {
1007
1048
Environment* env = Environment::GetCurrent (args);
1008
1049
Local<Object> obj = args.This ();
@@ -1013,33 +1054,10 @@ void MessagePort::PostMessage(const FunctionCallbackInfo<Value>& args) {
1013
1054
" MessagePort.postMessage" );
1014
1055
}
1015
1056
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
-
1023
1057
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 ;
1041
1060
}
1042
-
1043
1061
MessagePort* port = Unwrap<MessagePort>(args.This ());
1044
1062
// Even if the backing MessagePort object has already been deleted, we still
1045
1063
// want to serialize the message to ensure spec-compliant behavior w.r.t.
@@ -1531,6 +1549,56 @@ static void SetDeserializerCreateObjectFunction(
1531
1549
env->set_messaging_deserialize_create_object (args[0 ].As <Function>());
1532
1550
}
1533
1551
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 ]->IsNullOrUndefined ()) {
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
+
1534
1602
static void MessageChannel (const FunctionCallbackInfo<Value>& args) {
1535
1603
Environment* env = Environment::GetCurrent (args);
1536
1604
if (!args.IsConstructCall ()) {
@@ -1569,6 +1637,83 @@ static void BroadcastChannel(const FunctionCallbackInfo<Value>& args) {
1569
1637
}
1570
1638
}
1571
1639
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
+
1572
1717
static void CreatePerIsolateProperties (IsolateData* isolate_data,
1573
1718
Local<ObjectTemplate> target) {
1574
1719
Isolate* isolate = isolate_data->isolate ();
@@ -1608,18 +1753,21 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
1608
1753
" setDeserializerCreateObjectFunction" ,
1609
1754
SetDeserializerCreateObjectFunction);
1610
1755
SetMethod (isolate, target, " broadcastChannel" , BroadcastChannel);
1756
+ SetMethod (isolate, target, " structuredClone" , StructuredClone);
1611
1757
}
1612
1758
1613
1759
static void CreatePerContextProperties (Local<Object> target,
1614
1760
Local<Value> unused,
1615
1761
Local<Context> context,
1616
1762
void * priv) {
1763
+ Realm* realm = Realm::GetCurrent (context);
1617
1764
Isolate* isolate = context->GetIsolate ();
1618
1765
Local<Function> domexception = GetDOMException (context).ToLocalChecked ();
1619
1766
target
1620
1767
->Set (
1621
1768
context, FIXED_ONE_BYTE_STRING (isolate, " DOMException" ), domexception)
1622
1769
.Check ();
1770
+ realm->AddBindingData <BindingData>(target);
1623
1771
}
1624
1772
1625
1773
static void RegisterExternalReferences (ExternalReferenceRegistry* registry) {
@@ -1634,6 +1782,7 @@ static void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
1634
1782
registry->Register (MessagePort::ReceiveMessage);
1635
1783
registry->Register (MessagePort::MoveToContext);
1636
1784
registry->Register (SetDeserializerCreateObjectFunction);
1785
+ registry->Register (StructuredClone);
1637
1786
}
1638
1787
1639
1788
} // namespace messaging
0 commit comments