Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / Modules / websockets / WebSocket.cpp
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 #if ENABLE(WEB_SOCKETS)
34
35 #include "WebSocket.h"
36
37 #include "Blob.h"
38 #include "CloseEvent.h"
39 #include "ContentSecurityPolicy.h"
40 #include "DOMWindow.h"
41 #include "Document.h"
42 #include "Event.h"
43 #include "EventListener.h"
44 #include "EventNames.h"
45 #include "ExceptionCode.h"
46 #include "Frame.h"
47 #include "Logging.h"
48 #include "MessageEvent.h"
49 #include "ScriptController.h"
50 #include "ScriptExecutionContext.h"
51 #include "SecurityOrigin.h"
52 #include "ThreadableWebSocketChannel.h"
53 #include "WebSocketChannel.h"
54 #include <inspector/ScriptCallStack.h>
55 #include <runtime/ArrayBuffer.h>
56 #include <runtime/ArrayBufferView.h>
57 #include <wtf/HashSet.h>
58 #include <wtf/RunLoop.h>
59 #include <wtf/StdLibExtras.h>
60 #include <wtf/text/CString.h>
61 #include <wtf/text/StringBuilder.h>
62 #include <wtf/text/WTFString.h>
63
64 namespace WebCore {
65
66 const size_t maxReasonSizeInBytes = 123;
67
68 static inline bool isValidProtocolCharacter(UChar character)
69 {
70     // Hybi-10 says "(Subprotocol string must consist of) characters in the range U+0021 to U+007E not including
71     // separator characters as defined in [RFC2616]."
72     const UChar minimumProtocolCharacter = '!'; // U+0021.
73     const UChar maximumProtocolCharacter = '~'; // U+007E.
74     return character >= minimumProtocolCharacter && character <= maximumProtocolCharacter
75         && character != '"' && character != '(' && character != ')' && character != ',' && character != '/'
76         && !(character >= ':' && character <= '@') // U+003A - U+0040 (':', ';', '<', '=', '>', '?', '@').
77         && !(character >= '[' && character <= ']') // U+005B - U+005D ('[', '\\', ']').
78         && character != '{' && character != '}';
79 }
80
81 static bool isValidProtocolString(const String& protocol)
82 {
83     if (protocol.isEmpty())
84         return false;
85     for (size_t i = 0; i < protocol.length(); ++i) {
86         if (!isValidProtocolCharacter(protocol[i]))
87             return false;
88     }
89     return true;
90 }
91
92 static String encodeProtocolString(const String& protocol)
93 {
94     StringBuilder builder;
95     for (size_t i = 0; i < protocol.length(); i++) {
96         if (protocol[i] < 0x20 || protocol[i] > 0x7E)
97             builder.append(String::format("\\u%04X", protocol[i]));
98         else if (protocol[i] == 0x5c)
99             builder.appendLiteral("\\\\");
100         else
101             builder.append(protocol[i]);
102     }
103     return builder.toString();
104 }
105
106 static String joinStrings(const Vector<String>& strings, const char* separator)
107 {
108     StringBuilder builder;
109     for (size_t i = 0; i < strings.size(); ++i) {
110         if (i)
111             builder.append(separator);
112         builder.append(strings[i]);
113     }
114     return builder.toString();
115 }
116
117 static unsigned long saturateAdd(unsigned long a, unsigned long b)
118 {
119     if (std::numeric_limits<unsigned long>::max() - a < b)
120         return std::numeric_limits<unsigned long>::max();
121     return a + b;
122 }
123
124 static bool webSocketsAvailable = true;
125
126 void WebSocket::setIsAvailable(bool available)
127 {
128     webSocketsAvailable = available;
129 }
130
131 bool WebSocket::isAvailable()
132 {
133     return webSocketsAvailable;
134 }
135
136 const char* WebSocket::subProtocolSeperator()
137 {
138     return ", ";
139 }
140
141 WebSocket::WebSocket(ScriptExecutionContext& context)
142     : ActiveDOMObject(&context)
143     , m_state(CONNECTING)
144     , m_bufferedAmount(0)
145     , m_bufferedAmountAfterClose(0)
146     , m_binaryType(BinaryTypeBlob)
147     , m_subprotocol("")
148     , m_extensions("")
149     , m_resumeTimer(*this, &WebSocket::resumeTimerFired)
150 {
151 }
152
153 WebSocket::~WebSocket()
154 {
155     if (m_channel)
156         m_channel->disconnect();
157 }
158
159 Ref<WebSocket> WebSocket::create(ScriptExecutionContext& context)
160 {
161     Ref<WebSocket> webSocket(adoptRef(*new WebSocket(context)));
162     webSocket->suspendIfNeeded();
163     return webSocket;
164 }
165
166 RefPtr<WebSocket> WebSocket::create(ScriptExecutionContext& context, const String& url, ExceptionCode& ec)
167 {
168     Vector<String> protocols;
169     return WebSocket::create(context, url, protocols, ec);
170 }
171
172 RefPtr<WebSocket> WebSocket::create(ScriptExecutionContext& context, const String& url, const Vector<String>& protocols, ExceptionCode& ec)
173 {
174     if (url.isNull()) {
175         ec = SYNTAX_ERR;
176         return nullptr;
177     }
178
179     RefPtr<WebSocket> webSocket(adoptRef(*new WebSocket(context)));
180     webSocket->suspendIfNeeded();
181
182     webSocket->connect(context.completeURL(url), protocols, ec);
183     if (ec)
184         return nullptr;
185
186     return webSocket;
187 }
188
189 RefPtr<WebSocket> WebSocket::create(ScriptExecutionContext& context, const String& url, const String& protocol, ExceptionCode& ec)
190 {
191     Vector<String> protocols;
192     protocols.append(protocol);
193     return WebSocket::create(context, url, protocols, ec);
194 }
195
196 void WebSocket::connect(const String& url, ExceptionCode& ec)
197 {
198     Vector<String> protocols;
199     connect(url, protocols, ec);
200 }
201
202 void WebSocket::connect(const String& url, const String& protocol, ExceptionCode& ec)
203 {
204     Vector<String> protocols;
205     protocols.append(protocol);
206     connect(url, protocols, ec);
207 }
208
209 void WebSocket::connect(const String& url, const Vector<String>& protocols, ExceptionCode& ec)
210 {
211     LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data());
212     m_url = URL(URL(), url);
213
214     if (!m_url.isValid()) {
215         scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Invalid url for WebSocket " + m_url.stringCenterEllipsizedToLength());
216         m_state = CLOSED;
217         ec = SYNTAX_ERR;
218         return;
219     }
220
221     if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) {
222         scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Wrong url scheme for WebSocket " + m_url.stringCenterEllipsizedToLength());
223         m_state = CLOSED;
224         ec = SYNTAX_ERR;
225         return;
226     }
227     if (m_url.hasFragmentIdentifier()) {
228         scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "URL has fragment component " + m_url.stringCenterEllipsizedToLength());
229         m_state = CLOSED;
230         ec = SYNTAX_ERR;
231         return;
232     }
233     if (!portAllowed(m_url)) {
234         scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "WebSocket port " + String::number(m_url.port()) + " blocked");
235         m_state = CLOSED;
236         ec = SECURITY_ERR;
237         return;
238     }
239
240     // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
241     bool shouldBypassMainWorldContentSecurityPolicy = ContentSecurityPolicy::shouldBypassMainWorldContentSecurityPolicy(*scriptExecutionContext());
242     if (!scriptExecutionContext()->contentSecurityPolicy()->allowConnectToSource(m_url, shouldBypassMainWorldContentSecurityPolicy)) {
243         m_state = CLOSED;
244
245         // FIXME: Should this be throwing an exception?
246         ec = SECURITY_ERR;
247         return;
248     }
249
250     m_channel = ThreadableWebSocketChannel::create(scriptExecutionContext(), this);
251
252     // FIXME: There is a disagreement about restriction of subprotocols between WebSocket API and hybi-10 protocol
253     // draft. The former simply says "only characters in the range U+0021 to U+007E are allowed," while the latter
254     // imposes a stricter rule: "the elements MUST be non-empty strings with characters as defined in [RFC2616],
255     // and MUST all be unique strings."
256     //
257     // Here, we throw SYNTAX_ERR if the given protocols do not meet the latter criteria. This behavior does not
258     // comply with WebSocket API specification, but it seems to be the only reasonable way to handle this conflict.
259     for (auto& protocol : protocols) {
260         if (!isValidProtocolString(protocol)) {
261             scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Wrong protocol for WebSocket '" + encodeProtocolString(protocol) + "'");
262             m_state = CLOSED;
263             ec = SYNTAX_ERR;
264             return;
265         }
266     }
267     HashSet<String> visited;
268     for (auto& protocol : protocols) {
269         if (!visited.add(protocol).isNewEntry) {
270             scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "WebSocket protocols contain duplicates: '" + encodeProtocolString(protocol) + "'");
271             m_state = CLOSED;
272             ec = SYNTAX_ERR;
273             return;
274         }
275     }
276
277     if (is<Document>(*scriptExecutionContext())) {
278         Document& document = downcast<Document>(*scriptExecutionContext());
279         if (!document.frame()->loader().mixedContentChecker().canRunInsecureContent(document.securityOrigin(), m_url)) {
280             // Balanced by the call to ActiveDOMObject::unsetPendingActivity() in WebSocket::stop().
281             ActiveDOMObject::setPendingActivity(this);
282
283             // We must block this connection. Instead of throwing an exception, we indicate this
284             // using the error event. But since this code executes as part of the WebSocket's
285             // constructor, we have to wait until the constructor has completed before firing the
286             // event; otherwise, users can't connect to the event.
287             RunLoop::main().dispatch([this]() {
288                 dispatchOrQueueErrorEvent();
289                 stop();
290             });
291             return;
292         }
293     }
294
295     String protocolString;
296     if (!protocols.isEmpty())
297         protocolString = joinStrings(protocols, subProtocolSeperator());
298
299     m_channel->connect(m_url, protocolString);
300     ActiveDOMObject::setPendingActivity(this);
301 }
302
303 void WebSocket::send(const String& message, ExceptionCode& ec)
304 {
305     LOG(Network, "WebSocket %p send() Sending String '%s'", this, message.utf8().data());
306     if (m_state == CONNECTING) {
307         ec = INVALID_STATE_ERR;
308         return;
309     }
310     // No exception is raised if the connection was once established but has subsequently been closed.
311     if (m_state == CLOSING || m_state == CLOSED) {
312         size_t payloadSize = message.utf8().length();
313         m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
314         m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
315         return;
316     }
317     ASSERT(m_channel);
318     ThreadableWebSocketChannel::SendResult result = m_channel->send(message);
319     if (result == ThreadableWebSocketChannel::InvalidMessage) {
320         scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Websocket message contains invalid character(s)."));
321         ec = SYNTAX_ERR;
322         return;
323     }
324 }
325
326 void WebSocket::send(ArrayBuffer* binaryData, ExceptionCode& ec)
327 {
328     LOG(Network, "WebSocket %p send() Sending ArrayBuffer %p", this, binaryData);
329     ASSERT(binaryData);
330     if (m_state == CONNECTING) {
331         ec = INVALID_STATE_ERR;
332         return;
333     }
334     if (m_state == CLOSING || m_state == CLOSED) {
335         unsigned payloadSize = binaryData->byteLength();
336         m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
337         m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
338         return;
339     }
340     ASSERT(m_channel);
341     m_channel->send(*binaryData, 0, binaryData->byteLength());
342 }
343
344 void WebSocket::send(ArrayBufferView* arrayBufferView, ExceptionCode& ec)
345 {
346     LOG(Network, "WebSocket %p send() Sending ArrayBufferView %p", this, arrayBufferView);
347     ASSERT(arrayBufferView);
348     if (m_state == CONNECTING) {
349         ec = INVALID_STATE_ERR;
350         return;
351     }
352     if (m_state == CLOSING || m_state == CLOSED) {
353         unsigned payloadSize = arrayBufferView->byteLength();
354         m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
355         m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
356         return;
357     }
358     ASSERT(m_channel);
359     RefPtr<ArrayBuffer> arrayBuffer(arrayBufferView->buffer());
360     m_channel->send(*arrayBuffer, arrayBufferView->byteOffset(), arrayBufferView->byteLength());
361 }
362
363 void WebSocket::send(Blob* binaryData, ExceptionCode& ec)
364 {
365     LOG(Network, "WebSocket %p send() Sending Blob '%s'", this, binaryData->url().stringCenterEllipsizedToLength().utf8().data());
366     if (m_state == CONNECTING) {
367         ec = INVALID_STATE_ERR;
368         return;
369     }
370     if (m_state == CLOSING || m_state == CLOSED) {
371         unsigned long payloadSize = static_cast<unsigned long>(binaryData->size());
372         m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
373         m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
374         return;
375     }
376     ASSERT(m_channel);
377     m_channel->send(*binaryData);
378 }
379
380 void WebSocket::close(int code, const String& reason, ExceptionCode& ec)
381 {
382     if (code == WebSocketChannel::CloseEventCodeNotSpecified)
383         LOG(Network, "WebSocket %p close() without code and reason", this);
384     else {
385         LOG(Network, "WebSocket %p close() code=%d reason='%s'", this, code, reason.utf8().data());
386         if (!(code == WebSocketChannel::CloseEventCodeNormalClosure || (WebSocketChannel::CloseEventCodeMinimumUserDefined <= code && code <= WebSocketChannel::CloseEventCodeMaximumUserDefined))) {
387             ec = INVALID_ACCESS_ERR;
388             return;
389         }
390         CString utf8 = reason.utf8(StrictConversionReplacingUnpairedSurrogatesWithFFFD);
391         if (utf8.length() > maxReasonSizeInBytes) {
392             scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("WebSocket close message is too long."));
393             ec = SYNTAX_ERR;
394             return;
395         }
396     }
397
398     if (m_state == CLOSING || m_state == CLOSED)
399         return;
400     if (m_state == CONNECTING) {
401         m_state = CLOSING;
402         m_channel->fail("WebSocket is closed before the connection is established.");
403         return;
404     }
405     m_state = CLOSING;
406     if (m_channel)
407         m_channel->close(code, reason);
408 }
409
410 const URL& WebSocket::url() const
411 {
412     return m_url;
413 }
414
415 WebSocket::State WebSocket::readyState() const
416 {
417     return m_state;
418 }
419
420 unsigned long WebSocket::bufferedAmount() const
421 {
422     return saturateAdd(m_bufferedAmount, m_bufferedAmountAfterClose);
423 }
424
425 String WebSocket::protocol() const
426 {
427     return m_subprotocol;
428 }
429
430 String WebSocket::extensions() const
431 {
432     return m_extensions;
433 }
434
435 String WebSocket::binaryType() const
436 {
437     switch (m_binaryType) {
438     case BinaryTypeBlob:
439         return "blob";
440     case BinaryTypeArrayBuffer:
441         return "arraybuffer";
442     }
443     ASSERT_NOT_REACHED();
444     return String();
445 }
446
447 void WebSocket::setBinaryType(const String& binaryType)
448 {
449     if (binaryType == "blob") {
450         m_binaryType = BinaryTypeBlob;
451         return;
452     }
453     if (binaryType == "arraybuffer") {
454         m_binaryType = BinaryTypeArrayBuffer;
455         return;
456     }
457     scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "'" + binaryType + "' is not a valid value for binaryType; binaryType remains unchanged.");
458 }
459
460 EventTargetInterface WebSocket::eventTargetInterface() const
461 {
462     return WebSocketEventTargetInterfaceType;
463 }
464
465 ScriptExecutionContext* WebSocket::scriptExecutionContext() const
466 {
467     return ActiveDOMObject::scriptExecutionContext();
468 }
469
470 void WebSocket::contextDestroyed()
471 {
472     LOG(Network, "WebSocket %p contextDestroyed()", this);
473     ASSERT(!m_channel);
474     ASSERT(m_state == CLOSED);
475     ActiveDOMObject::contextDestroyed();
476 }
477
478 bool WebSocket::canSuspendForDocumentSuspension() const
479 {
480     return true;
481 }
482
483 void WebSocket::suspend(ReasonForSuspension reason)
484 {
485     if (m_resumeTimer.isActive())
486         m_resumeTimer.stop();
487
488     m_shouldDelayEventFiring = true;
489
490     if (m_channel) {
491         if (reason == ActiveDOMObject::PageCache) {
492             // This will cause didClose() to be called.
493             m_channel->fail("WebSocket is closed due to suspension.");
494         } else
495             m_channel->suspend();
496     }
497 }
498
499 void WebSocket::resume()
500 {
501     if (m_channel)
502         m_channel->resume();
503     else if (!m_pendingEvents.isEmpty() && !m_resumeTimer.isActive()) {
504         // Fire the pending events in a timer as we are not allowed to execute arbitrary JS from resume().
505         m_resumeTimer.startOneShot(0);
506     }
507
508     m_shouldDelayEventFiring = false;
509 }
510
511 void WebSocket::resumeTimerFired()
512 {
513     Ref<WebSocket> protect(*this);
514
515     ASSERT(!m_pendingEvents.isEmpty());
516
517     // Check m_shouldDelayEventFiring when iterating in case firing an event causes
518     // suspend() to be called.
519     while (!m_pendingEvents.isEmpty() && !m_shouldDelayEventFiring)
520         dispatchEvent(m_pendingEvents.takeFirst());
521 }
522
523 void WebSocket::stop()
524 {
525     bool pending = hasPendingActivity();
526     if (m_channel)
527         m_channel->disconnect();
528     m_channel = nullptr;
529     m_state = CLOSED;
530     m_pendingEvents.clear();
531     ActiveDOMObject::stop();
532     if (pending)
533         ActiveDOMObject::unsetPendingActivity(this);
534 }
535
536 const char* WebSocket::activeDOMObjectName() const
537 {
538     return "WebSocket";
539 }
540
541 void WebSocket::didConnect()
542 {
543     LOG(Network, "WebSocket %p didConnect()", this);
544     if (m_state != CONNECTING) {
545         didClose(0, ClosingHandshakeIncomplete, WebSocketChannel::CloseEventCodeAbnormalClosure, "");
546         return;
547     }
548     ASSERT(scriptExecutionContext());
549     m_state = OPEN;
550     m_subprotocol = m_channel->subprotocol();
551     m_extensions = m_channel->extensions();
552     dispatchEvent(Event::create(eventNames().openEvent, false, false));
553 }
554
555 void WebSocket::didReceiveMessage(const String& msg)
556 {
557     LOG(Network, "WebSocket %p didReceiveMessage() Text message '%s'", this, msg.utf8().data());
558     if (m_state != OPEN)
559         return;
560     ASSERT(scriptExecutionContext());
561     dispatchEvent(MessageEvent::create(msg, SecurityOrigin::create(m_url)->toString()));
562 }
563
564 void WebSocket::didReceiveBinaryData(Vector<char>&& binaryData)
565 {
566     LOG(Network, "WebSocket %p didReceiveBinaryData() %lu byte binary message", this, static_cast<unsigned long>(binaryData.size()));
567     switch (m_binaryType) {
568     case BinaryTypeBlob: {
569         // FIXME: We just received the data from NetworkProcess, and are sending it back. This is inefficient.
570         RefPtr<Blob> blob = Blob::create(WTFMove(binaryData), emptyString());
571         dispatchEvent(MessageEvent::create(blob.release(), SecurityOrigin::create(m_url)->toString()));
572         break;
573     }
574
575     case BinaryTypeArrayBuffer:
576         dispatchEvent(MessageEvent::create(ArrayBuffer::create(binaryData.data(), binaryData.size()), SecurityOrigin::create(m_url)->toString()));
577         break;
578     }
579 }
580
581 void WebSocket::didReceiveMessageError()
582 {
583     LOG(Network, "WebSocket %p didReceiveErrorMessage()", this);
584     m_state = CLOSED;
585     ASSERT(scriptExecutionContext());
586     dispatchOrQueueErrorEvent();
587 }
588
589 void WebSocket::didUpdateBufferedAmount(unsigned long bufferedAmount)
590 {
591     LOG(Network, "WebSocket %p didUpdateBufferedAmount() New bufferedAmount is %lu", this, bufferedAmount);
592     if (m_state == CLOSED)
593         return;
594     m_bufferedAmount = bufferedAmount;
595 }
596
597 void WebSocket::didStartClosingHandshake()
598 {
599     LOG(Network, "WebSocket %p didStartClosingHandshake()", this);
600     m_state = CLOSING;
601 }
602
603 void WebSocket::didClose(unsigned long unhandledBufferedAmount, ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason)
604 {
605     LOG(Network, "WebSocket %p didClose()", this);
606     if (!m_channel)
607         return;
608     bool wasClean = m_state == CLOSING && !unhandledBufferedAmount && closingHandshakeCompletion == ClosingHandshakeComplete && code != WebSocketChannel::CloseEventCodeAbnormalClosure;
609     m_state = CLOSED;
610     m_bufferedAmount = unhandledBufferedAmount;
611     ASSERT(scriptExecutionContext());
612
613     dispatchOrQueueEvent(CloseEvent::create(wasClean, code, reason));
614
615     if (m_channel) {
616         m_channel->disconnect();
617         m_channel = nullptr;
618     }
619     if (hasPendingActivity())
620         ActiveDOMObject::unsetPendingActivity(this);
621 }
622
623 size_t WebSocket::getFramingOverhead(size_t payloadSize)
624 {
625     static const size_t hybiBaseFramingOverhead = 2; // Every frame has at least two-byte header.
626     static const size_t hybiMaskingKeyLength = 4; // Every frame from client must have masking key.
627     static const size_t minimumPayloadSizeWithTwoByteExtendedPayloadLength = 126;
628     static const size_t minimumPayloadSizeWithEightByteExtendedPayloadLength = 0x10000;
629     size_t overhead = hybiBaseFramingOverhead + hybiMaskingKeyLength;
630     if (payloadSize >= minimumPayloadSizeWithEightByteExtendedPayloadLength)
631         overhead += 8;
632     else if (payloadSize >= minimumPayloadSizeWithTwoByteExtendedPayloadLength)
633         overhead += 2;
634     return overhead;
635 }
636
637 void WebSocket::dispatchOrQueueErrorEvent()
638 {
639     if (m_dispatchedErrorEvent)
640         return;
641
642     m_dispatchedErrorEvent = true;
643     dispatchOrQueueEvent(Event::create(eventNames().errorEvent, false, false));
644 }
645
646 void WebSocket::dispatchOrQueueEvent(Ref<Event>&& event)
647 {
648     if (m_shouldDelayEventFiring)
649         m_pendingEvents.append(WTFMove(event));
650     else
651         dispatchEvent(event);
652 }
653
654 }  // namespace WebCore
655
656 #endif