2009-03-03 Mike Belshe <mike@belshe.com>
[WebKit-https.git] / WebCore / dom / MessagePort.cpp
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:
43     static PassRefPtr<MessagePortCloseEventTask> create(PassRefPtr<MessagePort> port)
44     {
45         return adoptRef(new MessagePortCloseEventTask(port));
46     }
47
48 private:
49     MessagePortCloseEventTask(PassRefPtr<MessagePort> port)
50         : m_port(port)
51     {
52         ASSERT(m_port);
53     }
54
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
70 PassRefPtr<MessagePort::EventData> MessagePort::EventData::create(const String& message, PassRefPtr<MessagePort> port)
71 {
72     return adoptRef(new EventData(message, port));
73 }
74
75 MessagePort::EventData::EventData(const String& message, PassRefPtr<MessagePort> messagePort)
76     : message(message.copy())
77     , messagePort(messagePort)
78 {
79 }
80
81 MessagePort::EventData::~EventData()
82 {
83 }
84
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
95 MessagePort::~MessagePort()
96 {
97     if (m_entangledPort)
98         unentangle();
99
100     if (m_scriptExecutionContext)
101         m_scriptExecutionContext->destroyedMessagePort(this);
102 }
103
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
124 void MessagePort::postMessage(const String& message, ExceptionCode& ec)
125 {
126     postMessage(message, 0, ec);
127 }
128
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         ec = 0;
141         newMessagePort = dataPort->clone(ec);
142         if (ec)
143             return;
144     }
145
146     m_entangledPort->m_messageQueue.append(EventData::create(message, newMessagePort));
147     if (m_entangledPort->m_queueIsOpen && m_entangledPort->m_scriptExecutionContext)
148         m_entangledPort->m_scriptExecutionContext->processMessagePortMessagesSoon();
149 }
150
151 PassRefPtr<MessagePort> MessagePort::startConversation(ScriptExecutionContext* scriptExecutionContext, const String& message)
152 {
153     RefPtr<MessagePort> port1 = MessagePort::create(scriptExecutionContext);
154     if (!m_entangledPort || !m_scriptExecutionContext)
155         return port1;
156     RefPtr<MessagePort> port2 = MessagePort::create(0);
157
158     entangle(port1.get(), port2.get());
159
160     m_entangledPort->m_messageQueue.append(EventData::create(message, port2));
161     if (m_entangledPort->m_queueIsOpen && m_entangledPort->m_scriptExecutionContext)
162         m_entangledPort->m_scriptExecutionContext->processMessagePortMessagesSoon();
163     return port1;
164 }
165
166 void MessagePort::start()
167 {
168     if (m_queueIsOpen || !m_scriptExecutionContext)
169         return;
170
171     m_queueIsOpen = true;
172     m_scriptExecutionContext->processMessagePortMessagesSoon();
173 }
174
175 void MessagePort::close()
176 {
177     if (!m_entangledPort)
178         return;
179
180     MessagePort* otherPort = m_entangledPort;
181     unentangle();
182
183     queueCloseEvent();
184     otherPort->queueCloseEvent();
185 }
186
187 void MessagePort::entangle(MessagePort* port1, MessagePort* port2)
188 {
189     if (port1->m_entangledPort) {
190         ASSERT(port1->m_entangledPort != port2);
191         port1->unentangle();
192     }
193
194     if (port2->m_entangledPort) {
195         ASSERT(port2->m_entangledPort != port1);
196         port2->unentangle();
197     }
198
199     port1->m_entangledPort = port2;
200     port2->m_entangledPort = port1;
201 }
202
203 void MessagePort::unentangle()
204 {
205     ASSERT(this == m_entangledPort->m_entangledPort);
206
207     m_entangledPort->m_entangledPort = 0;
208     m_entangledPort = 0;
209 }
210
211 void MessagePort::contextDestroyed()
212 {
213     ASSERT(m_scriptExecutionContext);
214
215     if (m_entangledPort)
216         unentangle();
217
218     m_scriptExecutionContext = 0;
219 }
220
221 void MessagePort::attachToContext(ScriptExecutionContext* scriptExecutionContext)
222 {
223     ASSERT(!m_scriptExecutionContext);
224     ASSERT(!m_queueIsOpen);
225
226     m_scriptExecutionContext = scriptExecutionContext;
227     m_scriptExecutionContext->createdMessagePort(this);
228     
229     // FIXME: Need to call processMessagePortMessagesSoon()?
230 }
231
232 ScriptExecutionContext* MessagePort::scriptExecutionContext() const
233 {
234     return m_scriptExecutionContext;
235 }
236
237 void MessagePort::dispatchMessages()
238 {
239     // Messages for contexts that are not fully active get dispatched too, but JSAbstractEventListener::handleEvent() doesn't call handlers for these.
240     // 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.
241     ASSERT(queueIsOpen());
242
243     RefPtr<EventData> eventData;
244     while (m_messageQueue.tryGetMessage(eventData)) {
245
246         ASSERT(!eventData->messagePort || !eventData->messagePort->m_scriptExecutionContext);
247         if (eventData->messagePort)
248             eventData->messagePort->attachToContext(m_scriptExecutionContext);
249
250         RefPtr<Event> evt = MessageEvent::create(eventData->message, "", "", 0, eventData->messagePort);
251
252         if (m_onMessageListener) {
253             evt->setTarget(this);
254             evt->setCurrentTarget(this);
255             m_onMessageListener->handleEvent(evt.get(), false);
256         }
257
258         ExceptionCode ec = 0;
259         dispatchEvent(evt.release(), ec);
260         ASSERT(!ec);
261     }
262 }
263
264 void MessagePort::queueCloseEvent()
265 {
266     ASSERT(!m_pendingCloseEvent);
267     m_pendingCloseEvent = true;
268
269     m_scriptExecutionContext->postTask(MessagePortCloseEventTask::create(this));
270 }
271
272 void MessagePort::dispatchCloseEvent()
273 {
274     ASSERT(m_pendingCloseEvent);
275     m_pendingCloseEvent = false;
276
277     RefPtr<Event> evt = Event::create(eventNames().closeEvent, false, true);
278     if (m_onCloseListener) {
279         evt->setTarget(this);
280         evt->setCurrentTarget(this);
281         m_onCloseListener->handleEvent(evt.get(), false);
282     }
283
284     ExceptionCode ec = 0;
285     dispatchEvent(evt.release(), ec);
286     ASSERT(!ec);
287 }
288
289 void MessagePort::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool)
290 {
291     EventListenersMap::iterator iter = m_eventListeners.find(eventType);
292     if (iter == m_eventListeners.end()) {
293         ListenerVector listeners;
294         listeners.append(eventListener);
295         m_eventListeners.add(eventType, listeners);
296     } else {
297         ListenerVector& listeners = iter->second;
298         for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
299             if (*listenerIter == eventListener)
300                 return;
301         }
302         
303         listeners.append(eventListener);
304         m_eventListeners.add(eventType, listeners);
305     }    
306 }
307
308 void MessagePort::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool)
309 {
310     EventListenersMap::iterator iter = m_eventListeners.find(eventType);
311     if (iter == m_eventListeners.end())
312         return;
313     
314     ListenerVector& listeners = iter->second;
315     for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
316         if (*listenerIter == eventListener) {
317             listeners.remove(listenerIter - listeners.begin());
318             return;
319         }
320     }
321 }
322
323 bool MessagePort::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec)
324 {
325     if (!event || event->type().isEmpty()) {
326         ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR;
327         return true;
328     }
329     
330     ListenerVector listenersCopy = m_eventListeners.get(event->type());
331     for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) {
332         event->setTarget(this);
333         event->setCurrentTarget(this);
334         listenerIter->get()->handleEvent(event.get(), false);
335     }
336     
337     return !event->defaultPrevented();
338 }
339
340 bool MessagePort::hasPendingActivity()
341 {
342     return m_pendingCloseEvent || (m_queueIsOpen && !m_messageQueue.isEmpty());
343 }
344
345 } // namespace WebCore