1/*
2 * Copyright (C) 2009 Google 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 are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "PlatformMessagePortChannel.h"
33
34#include "MessagePort.h"
35#include "ScriptExecutionContext.h"
36
37namespace WebCore {
38
39// MessagePortChannel implementations - just delegate to the PlatformMessagePortChannel.
40void MessagePortChannel::createChannel(PassRefPtr<MessagePort> port1, PassRefPtr<MessagePort> port2)
41{
42    PlatformMessagePortChannel::createChannel(port1, port2);
43}
44
45PassOwnPtr<MessagePortChannel> MessagePortChannel::create(PassRefPtr<PlatformMessagePortChannel> channel)
46{
47    return new MessagePortChannel(channel);
48}
49
50MessagePortChannel::MessagePortChannel(PassRefPtr<PlatformMessagePortChannel> channel)
51    : m_channel(channel)
52{
53}
54
55MessagePortChannel::~MessagePortChannel()
56{
57    // Make sure we close our platform channel when the base is freed, to keep the channel objects from leaking.
58    m_channel->close();
59}
60
61bool MessagePortChannel::entangleIfOpen(MessagePort* port)
62{
63    return m_channel->entangleIfOpen(port);
64}
65
66void MessagePortChannel::disentangle()
67{
68    m_channel->disentangle();
69}
70
71void MessagePortChannel::postMessageToRemote(PassOwnPtr<MessagePortChannel::EventData> message)
72{
73    m_channel->postMessageToRemote(message);
74}
75
76bool MessagePortChannel::tryGetMessageFromRemote(OwnPtr<MessagePortChannel::EventData>& result)
77{
78    return m_channel->tryGetMessageFromRemote(result);
79}
80
81void MessagePortChannel::close()
82{
83    m_channel->close();
84}
85
86bool MessagePortChannel::isConnectedTo(MessagePort* port)
87{
88    return m_channel->isConnectedTo(port);
89}
90
91bool MessagePortChannel::hasPendingActivity()
92{
93    return m_channel->hasPendingActivity();
94}
95
96MessagePort* MessagePortChannel::locallyEntangledPort(const ScriptExecutionContext* context)
97{
98    return m_channel->locallyEntangledPort(context);
99}
100
101PassRefPtr<PlatformMessagePortChannel> PlatformMessagePortChannel::create(PassRefPtr<MessagePortQueue> incoming, PassRefPtr<MessagePortQueue> outgoing)
102{
103    return adoptRef(new PlatformMessagePortChannel(incoming, outgoing));
104}
105
106PlatformMessagePortChannel::PlatformMessagePortChannel(PassRefPtr<MessagePortQueue> incoming, PassRefPtr<MessagePortQueue> outgoing)
107    : m_entangledChannel(0)
108    , m_incomingQueue(incoming)
109    , m_outgoingQueue(outgoing)
110    , m_remotePort(0)
111{
112}
113
114PlatformMessagePortChannel::~PlatformMessagePortChannel()
115{
116}
117
118void PlatformMessagePortChannel::createChannel(PassRefPtr<MessagePort> port1, PassRefPtr<MessagePort> port2)
119{
120    // Create incoming/outgoing queues.
121    RefPtr<PlatformMessagePortChannel::MessagePortQueue> queue1 = PlatformMessagePortChannel::MessagePortQueue::create();
122    RefPtr<PlatformMessagePortChannel::MessagePortQueue> queue2 = PlatformMessagePortChannel::MessagePortQueue::create();
123
124    // Create proxies for each endpoint.
125    RefPtr<PlatformMessagePortChannel> channel1 = PlatformMessagePortChannel::create(queue1, queue2);
126    RefPtr<PlatformMessagePortChannel> channel2 = PlatformMessagePortChannel::create(queue2, queue1);
127
128    // Entangle the two endpoints.
129    channel1->setEntangledChannel(channel2);
130    channel2->setEntangledChannel(channel1);
131
132    // Now entangle the proxies with the appropriate local ports.
133    port1->entangle(MessagePortChannel::create(channel2));
134    port2->entangle(MessagePortChannel::create(channel1));
135}
136
137bool PlatformMessagePortChannel::entangleIfOpen(MessagePort* port)
138{
139    // 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.
140    RefPtr<PlatformMessagePortChannel> remote = entangledChannel();
141    if (!remote)
142        return false;
143    remote->setRemotePort(port);
144    return true;
145}
146
147void PlatformMessagePortChannel::disentangle()
148{
149    RefPtr<PlatformMessagePortChannel> remote = entangledChannel();
150    if (remote)
151        remote->setRemotePort(0);
152}
153
154void PlatformMessagePortChannel::setRemotePort(MessagePort* port)
155{
156    MutexLocker lock(m_mutex);
157    // Should never set port if it is already set.
158    ASSERT(!port || !m_remotePort);
159    m_remotePort = port;
160}
161
162MessagePort* PlatformMessagePortChannel::remotePort()
163{
164    MutexLocker lock(m_mutex);
165    return m_remotePort;
166}
167
168PassRefPtr<PlatformMessagePortChannel> PlatformMessagePortChannel::entangledChannel()
169{
170    MutexLocker lock(m_mutex);
171    return m_entangledChannel;
172}
173
174void PlatformMessagePortChannel::setEntangledChannel(PassRefPtr<PlatformMessagePortChannel> remote)
175{
176    MutexLocker lock(m_mutex);
177    // Should only be set as part of initial creation/entanglement.
178    if (remote)
179        ASSERT(!m_entangledChannel.get());
180    m_entangledChannel = remote;
181}
182
183void PlatformMessagePortChannel::postMessageToRemote(PassOwnPtr<MessagePortChannel::EventData> message)
184{
185    MutexLocker lock(m_mutex);
186    if (!m_outgoingQueue)
187        return;
188    bool wasEmpty = m_outgoingQueue->appendAndCheckEmpty(message);
189    if (wasEmpty && m_remotePort)
190        m_remotePort->messageAvailable();
191}
192
193bool PlatformMessagePortChannel::tryGetMessageFromRemote(OwnPtr<MessagePortChannel::EventData>& result)
194{
195    MutexLocker lock(m_mutex);
196    result = m_incomingQueue->tryGetMessage();
197    return result;
198}
199
200bool PlatformMessagePortChannel::isConnectedTo(MessagePort* port)
201{
202    MutexLocker lock(m_mutex);
203    return m_remotePort == port;
204}
205
206// Closes the port so no further messages can be sent from either end.
207void PlatformMessagePortChannel::close()
208{
209    RefPtr<PlatformMessagePortChannel> remote = entangledChannel();
210    if (!remote)
211        return;
212    closeInternal();
213    remote->closeInternal();
214}
215
216void PlatformMessagePortChannel::closeInternal()
217{
218    MutexLocker lock(m_mutex);
219    // Disentangle ourselves from the other end. We still maintain a reference to our incoming queue, since previously-existing messages should still be delivered.
220    m_remotePort = 0;
221    m_entangledChannel = 0;
222    m_outgoingQueue = 0;
223}
224
225bool PlatformMessagePortChannel::hasPendingActivity()
226{
227    MutexLocker lock(m_mutex);
228    return !m_incomingQueue->isEmpty();
229}
230
231MessagePort* PlatformMessagePortChannel::locallyEntangledPort(const ScriptExecutionContext* context)
232{
233    MutexLocker lock(m_mutex);
234    // See if both contexts are run by the same thread (are the same context, or are both documents).
235    if (m_remotePort) {
236        // The remote port's ScriptExecutionContext is guaranteed not to change here - MessagePort::contextDestroyed() will close the port before the context goes away, and close() will block because we are holding the mutex.
237        ScriptExecutionContext* remoteContext = m_remotePort->scriptExecutionContext();
238        if (remoteContext == context || (remoteContext && remoteContext->isDocument() && context->isDocument()))
239            return m_remotePort;
240    }
241    return 0;
242}
243
244} // namespace WebCore
245