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