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
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.
28 #include "MessagePort.h"
30 #include "AtomicString.h"
31 #include "DOMWindow.h"
33 #include "EventException.h"
34 #include "EventNames.h"
35 #include "MessageEvent.h"
36 #include "SecurityOrigin.h"
41 class CloseMessagePortTimer
: public TimerBase
{
43 CloseMessagePortTimer(PassRefPtr
<MessagePort
> port
)
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();
62 RefPtr
<MessagePort
> m_port
;
65 MessagePort::MessagePort(ScriptExecutionContext
* scriptExecutionContext
)
67 , m_queueIsOpen(false)
68 , m_scriptExecutionContext(scriptExecutionContext
)
69 , m_pendingCloseEvent(false)
70 , m_jsWrapperIsInaccessible(false)
72 scriptExecutionContext
->createdMessagePort(this);
75 MessagePort::~MessagePort()
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
;
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.
97 while (m_messageQueue
.tryGetMessage(event
))
98 newPort
->m_messageQueue
.append(event
);
100 entangle(remotePort
.get(), newPort
.get()); // The port object will be unentangled.
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
)
114 RefPtr
<MessagePort
> newMessagePort
;
116 if (dataPort
== this || dataPort
== m_entangledPort
) {
117 ec
= INVALID_ACCESS_ERR
;
120 newMessagePort
= dataPort
->clone(m_entangledPort
->m_scriptExecutionContext
, ec
);
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
)
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();
149 void MessagePort::start()
151 if (m_queueIsOpen
|| !m_scriptExecutionContext
)
154 m_queueIsOpen
= true;
155 m_scriptExecutionContext
->processMessagePortMessagesSoon();
158 void MessagePort::close()
160 if (!m_entangledPort
)
163 MessagePort
* otherPort
= m_entangledPort
;
167 otherPort
->queueCloseEvent();
170 void MessagePort::entangle(MessagePort
* port1
, MessagePort
* port2
)
172 if (port1
->m_entangledPort
) {
173 ASSERT(port1
->m_entangledPort
!= port2
);
177 if (port2
->m_entangledPort
) {
178 ASSERT(port2
->m_entangledPort
!= port1
);
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;
194 void MessagePort::contextDestroyed()
196 if (m_entangledPort
) {
197 RefPtr
<MessagePort
> survivingPort
= m_entangledPort
;
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());
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
);
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
);
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
);
261 ListenerVector
& listeners
= iter
->second
;
262 for (ListenerVector::iterator listenerIter
= listeners
.begin(); listenerIter
!= listeners
.end(); ++listenerIter
) {
263 if (*listenerIter
== eventListener
)
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())
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());
287 bool MessagePort::dispatchEvent(PassRefPtr
<Event
> event
, ExceptionCode
& ec
)
289 if (event
->type().isEmpty()) {
290 ec
= EventException::UNSPECIFIED_EVENT_TYPE_ERR
;
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