2008-11-04 Anders Carlsson <andersca@apple.com>
[webkit/qt.git] / WebCore / dom / MessagePort.cpp
blob58467af62203599403113a93b4a88dbc92286022
1 /*
2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
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.
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.
27 #include "config.h"
28 #include "MessagePort.h"
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"
39 namespace WebCore {
41 class CloseMessagePortTimer : public TimerBase {
42 public:
43 CloseMessagePortTimer(PassRefPtr<MessagePort> port)
44 : m_port(port)
46 ASSERT(m_port);
49 private:
50 virtual void fired()
52 ASSERT(!m_port->active());
54 // Closing may destroy the port, dispatch any remaining messages now.
55 if (m_port->queueIsOpen())
56 m_port->dispatchMessages();
58 m_port->dispatchCloseEvent();
59 delete this;
62 RefPtr<MessagePort> m_port;
65 MessagePort::MessagePort(ScriptExecutionContext* scriptExecutionContext)
66 : m_entangledPort(0)
67 , m_queueIsOpen(false)
68 , m_scriptExecutionContext(scriptExecutionContext)
69 , m_pendingCloseEvent(false)
70 , m_jsWrapperIsInaccessible(false)
72 scriptExecutionContext->createdMessagePort(this);
75 MessagePort::~MessagePort()
77 if (m_entangledPort)
78 unentangle();
80 if (m_scriptExecutionContext)
81 m_scriptExecutionContext->destroyedMessagePort(this);
84 PassRefPtr<MessagePort> MessagePort::clone(ScriptExecutionContext* newOwner, ExceptionCode& ec)
86 if (!m_entangledPort) {
87 ec = INVALID_STATE_ERR;
88 return 0;
91 RefPtr<MessagePort> remotePort = m_entangledPort;
92 RefPtr<MessagePort> newPort = MessagePort::create(newOwner);
94 // 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.
95 // 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.
96 RefPtr<Event> event;
97 while (m_messageQueue.tryGetMessage(event))
98 newPort->m_messageQueue.append(event);
100 entangle(remotePort.get(), newPort.get()); // The port object will be unentangled.
101 return newPort;
104 void MessagePort::postMessage(const String& message, ExceptionCode& ec)
106 postMessage(message, 0, ec);
109 void MessagePort::postMessage(const String& message, MessagePort* dataPort, ExceptionCode& ec)
111 if (!m_entangledPort || !m_scriptExecutionContext || !m_entangledPort->m_scriptExecutionContext)
112 return;
114 RefPtr<MessagePort> newMessagePort;
115 if (dataPort) {
116 if (dataPort == this || dataPort == m_entangledPort) {
117 ec = INVALID_ACCESS_ERR;
118 return;
120 newMessagePort = dataPort->clone(m_entangledPort->m_scriptExecutionContext, ec);
121 if (ec)
122 return;
125 DOMWindow* window = (m_scriptExecutionContext->isDocument() && m_entangledPort->m_scriptExecutionContext->isDocument()) ?
126 static_cast<Document*>(m_scriptExecutionContext)->domWindow() : 0;
127 m_entangledPort->m_messageQueue.append(MessageEvent::create(message, "", "", window, newMessagePort.get()));
128 if (m_entangledPort->m_queueIsOpen)
129 m_entangledPort->m_scriptExecutionContext->processMessagePortMessagesSoon();
132 PassRefPtr<MessagePort> MessagePort::startConversation(ScriptExecutionContext* scriptExecutionContext, const String& message)
134 RefPtr<MessagePort> port1 = MessagePort::create(scriptExecutionContext);
135 if (!m_entangledPort || !m_scriptExecutionContext || !m_entangledPort->m_scriptExecutionContext)
136 return port1;
137 RefPtr<MessagePort> port2 = MessagePort::create(m_entangledPort->m_scriptExecutionContext);
139 entangle(port1.get(), port2.get());
141 DOMWindow* window = (m_scriptExecutionContext->isDocument() && m_entangledPort->m_scriptExecutionContext->isDocument()) ?
142 static_cast<Document*>(m_scriptExecutionContext)->domWindow() : 0;
143 m_entangledPort->m_messageQueue.append(MessageEvent::create(message, "", "", window, port2.get()));
144 if (m_entangledPort->m_queueIsOpen)
145 m_entangledPort->m_scriptExecutionContext->processMessagePortMessagesSoon();
146 return port1;
149 void MessagePort::start()
151 if (m_queueIsOpen || !m_scriptExecutionContext)
152 return;
154 m_queueIsOpen = true;
155 m_scriptExecutionContext->processMessagePortMessagesSoon();
158 void MessagePort::close()
160 if (!m_entangledPort)
161 return;
163 MessagePort* otherPort = m_entangledPort;
164 unentangle();
166 queueCloseEvent();
167 otherPort->queueCloseEvent();
170 void MessagePort::entangle(MessagePort* port1, MessagePort* port2)
172 if (port1->m_entangledPort) {
173 ASSERT(port1->m_entangledPort != port2);
174 port1->unentangle();
177 if (port2->m_entangledPort) {
178 ASSERT(port2->m_entangledPort != port1);
179 port2->unentangle();
182 port1->m_entangledPort = port2;
183 port2->m_entangledPort = port1;
186 void MessagePort::unentangle()
188 ASSERT(this == m_entangledPort->m_entangledPort);
190 m_entangledPort->m_entangledPort = 0;
191 m_entangledPort = 0;
194 void MessagePort::contextDestroyed()
196 if (m_entangledPort) {
197 RefPtr<MessagePort> survivingPort = m_entangledPort;
198 unentangle();
199 if (survivingPort->m_scriptExecutionContext != m_scriptExecutionContext) // Otherwise, survivingPort won't really survive.
200 survivingPort->queueCloseEvent();
202 m_scriptExecutionContext = 0;
205 void MessagePort::dispatchMessages()
207 // Messages for contexts that are not fully active get dispatched too, but JSAbstractEventListener::handleEvent() doesn't call handlers for these.
208 ASSERT(queueIsOpen());
210 RefPtr<Event> evt;
211 while (m_messageQueue.tryGetMessage(evt)) {
213 ASSERT(evt->type() == eventNames().messageEvent);
215 if (m_onMessageListener) {
216 evt->setTarget(this);
217 evt->setCurrentTarget(this);
218 m_onMessageListener->handleEvent(evt.get(), false);
221 ExceptionCode ec = 0;
222 dispatchEvent(evt.release(), ec);
223 ASSERT(!ec);
227 void MessagePort::queueCloseEvent()
229 ASSERT(!m_pendingCloseEvent);
230 m_pendingCloseEvent = true;
232 CloseMessagePortTimer* timer = new CloseMessagePortTimer(this);
233 timer->startOneShot(0);
236 void MessagePort::dispatchCloseEvent()
238 ASSERT(m_pendingCloseEvent);
239 m_pendingCloseEvent = false;
241 RefPtr<Event> evt = Event::create(eventNames().closeEvent, false, true);
242 if (m_onCloseListener) {
243 evt->setTarget(this);
244 evt->setCurrentTarget(this);
245 m_onCloseListener->handleEvent(evt.get(), false);
248 ExceptionCode ec = 0;
249 dispatchEvent(evt.release(), ec);
250 ASSERT(!ec);
253 void MessagePort::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool)
255 EventListenersMap::iterator iter = m_eventListeners.find(eventType);
256 if (iter == m_eventListeners.end()) {
257 ListenerVector listeners;
258 listeners.append(eventListener);
259 m_eventListeners.add(eventType, listeners);
260 } else {
261 ListenerVector& listeners = iter->second;
262 for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
263 if (*listenerIter == eventListener)
264 return;
267 listeners.append(eventListener);
268 m_eventListeners.add(eventType, listeners);
272 void MessagePort::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool useCapture)
274 EventListenersMap::iterator iter = m_eventListeners.find(eventType);
275 if (iter == m_eventListeners.end())
276 return;
278 ListenerVector& listeners = iter->second;
279 for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
280 if (*listenerIter == eventListener) {
281 listeners.remove(listenerIter - listeners.begin());
282 return;
287 bool MessagePort::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec)
289 if (event->type().isEmpty()) {
290 ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR;
291 return true;
294 ListenerVector listenersCopy = m_eventListeners.get(event->type());
295 for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) {
296 event->setTarget(this);
297 event->setCurrentTarget(this);
298 listenerIter->get()->handleEvent(event.get(), false);
301 return !event->defaultPrevented();
304 bool MessagePort::hasPendingActivity()
306 // We only care about the result of this function when there is no entangled port, or it is inaccessible, so no more messages can be added to the queue.
307 // Thus, using MessageQueue::isEmpty() does not cause a race condition here.
308 return m_pendingCloseEvent || (m_queueIsOpen && !m_messageQueue.isEmpty());
311 } // namespace WebCore