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
41 class MessagePortCloseEventTask : public ScriptExecutionContext::Task {
42 public:
create(PassRefPtr<MessagePort> port)43 static PassRefPtr<MessagePortCloseEventTask> create(PassRefPtr<MessagePort> port)
44 {
45 return adoptRef(new MessagePortCloseEventTask(port));
46 }
47
48 private:
MessagePortCloseEventTask(PassRefPtr<MessagePort> port)49 MessagePortCloseEventTask(PassRefPtr<MessagePort> port)
50 : m_port(port)
51 {
52 ASSERT(m_port);
53 }
54
performTask(ScriptExecutionContext * unusedContext)55 virtual void performTask(ScriptExecutionContext* unusedContext)
56 {
57 ASSERT_UNUSED(unusedContext, unusedContext == m_port->scriptExecutionContext());
58 ASSERT(!m_port->active());
59
60 // Closing may destroy the port, dispatch any remaining messages now.
61 if (m_port->queueIsOpen())
62 m_port->dispatchMessages();
63
64 m_port->dispatchCloseEvent();
65 }
66
67 RefPtr<MessagePort> m_port;
68 };
69
create(const String & message,PassRefPtr<MessagePort> port)70 PassRefPtr<MessagePort::EventData> MessagePort::EventData::create(const String& message, PassRefPtr<MessagePort> port)
71 {
72 return adoptRef(new EventData(message, port));
73 }
74
EventData(const String & message,PassRefPtr<MessagePort> messagePort)75 MessagePort::EventData::EventData(const String& message, PassRefPtr<MessagePort> messagePort)
76 : message(message.copy())
77 , messagePort(messagePort)
78 {
79 }
80
~EventData()81 MessagePort::EventData::~EventData()
82 {
83 }
84
MessagePort(ScriptExecutionContext * scriptExecutionContext)85 MessagePort::MessagePort(ScriptExecutionContext* scriptExecutionContext)
86 : m_entangledPort(0)
87 , m_queueIsOpen(false)
88 , m_scriptExecutionContext(scriptExecutionContext)
89 , m_pendingCloseEvent(false)
90 {
91 if (scriptExecutionContext)
92 scriptExecutionContext->createdMessagePort(this);
93 }
94
~MessagePort()95 MessagePort::~MessagePort()
96 {
97 if (m_entangledPort)
98 unentangle();
99
100 if (m_scriptExecutionContext)
101 m_scriptExecutionContext->destroyedMessagePort(this);
102 }
103
clone(ExceptionCode & ec)104 PassRefPtr<MessagePort> MessagePort::clone(ExceptionCode& ec)
105 {
106 if (!m_entangledPort) {
107 ec = INVALID_STATE_ERR;
108 return 0;
109 }
110
111 RefPtr<MessagePort> remotePort = m_entangledPort;
112 RefPtr<MessagePort> newPort = MessagePort::create(0);
113
114 // Move all the events in the port message queue of original port to the port message queue of new port, if any, leaving the new port's port message queue in its initial closed state.
115 // If events are posted (e.g. from a worker thread) while this code is executing, there is no guarantee whether they end up in the original or new port's message queue.
116 RefPtr<EventData> eventData;
117 while (m_messageQueue.tryGetMessage(eventData))
118 newPort->m_messageQueue.append(eventData);
119
120 entangle(remotePort.get(), newPort.get()); // The port object will be unentangled.
121 return newPort;
122 }
123
postMessage(const String & message,ExceptionCode & ec)124 void MessagePort::postMessage(const String& message, ExceptionCode& ec)
125 {
126 postMessage(message, 0, ec);
127 }
128
postMessage(const String & message,MessagePort * dataPort,ExceptionCode & ec)129 void MessagePort::postMessage(const String& message, MessagePort* dataPort, ExceptionCode& ec)
130 {
131 if (!m_entangledPort || !m_scriptExecutionContext)
132 return;
133
134 RefPtr<MessagePort> newMessagePort;
135 if (dataPort) {
136 if (dataPort == this || dataPort == m_entangledPort) {
137 ec = INVALID_ACCESS_ERR;
138 return;
139 }
140 newMessagePort = dataPort->clone(ec);
141 if (ec)
142 return;
143 }
144
145 m_entangledPort->m_messageQueue.append(EventData::create(message, newMessagePort));
146 if (m_entangledPort->m_queueIsOpen && m_entangledPort->m_scriptExecutionContext)
147 m_entangledPort->m_scriptExecutionContext->processMessagePortMessagesSoon();
148 }
149
startConversation(ScriptExecutionContext * scriptExecutionContext,const String & message)150 PassRefPtr<MessagePort> MessagePort::startConversation(ScriptExecutionContext* scriptExecutionContext, const String& message)
151 {
152 RefPtr<MessagePort> port1 = MessagePort::create(scriptExecutionContext);
153 if (!m_entangledPort || !m_scriptExecutionContext)
154 return port1;
155 RefPtr<MessagePort> port2 = MessagePort::create(0);
156
157 entangle(port1.get(), port2.get());
158
159 m_entangledPort->m_messageQueue.append(EventData::create(message, port2));
160 if (m_entangledPort->m_queueIsOpen && m_entangledPort->m_scriptExecutionContext)
161 m_entangledPort->m_scriptExecutionContext->processMessagePortMessagesSoon();
162 return port1;
163 }
164
start()165 void MessagePort::start()
166 {
167 if (m_queueIsOpen || !m_scriptExecutionContext)
168 return;
169
170 m_queueIsOpen = true;
171 m_scriptExecutionContext->processMessagePortMessagesSoon();
172 }
173
close()174 void MessagePort::close()
175 {
176 if (!m_entangledPort)
177 return;
178
179 MessagePort* otherPort = m_entangledPort;
180 unentangle();
181
182 queueCloseEvent();
183 otherPort->queueCloseEvent();
184 }
185
entangle(MessagePort * port1,MessagePort * port2)186 void MessagePort::entangle(MessagePort* port1, MessagePort* port2)
187 {
188 if (port1->m_entangledPort) {
189 ASSERT(port1->m_entangledPort != port2);
190 port1->unentangle();
191 }
192
193 if (port2->m_entangledPort) {
194 ASSERT(port2->m_entangledPort != port1);
195 port2->unentangle();
196 }
197
198 port1->m_entangledPort = port2;
199 port2->m_entangledPort = port1;
200 }
201
unentangle()202 void MessagePort::unentangle()
203 {
204 ASSERT(this == m_entangledPort->m_entangledPort);
205
206 m_entangledPort->m_entangledPort = 0;
207 m_entangledPort = 0;
208 }
209
contextDestroyed()210 void MessagePort::contextDestroyed()
211 {
212 ASSERT(m_scriptExecutionContext);
213
214 if (m_entangledPort)
215 unentangle();
216
217 m_scriptExecutionContext = 0;
218 }
219
attachToContext(ScriptExecutionContext * scriptExecutionContext)220 void MessagePort::attachToContext(ScriptExecutionContext* scriptExecutionContext)
221 {
222 ASSERT(!m_scriptExecutionContext);
223 ASSERT(!m_queueIsOpen);
224
225 m_scriptExecutionContext = scriptExecutionContext;
226 m_scriptExecutionContext->createdMessagePort(this);
227
228 // FIXME: Need to call processMessagePortMessagesSoon()?
229 }
230
scriptExecutionContext() const231 ScriptExecutionContext* MessagePort::scriptExecutionContext() const
232 {
233 return m_scriptExecutionContext;
234 }
235
dispatchMessages()236 void MessagePort::dispatchMessages()
237 {
238 // Messages for contexts that are not fully active get dispatched too, but JSAbstractEventListener::handleEvent() doesn't call handlers for these.
239 // FIXME: Such messages should be dispatched if the document returns from page cache. They are only allowed to be lost if the document is discarded.
240 ASSERT(queueIsOpen());
241
242 RefPtr<EventData> eventData;
243 while (m_messageQueue.tryGetMessage(eventData)) {
244
245 ASSERT(!eventData->messagePort || !eventData->messagePort->m_scriptExecutionContext);
246 if (eventData->messagePort)
247 eventData->messagePort->attachToContext(m_scriptExecutionContext);
248
249 RefPtr<Event> evt = MessageEvent::create(eventData->message, "", "", 0, eventData->messagePort);
250
251 if (m_onMessageListener) {
252 evt->setTarget(this);
253 evt->setCurrentTarget(this);
254 m_onMessageListener->handleEvent(evt.get(), false);
255 }
256
257 ExceptionCode ec = 0;
258 dispatchEvent(evt.release(), ec);
259 ASSERT(!ec);
260 }
261 }
262
queueCloseEvent()263 void MessagePort::queueCloseEvent()
264 {
265 ASSERT(!m_pendingCloseEvent);
266 m_pendingCloseEvent = true;
267
268 m_scriptExecutionContext->postTask(MessagePortCloseEventTask::create(this));
269 }
270
dispatchCloseEvent()271 void MessagePort::dispatchCloseEvent()
272 {
273 ASSERT(m_pendingCloseEvent);
274 m_pendingCloseEvent = false;
275
276 RefPtr<Event> evt = Event::create(eventNames().closeEvent, false, true);
277 if (m_onCloseListener) {
278 evt->setTarget(this);
279 evt->setCurrentTarget(this);
280 m_onCloseListener->handleEvent(evt.get(), false);
281 }
282
283 ExceptionCode ec = 0;
284 dispatchEvent(evt.release(), ec);
285 ASSERT(!ec);
286 }
287
addEventListener(const AtomicString & eventType,PassRefPtr<EventListener> eventListener,bool)288 void MessagePort::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool)
289 {
290 EventListenersMap::iterator iter = m_eventListeners.find(eventType);
291 if (iter == m_eventListeners.end()) {
292 ListenerVector listeners;
293 listeners.append(eventListener);
294 m_eventListeners.add(eventType, listeners);
295 } else {
296 ListenerVector& listeners = iter->second;
297 for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
298 if (*listenerIter == eventListener)
299 return;
300 }
301
302 listeners.append(eventListener);
303 m_eventListeners.add(eventType, listeners);
304 }
305 }
306
removeEventListener(const AtomicString & eventType,EventListener * eventListener,bool)307 void MessagePort::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool)
308 {
309 EventListenersMap::iterator iter = m_eventListeners.find(eventType);
310 if (iter == m_eventListeners.end())
311 return;
312
313 ListenerVector& listeners = iter->second;
314 for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
315 if (*listenerIter == eventListener) {
316 listeners.remove(listenerIter - listeners.begin());
317 return;
318 }
319 }
320 }
321
dispatchEvent(PassRefPtr<Event> event,ExceptionCode & ec)322 bool MessagePort::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec)
323 {
324 if (!event || event->type().isEmpty()) {
325 ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR;
326 return true;
327 }
328
329 ListenerVector listenersCopy = m_eventListeners.get(event->type());
330 for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) {
331 event->setTarget(this);
332 event->setCurrentTarget(this);
333 listenerIter->get()->handleEvent(event.get(), false);
334 }
335
336 return !event->defaultPrevented();
337 }
338
hasPendingActivity()339 bool MessagePort::hasPendingActivity()
340 {
341 return m_pendingCloseEvent || (m_queueIsOpen && !m_messageQueue.isEmpty());
342 }
343
344 } // namespace WebCore
345