Identify MessagePorts by a globally unique MessagePortIdentifier.
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 2 Jan 2018 22:42:16 +0000 (22:42 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 2 Jan 2018 22:42:16 +0000 (22:42 +0000)
https://bugs.webkit.org/show_bug.cgi?id=181172

Reviewed by Alex Christensen.

No new tests (Behavior change covered by all existing tests).

This cleans up the abstract MessagePortChannel interface to be in terms of identifiers
instead of actual MessagePort objects.

The identifiers are compounded with the current ProcessIdentifier meaning they are global
across all processes for the running UI process, enabling easy cross-process communication.

(Actual cross-process communication comes in a followup)

* WebCore.xcodeproj/project.pbxproj:

* dom/InProcessMessagePortChannel.cpp:
(WebCore::InProcessMessagePortChannel::createChannelBetweenPorts):
(WebCore::InProcessMessagePortChannel::isConnectedTo):
(WebCore::InProcessMessagePortChannel::entangleWithRemoteIfOpen):
(WebCore::InProcessMessagePortChannel::entangleIfOpen): Deleted.
* dom/InProcessMessagePortChannel.h:

* dom/MessageChannel.cpp:
(WebCore::MessageChannel::MessageChannel):
(WebCore::m_port2):

* dom/MessagePort.cpp:
(WebCore::allMessagePortsLock):
(WebCore::MessagePort::ref const):
(WebCore::MessagePort::deref const):
(WebCore::MessagePort::existingMessagePortForIdentifier):
(WebCore::MessagePort::MessagePort):
(WebCore::MessagePort::~MessagePort):
(WebCore::MessagePort::postMessage):
(WebCore::MessagePort::entangleWithRemote):
(WebCore::MessagePort::entanglePorts):
(WebCore::MessagePort::entangle): Deleted.
* dom/MessagePort.h:

* dom/MessagePortChannel.h:

* dom/MessagePortIdentifier.h: Added.
(WebCore::operator==):
(WebCore::MessagePortIdentifier::encode const):
(WebCore::MessagePortIdentifier::decode):
(WebCore::MessagePortIdentifier::hash const):
(WTF::MessagePortIdentifierHash::hash):
(WTF::MessagePortIdentifierHash::equal):
(WTF::HashTraits<WebCore::MessagePortIdentifier>::emptyValue):
(WTF::HashTraits<WebCore::MessagePortIdentifier>::constructDeletedValue):
(WTF::HashTraits<WebCore::MessagePortIdentifier>::isDeletedValue):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@226336 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/dom/InProcessMessagePortChannel.cpp
Source/WebCore/dom/InProcessMessagePortChannel.h
Source/WebCore/dom/MessageChannel.cpp
Source/WebCore/dom/MessagePort.cpp
Source/WebCore/dom/MessagePort.h
Source/WebCore/dom/MessagePortChannel.h
Source/WebCore/dom/MessagePortIdentifier.h [new file with mode: 0644]

index b22d5fe..2a7c481 100644 (file)
@@ -1,3 +1,59 @@
+2018-01-02  Brady Eidson  <beidson@apple.com>
+
+        Identify MessagePorts by a globally unique MessagePortIdentifier.
+        https://bugs.webkit.org/show_bug.cgi?id=181172
+
+        Reviewed by Alex Christensen.
+
+        No new tests (Behavior change covered by all existing tests).
+
+        This cleans up the abstract MessagePortChannel interface to be in terms of identifiers
+        instead of actual MessagePort objects.
+        
+        The identifiers are compounded with the current ProcessIdentifier meaning they are global
+        across all processes for the running UI process, enabling easy cross-process communication.
+        
+        (Actual cross-process communication comes in a followup)
+        
+        * WebCore.xcodeproj/project.pbxproj:
+        
+        * dom/InProcessMessagePortChannel.cpp:
+        (WebCore::InProcessMessagePortChannel::createChannelBetweenPorts):
+        (WebCore::InProcessMessagePortChannel::isConnectedTo):
+        (WebCore::InProcessMessagePortChannel::entangleWithRemoteIfOpen):
+        (WebCore::InProcessMessagePortChannel::entangleIfOpen): Deleted.
+        * dom/InProcessMessagePortChannel.h:
+        
+        * dom/MessageChannel.cpp:
+        (WebCore::MessageChannel::MessageChannel):
+        (WebCore::m_port2):
+        
+        * dom/MessagePort.cpp:
+        (WebCore::allMessagePortsLock):
+        (WebCore::MessagePort::ref const):
+        (WebCore::MessagePort::deref const):
+        (WebCore::MessagePort::existingMessagePortForIdentifier):
+        (WebCore::MessagePort::MessagePort):
+        (WebCore::MessagePort::~MessagePort):
+        (WebCore::MessagePort::postMessage):
+        (WebCore::MessagePort::entangleWithRemote):
+        (WebCore::MessagePort::entanglePorts):
+        (WebCore::MessagePort::entangle): Deleted.
+        * dom/MessagePort.h:
+        
+        * dom/MessagePortChannel.h:
+        
+        * dom/MessagePortIdentifier.h: Added.
+        (WebCore::operator==):
+        (WebCore::MessagePortIdentifier::encode const):
+        (WebCore::MessagePortIdentifier::decode):
+        (WebCore::MessagePortIdentifier::hash const):
+        (WTF::MessagePortIdentifierHash::hash):
+        (WTF::MessagePortIdentifierHash::equal):
+        (WTF::HashTraits<WebCore::MessagePortIdentifier>::emptyValue):
+        (WTF::HashTraits<WebCore::MessagePortIdentifier>::constructDeletedValue):
+        (WTF::HashTraits<WebCore::MessagePortIdentifier>::isDeletedValue):
+
 2018-01-02  Youenn Fablet  <youenn@apple.com>
 
         Memory cache should not reuse resources with different credential fetch option
index ec8f0d4..66a46c5 100644 (file)
                51F6456A1F4539C000B54DED /* ServiceWorkerJobType.h in Headers */ = {isa = PBXBuildFile; fileRef = 51F645651F45399F00B54DED /* ServiceWorkerJobType.h */; settings = {ATTRIBUTES = (Private, ); }; };
                51F645971F4A686F00B54DED /* SWServerRegistration.h in Headers */ = {isa = PBXBuildFile; fileRef = 51F645941F4A684F00B54DED /* SWServerRegistration.h */; settings = {ATTRIBUTES = (Private, ); }; };
                51F645D51FECDBCE00B54DED /* Process.h in Headers */ = {isa = PBXBuildFile; fileRef = 51F645D31FECDBC800B54DED /* Process.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               51F645E01FF4594E00B54DED /* MessagePortIdentifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 51F645DE1FF4594B00B54DED /* MessagePortIdentifier.h */; settings = {ATTRIBUTES = (Private, ); }; };
                51F798F01BE880E7008AE491 /* IDBIndexInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 51F798EC1BE880D3008AE491 /* IDBIndexInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
                51F886C01F32923100C193EF /* JSNavigatorServiceWorker.h in Headers */ = {isa = PBXBuildFile; fileRef = 51F886BF1F32920700C193EF /* JSNavigatorServiceWorker.h */; };
                51FA2D78152132B300C1BA0B /* DOMWindowExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = 517FBA18151AA71B00B57959 /* DOMWindowExtension.h */; settings = {ATTRIBUTES = (Private, ); }; };
                51F645A31F4C000C00B54DED /* ExceptionData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExceptionData.cpp; sourceTree = "<group>"; };
                51F645D21FECDBC800B54DED /* Process.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Process.cpp; sourceTree = "<group>"; };
                51F645D31FECDBC800B54DED /* Process.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Process.h; sourceTree = "<group>"; };
+               51F645DE1FF4594B00B54DED /* MessagePortIdentifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MessagePortIdentifier.h; sourceTree = "<group>"; };
                51F6A3D50663BF04004D2919 /* HTMLCanvasElement.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTMLCanvasElement.cpp; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
                51F6A3D60663BF04004D2919 /* HTMLCanvasElement.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = HTMLCanvasElement.h; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
                51F798EB1BE880D3008AE491 /* IDBIndexInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IDBIndexInfo.cpp; sourceTree = "<group>"; };
                                E1ADECC60E76AD1F004A1A5E /* MessagePort.idl */,
                                512B57BE1FE9902D000A1E5E /* MessagePortChannel.cpp */,
                                41BF700A0FE86F49005E8DEC /* MessagePortChannel.h */,
+                               51F645DE1FF4594B00B54DED /* MessagePortIdentifier.h */,
                                CB8CF0151A934B43000D510B /* Microtasks.cpp */,
                                53B895AD19DC7C37009CAA93 /* Microtasks.h */,
                                E38D06091F8E811900649CF2 /* ModuleFetchParameters.h */,
                                75793E840D0CE0B3007FC0AC /* MessageEvent.h in Headers */,
                                E1ADECBF0E76ACF1004A1A5E /* MessagePort.h in Headers */,
                                41BF700C0FE86F49005E8DEC /* MessagePortChannel.h in Headers */,
+                               51F645E01FF4594E00B54DED /* MessagePortIdentifier.h in Headers */,
                                53B895AF19DC7ED9009CAA93 /* Microtasks.h in Headers */,
                                BC772C4F0C4EB3040083285F /* MIMETypeRegistry.h in Headers */,
                                52F10866162B6DA8009AC81E /* MixedContentChecker.h in Headers */,
index c215df8..ccf28d8 100644 (file)
@@ -42,8 +42,8 @@ void InProcessMessagePortChannel::createChannelBetweenPorts(MessagePort& port1,
     channel1->m_entangledChannel = channel2.ptr();
     channel2->m_entangledChannel = channel1.ptr();
 
-    port1.entangle(WTFMove(channel2));
-    port2.entangle(WTFMove(channel1));
+    port1.entangleWithRemote(WTFMove(channel2));
+    port2.entangleWithRemote(WTFMove(channel1));
 }
 
 Ref<InProcessMessagePortChannel> InProcessMessagePortChannel::create(MessagePortQueue& incoming, MessagePortQueue& outgoing)
@@ -81,14 +81,14 @@ Deque<std::unique_ptr<MessagePortChannel::EventData>> InProcessMessagePortChanne
     return m_incomingQueue->takeAllMessages();
 }
 
-bool InProcessMessagePortChannel::isConnectedTo(MessagePort& port)
+bool InProcessMessagePortChannel::isConnectedTo(const MessagePortIdentifier& identifier)
 {
     // FIXME: What guarantees that the result remains the same after we release the lock?
     Locker<Lock> locker(m_lock);
-    return m_remotePort == &port;
+    return m_remotePort && m_remotePort->identifier() == identifier;
 }
 
-bool InProcessMessagePortChannel::entangleIfOpen(MessagePort& port)
+bool InProcessMessagePortChannel::entangleWithRemoteIfOpen(const MessagePortIdentifier& identifier)
 {
     // We can't call member functions on our remote pair while holding our mutex or we'll deadlock,
     // but we need to guard against the remote port getting closed/freed, so create a standalone reference.
@@ -101,7 +101,10 @@ bool InProcessMessagePortChannel::entangleIfOpen(MessagePort& port)
     if (!remote)
         return false;
 
-    remote->setRemotePort(&port);
+    auto entangledPort = MessagePort::existingMessagePortForIdentifier(identifier);
+    ASSERT(entangledPort);
+
+    remote->setRemotePort(entangledPort.get());
 
     return true;
 }
index 44bd9d1..fc35b69 100644 (file)
@@ -38,8 +38,8 @@ public:
 
     void postMessageToRemote(Ref<SerializedScriptValue>&&, std::unique_ptr<MessagePortChannelArray>&&) final;
     Deque<std::unique_ptr<EventData>> takeAllMessagesFromRemote() final;
-    bool isConnectedTo(MessagePort&) final;
-    bool entangleIfOpen(MessagePort&) final;
+    bool isConnectedTo(const MessagePortIdentifier&) final;
+    bool entangleWithRemoteIfOpen(const MessagePortIdentifier&) final;
     void disentangle() final;
     bool hasPendingActivity() final;
     MessagePort* locallyEntangledPort(const ScriptExecutionContext*) final;
index 5808025..2637d9f 100644 (file)
@@ -33,8 +33,8 @@
 namespace WebCore {
 
 MessageChannel::MessageChannel(ScriptExecutionContext& context)
-    : m_port1(MessagePort::create(context))
-    , m_port2(MessagePort::create(context))
+    : m_port1(MessagePort::create(context, { Process::identifier(), generateObjectIdentifier<MessagePortIdentifier::PortIdentifierType>() }))
+    , m_port2(MessagePort::create(context, { Process::identifier(), generateObjectIdentifier<MessagePortIdentifier::PortIdentifierType>() }))
 {
     MessagePortChannel::createChannelBetweenPorts(*m_port1, *m_port2);
 }
index de1de88..5657d75 100644 (file)
 
 namespace WebCore {
 
-MessagePort::MessagePort(ScriptExecutionContext& scriptExecutionContext)
+static HashMap<MessagePortIdentifier, MessagePort*>& allMessagePorts()
+{
+    static NeverDestroyed<HashMap<MessagePortIdentifier, MessagePort*>> map;
+    return map;
+}
+
+static Lock& allMessagePortsLock()
+{
+    static NeverDestroyed<Lock> lock;
+    return lock;
+}
+
+void MessagePort::ref() const
+{
+    ++m_refCount;
+}
+
+void MessagePort::deref() const
+{
+    // MessagePort::existingMessagePortForIdentifier() is unique in that it holds a raw pointer to a MessagePort
+    // but might create a RefPtr from it.
+    // If that happens on one thread at the same time that a MessagePort is being deref'ed and destroyed on a
+    // different thread then Bad Things could happen.
+    // This custom deref() function is designed to handle that contention by guaranteeing that nobody can be
+    // creating a RefPtr inside existingMessagePortForIdentifier while the object is mid-deletion.
+
+    if (!--m_refCount) {
+        Locker<Lock> locker(allMessagePortsLock());
+
+        if (m_refCount)
+            return;
+
+        allMessagePorts().remove(m_identifier);
+        delete this;
+    }
+}
+
+RefPtr<MessagePort> MessagePort::existingMessagePortForIdentifier(const MessagePortIdentifier& identifier)
+{
+    Locker<Lock> locker(allMessagePortsLock());
+
+    return allMessagePorts().get(identifier);
+}
+
+MessagePort::MessagePort(ScriptExecutionContext& scriptExecutionContext, const MessagePortIdentifier& identifier)
     : ActiveDOMObject(&scriptExecutionContext)
+    , m_identifier(identifier)
 {
+    Locker<Lock> locker(allMessagePortsLock());
+    allMessagePorts().set(m_identifier, this);
+
     m_scriptExecutionContext->createdMessagePort(*this);
     suspendIfNeeded();
 
@@ -45,6 +93,8 @@ MessagePort::MessagePort(ScriptExecutionContext& scriptExecutionContext)
 
 MessagePort::~MessagePort()
 {
+    ASSERT(allMessagePortsLock().isLocked());
+
     close();
     if (m_scriptExecutionContext)
         m_scriptExecutionContext->destroyedMessagePort(*this);
@@ -65,9 +115,10 @@ ExceptionOr<void> MessagePort::postMessage(JSC::ExecState& state, JSC::JSValue m
     // Make sure we aren't connected to any of the passed-in ports.
     if (!ports.isEmpty()) {
         for (auto& dataPort : ports) {
-            if (dataPort == this || m_entangledChannel->isConnectedTo(*dataPort))
+            if (dataPort == this || m_entangledChannel->isConnectedTo(dataPort->identifier()))
                 return Exception { DataCloneError };
         }
+
         auto disentangleResult = MessagePort::disentanglePorts(WTFMove(ports));
         if (disentangleResult.hasException())
             return disentangleResult.releaseException();
@@ -123,14 +174,14 @@ void MessagePort::close()
     m_closed = true;
 }
 
-void MessagePort::entangle(RefPtr<MessagePortChannel>&& remote)
+void MessagePort::entangleWithRemote(RefPtr<MessagePortChannel>&& remote)
 {
     // Only invoked to set our initial entanglement.
     ASSERT(!m_entangledChannel);
     ASSERT(m_scriptExecutionContext);
 
     // Don't entangle the ports if the channel is closed.
-    if (remote->entangleIfOpen(*this))
+    if (remote->entangleWithRemoteIfOpen(m_identifier))
         m_entangledChannel = WTFMove(remote);
 }
 
@@ -210,8 +261,8 @@ Vector<RefPtr<MessagePort>> MessagePort::entanglePorts(ScriptExecutionContext& c
     Vector<RefPtr<MessagePort>> portArray;
     portArray.reserveInitialCapacity(channels->size());
     for (unsigned int i = 0; i < channels->size(); ++i) {
-        auto port = MessagePort::create(context);
-        port->entangle(WTFMove((*channels)[i]));
+        auto port = MessagePort::create(context, { Process::identifier(), generateObjectIdentifier<MessagePortIdentifier::PortIdentifierType>() });
+        port->entangleWithRemote(WTFMove((*channels)[i]));
         portArray.uncheckedAppend(WTFMove(port));
     }
     return portArray;
index e567859..e73b632 100644 (file)
@@ -30,6 +30,7 @@
 #include "EventTarget.h"
 #include "ExceptionOr.h"
 #include "MessagePortChannel.h"
+#include "MessagePortIdentifier.h"
 
 namespace JSC {
 class ExecState;
@@ -41,9 +42,9 @@ namespace WebCore {
 
 class Frame;
 
-class MessagePort final : public ThreadSafeRefCounted<MessagePort>, public ActiveDOMObject, public EventTargetWithInlineData {
+class MessagePort final : public ActiveDOMObject, public EventTargetWithInlineData {
 public:
-    static Ref<MessagePort> create(ScriptExecutionContext& scriptExecutionContext) { return adoptRef(*new MessagePort(scriptExecutionContext)); }
+    static Ref<MessagePort> create(ScriptExecutionContext& scriptExecutionContext, const MessagePortIdentifier& identifier) { return adoptRef(*new MessagePort(scriptExecutionContext, identifier)); }
     virtual ~MessagePort();
 
     ExceptionOr<void> postMessage(JSC::ExecState&, JSC::JSValue message, Vector<JSC::Strong<JSC::JSObject>>&&);
@@ -51,12 +52,12 @@ public:
     void start();
     void close();
 
-    void entangle(RefPtr<MessagePortChannel>&&);
+    void entangleWithRemote(RefPtr<MessagePortChannel>&&);
 
     // Returns nullptr if the passed-in vector is empty.
     static ExceptionOr<std::unique_ptr<MessagePortChannelArray>> disentanglePorts(Vector<RefPtr<MessagePort>>&&);
-
     static Vector<RefPtr<MessagePort>> entanglePorts(ScriptExecutionContext&, std::unique_ptr<MessagePortChannelArray>&&);
+    static RefPtr<MessagePort> existingMessagePortForIdentifier(const MessagePortIdentifier&);
 
     void messageAvailable();
     bool started() const { return m_started; }
@@ -68,8 +69,10 @@ public:
     // of the remote port (since it may live cross-process) - those platforms may always return null.
     MessagePort* locallyEntangledPort() const;
 
-    using ThreadSafeRefCounted::ref;
-    using ThreadSafeRefCounted::deref;
+    const MessagePortIdentifier& identifier() const { return m_identifier; }
+
+    void ref() const;
+    void deref() const;
 
     // ActiveDOMObject
     const char* activeDOMObjectName() const final;
@@ -85,7 +88,7 @@ public:
     void derefEventTarget() final { deref(); }
 
 private:
-    explicit MessagePort(ScriptExecutionContext&);
+    explicit MessagePort(ScriptExecutionContext&, const MessagePortIdentifier&);
 
     bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) final;
 
@@ -101,6 +104,10 @@ private:
     RefPtr<MessagePort> m_messageProtector;
     bool m_started { false };
     bool m_closed { false };
+
+    MessagePortIdentifier m_identifier;
+
+    mutable std::atomic<unsigned> m_refCount { 1 };
 };
 
 } // namespace WebCore
index e2ff7af..cdf03f9 100644 (file)
@@ -43,6 +43,8 @@ class MessagePort;
 class MessagePortChannel;
 class ScriptExecutionContext;
 
+struct MessagePortIdentifier;
+
 // The overwhelmingly common case is sending a single port, so handle that efficiently with an inline buffer of size 1.
 typedef Vector<RefPtr<MessagePortChannel>, 1> MessagePortChannelArray;
 
@@ -64,12 +66,10 @@ public:
 
     static void createChannelBetweenPorts(MessagePort&, MessagePort&);
 
-    void setRemotePort(MessagePort*);
-
     virtual void postMessageToRemote(Ref<SerializedScriptValue>&&, std::unique_ptr<MessagePortChannelArray>&&) = 0;
     virtual Deque<std::unique_ptr<EventData>> takeAllMessagesFromRemote() = 0;
-    virtual bool isConnectedTo(MessagePort&) = 0;
-    virtual bool entangleIfOpen(MessagePort&) = 0;
+    virtual bool isConnectedTo(const MessagePortIdentifier&) = 0;
+    virtual bool entangleWithRemoteIfOpen(const MessagePortIdentifier&) = 0;
     virtual void disentangle() = 0;
     virtual bool hasPendingActivity() = 0;
     virtual MessagePort* locallyEntangledPort(const ScriptExecutionContext*) = 0;
diff --git a/Source/WebCore/dom/MessagePortIdentifier.h b/Source/WebCore/dom/MessagePortIdentifier.h
new file mode 100644 (file)
index 0000000..035cdae
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "Process.h"
+#include <wtf/Hasher.h>
+
+namespace WebCore {
+
+struct MessagePortIdentifier {
+    ProcessIdentifier processIdentifier;
+    enum PortIdentifierType { };
+    ObjectIdentifier<PortIdentifierType> portIdentifier;
+
+    unsigned hash() const;
+
+    template<class Encoder> void encode(Encoder&) const;
+    template<class Decoder> static std::optional<MessagePortIdentifier> decode(Decoder&);
+};
+
+inline bool operator==(const MessagePortIdentifier& a, const MessagePortIdentifier& b)
+{
+    return a.processIdentifier == b.processIdentifier &&  a.portIdentifier == b.portIdentifier;
+}
+
+template<class Encoder>
+void MessagePortIdentifier::encode(Encoder& encoder) const
+{
+    encoder << processIdentifier << portIdentifier;
+}
+
+template<class Decoder>
+std::optional<MessagePortIdentifier> MessagePortIdentifier::decode(Decoder& decoder)
+{
+    std::optional<ProcessIdentifier> processIdentifier;
+    decoder >> processIdentifier;
+    if (!processIdentifier)
+        return std::nullopt;
+
+    std::optional<ObjectIdentifier<PortIdentifierType>> portIdentifier;
+    decoder >> portIdentifier;
+    if (!portIdentifier)
+        return std::nullopt;
+
+    return { { WTFMove(*processIdentifier), WTFMove(*portIdentifier) } };
+}
+
+inline unsigned MessagePortIdentifier::hash() const
+{
+    return computeHash(processIdentifier.toUInt64(), portIdentifier.toUInt64());
+}
+
+} // namespace WebCore
+
+namespace WTF {
+
+struct MessagePortIdentifierHash {
+    static unsigned hash(const WebCore::MessagePortIdentifier& key) { return key.hash(); }
+    static bool equal(const WebCore::MessagePortIdentifier& a, const WebCore::MessagePortIdentifier& b) { return a == b; }
+    static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+template<> struct HashTraits<WebCore::MessagePortIdentifier> : GenericHashTraits<WebCore::MessagePortIdentifier> {
+    static WebCore::MessagePortIdentifier emptyValue() { return { }; }
+
+    static void constructDeletedValue(WebCore::MessagePortIdentifier& slot) { slot.processIdentifier = makeObjectIdentifier<WebCore::ProcessIdentifierType>(std::numeric_limits<uint64_t>::max()); }
+
+    static bool isDeletedValue(const WebCore::MessagePortIdentifier& slot) { return slot.processIdentifier.toUInt64() == std::numeric_limits<uint64_t>::max(); }
+};
+
+template<> struct DefaultHash<WebCore::MessagePortIdentifier> {
+    typedef MessagePortIdentifierHash Hash;
+};
+
+} // namespace WTF