1 /*
2 * Copyright (C) 2008 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 COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *
25 */
26
27 #include "config.h"
28 #include "MessagePort.h"
29
30 #include "AtomicString.h"
31 #include "DOMWindow.h"
32 #include "Document.h"
33 #include "EventException.h"
34 #include "EventNames.h"
35 #include "MessageEvent.h"
36 #include "SecurityOrigin.h"
37 #include "Timer.h"
38
39 namespace WebCore {
40
MessagePort(ScriptExecutionContext & scriptExecutionContext)41 MessagePort::MessagePort(ScriptExecutionContext& scriptExecutionContext)
42 : m_entangledChannel(0)
43 , m_started(false)
44 , m_scriptExecutionContext(&scriptExecutionContext)
45 {
46 m_scriptExecutionContext->createdMessagePort(this);
47
48 // Don't need to call processMessagePortMessagesSoon() here, because the port will not be opened until start() is invoked.
49 }
50
~MessagePort()51 MessagePort::~MessagePort()
52 {
53 close();
54 if (m_scriptExecutionContext)
55 m_scriptExecutionContext->destroyedMessagePort(this);
56 }
57
postMessage(const String & message,ExceptionCode & ec)58 void MessagePort::postMessage(const String& message, ExceptionCode& ec)
59 {
60 postMessage(message, 0, ec);
61 }
62
postMessage(const String & message,MessagePort * dataPort,ExceptionCode & ec)63 void MessagePort::postMessage(const String& message, MessagePort* dataPort, ExceptionCode& ec)
64 {
65 if (!m_entangledChannel)
66 return;
67 ASSERT(m_scriptExecutionContext);
68
69 OwnPtr<MessagePortChannel> channel;
70 if (dataPort) {
71 if (dataPort == this || m_entangledChannel->isConnectedTo(dataPort)) {
72 ec = INVALID_STATE_ERR;
73 return;
74 }
75 channel = dataPort->disentangle(ec);
76 if (ec)
77 return;
78 }
79 m_entangledChannel->postMessageToRemote(MessagePortChannel::EventData::create(message, channel.release()));
80 }
81
disentangle(ExceptionCode & ec)82 PassOwnPtr<MessagePortChannel> MessagePort::disentangle(ExceptionCode& ec)
83 {
84 if (!m_entangledChannel)
85 ec = INVALID_STATE_ERR;
86 else {
87 m_entangledChannel->disentangle();
88
89 // We can't receive any messages or generate any events, so remove ourselves from the list of active ports.
90 ASSERT(m_scriptExecutionContext);
91 m_scriptExecutionContext->destroyedMessagePort(this);
92 m_scriptExecutionContext = 0;
93 }
94 return m_entangledChannel.release();
95 }
96
97 // Invoked to notify us that there are messages available for this port.
98 // This code may be called from another thread, and so should not call any non-threadsafe APIs (i.e. should not call into the entangled channel or access mutable variables).
messageAvailable()99 void MessagePort::messageAvailable()
100 {
101 ASSERT(m_scriptExecutionContext);
102 m_scriptExecutionContext->processMessagePortMessagesSoon();
103 }
104
start()105 void MessagePort::start()
106 {
107 // Do nothing if we've been cloned
108 if (!m_entangledChannel)
109 return;
110
111 ASSERT(m_scriptExecutionContext);
112 if (m_started)
113 return;
114
115 m_started = true;
116 m_scriptExecutionContext->processMessagePortMessagesSoon();
117 }
118
close()119 void MessagePort::close()
120 {
121 if (!m_entangledChannel)
122 return;
123 m_entangledChannel->close();
124 }
125
entangle(PassOwnPtr<MessagePortChannel> remote)126 void MessagePort::entangle(PassOwnPtr<MessagePortChannel> remote)
127 {
128 // Only invoked to set our initial entanglement.
129 ASSERT(!m_entangledChannel);
130 ASSERT(m_scriptExecutionContext);
131
132 // Don't entangle the ports if the channel is closed.
133 if (remote->entangleIfOpen(this))
134 m_entangledChannel = remote;
135 }
136
contextDestroyed()137 void MessagePort::contextDestroyed()
138 {
139 ASSERT(m_scriptExecutionContext);
140 // Must close port before blowing away the cached context, to ensure that we get no more calls to messageAvailable().
141 close();
142 m_scriptExecutionContext = 0;
143 }
144
scriptExecutionContext() const145 ScriptExecutionContext* MessagePort::scriptExecutionContext() const
146 {
147 return m_scriptExecutionContext;
148 }
149
dispatchMessages()150 void MessagePort::dispatchMessages()
151 {
152 // Messages for contexts that are not fully active get dispatched too, but JSAbstractEventListener::handleEvent() doesn't call handlers for these.
153 // The HTML5 spec specifies that any messages sent to a document that is not fully active should be dropped, so this behavior is OK.
154 ASSERT(started());
155
156 OwnPtr<MessagePortChannel::EventData> eventData;
157 while (m_entangledChannel && m_entangledChannel->tryGetMessageFromRemote(eventData)) {
158 RefPtr<MessagePort> port;
159 OwnPtr<MessagePortChannel> channel = eventData->channel();
160 if (channel) {
161 // The remote side sent over a MessagePortChannel, so create a MessagePort to wrap it.
162 port = MessagePort::create(*m_scriptExecutionContext);
163 port->entangle(channel.release());
164 }
165 RefPtr<Event> evt = MessageEvent::create(eventData->message(), "", "", 0, port.release());
166
167 if (m_onMessageListener) {
168 evt->setTarget(this);
169 evt->setCurrentTarget(this);
170 m_onMessageListener->handleEvent(evt.get(), false);
171 }
172
173 ExceptionCode ec = 0;
174 dispatchEvent(evt.release(), ec);
175 ASSERT(!ec);
176 }
177 }
178
addEventListener(const AtomicString & eventType,PassRefPtr<EventListener> eventListener,bool)179 void MessagePort::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool)
180 {
181 EventListenersMap::iterator iter = m_eventListeners.find(eventType);
182 if (iter == m_eventListeners.end()) {
183 ListenerVector listeners;
184 listeners.append(eventListener);
185 m_eventListeners.add(eventType, listeners);
186 } else {
187 ListenerVector& listeners = iter->second;
188 for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
189 if (*listenerIter == eventListener)
190 return;
191 }
192
193 listeners.append(eventListener);
194 m_eventListeners.add(eventType, listeners);
195 }
196 }
197
removeEventListener(const AtomicString & eventType,EventListener * eventListener,bool)198 void MessagePort::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool)
199 {
200 EventListenersMap::iterator iter = m_eventListeners.find(eventType);
201 if (iter == m_eventListeners.end())
202 return;
203
204 ListenerVector& listeners = iter->second;
205 for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
206 if (*listenerIter == eventListener) {
207 listeners.remove(listenerIter - listeners.begin());
208 return;
209 }
210 }
211 }
212
dispatchEvent(PassRefPtr<Event> event,ExceptionCode & ec)213 bool MessagePort::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec)
214 {
215 if (!event || event->type().isEmpty()) {
216 ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR;
217 return true;
218 }
219
220 ListenerVector listenersCopy = m_eventListeners.get(event->type());
221 for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) {
222 event->setTarget(this);
223 event->setCurrentTarget(this);
224 listenerIter->get()->handleEvent(event.get(), false);
225 }
226
227 return !event->defaultPrevented();
228 }
229
setOnmessage(PassRefPtr<EventListener> eventListener)230 void MessagePort::setOnmessage(PassRefPtr<EventListener> eventListener)
231 {
232 m_onMessageListener = eventListener;
233 start();
234 }
235
hasPendingActivity()236 bool MessagePort::hasPendingActivity()
237 {
238 // The spec says that entangled message ports should always be treated as if they have a strong reference.
239 // We'll also stipulate that the queue needs to be open (if the app drops its reference to the port before start()-ing it, then it's not really entangled as it's unreachable).
240 return m_started && m_entangledChannel && m_entangledChannel->hasPendingActivity();
241 }
242
locallyEntangledPort()243 MessagePort* MessagePort::locallyEntangledPort()
244 {
245 return m_entangledChannel ? m_entangledChannel->locallyEntangledPort(m_scriptExecutionContext) : 0;
246 }
247
248 } // namespace WebCore
249