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/WebSocket.h"
34
35#include "bindings/v8/ExceptionState.h"
36#include "bindings/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/Event.h"
41#include "core/events/MessageEvent.h"
42#include "core/fileapi/Blob.h"
43#include "core/frame/ConsoleTypes.h"
44#include "core/frame/ContentSecurityPolicy.h"
45#include "core/frame/DOMWindow.h"
46#include "core/frame/Frame.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/weborigin/KnownPorts.h"
52#include "platform/weborigin/SecurityOrigin.h"
53#include "wtf/ArrayBuffer.h"
54#include "wtf/ArrayBufferView.h"
55#include "wtf/HashSet.h"
56#include "wtf/PassOwnPtr.h"
57#include "wtf/StdLibExtras.h"
58#include "wtf/text/CString.h"
59#include "wtf/text/StringBuilder.h"
60#include "wtf/text/WTFString.h"
61
62using namespace std;
63
64namespace WebCore {
65
66WebSocket::EventQueue::EventQueue(EventTarget* target)
67    : m_state(Active)
68    , m_target(target)
69    , m_resumeTimer(this, &EventQueue::resumeTimerFired) { }
70
71WebSocket::EventQueue::~EventQueue() { stop(); }
72
73void WebSocket::EventQueue::dispatch(PassRefPtr<Event> event)
74{
75    switch (m_state) {
76    case Active:
77        ASSERT(m_events.isEmpty());
78        ASSERT(m_target->executionContext());
79        m_target->dispatchEvent(event);
80        break;
81    case Suspended:
82        m_events.append(event);
83        break;
84    case Stopped:
85        ASSERT(m_events.isEmpty());
86        // Do nothing.
87        break;
88    }
89}
90
91void WebSocket::EventQueue::suspend()
92{
93    if (m_state != Active)
94        return;
95
96    m_state = Suspended;
97}
98
99void WebSocket::EventQueue::resume()
100{
101    if (m_state != Suspended || m_resumeTimer.isActive())
102        return;
103
104    m_resumeTimer.startOneShot(0);
105}
106
107void WebSocket::EventQueue::stop()
108{
109    if (m_state == Stopped)
110        return;
111
112    m_state = Stopped;
113    m_resumeTimer.stop();
114    m_events.clear();
115}
116
117void WebSocket::EventQueue::dispatchQueuedEvents()
118{
119    if (m_state != Active)
120        return;
121
122    RefPtr<EventQueue> protect(this);
123
124    Deque<RefPtr<Event> > events;
125    events.swap(m_events);
126    while (!events.isEmpty()) {
127        if (m_state == Stopped || m_state == Suspended)
128            break;
129        ASSERT(m_state == Active);
130        ASSERT(m_target->executionContext());
131        m_target->dispatchEvent(events.takeFirst());
132        // |this| can be stopped here.
133    }
134    if (m_state == Suspended) {
135        while (!m_events.isEmpty())
136            events.append(m_events.takeFirst());
137        events.swap(m_events);
138    }
139}
140
141void WebSocket::EventQueue::resumeTimerFired(Timer<EventQueue>*)
142{
143    ASSERT(m_state == Suspended);
144    m_state = Active;
145    dispatchQueuedEvents();
146}
147
148const size_t maxReasonSizeInBytes = 123;
149
150static inline bool isValidProtocolCharacter(UChar character)
151{
152    // Hybi-10 says "(Subprotocol string must consist of) characters in the range U+0021 to U+007E not including
153    // separator characters as defined in [RFC2616]."
154    const UChar minimumProtocolCharacter = '!'; // U+0021.
155    const UChar maximumProtocolCharacter = '~'; // U+007E.
156    return character >= minimumProtocolCharacter && character <= maximumProtocolCharacter
157        && character != '"' && character != '(' && character != ')' && character != ',' && character != '/'
158        && !(character >= ':' && character <= '@') // U+003A - U+0040 (':', ';', '<', '=', '>', '?', '@').
159        && !(character >= '[' && character <= ']') // U+005B - U+005D ('[', '\\', ']').
160        && character != '{' && character != '}';
161}
162
163static bool isValidProtocolString(const String& protocol)
164{
165    if (protocol.isEmpty())
166        return false;
167    for (size_t i = 0; i < protocol.length(); ++i) {
168        if (!isValidProtocolCharacter(protocol[i]))
169            return false;
170    }
171    return true;
172}
173
174static String encodeProtocolString(const String& protocol)
175{
176    StringBuilder builder;
177    for (size_t i = 0; i < protocol.length(); i++) {
178        if (protocol[i] < 0x20 || protocol[i] > 0x7E)
179            builder.append(String::format("\\u%04X", protocol[i]));
180        else if (protocol[i] == 0x5c)
181            builder.append("\\\\");
182        else
183            builder.append(protocol[i]);
184    }
185    return builder.toString();
186}
187
188static String joinStrings(const Vector<String>& strings, const char* separator)
189{
190    StringBuilder builder;
191    for (size_t i = 0; i < strings.size(); ++i) {
192        if (i)
193            builder.append(separator);
194        builder.append(strings[i]);
195    }
196    return builder.toString();
197}
198
199static unsigned long saturateAdd(unsigned long a, unsigned long b)
200{
201    if (numeric_limits<unsigned long>::max() - a < b)
202        return numeric_limits<unsigned long>::max();
203    return a + b;
204}
205
206static void setInvalidStateErrorForSendMethod(ExceptionState& exceptionState)
207{
208    exceptionState.throwDOMException(InvalidStateError, "Still in CONNECTING state.");
209}
210
211const char* WebSocket::subProtocolSeperator()
212{
213    return ", ";
214}
215
216WebSocket::WebSocket(ExecutionContext* context)
217    : ActiveDOMObject(context)
218    , m_state(CONNECTING)
219    , m_bufferedAmount(0)
220    , m_bufferedAmountAfterClose(0)
221    , m_binaryType(BinaryTypeBlob)
222    , m_subprotocol("")
223    , m_extensions("")
224    , m_eventQueue(EventQueue::create(this))
225{
226    ScriptWrappable::init(this);
227}
228
229WebSocket::~WebSocket()
230{
231    if (m_channel)
232        m_channel->disconnect();
233}
234
235void WebSocket::logError(const String& message)
236{
237    executionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, message);
238}
239
240PassRefPtr<WebSocket> WebSocket::create(ExecutionContext* context, const String& url, ExceptionState& exceptionState)
241{
242    Vector<String> protocols;
243    return create(context, url, protocols, exceptionState);
244}
245
246PassRefPtr<WebSocket> WebSocket::create(ExecutionContext* context, const String& url, const Vector<String>& protocols, ExceptionState& exceptionState)
247{
248    if (url.isNull()) {
249        exceptionState.throwDOMException(SyntaxError, "Failed to create a WebSocket: the provided URL is invalid.");
250        return 0;
251    }
252
253    RefPtr<WebSocket> webSocket(adoptRef(new WebSocket(context)));
254    webSocket->suspendIfNeeded();
255
256    webSocket->connect(context->completeURL(url), protocols, exceptionState);
257    if (exceptionState.hadException())
258        return 0;
259
260    return webSocket.release();
261}
262
263PassRefPtr<WebSocket> WebSocket::create(ExecutionContext* context, const String& url, const String& protocol, ExceptionState& exceptionState)
264{
265    Vector<String> protocols;
266    protocols.append(protocol);
267    return create(context, url, protocols, exceptionState);
268}
269
270void WebSocket::connect(const String& url, ExceptionState& exceptionState)
271{
272    Vector<String> protocols;
273    connect(url, protocols, exceptionState);
274}
275
276void WebSocket::connect(const String& url, const String& protocol, ExceptionState& exceptionState)
277{
278    Vector<String> protocols;
279    protocols.append(protocol);
280    connect(url, protocols, exceptionState);
281}
282
283void WebSocket::connect(const String& url, const Vector<String>& protocols, ExceptionState& exceptionState)
284{
285    WTF_LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data());
286    m_url = KURL(KURL(), url);
287
288    if (!m_url.isValid()) {
289        m_state = CLOSED;
290        exceptionState.throwDOMException(SyntaxError, "The URL '" + url + "' is invalid.");
291        return;
292    }
293    if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) {
294        m_state = CLOSED;
295        exceptionState.throwDOMException(SyntaxError, "The URL's scheme must be either 'ws' or 'wss'. '" + m_url.protocol() + "' is not allowed.");
296        return;
297    }
298    if (m_url.hasFragmentIdentifier()) {
299        m_state = CLOSED;
300        exceptionState.throwDOMException(SyntaxError, "The URL contains a fragment identifier ('" + m_url.fragmentIdentifier() + "'). Fragment identifiers are not allowed in WebSocket URLs.");
301        return;
302    }
303    if (!portAllowed(m_url)) {
304        m_state = CLOSED;
305        exceptionState.throwSecurityError("The port " + String::number(m_url.port()) + " is not allowed.");
306        return;
307    }
308
309    // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
310    bool shouldBypassMainWorldContentSecurityPolicy = false;
311    if (executionContext()->isDocument()) {
312        Document* document = toDocument(executionContext());
313        shouldBypassMainWorldContentSecurityPolicy = document->frame()->script().shouldBypassMainWorldContentSecurityPolicy();
314    }
315    if (!shouldBypassMainWorldContentSecurityPolicy && !executionContext()->contentSecurityPolicy()->allowConnectToSource(m_url)) {
316        m_state = CLOSED;
317        // The URL is safe to expose to JavaScript, as this check happens synchronously before redirection.
318        exceptionState.throwSecurityError("Refused to connect to '" + m_url.elidedString() + "' because it violates the document's Content Security Policy.");
319        return;
320    }
321
322    m_channel = WebSocketChannel::create(executionContext(), this);
323
324    // FIXME: There is a disagreement about restriction of subprotocols between WebSocket API and hybi-10 protocol
325    // draft. The former simply says "only characters in the range U+0021 to U+007E are allowed," while the latter
326    // imposes a stricter rule: "the elements MUST be non-empty strings with characters as defined in [RFC2616],
327    // and MUST all be unique strings."
328    //
329    // Here, we throw SyntaxError if the given protocols do not meet the latter criteria. This behavior does not
330    // comply with WebSocket API specification, but it seems to be the only reasonable way to handle this conflict.
331    for (size_t i = 0; i < protocols.size(); ++i) {
332        if (!isValidProtocolString(protocols[i])) {
333            m_state = CLOSED;
334            exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeProtocolString(protocols[i]) + "' is invalid.");
335            return;
336        }
337    }
338    HashSet<String> visited;
339    for (size_t i = 0; i < protocols.size(); ++i) {
340        if (!visited.add(protocols[i]).isNewEntry) {
341            m_state = CLOSED;
342            exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeProtocolString(protocols[i]) + "' is duplicated.");
343            return;
344        }
345    }
346
347    String protocolString;
348    if (!protocols.isEmpty())
349        protocolString = joinStrings(protocols, subProtocolSeperator());
350
351    m_channel->connect(m_url, protocolString);
352}
353
354void WebSocket::handleSendResult(WebSocketChannel::SendResult result, ExceptionState& exceptionState)
355{
356    switch (result) {
357    case WebSocketChannel::InvalidMessage:
358        exceptionState.throwDOMException(SyntaxError, "The message contains invalid characters.");
359        return;
360    case WebSocketChannel::SendFail:
361        logError("WebSocket send() failed.");
362        return;
363    case WebSocketChannel::SendSuccess:
364        return;
365    }
366    ASSERT_NOT_REACHED();
367}
368
369void WebSocket::updateBufferedAmountAfterClose(unsigned long payloadSize)
370{
371    m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
372    m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
373
374    logError("WebSocket is already in CLOSING or CLOSED state.");
375}
376
377void WebSocket::send(const String& message, ExceptionState& exceptionState)
378{
379    WTF_LOG(Network, "WebSocket %p send() Sending String '%s'", this, message.utf8().data());
380    if (m_state == CONNECTING) {
381        setInvalidStateErrorForSendMethod(exceptionState);
382        return;
383    }
384    // No exception is raised if the connection was once established but has subsequently been closed.
385    if (m_state == CLOSING || m_state == CLOSED) {
386        updateBufferedAmountAfterClose(message.utf8().length());
387        return;
388    }
389    ASSERT(m_channel);
390    handleSendResult(m_channel->send(message), exceptionState);
391}
392
393void WebSocket::send(ArrayBuffer* binaryData, ExceptionState& exceptionState)
394{
395    WTF_LOG(Network, "WebSocket %p send() Sending ArrayBuffer %p", this, binaryData);
396    ASSERT(binaryData);
397    if (m_state == CONNECTING) {
398        setInvalidStateErrorForSendMethod(exceptionState);
399        return;
400    }
401    if (m_state == CLOSING || m_state == CLOSED) {
402        updateBufferedAmountAfterClose(binaryData->byteLength());
403        return;
404    }
405    ASSERT(m_channel);
406    handleSendResult(m_channel->send(*binaryData, 0, binaryData->byteLength()), exceptionState);
407}
408
409void WebSocket::send(ArrayBufferView* arrayBufferView, ExceptionState& exceptionState)
410{
411    WTF_LOG(Network, "WebSocket %p send() Sending ArrayBufferView %p", this, arrayBufferView);
412    ASSERT(arrayBufferView);
413    if (m_state == CONNECTING) {
414        setInvalidStateErrorForSendMethod(exceptionState);
415        return;
416    }
417    if (m_state == CLOSING || m_state == CLOSED) {
418        updateBufferedAmountAfterClose(arrayBufferView->byteLength());
419        return;
420    }
421    ASSERT(m_channel);
422    RefPtr<ArrayBuffer> arrayBuffer(arrayBufferView->buffer());
423    handleSendResult(m_channel->send(*arrayBuffer, arrayBufferView->byteOffset(), arrayBufferView->byteLength()), exceptionState);
424}
425
426void WebSocket::send(Blob* binaryData, ExceptionState& exceptionState)
427{
428    WTF_LOG(Network, "WebSocket %p send() Sending Blob '%s'", this, binaryData->uuid().utf8().data());
429    ASSERT(binaryData);
430    if (m_state == CONNECTING) {
431        setInvalidStateErrorForSendMethod(exceptionState);
432        return;
433    }
434    if (m_state == CLOSING || m_state == CLOSED) {
435        updateBufferedAmountAfterClose(static_cast<unsigned long>(binaryData->size()));
436        return;
437    }
438    ASSERT(m_channel);
439    handleSendResult(m_channel->send(binaryData->blobDataHandle()), exceptionState);
440}
441
442void WebSocket::close(unsigned short code, const String& reason, ExceptionState& exceptionState)
443{
444    closeInternal(code, reason, exceptionState);
445}
446
447void WebSocket::close(ExceptionState& exceptionState)
448{
449    closeInternal(WebSocketChannel::CloseEventCodeNotSpecified, String(), exceptionState);
450}
451
452void WebSocket::close(unsigned short code, ExceptionState& exceptionState)
453{
454    closeInternal(code, String(), exceptionState);
455}
456
457void WebSocket::closeInternal(int code, const String& reason, ExceptionState& exceptionState)
458{
459    if (code == WebSocketChannel::CloseEventCodeNotSpecified) {
460        WTF_LOG(Network, "WebSocket %p close() without code and reason", this);
461    } else {
462        WTF_LOG(Network, "WebSocket %p close() code=%d reason='%s'", this, code, reason.utf8().data());
463        if (!(code == WebSocketChannel::CloseEventCodeNormalClosure || (WebSocketChannel::CloseEventCodeMinimumUserDefined <= code && code <= WebSocketChannel::CloseEventCodeMaximumUserDefined))) {
464            exceptionState.throwDOMException(InvalidAccessError, "The code must be either 1000, or between 3000 and 4999. " + String::number(code) + " is neither.");
465            return;
466        }
467        CString utf8 = reason.utf8(String::StrictConversionReplacingUnpairedSurrogatesWithFFFD);
468        if (utf8.length() > maxReasonSizeInBytes) {
469            exceptionState.throwDOMException(SyntaxError, "The message must not be greater than " + String::number(maxReasonSizeInBytes) + " bytes.");
470            return;
471        }
472    }
473
474    if (m_state == CLOSING || m_state == CLOSED)
475        return;
476    if (m_state == CONNECTING) {
477        m_state = CLOSING;
478        m_channel->fail("WebSocket is closed before the connection is established.", WarningMessageLevel);
479        return;
480    }
481    m_state = CLOSING;
482    if (m_channel)
483        m_channel->close(code, reason);
484}
485
486const KURL& WebSocket::url() const
487{
488    return m_url;
489}
490
491WebSocket::State WebSocket::readyState() const
492{
493    return m_state;
494}
495
496unsigned long WebSocket::bufferedAmount() const
497{
498    return saturateAdd(m_bufferedAmount, m_bufferedAmountAfterClose);
499}
500
501String WebSocket::protocol() const
502{
503    return m_subprotocol;
504}
505
506String WebSocket::extensions() const
507{
508    return m_extensions;
509}
510
511String WebSocket::binaryType() const
512{
513    switch (m_binaryType) {
514    case BinaryTypeBlob:
515        return "blob";
516    case BinaryTypeArrayBuffer:
517        return "arraybuffer";
518    }
519    ASSERT_NOT_REACHED();
520    return String();
521}
522
523void WebSocket::setBinaryType(const String& binaryType)
524{
525    if (binaryType == "blob") {
526        m_binaryType = BinaryTypeBlob;
527        return;
528    }
529    if (binaryType == "arraybuffer") {
530        m_binaryType = BinaryTypeArrayBuffer;
531        return;
532    }
533    logError("'" + binaryType + "' is not a valid value for binaryType; binaryType remains unchanged.");
534}
535
536const AtomicString& WebSocket::interfaceName() const
537{
538    return EventTargetNames::WebSocket;
539}
540
541ExecutionContext* WebSocket::executionContext() const
542{
543    return ActiveDOMObject::executionContext();
544}
545
546void WebSocket::contextDestroyed()
547{
548    WTF_LOG(Network, "WebSocket %p contextDestroyed()", this);
549    ASSERT(!m_channel);
550    ASSERT(m_state == CLOSED);
551    ActiveDOMObject::contextDestroyed();
552}
553
554bool WebSocket::hasPendingActivity() const
555{
556    return m_state != CLOSED;
557}
558
559void WebSocket::suspend()
560{
561    if (m_channel)
562        m_channel->suspend();
563    m_eventQueue->suspend();
564}
565
566void WebSocket::resume()
567{
568    if (m_channel)
569        m_channel->resume();
570    m_eventQueue->resume();
571}
572
573void WebSocket::stop()
574{
575    m_eventQueue->stop();
576
577    if (!hasPendingActivity()) {
578        ASSERT(!m_channel);
579        return;
580    }
581    if (m_channel) {
582        m_channel->close(WebSocketChannel::CloseEventCodeGoingAway, String());
583        m_channel->disconnect();
584        m_channel = 0;
585    }
586    m_state = CLOSED;
587}
588
589void WebSocket::didConnect()
590{
591    WTF_LOG(Network, "WebSocket %p didConnect()", this);
592    if (m_state != CONNECTING)
593        return;
594    m_state = OPEN;
595    m_subprotocol = m_channel->subprotocol();
596    m_extensions = m_channel->extensions();
597    m_eventQueue->dispatch(Event::create(EventTypeNames::open));
598}
599
600void WebSocket::didReceiveMessage(const String& msg)
601{
602    WTF_LOG(Network, "WebSocket %p didReceiveMessage() Text message '%s'", this, msg.utf8().data());
603    if (m_state != OPEN)
604        return;
605    m_eventQueue->dispatch(MessageEvent::create(msg, SecurityOrigin::create(m_url)->toString()));
606}
607
608void WebSocket::didReceiveBinaryData(PassOwnPtr<Vector<char> > binaryData)
609{
610    WTF_LOG(Network, "WebSocket %p didReceiveBinaryData() %lu byte binary message", this, static_cast<unsigned long>(binaryData->size()));
611    switch (m_binaryType) {
612    case BinaryTypeBlob: {
613        size_t size = binaryData->size();
614        RefPtr<RawData> rawData = RawData::create();
615        binaryData->swap(*rawData->mutableData());
616        OwnPtr<BlobData> blobData = BlobData::create();
617        blobData->appendData(rawData.release(), 0, BlobDataItem::toEndOfFile);
618        RefPtr<Blob> blob = Blob::create(BlobDataHandle::create(blobData.release(), size));
619        m_eventQueue->dispatch(MessageEvent::create(blob.release(), SecurityOrigin::create(m_url)->toString()));
620        break;
621    }
622
623    case BinaryTypeArrayBuffer:
624        m_eventQueue->dispatch(MessageEvent::create(ArrayBuffer::create(binaryData->data(), binaryData->size()), SecurityOrigin::create(m_url)->toString()));
625        break;
626    }
627}
628
629void WebSocket::didReceiveMessageError()
630{
631    WTF_LOG(Network, "WebSocket %p didReceiveMessageError()", this);
632    m_eventQueue->dispatch(Event::create(EventTypeNames::error));
633}
634
635void WebSocket::didUpdateBufferedAmount(unsigned long bufferedAmount)
636{
637    WTF_LOG(Network, "WebSocket %p didUpdateBufferedAmount() New bufferedAmount is %lu", this, bufferedAmount);
638    if (m_state == CLOSED)
639        return;
640    m_bufferedAmount = bufferedAmount;
641}
642
643void WebSocket::didStartClosingHandshake()
644{
645    WTF_LOG(Network, "WebSocket %p didStartClosingHandshake()", this);
646    m_state = CLOSING;
647}
648
649void WebSocket::didClose(unsigned long unhandledBufferedAmount, ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason)
650{
651    WTF_LOG(Network, "WebSocket %p didClose()", this);
652    if (!m_channel)
653        return;
654    bool wasClean = m_state == CLOSING && !unhandledBufferedAmount && closingHandshakeCompletion == ClosingHandshakeComplete && code != WebSocketChannel::CloseEventCodeAbnormalClosure;
655
656    // hasPendingActivity() returns false when m_state is CLOSED. So, it's
657    // possible that the wrapper gets garbage-collected during execution of the
658    // event. We need to keep this object alive to continue the rest of this
659    // method. Since the event we dispatch below sets "this" to the wrapper
660    // object, it doesn't get collected, but just to be sure, put a reference
661    // for protection.
662    //
663    // We can move m_channel clean up code before event dispatching, but it
664    // makes it harder to check correctness. Comparing cost, we now have this
665    // temporary reference.
666    RefPtr<WebSocket> protect(this);
667
668    m_state = CLOSED;
669    m_bufferedAmount = unhandledBufferedAmount;
670    m_eventQueue->dispatch(CloseEvent::create(wasClean, code, reason));
671
672    if (m_channel) {
673        m_channel->disconnect();
674        m_channel = 0;
675    }
676}
677
678size_t WebSocket::getFramingOverhead(size_t payloadSize)
679{
680    static const size_t hybiBaseFramingOverhead = 2; // Every frame has at least two-byte header.
681    static const size_t hybiMaskingKeyLength = 4; // Every frame from client must have masking key.
682    static const size_t minimumPayloadSizeWithTwoByteExtendedPayloadLength = 126;
683    static const size_t minimumPayloadSizeWithEightByteExtendedPayloadLength = 0x10000;
684    size_t overhead = hybiBaseFramingOverhead + hybiMaskingKeyLength;
685    if (payloadSize >= minimumPayloadSizeWithEightByteExtendedPayloadLength)
686        overhead += 8;
687    else if (payloadSize >= minimumPayloadSizeWithTwoByteExtendedPayloadLength)
688        overhead += 2;
689    return overhead;
690}
691
692} // namespace WebCore
693