0607beae6a9994043ff5ccf456228a018b311006
[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         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
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
165 void MessagePort::start()
166 {
167     if (m_queueIsOpen || !m_scriptExecutionContext)
168         return;
169
170     m_queueIsOpen = true;
171     m_scriptExecutionContext->processMessagePortMessagesSoon();
172 }
173
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
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
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
210 void MessagePort::contextDestroyed()
211 {
212     ASSERT(m_scriptExecutionContext);
213
214     if (m_entangledPort)
215         unentangle();
216
217     m_scriptExecutionContext = 0;
218 }
219
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
231 ScriptExecutionContext* MessagePort::scriptExecutionContext() const
232 {
233     return m_scriptExecutionContext;
234 }
235
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
263 void MessagePort::queueCloseEvent()
264 {
265     ASSERT(!m_pendingCloseEvent);
266     m_pendingCloseEvent = true;
267
268     m_scriptExecutionContext->postTask(MessagePortCloseEventTask::create(this));
269 }
270
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
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
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
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
339 bool MessagePort::hasPendingActivity()
340 {
341     return m_pendingCloseEvent || (m_queueIsOpen && !m_messageQueue.isEmpty());
342 }
343
344 } // namespace WebCore