Home | History | Annotate | Download | only in websockets
      1 /*
      2  * Copyright (C) 2011 Google 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 are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 
     33 #include "modules/websockets/DOMWebSocket.h"
     34 
     35 #include "bindings/core/v8/ExceptionState.h"
     36 #include "bindings/core/v8/ScriptController.h"
     37 #include "core/dom/Document.h"
     38 #include "core/dom/ExceptionCode.h"
     39 #include "core/dom/ExecutionContext.h"
     40 #include "core/events/MessageEvent.h"
     41 #include "core/fileapi/Blob.h"
     42 #include "core/frame/ConsoleTypes.h"
     43 #include "core/frame/LocalDOMWindow.h"
     44 #include "core/frame/LocalFrame.h"
     45 #include "core/frame/csp/ContentSecurityPolicy.h"
     46 #include "core/inspector/ConsoleMessage.h"
     47 #include "core/inspector/ScriptCallStack.h"
     48 #include "modules/websockets/CloseEvent.h"
     49 #include "platform/Logging.h"
     50 #include "platform/blob/BlobData.h"
     51 #include "platform/heap/Handle.h"
     52 #include "platform/weborigin/KnownPorts.h"
     53 #include "platform/weborigin/SecurityOrigin.h"
     54 #include "public/platform/Platform.h"
     55 #include "wtf/ArrayBuffer.h"
     56 #include "wtf/ArrayBufferView.h"
     57 #include "wtf/Assertions.h"
     58 #include "wtf/HashSet.h"
     59 #include "wtf/PassOwnPtr.h"
     60 #include "wtf/StdLibExtras.h"
     61 #include "wtf/text/CString.h"
     62 #include "wtf/text/StringBuilder.h"
     63 #include "wtf/text/WTFString.h"
     64 
     65 namespace blink {
     66 
     67 DOMWebSocket::EventQueue::EventQueue(EventTarget* target)
     68     : m_state(Active)
     69     , m_target(target)
     70     , m_resumeTimer(this, &EventQueue::resumeTimerFired) { }
     71 
     72 DOMWebSocket::EventQueue::~EventQueue() { stop(); }
     73 
     74 void DOMWebSocket::EventQueue::dispatch(PassRefPtrWillBeRawPtr<Event> event)
     75 {
     76     switch (m_state) {
     77     case Active:
     78         ASSERT(m_events.isEmpty());
     79         ASSERT(m_target->executionContext());
     80         m_target->dispatchEvent(event);
     81         break;
     82     case Suspended:
     83         m_events.append(event);
     84         break;
     85     case Stopped:
     86         ASSERT(m_events.isEmpty());
     87         // Do nothing.
     88         break;
     89     }
     90 }
     91 
     92 bool DOMWebSocket::EventQueue::isEmpty() const
     93 {
     94     return m_events.isEmpty();
     95 }
     96 
     97 void DOMWebSocket::EventQueue::suspend()
     98 {
     99     m_resumeTimer.stop();
    100     if (m_state != Active)
    101         return;
    102 
    103     m_state = Suspended;
    104 }
    105 
    106 void DOMWebSocket::EventQueue::resume()
    107 {
    108     if (m_state != Suspended || m_resumeTimer.isActive())
    109         return;
    110 
    111     m_resumeTimer.startOneShot(0, FROM_HERE);
    112 }
    113 
    114 void DOMWebSocket::EventQueue::stop()
    115 {
    116     if (m_state == Stopped)
    117         return;
    118 
    119     m_state = Stopped;
    120     m_resumeTimer.stop();
    121     m_events.clear();
    122 }
    123 
    124 void DOMWebSocket::EventQueue::dispatchQueuedEvents()
    125 {
    126     if (m_state != Active)
    127         return;
    128 
    129     WillBeHeapDeque<RefPtrWillBeMember<Event> > events;
    130     events.swap(m_events);
    131     while (!events.isEmpty()) {
    132         if (m_state == Stopped || m_state == Suspended)
    133             break;
    134         ASSERT(m_state == Active);
    135         ASSERT(m_target->executionContext());
    136         m_target->dispatchEvent(events.takeFirst());
    137         // |this| can be stopped here.
    138     }
    139     if (m_state == Suspended) {
    140         while (!m_events.isEmpty())
    141             events.append(m_events.takeFirst());
    142         events.swap(m_events);
    143     }
    144 }
    145 
    146 void DOMWebSocket::EventQueue::resumeTimerFired(Timer<EventQueue>*)
    147 {
    148     ASSERT(m_state == Suspended);
    149     m_state = Active;
    150     dispatchQueuedEvents();
    151 }
    152 
    153 void DOMWebSocket::EventQueue::trace(Visitor* visitor)
    154 {
    155     visitor->trace(m_events);
    156 }
    157 
    158 const size_t maxReasonSizeInBytes = 123;
    159 
    160 static inline bool isValidSubprotocolCharacter(UChar character)
    161 {
    162     const UChar minimumProtocolCharacter = '!'; // U+0021.
    163     const UChar maximumProtocolCharacter = '~'; // U+007E.
    164     // Set to true if character does not matches "separators" ABNF defined in
    165     // RFC2616. SP and HT are excluded since the range check excludes them.
    166     bool isNotSeparator = character != '"' && character != '(' && character != ')' && character != ',' && character != '/'
    167         && !(character >= ':' && character <= '@') // U+003A - U+0040 (':', ';', '<', '=', '>', '?', '@').
    168         && !(character >= '[' && character <= ']') // U+005B - U+005D ('[', '\\', ']').
    169         && character != '{' && character != '}';
    170     return character >= minimumProtocolCharacter && character <= maximumProtocolCharacter && isNotSeparator;
    171 }
    172 
    173 bool DOMWebSocket::isValidSubprotocolString(const String& protocol)
    174 {
    175     if (protocol.isEmpty())
    176         return false;
    177     for (size_t i = 0; i < protocol.length(); ++i) {
    178         if (!isValidSubprotocolCharacter(protocol[i]))
    179             return false;
    180     }
    181     return true;
    182 }
    183 
    184 static String encodeSubprotocolString(const String& protocol)
    185 {
    186     StringBuilder builder;
    187     for (size_t i = 0; i < protocol.length(); i++) {
    188         if (protocol[i] < 0x20 || protocol[i] > 0x7E)
    189             builder.append(String::format("\\u%04X", protocol[i]));
    190         else if (protocol[i] == 0x5c)
    191             builder.appendLiteral("\\\\");
    192         else
    193             builder.append(protocol[i]);
    194     }
    195     return builder.toString();
    196 }
    197 
    198 static String joinStrings(const Vector<String>& strings, const char* separator)
    199 {
    200     StringBuilder builder;
    201     for (size_t i = 0; i < strings.size(); ++i) {
    202         if (i)
    203             builder.append(separator);
    204         builder.append(strings[i]);
    205     }
    206     return builder.toString();
    207 }
    208 
    209 static unsigned long saturateAdd(unsigned long a, unsigned long b)
    210 {
    211     if (std::numeric_limits<unsigned long>::max() - a < b)
    212         return std::numeric_limits<unsigned long>::max();
    213     return a + b;
    214 }
    215 
    216 static void setInvalidStateErrorForSendMethod(ExceptionState& exceptionState)
    217 {
    218     exceptionState.throwDOMException(InvalidStateError, "Still in CONNECTING state.");
    219 }
    220 
    221 const char* DOMWebSocket::subprotocolSeperator()
    222 {
    223     return ", ";
    224 }
    225 
    226 DOMWebSocket::DOMWebSocket(ExecutionContext* context)
    227     : ActiveDOMObject(context)
    228     , m_state(CONNECTING)
    229     , m_bufferedAmount(0)
    230     , m_consumedBufferedAmount(0)
    231     , m_bufferedAmountAfterClose(0)
    232     , m_binaryType(BinaryTypeBlob)
    233     , m_subprotocol("")
    234     , m_extensions("")
    235     , m_eventQueue(EventQueue::create(this))
    236     , m_bufferedAmountConsumeTimer(this, &DOMWebSocket::reflectBufferedAmountConsumption)
    237 {
    238 }
    239 
    240 DOMWebSocket::~DOMWebSocket()
    241 {
    242     ASSERT(!m_channel);
    243 }
    244 
    245 void DOMWebSocket::logError(const String& message)
    246 {
    247     executionContext()->addConsoleMessage(ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, message));
    248 }
    249 
    250 DOMWebSocket* DOMWebSocket::create(ExecutionContext* context, const String& url, ExceptionState& exceptionState)
    251 {
    252     Vector<String> protocols;
    253     return create(context, url, protocols, exceptionState);
    254 }
    255 
    256 DOMWebSocket* DOMWebSocket::create(ExecutionContext* context, const String& url, const Vector<String>& protocols, ExceptionState& exceptionState)
    257 {
    258     if (url.isNull()) {
    259         exceptionState.throwDOMException(SyntaxError, "Failed to create a WebSocket: the provided URL is invalid.");
    260         return nullptr;
    261     }
    262 
    263     DOMWebSocket* webSocket(adoptRefCountedGarbageCollectedWillBeNoop(new DOMWebSocket(context)));
    264     webSocket->suspendIfNeeded();
    265 
    266     webSocket->connect(url, protocols, exceptionState);
    267     if (exceptionState.hadException())
    268         return nullptr;
    269 
    270     return webSocket;
    271 }
    272 
    273 DOMWebSocket* DOMWebSocket::create(ExecutionContext* context, const String& url, const String& protocol, ExceptionState& exceptionState)
    274 {
    275     Vector<String> protocols;
    276     protocols.append(protocol);
    277     return create(context, url, protocols, exceptionState);
    278 }
    279 
    280 void DOMWebSocket::connect(const String& url, const Vector<String>& protocols, ExceptionState& exceptionState)
    281 {
    282     WTF_LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data());
    283     m_url = KURL(KURL(), url);
    284 
    285     if (!m_url.isValid()) {
    286         m_state = CLOSED;
    287         exceptionState.throwDOMException(SyntaxError, "The URL '" + url + "' is invalid.");
    288         return;
    289     }
    290     if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) {
    291         m_state = CLOSED;
    292         exceptionState.throwDOMException(SyntaxError, "The URL's scheme must be either 'ws' or 'wss'. '" + m_url.protocol() + "' is not allowed.");
    293         return;
    294     }
    295 
    296     if (m_url.hasFragmentIdentifier()) {
    297         m_state = CLOSED;
    298         exceptionState.throwDOMException(SyntaxError, "The URL contains a fragment identifier ('" + m_url.fragmentIdentifier() + "'). Fragment identifiers are not allowed in WebSocket URLs.");
    299         return;
    300     }
    301     if (!portAllowed(m_url)) {
    302         m_state = CLOSED;
    303         exceptionState.throwSecurityError("The port " + String::number(m_url.port()) + " is not allowed.");
    304         return;
    305     }
    306 
    307     // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
    308     bool shouldBypassMainWorldCSP = false;
    309     if (executionContext()->isDocument()) {
    310         Document* document = toDocument(executionContext());
    311         shouldBypassMainWorldCSP = document->frame()->script().shouldBypassMainWorldCSP();
    312     }
    313     if (!shouldBypassMainWorldCSP && !executionContext()->contentSecurityPolicy()->allowConnectToSource(m_url)) {
    314         m_state = CLOSED;
    315         // The URL is safe to expose to JavaScript, as this check happens synchronously before redirection.
    316         exceptionState.throwSecurityError("Refused to connect to '" + m_url.elidedString() + "' because it violates the document's Content Security Policy.");
    317         return;
    318     }
    319 
    320     m_channel = createChannel(executionContext(), this);
    321 
    322     for (size_t i = 0; i < protocols.size(); ++i) {
    323         if (!isValidSubprotocolString(protocols[i])) {
    324             m_state = CLOSED;
    325             exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeSubprotocolString(protocols[i]) + "' is invalid.");
    326             releaseChannel();
    327             return;
    328         }
    329     }
    330     HashSet<String> visited;
    331     for (size_t i = 0; i < protocols.size(); ++i) {
    332         if (!visited.add(protocols[i]).isNewEntry) {
    333             m_state = CLOSED;
    334             exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeSubprotocolString(protocols[i]) + "' is duplicated.");
    335             releaseChannel();
    336             return;
    337         }
    338     }
    339 
    340     String protocolString;
    341     if (!protocols.isEmpty())
    342         protocolString = joinStrings(protocols, subprotocolSeperator());
    343 
    344     if (!m_channel->connect(m_url, protocolString)) {
    345         m_state = CLOSED;
    346         exceptionState.throwSecurityError("An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.");
    347         releaseChannel();
    348         return;
    349     }
    350 }
    351 
    352 void DOMWebSocket::updateBufferedAmountAfterClose(unsigned long payloadSize)
    353 {
    354     m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
    355     m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
    356 
    357     logError("WebSocket is already in CLOSING or CLOSED state.");
    358 }
    359 
    360 void DOMWebSocket::reflectBufferedAmountConsumption(Timer<DOMWebSocket>*)
    361 {
    362     ASSERT(m_bufferedAmount >= m_consumedBufferedAmount);
    363     WTF_LOG(Network, "WebSocket %p reflectBufferedAmountConsumption() %lu => %lu", this, m_bufferedAmount, m_bufferedAmount - m_consumedBufferedAmount);
    364 
    365     m_bufferedAmount -= m_consumedBufferedAmount;
    366     m_consumedBufferedAmount = 0;
    367 }
    368 
    369 void DOMWebSocket::releaseChannel()
    370 {
    371     ASSERT(m_channel);
    372     m_channel->disconnect();
    373     m_channel = nullptr;
    374 }
    375 
    376 void DOMWebSocket::send(const String& message, ExceptionState& exceptionState)
    377 {
    378     WTF_LOG(Network, "WebSocket %p send() Sending String '%s'", this, message.utf8().data());
    379     if (m_state == CONNECTING) {
    380         setInvalidStateErrorForSendMethod(exceptionState);
    381         return;
    382     }
    383     // No exception is raised if the connection was once established but has subsequently been closed.
    384     if (m_state == CLOSING || m_state == CLOSED) {
    385         updateBufferedAmountAfterClose(message.utf8().length());
    386         return;
    387     }
    388     Platform::current()->histogramEnumeration("WebCore.WebSocket.SendType", WebSocketSendTypeString, WebSocketSendTypeMax);
    389     ASSERT(m_channel);
    390     m_bufferedAmount += message.utf8().length();
    391     m_channel->send(message);
    392 }
    393 
    394 void DOMWebSocket::send(ArrayBuffer* binaryData, ExceptionState& exceptionState)
    395 {
    396     WTF_LOG(Network, "WebSocket %p send() Sending ArrayBuffer %p", this, binaryData);
    397     ASSERT(binaryData);
    398     if (m_state == CONNECTING) {
    399         setInvalidStateErrorForSendMethod(exceptionState);
    400         return;
    401     }
    402     if (m_state == CLOSING || m_state == CLOSED) {
    403         updateBufferedAmountAfterClose(binaryData->byteLength());
    404         return;
    405     }
    406     Platform::current()->histogramEnumeration("WebCore.WebSocket.SendType", WebSocketSendTypeArrayBuffer, WebSocketSendTypeMax);
    407     ASSERT(m_channel);
    408     m_bufferedAmount += binaryData->byteLength();
    409     m_channel->send(*binaryData, 0, binaryData->byteLength());
    410 }
    411 
    412 void DOMWebSocket::send(ArrayBufferView* arrayBufferView, ExceptionState& exceptionState)
    413 {
    414     WTF_LOG(Network, "WebSocket %p send() Sending ArrayBufferView %p", this, arrayBufferView);
    415     ASSERT(arrayBufferView);
    416     if (m_state == CONNECTING) {
    417         setInvalidStateErrorForSendMethod(exceptionState);
    418         return;
    419     }
    420     if (m_state == CLOSING || m_state == CLOSED) {
    421         updateBufferedAmountAfterClose(arrayBufferView->byteLength());
    422         return;
    423     }
    424     Platform::current()->histogramEnumeration("WebCore.WebSocket.SendType", WebSocketSendTypeArrayBufferView, WebSocketSendTypeMax);
    425     ASSERT(m_channel);
    426     m_bufferedAmount += arrayBufferView->byteLength();
    427     RefPtr<ArrayBuffer> arrayBuffer(arrayBufferView->buffer());
    428     m_channel->send(*arrayBuffer, arrayBufferView->byteOffset(), arrayBufferView->byteLength());
    429 }
    430 
    431 void DOMWebSocket::send(Blob* binaryData, ExceptionState& exceptionState)
    432 {
    433     WTF_LOG(Network, "WebSocket %p send() Sending Blob '%s'", this, binaryData->uuid().utf8().data());
    434     ASSERT(binaryData);
    435     if (m_state == CONNECTING) {
    436         setInvalidStateErrorForSendMethod(exceptionState);
    437         return;
    438     }
    439     if (m_state == CLOSING || m_state == CLOSED) {
    440         updateBufferedAmountAfterClose(static_cast<unsigned long>(binaryData->size()));
    441         return;
    442     }
    443     Platform::current()->histogramEnumeration("WebCore.WebSocket.SendType", WebSocketSendTypeBlob, WebSocketSendTypeMax);
    444     m_bufferedAmount += binaryData->size();
    445     ASSERT(m_channel);
    446     m_channel->send(binaryData->blobDataHandle());
    447 }
    448 
    449 void DOMWebSocket::close(unsigned short code, const String& reason, ExceptionState& exceptionState)
    450 {
    451     closeInternal(code, reason, exceptionState);
    452 }
    453 
    454 void DOMWebSocket::close(ExceptionState& exceptionState)
    455 {
    456     closeInternal(WebSocketChannel::CloseEventCodeNotSpecified, String(), exceptionState);
    457 }
    458 
    459 void DOMWebSocket::close(unsigned short code, ExceptionState& exceptionState)
    460 {
    461     closeInternal(code, String(), exceptionState);
    462 }
    463 
    464 void DOMWebSocket::closeInternal(int code, const String& reason, ExceptionState& exceptionState)
    465 {
    466     if (code == WebSocketChannel::CloseEventCodeNotSpecified) {
    467         WTF_LOG(Network, "WebSocket %p close() without code and reason", this);
    468     } else {
    469         WTF_LOG(Network, "WebSocket %p close() code=%d reason='%s'", this, code, reason.utf8().data());
    470         if (!(code == WebSocketChannel::CloseEventCodeNormalClosure || (WebSocketChannel::CloseEventCodeMinimumUserDefined <= code && code <= WebSocketChannel::CloseEventCodeMaximumUserDefined))) {
    471             exceptionState.throwDOMException(InvalidAccessError, "The code must be either 1000, or between 3000 and 4999. " + String::number(code) + " is neither.");
    472             return;
    473         }
    474         CString utf8 = reason.utf8(StrictUTF8ConversionReplacingUnpairedSurrogatesWithFFFD);
    475         if (utf8.length() > maxReasonSizeInBytes) {
    476             exceptionState.throwDOMException(SyntaxError, "The message must not be greater than " + String::number(maxReasonSizeInBytes) + " bytes.");
    477             return;
    478         }
    479     }
    480 
    481     if (m_state == CLOSING || m_state == CLOSED)
    482         return;
    483     if (m_state == CONNECTING) {
    484         m_state = CLOSING;
    485         m_channel->fail("WebSocket is closed before the connection is established.", WarningMessageLevel, String(), 0);
    486         return;
    487     }
    488     m_state = CLOSING;
    489     if (m_channel)
    490         m_channel->close(code, reason);
    491 }
    492 
    493 const KURL& DOMWebSocket::url() const
    494 {
    495     return m_url;
    496 }
    497 
    498 DOMWebSocket::State DOMWebSocket::readyState() const
    499 {
    500     return m_state;
    501 }
    502 
    503 unsigned long DOMWebSocket::bufferedAmount() const
    504 {
    505     return saturateAdd(m_bufferedAmount, m_bufferedAmountAfterClose);
    506 }
    507 
    508 String DOMWebSocket::protocol() const
    509 {
    510     return m_subprotocol;
    511 }
    512 
    513 String DOMWebSocket::extensions() const
    514 {
    515     return m_extensions;
    516 }
    517 
    518 String DOMWebSocket::binaryType() const
    519 {
    520     switch (m_binaryType) {
    521     case BinaryTypeBlob:
    522         return "blob";
    523     case BinaryTypeArrayBuffer:
    524         return "arraybuffer";
    525     }
    526     ASSERT_NOT_REACHED();
    527     return String();
    528 }
    529 
    530 void DOMWebSocket::setBinaryType(const String& binaryType)
    531 {
    532     if (binaryType == "blob") {
    533         m_binaryType = BinaryTypeBlob;
    534         return;
    535     }
    536     if (binaryType == "arraybuffer") {
    537         m_binaryType = BinaryTypeArrayBuffer;
    538         return;
    539     }
    540     logError("'" + binaryType + "' is not a valid value for binaryType; binaryType remains unchanged.");
    541 }
    542 
    543 const AtomicString& DOMWebSocket::interfaceName() const
    544 {
    545     return EventTargetNames::DOMWebSocket;
    546 }
    547 
    548 ExecutionContext* DOMWebSocket::executionContext() const
    549 {
    550     return ActiveDOMObject::executionContext();
    551 }
    552 
    553 void DOMWebSocket::contextDestroyed()
    554 {
    555     WTF_LOG(Network, "WebSocket %p contextDestroyed()", this);
    556     ASSERT(!m_channel);
    557     ASSERT(m_state == CLOSED);
    558     ActiveDOMObject::contextDestroyed();
    559 }
    560 
    561 bool DOMWebSocket::hasPendingActivity() const
    562 {
    563     return m_channel || !m_eventQueue->isEmpty();
    564 }
    565 
    566 void DOMWebSocket::suspend()
    567 {
    568     if (m_channel)
    569         m_channel->suspend();
    570     m_eventQueue->suspend();
    571 }
    572 
    573 void DOMWebSocket::resume()
    574 {
    575     if (m_channel)
    576         m_channel->resume();
    577     m_eventQueue->resume();
    578 }
    579 
    580 void DOMWebSocket::stop()
    581 {
    582     m_eventQueue->stop();
    583     if (m_channel) {
    584         m_channel->close(WebSocketChannel::CloseEventCodeGoingAway, String());
    585         releaseChannel();
    586     }
    587     m_state = CLOSED;
    588 }
    589 
    590 void DOMWebSocket::didConnect(const String& subprotocol, const String& extensions)
    591 {
    592     WTF_LOG(Network, "WebSocket %p didConnect()", this);
    593     if (m_state != CONNECTING)
    594         return;
    595     m_state = OPEN;
    596     m_subprotocol = subprotocol;
    597     m_extensions = extensions;
    598     m_eventQueue->dispatch(Event::create(EventTypeNames::open));
    599 }
    600 
    601 void DOMWebSocket::didReceiveMessage(const String& msg)
    602 {
    603     WTF_LOG(Network, "WebSocket %p didReceiveMessage() Text message '%s'", this, msg.utf8().data());
    604     if (m_state != OPEN)
    605         return;
    606     m_eventQueue->dispatch(MessageEvent::create(msg, SecurityOrigin::create(m_url)->toString()));
    607 }
    608 
    609 void DOMWebSocket::didReceiveBinaryData(PassOwnPtr<Vector<char> > binaryData)
    610 {
    611     WTF_LOG(Network, "WebSocket %p didReceiveBinaryData() %lu byte binary message", this, static_cast<unsigned long>(binaryData->size()));
    612     switch (m_binaryType) {
    613     case BinaryTypeBlob: {
    614         size_t size = binaryData->size();
    615         RefPtr<RawData> rawData = RawData::create();
    616         binaryData->swap(*rawData->mutableData());
    617         OwnPtr<BlobData> blobData = BlobData::create();
    618         blobData->appendData(rawData.release(), 0, BlobDataItem::toEndOfFile);
    619         RefPtrWillBeRawPtr<Blob> blob = Blob::create(BlobDataHandle::create(blobData.release(), size));
    620         m_eventQueue->dispatch(MessageEvent::create(blob.release(), SecurityOrigin::create(m_url)->toString()));
    621         break;
    622     }
    623 
    624     case BinaryTypeArrayBuffer:
    625         RefPtr<ArrayBuffer> arrayBuffer = ArrayBuffer::create(binaryData->data(), binaryData->size());
    626         if (!arrayBuffer) {
    627             // Failed to allocate an ArrayBuffer. We need to crash the renderer
    628             // since there's no way defined in the spec to tell this to the
    629             // user.
    630             CRASH();
    631         }
    632         m_eventQueue->dispatch(MessageEvent::create(arrayBuffer.release(), SecurityOrigin::create(m_url)->toString()));
    633         break;
    634     }
    635 }
    636 
    637 void DOMWebSocket::didReceiveMessageError()
    638 {
    639     WTF_LOG(Network, "WebSocket %p didReceiveMessageError()", this);
    640     m_state = CLOSED;
    641     m_eventQueue->dispatch(Event::create(EventTypeNames::error));
    642 }
    643 
    644 void DOMWebSocket::didConsumeBufferedAmount(unsigned long consumed)
    645 {
    646     ASSERT(m_bufferedAmount >= consumed);
    647     WTF_LOG(Network, "WebSocket %p didConsumeBufferedAmount(%lu)", this, consumed);
    648     if (m_state == CLOSED)
    649         return;
    650     m_consumedBufferedAmount += consumed;
    651     if (!m_bufferedAmountConsumeTimer.isActive())
    652         m_bufferedAmountConsumeTimer.startOneShot(0, FROM_HERE);
    653 }
    654 
    655 void DOMWebSocket::didStartClosingHandshake()
    656 {
    657     WTF_LOG(Network, "WebSocket %p didStartClosingHandshake()", this);
    658     m_state = CLOSING;
    659 }
    660 
    661 void DOMWebSocket::didClose(ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason)
    662 {
    663     WTF_LOG(Network, "WebSocket %p didClose()", this);
    664     if (!m_channel)
    665         return;
    666     bool hasAllDataConsumed = m_bufferedAmount == m_consumedBufferedAmount;
    667     bool wasClean = m_state == CLOSING && hasAllDataConsumed && closingHandshakeCompletion == ClosingHandshakeComplete && code != WebSocketChannel::CloseEventCodeAbnormalClosure;
    668     m_state = CLOSED;
    669 
    670     m_eventQueue->dispatch(CloseEvent::create(wasClean, code, reason));
    671     releaseChannel();
    672 }
    673 
    674 size_t DOMWebSocket::getFramingOverhead(size_t payloadSize)
    675 {
    676     static const size_t hybiBaseFramingOverhead = 2; // Every frame has at least two-byte header.
    677     static const size_t hybiMaskingKeyLength = 4; // Every frame from client must have masking key.
    678     static const size_t minimumPayloadSizeWithTwoByteExtendedPayloadLength = 126;
    679     static const size_t minimumPayloadSizeWithEightByteExtendedPayloadLength = 0x10000;
    680     size_t overhead = hybiBaseFramingOverhead + hybiMaskingKeyLength;
    681     if (payloadSize >= minimumPayloadSizeWithEightByteExtendedPayloadLength)
    682         overhead += 8;
    683     else if (payloadSize >= minimumPayloadSizeWithTwoByteExtendedPayloadLength)
    684         overhead += 2;
    685     return overhead;
    686 }
    687 
    688 void DOMWebSocket::trace(Visitor* visitor)
    689 {
    690     visitor->trace(m_channel);
    691     visitor->trace(m_eventQueue);
    692     WebSocketChannelClient::trace(visitor);
    693     EventTargetWithInlineData::trace(visitor);
    694 }
    695 
    696 } // namespace blink
    697