@@ -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