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 "core/dom/MessagePort.h" 29 30 #include "bindings/v8/ExceptionState.h" 31 #include "bindings/v8/ExceptionStatePlaceholder.h" 32 #include "bindings/v8/SerializedScriptValue.h" 33 #include "core/dom/Document.h" 34 #include "core/dom/ExceptionCode.h" 35 #include "core/dom/ExecutionContext.h" 36 #include "core/events/MessageEvent.h" 37 #include "core/frame/LocalDOMWindow.h" 38 #include "core/workers/WorkerGlobalScope.h" 39 #include "public/platform/WebString.h" 40 #include "wtf/Functional.h" 41 #include "wtf/text/AtomicString.h" 42 43 namespace WebCore { 44 45 PassRefPtrWillBeRawPtr<MessagePort> MessagePort::create(ExecutionContext& executionContext) 46 { 47 RefPtrWillBeRawPtr<MessagePort> port = adoptRefWillBeRefCountedGarbageCollected(new MessagePort(executionContext)); 48 port->suspendIfNeeded(); 49 return port.release(); 50 } 51 52 MessagePort::MessagePort(ExecutionContext& executionContext) 53 : ActiveDOMObject(&executionContext) 54 , m_started(false) 55 , m_closed(false) 56 , m_weakFactory(this) 57 { 58 ScriptWrappable::init(this); 59 } 60 61 MessagePort::~MessagePort() 62 { 63 close(); 64 } 65 66 void MessagePort::postMessage(PassRefPtr<SerializedScriptValue> message, const MessagePortArray* ports, ExceptionState& exceptionState) 67 { 68 if (!isEntangled()) 69 return; 70 ASSERT(executionContext()); 71 ASSERT(m_entangledChannel); 72 73 OwnPtr<MessagePortChannelArray> channels; 74 // Make sure we aren't connected to any of the passed-in ports. 75 if (ports) { 76 for (unsigned i = 0; i < ports->size(); ++i) { 77 MessagePort* dataPort = (*ports)[i].get(); 78 if (dataPort == this) { 79 exceptionState.throwDOMException(DataCloneError, "Port at index " + String::number(i) + " contains the source port."); 80 return; 81 } 82 } 83 channels = MessagePort::disentanglePorts(ports, exceptionState); 84 if (exceptionState.hadException()) 85 return; 86 } 87 88 blink::WebString messageString = message->toWireString(); 89 OwnPtr<blink::WebMessagePortChannelArray> webChannels = toWebMessagePortChannelArray(channels.release()); 90 m_entangledChannel->postMessage(messageString, webChannels.leakPtr()); 91 } 92 93 // static 94 PassOwnPtr<blink::WebMessagePortChannelArray> MessagePort::toWebMessagePortChannelArray(PassOwnPtr<MessagePortChannelArray> channels) 95 { 96 OwnPtr<blink::WebMessagePortChannelArray> webChannels; 97 if (channels && channels->size()) { 98 webChannels = adoptPtr(new blink::WebMessagePortChannelArray(channels->size())); 99 for (size_t i = 0; i < channels->size(); ++i) 100 (*webChannels)[i] = (*channels)[i].leakPtr(); 101 } 102 return webChannels.release(); 103 } 104 105 // static 106 PassOwnPtr<MessagePortArray> MessagePort::toMessagePortArray(ExecutionContext* context, const blink::WebMessagePortChannelArray& webChannels) 107 { 108 OwnPtr<MessagePortArray> ports; 109 if (!webChannels.isEmpty()) { 110 OwnPtr<MessagePortChannelArray> channels = adoptPtr(new MessagePortChannelArray(webChannels.size())); 111 for (size_t i = 0; i < webChannels.size(); ++i) 112 (*channels)[i] = adoptPtr(webChannels[i]); 113 ports = MessagePort::entanglePorts(*context, channels.release()); 114 } 115 return ports.release(); 116 } 117 118 PassOwnPtr<blink::WebMessagePortChannel> MessagePort::disentangle() 119 { 120 ASSERT(m_entangledChannel); 121 m_entangledChannel->setClient(0); 122 return m_entangledChannel.release(); 123 } 124 125 // Invoked to notify us that there are messages available for this port. 126 // This code may be called from another thread, and so should not call any non-threadsafe APIs (i.e. should not call into the entangled channel or access mutable variables). 127 void MessagePort::messageAvailable() 128 { 129 ASSERT(executionContext()); 130 executionContext()->postTask(bind(&MessagePort::dispatchMessages, m_weakFactory.createWeakPtr())); 131 } 132 133 void MessagePort::start() 134 { 135 // Do nothing if we've been cloned or closed. 136 if (!isEntangled()) 137 return; 138 139 ASSERT(executionContext()); 140 if (m_started) 141 return; 142 143 m_started = true; 144 messageAvailable(); 145 } 146 147 void MessagePort::close() 148 { 149 if (isEntangled()) 150 m_entangledChannel->setClient(0); 151 m_closed = true; 152 } 153 154 void MessagePort::entangle(PassOwnPtr<blink::WebMessagePortChannel> remote) 155 { 156 // Only invoked to set our initial entanglement. 157 ASSERT(!m_entangledChannel); 158 ASSERT(executionContext()); 159 160 m_entangledChannel = remote; 161 m_entangledChannel->setClient(this); 162 } 163 164 const AtomicString& MessagePort::interfaceName() const 165 { 166 return EventTargetNames::MessagePort; 167 } 168 169 static bool tryGetMessageFrom(blink::WebMessagePortChannel& webChannel, RefPtr<SerializedScriptValue>& message, OwnPtr<MessagePortChannelArray>& channels) 170 { 171 blink::WebString messageString; 172 blink::WebMessagePortChannelArray webChannels; 173 if (!webChannel.tryGetMessage(&messageString, webChannels)) 174 return false; 175 176 if (webChannels.size()) { 177 channels = adoptPtr(new MessagePortChannelArray(webChannels.size())); 178 for (size_t i = 0; i < webChannels.size(); ++i) 179 (*channels)[i] = adoptPtr(webChannels[i]); 180 } 181 message = SerializedScriptValue::createFromWire(messageString); 182 return true; 183 } 184 185 void MessagePort::dispatchMessages() 186 { 187 // Messages for contexts that are not fully active get dispatched too, but JSAbstractEventListener::handleEvent() doesn't call handlers for these. 188 // The HTML5 spec specifies that any messages sent to a document that is not fully active should be dropped, so this behavior is OK. 189 if (!started()) 190 return; 191 192 RefPtr<SerializedScriptValue> message; 193 OwnPtr<MessagePortChannelArray> channels; 194 while (m_entangledChannel && tryGetMessageFrom(*m_entangledChannel, message, channels)) { 195 // close() in Worker onmessage handler should prevent next message from dispatching. 196 if (executionContext()->isWorkerGlobalScope() && toWorkerGlobalScope(executionContext())->isClosing()) 197 return; 198 199 OwnPtr<MessagePortArray> ports = MessagePort::entanglePorts(*executionContext(), channels.release()); 200 RefPtrWillBeRawPtr<Event> evt = MessageEvent::create(ports.release(), message.release()); 201 202 dispatchEvent(evt.release(), ASSERT_NO_EXCEPTION); 203 } 204 } 205 206 bool MessagePort::hasPendingActivity() const 207 { 208 // The spec says that entangled message ports should always be treated as if they have a strong reference. 209 // We'll also stipulate that the queue needs to be open (if the app drops its reference to the port before start()-ing it, then it's not really entangled as it's unreachable). 210 return m_started && isEntangled(); 211 } 212 213 PassOwnPtr<MessagePortChannelArray> MessagePort::disentanglePorts(const MessagePortArray* ports, ExceptionState& exceptionState) 214 { 215 if (!ports || !ports->size()) 216 return nullptr; 217 218 // HashSet used to efficiently check for duplicates in the passed-in array. 219 HashSet<MessagePort*> portSet; 220 221 // Walk the incoming array - if there are any duplicate ports, or null ports or cloned ports, throw an error (per section 8.3.3 of the HTML5 spec). 222 for (unsigned i = 0; i < ports->size(); ++i) { 223 MessagePort* port = (*ports)[i].get(); 224 if (!port || port->isNeutered() || portSet.contains(port)) { 225 String type; 226 if (!port) 227 type = "null"; 228 else if (port->isNeutered()) 229 type = "already neutered"; 230 else 231 type = "a duplicate"; 232 exceptionState.throwDOMException(DataCloneError, "Port at index " + String::number(i) + " is " + type + "."); 233 return nullptr; 234 } 235 portSet.add(port); 236 } 237 238 // Passed-in ports passed validity checks, so we can disentangle them. 239 OwnPtr<MessagePortChannelArray> portArray = adoptPtr(new MessagePortChannelArray(ports->size())); 240 for (unsigned i = 0; i < ports->size(); ++i) 241 (*portArray)[i] = (*ports)[i]->disentangle(); 242 return portArray.release(); 243 } 244 245 PassOwnPtr<MessagePortArray> MessagePort::entanglePorts(ExecutionContext& context, PassOwnPtr<MessagePortChannelArray> channels) 246 { 247 if (!channels || !channels->size()) 248 return nullptr; 249 250 OwnPtr<MessagePortArray> portArray = adoptPtr(new MessagePortArray(channels->size())); 251 for (unsigned i = 0; i < channels->size(); ++i) { 252 RefPtr<MessagePort> port = MessagePort::create(context); 253 port->entangle((*channels)[i].release()); 254 (*portArray)[i] = port.release(); 255 } 256 return portArray.release(); 257 } 258 259 } // namespace WebCore 260