• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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