Make MessagePortChannel::takeAllMessagesFromRemote asynchronous.
[WebKit-https.git] / Source / WebCore / dom / InProcessMessagePortChannel.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "InProcessMessagePortChannel.h"
28
29 #include "MessagePort.h"
30 #include <wtf/CompletionHandler.h>
31 #include <wtf/Locker.h>
32
33 namespace WebCore {
34
35 void InProcessMessagePortChannel::createChannelBetweenPorts(MessagePort& port1, MessagePort& port2)
36 {
37     auto queue1 = MessagePortQueue::create();
38     auto queue2 = MessagePortQueue::create();
39
40     auto channel1 = InProcessMessagePortChannel::create(queue1.get(), queue2.get());
41     auto channel2 = InProcessMessagePortChannel::create(queue2.get(), queue1.get());
42
43     channel1->m_entangledChannel = channel2.ptr();
44     channel2->m_entangledChannel = channel1.ptr();
45
46     port1.entangleWithRemote(WTFMove(channel2));
47     port2.entangleWithRemote(WTFMove(channel1));
48 }
49
50 Ref<InProcessMessagePortChannel> InProcessMessagePortChannel::create(MessagePortQueue& incoming, MessagePortQueue& outgoing)
51 {
52     return adoptRef(*new InProcessMessagePortChannel(incoming, outgoing));
53 }
54
55 InProcessMessagePortChannel::InProcessMessagePortChannel(MessagePortQueue& incoming, MessagePortQueue& outgoing)
56     : m_incomingQueue(&incoming)
57     , m_outgoingQueue(&outgoing)
58 {
59 }
60
61 InProcessMessagePortChannel::~InProcessMessagePortChannel()
62 {
63     // Channels being destroyed should to have been closed.
64     ASSERT(!m_outgoingQueue);
65 }
66
67 void InProcessMessagePortChannel::postMessageToRemote(Ref<SerializedScriptValue>&& message, std::unique_ptr<MessagePortChannelArray>&& channels)
68 {
69     Locker<Lock> locker(m_lock);
70
71     if (!m_outgoingQueue)
72         return;
73
74     bool wasEmpty = m_outgoingQueue->appendAndCheckEmpty(std::make_unique<EventData>(WTFMove(message), WTFMove(channels)));
75     if (wasEmpty && m_remotePort)
76         m_remotePort->messageAvailable();
77 }
78
79 void InProcessMessagePortChannel::takeAllMessagesFromRemote(CompletionHandler<void(Deque<std::unique_ptr<EventData>>&&)>&& callback)
80 {
81     Deque<std::unique_ptr<EventData>> messages;
82     {
83         Locker<Lock> locker(m_lock);
84         messages = m_incomingQueue->takeAllMessages();
85     }
86
87     callback(WTFMove(messages));
88 }
89
90 bool InProcessMessagePortChannel::isConnectedTo(const MessagePortIdentifier& identifier)
91 {
92     // FIXME: What guarantees that the result remains the same after we release the lock?
93     Locker<Lock> locker(m_lock);
94     return m_remotePort && m_remotePort->identifier() == identifier;
95 }
96
97 bool InProcessMessagePortChannel::entangleWithRemoteIfOpen(const MessagePortIdentifier& identifier)
98 {
99     // We can't call member functions on our remote pair while holding our mutex or we'll deadlock,
100     // but we need to guard against the remote port getting closed/freed, so create a standalone reference.
101     RefPtr<InProcessMessagePortChannel> remote;
102     {
103         Locker<Lock> locker(m_lock);
104         remote = m_entangledChannel;
105     }
106
107     if (!remote)
108         return false;
109
110     auto entangledPort = MessagePort::existingMessagePortForIdentifier(identifier);
111     ASSERT(entangledPort);
112
113     remote->setRemotePort(entangledPort.get());
114
115     return true;
116 }
117
118 void InProcessMessagePortChannel::disentangle()
119 {
120     Locker<Lock> locker(m_lock);
121
122     if (m_entangledChannel)
123         m_entangledChannel->setRemotePort(nullptr);
124 }
125
126 bool InProcessMessagePortChannel::hasPendingActivity()
127 {
128     // FIXME: What guarantees that the result remains the same after we release the lock?
129     Locker<Lock> locker(m_lock);
130     return !m_incomingQueue->isEmpty();
131 }
132
133 MessagePort* InProcessMessagePortChannel::locallyEntangledPort(const ScriptExecutionContext* context)
134 {
135     Locker<Lock> locker(m_lock);
136
137     // See if both contexts are run by the same thread (are the same context, or are both documents).
138     if (!m_remotePort)
139         return nullptr;
140
141     // The remote port's ScriptExecutionContext is guaranteed not to change here - MessagePort::contextDestroyed()
142     // will close the port before the context goes away, and close() will block because we are holding the mutex.
143     ScriptExecutionContext* remoteContext = m_remotePort->scriptExecutionContext();
144     if (remoteContext == context || (remoteContext && remoteContext->isDocument() && context->isDocument()))
145         return m_remotePort;
146
147     return nullptr;
148 }
149
150 RefPtr<InProcessMessagePortChannel> InProcessMessagePortChannel::takeEntangledChannel()
151 {
152     RefPtr<InProcessMessagePortChannel> channel;
153
154     {
155         Locker<Lock> locker(m_lock);
156         channel = WTFMove(m_entangledChannel);
157     }
158
159     return channel;
160 }
161
162 void InProcessMessagePortChannel::close()
163 {
164     Locker<Lock> locker(m_lock);
165
166     RefPtr<InProcessMessagePortChannel> channel;
167     if (m_entangledChannel) {
168         channel = m_entangledChannel->takeEntangledChannel();
169         ASSERT(channel == this);
170         m_entangledChannel->close();
171     }
172
173     // Disentangle ourselves from the other end. We still maintain a reference to our incoming queue, since previously-existing messages should still be delivered.
174     m_remotePort = nullptr;
175     m_outgoingQueue = nullptr;
176     m_entangledChannel = nullptr;
177 }
178
179 void InProcessMessagePortChannel::setRemotePort(MessagePort* port)
180 {
181     Locker<Lock> locker(m_lock);
182
183     // Should never set port if it is already set.
184     ASSERT(!port || !m_remotePort);
185
186     m_remotePort = port;
187 }
188
189 } // namespace WebCore