e359b3bde582fba01a088574a822e936357c67f0
[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 "EventException.h"
44 #include "EventListener.h"
45 #include "EventNames.h"
46 #include "ExceptionCode.h"
47 #include "Frame.h"
48 #include "Logging.h"
49 #include "MessageEvent.h"
50 #include "ScriptController.h"
51 #include "ScriptExecutionContext.h"
52 #include "SecurityOrigin.h"
53 #include "ThreadableWebSocketChannel.h"
54 #include "WebSocketChannel.h"
55 #include <inspector/ScriptCallStack.h>
56 #include <runtime/ArrayBuffer.h>
57 #include <runtime/ArrayBufferView.h>
58 #include <wtf/HashSet.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 WTF::move(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 = false;
242     if (is<Document>(*scriptExecutionContext())) {
243         Document& document = downcast<Document>(*scriptExecutionContext());
244         shouldBypassMainWorldContentSecurityPolicy = document.frame()->script().shouldBypassMainWorldContentSecurityPolicy();
245     }
246     if (!shouldBypassMainWorldContentSecurityPolicy && !scriptExecutionContext()->contentSecurityPolicy()->allowConnectToSource(m_url)) {
247         m_state = CLOSED;
248
249         // FIXME: Should this be throwing an exception?
250         ec = SECURITY_ERR;
251         return;
252     }
253
254     m_channel = ThreadableWebSocketChannel::create(scriptExecutionContext(), this);
255
256     // FIXME: There is a disagreement about restriction of subprotocols between WebSocket API and hybi-10 protocol
257     // draft. The former simply says "only characters in the range U+0021 to U+007E are allowed," while the latter
258     // imposes a stricter rule: "the elements MUST be non-empty strings with characters as defined in [RFC2616],
259     // and MUST all be unique strings."
260     //
261     // Here, we throw SYNTAX_ERR if the given protocols do not meet the latter criteria. This behavior does not
262     // comply with WebSocket API specification, but it seems to be the only reasonable way to handle this conflict.
263     for (size_t i = 0; i < protocols.size(); ++i) {
264         if (!isValidProtocolString(protocols[i])) {
265             scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Wrong protocol for WebSocket '" + encodeProtocolString(protocols[i]) + "'");
266             m_state = CLOSED;
267             ec = SYNTAX_ERR;
268             return;
269         }
270     }
271     HashSet<String> visited;
272     for (size_t i = 0; i < protocols.size(); ++i) {
273         if (!visited.add(protocols[i]).isNewEntry) {
274             scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "WebSocket protocols contain duplicates: '" + encodeProtocolString(protocols[i]) + "'");
275             m_state = CLOSED;
276             ec = SYNTAX_ERR;
277             return;
278         }
279     }
280
281     String protocolString;
282     if (!protocols.isEmpty())
283         protocolString = joinStrings(protocols, subProtocolSeperator());
284
285     m_channel->connect(m_url, protocolString);
286     ActiveDOMObject::setPendingActivity(this);
287 }
288
289 void WebSocket::send(const String& message, ExceptionCode& ec)
290 {
291     LOG(Network, "WebSocket %p send() Sending String '%s'", this, message.utf8().data());
292     if (m_state == CONNECTING) {
293         ec = INVALID_STATE_ERR;
294         return;
295     }
296     // No exception is raised if the connection was once established but has subsequently been closed.
297     if (m_state == CLOSING || m_state == CLOSED) {
298         size_t payloadSize = message.utf8().length();
299         m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
300         m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
301         return;
302     }
303     ASSERT(m_channel);
304     ThreadableWebSocketChannel::SendResult result = m_channel->send(message);
305     if (result == ThreadableWebSocketChannel::InvalidMessage) {
306         scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Websocket message contains invalid character(s)."));
307         ec = SYNTAX_ERR;
308         return;
309     }
310 }
311
312 void WebSocket::send(ArrayBuffer* binaryData, ExceptionCode& ec)
313 {
314     LOG(Network, "WebSocket %p send() Sending ArrayBuffer %p", this, binaryData);
315     ASSERT(binaryData);
316     if (m_state == CONNECTING) {
317         ec = INVALID_STATE_ERR;
318         return;
319     }
320     if (m_state == CLOSING || m_state == CLOSED) {
321         unsigned payloadSize = binaryData->byteLength();
322         m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
323         m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
324         return;
325     }
326     ASSERT(m_channel);
327     m_channel->send(*binaryData, 0, binaryData->byteLength());
328 }
329
330 void WebSocket::send(ArrayBufferView* arrayBufferView, ExceptionCode& ec)
331 {
332     LOG(Network, "WebSocket %p send() Sending ArrayBufferView %p", this, arrayBufferView);
333     ASSERT(arrayBufferView);
334     if (m_state == CONNECTING) {
335         ec = INVALID_STATE_ERR;
336         return;
337     }
338     if (m_state == CLOSING || m_state == CLOSED) {
339         unsigned payloadSize = arrayBufferView->byteLength();
340         m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
341         m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
342         return;
343     }
344     ASSERT(m_channel);
345     RefPtr<ArrayBuffer> arrayBuffer(arrayBufferView->buffer());
346     m_channel->send(*arrayBuffer, arrayBufferView->byteOffset(), arrayBufferView->byteLength());
347 }
348
349 void WebSocket::send(Blob* binaryData, ExceptionCode& ec)
350 {
351     LOG(Network, "WebSocket %p send() Sending Blob '%s'", this, binaryData->url().stringCenterEllipsizedToLength().utf8().data());
352     if (m_state == CONNECTING) {
353         ec = INVALID_STATE_ERR;
354         return;
355     }
356     if (m_state == CLOSING || m_state == CLOSED) {
357         unsigned long payloadSize = static_cast<unsigned long>(binaryData->size());
358         m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
359         m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
360         return;
361     }
362     ASSERT(m_channel);
363     m_channel->send(*binaryData);
364 }
365
366 void WebSocket::close(int code, const String& reason, ExceptionCode& ec)
367 {
368     if (code == WebSocketChannel::CloseEventCodeNotSpecified)
369         LOG(Network, "WebSocket %p close() without code and reason", this);
370     else {
371         LOG(Network, "WebSocket %p close() code=%d reason='%s'", this, code, reason.utf8().data());
372         if (!(code == WebSocketChannel::CloseEventCodeNormalClosure || (WebSocketChannel::CloseEventCodeMinimumUserDefined <= code && code <= WebSocketChannel::CloseEventCodeMaximumUserDefined))) {
373             ec = INVALID_ACCESS_ERR;
374             return;
375         }
376         CString utf8 = reason.utf8(StrictConversionReplacingUnpairedSurrogatesWithFFFD);
377         if (utf8.length() > maxReasonSizeInBytes) {
378             scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("WebSocket close message is too long."));
379             ec = SYNTAX_ERR;
380             return;
381         }
382     }
383
384     if (m_state == CLOSING || m_state == CLOSED)
385         return;
386     if (m_state == CONNECTING) {
387         m_state = CLOSING;
388         m_channel->fail("WebSocket is closed before the connection is established.");
389         return;
390     }
391     m_state = CLOSING;
392     if (m_channel)
393         m_channel->close(code, reason);
394 }
395
396 const URL& WebSocket::url() const
397 {
398     return m_url;
399 }
400
401 WebSocket::State WebSocket::readyState() const
402 {
403     return m_state;
404 }
405
406 unsigned long WebSocket::bufferedAmount() const
407 {
408     return saturateAdd(m_bufferedAmount, m_bufferedAmountAfterClose);
409 }
410
411 String WebSocket::protocol() const
412 {
413     return m_subprotocol;
414 }
415
416 String WebSocket::extensions() const
417 {
418     return m_extensions;
419 }
420
421 String WebSocket::binaryType() const
422 {
423     switch (m_binaryType) {
424     case BinaryTypeBlob:
425         return "blob";
426     case BinaryTypeArrayBuffer:
427         return "arraybuffer";
428     }
429     ASSERT_NOT_REACHED();
430     return String();
431 }
432
433 void WebSocket::setBinaryType(const String& binaryType)
434 {
435     if (binaryType == "blob") {
436         m_binaryType = BinaryTypeBlob;
437         return;
438     }
439     if (binaryType == "arraybuffer") {
440         m_binaryType = BinaryTypeArrayBuffer;
441         return;
442     }
443     scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "'" + binaryType + "' is not a valid value for binaryType; binaryType remains unchanged.");
444 }
445
446 EventTargetInterface WebSocket::eventTargetInterface() const
447 {
448     return WebSocketEventTargetInterfaceType;
449 }
450
451 ScriptExecutionContext* WebSocket::scriptExecutionContext() const
452 {
453     return ActiveDOMObject::scriptExecutionContext();
454 }
455
456 void WebSocket::contextDestroyed()
457 {
458     LOG(Network, "WebSocket %p contextDestroyed()", this);
459     ASSERT(!m_channel);
460     ASSERT(m_state == CLOSED);
461     ActiveDOMObject::contextDestroyed();
462 }
463
464 bool WebSocket::canSuspendForPageCache() const
465 {
466     return true;
467 }
468
469 void WebSocket::suspend(ReasonForSuspension reason)
470 {
471     if (m_resumeTimer.isActive())
472         m_resumeTimer.stop();
473
474     m_shouldDelayEventFiring = true;
475
476     if (m_channel) {
477         if (reason == ActiveDOMObject::PageCache) {
478             // This will cause didClose() to be called.
479             m_channel->fail("WebSocket is closed due to suspension.");
480         } else
481             m_channel->suspend();
482     }
483 }
484
485 void WebSocket::resume()
486 {
487     if (m_channel)
488         m_channel->resume();
489     else if (!m_pendingEvents.isEmpty() && !m_resumeTimer.isActive()) {
490         // Fire the pending events in a timer as we are not allowed to execute arbitrary JS from resume().
491         m_resumeTimer.startOneShot(0);
492     }
493
494     m_shouldDelayEventFiring = false;
495 }
496
497 void WebSocket::resumeTimerFired()
498 {
499     Ref<WebSocket> protect(*this);
500
501     ASSERT(!m_pendingEvents.isEmpty());
502
503     // Check m_shouldDelayEventFiring when iterating in case firing an event causes
504     // suspend() to be called.
505     while (!m_pendingEvents.isEmpty() && !m_shouldDelayEventFiring)
506         dispatchEvent(m_pendingEvents.takeFirst());
507 }
508
509 void WebSocket::stop()
510 {
511     bool pending = hasPendingActivity();
512     if (m_channel)
513         m_channel->disconnect();
514     m_channel = 0;
515     m_state = CLOSED;
516     m_pendingEvents.clear();
517     ActiveDOMObject::stop();
518     if (pending)
519         ActiveDOMObject::unsetPendingActivity(this);
520 }
521
522 const char* WebSocket::activeDOMObjectName() const
523 {
524     return "WebSocket";
525 }
526
527 void WebSocket::didConnect()
528 {
529     LOG(Network, "WebSocket %p didConnect()", this);
530     if (m_state != CONNECTING) {
531         didClose(0, ClosingHandshakeIncomplete, WebSocketChannel::CloseEventCodeAbnormalClosure, "");
532         return;
533     }
534     ASSERT(scriptExecutionContext());
535     m_state = OPEN;
536     m_subprotocol = m_channel->subprotocol();
537     m_extensions = m_channel->extensions();
538     dispatchEvent(Event::create(eventNames().openEvent, false, false));
539 }
540
541 void WebSocket::didReceiveMessage(const String& msg)
542 {
543     LOG(Network, "WebSocket %p didReceiveMessage() Text message '%s'", this, msg.utf8().data());
544     if (m_state != OPEN)
545         return;
546     ASSERT(scriptExecutionContext());
547     dispatchEvent(MessageEvent::create(msg, SecurityOrigin::create(m_url)->toString()));
548 }
549
550 void WebSocket::didReceiveBinaryData(Vector<char>&& binaryData)
551 {
552     LOG(Network, "WebSocket %p didReceiveBinaryData() %lu byte binary message", this, static_cast<unsigned long>(binaryData.size()));
553     switch (m_binaryType) {
554     case BinaryTypeBlob: {
555         // FIXME: We just received the data from NetworkProcess, and are sending it back. This is inefficient.
556         RefPtr<Blob> blob = Blob::create(WTF::move(binaryData), emptyString());
557         dispatchEvent(MessageEvent::create(blob.release(), SecurityOrigin::create(m_url)->toString()));
558         break;
559     }
560
561     case BinaryTypeArrayBuffer:
562         dispatchEvent(MessageEvent::create(ArrayBuffer::create(binaryData.data(), binaryData.size()), SecurityOrigin::create(m_url)->toString()));
563         break;
564     }
565 }
566
567 void WebSocket::didReceiveMessageError()
568 {
569     LOG(Network, "WebSocket %p didReceiveErrorMessage()", this);
570     m_state = CLOSED;
571     ASSERT(scriptExecutionContext());
572     dispatchOrQueueEvent(Event::create(eventNames().errorEvent, false, false));
573 }
574
575 void WebSocket::didUpdateBufferedAmount(unsigned long bufferedAmount)
576 {
577     LOG(Network, "WebSocket %p didUpdateBufferedAmount() New bufferedAmount is %lu", this, bufferedAmount);
578     if (m_state == CLOSED)
579         return;
580     m_bufferedAmount = bufferedAmount;
581 }
582
583 void WebSocket::didStartClosingHandshake()
584 {
585     LOG(Network, "WebSocket %p didStartClosingHandshake()", this);
586     m_state = CLOSING;
587 }
588
589 void WebSocket::didClose(unsigned long unhandledBufferedAmount, ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason)
590 {
591     LOG(Network, "WebSocket %p didClose()", this);
592     if (!m_channel)
593         return;
594     bool wasClean = m_state == CLOSING && !unhandledBufferedAmount && closingHandshakeCompletion == ClosingHandshakeComplete && code != WebSocketChannel::CloseEventCodeAbnormalClosure;
595     m_state = CLOSED;
596     m_bufferedAmount = unhandledBufferedAmount;
597     ASSERT(scriptExecutionContext());
598
599     dispatchOrQueueEvent(CloseEvent::create(wasClean, code, reason));
600
601     if (m_channel) {
602         m_channel->disconnect();
603         m_channel = nullptr;
604     }
605     if (hasPendingActivity())
606         ActiveDOMObject::unsetPendingActivity(this);
607 }
608
609 size_t WebSocket::getFramingOverhead(size_t payloadSize)
610 {
611     static const size_t hybiBaseFramingOverhead = 2; // Every frame has at least two-byte header.
612     static const size_t hybiMaskingKeyLength = 4; // Every frame from client must have masking key.
613     static const size_t minimumPayloadSizeWithTwoByteExtendedPayloadLength = 126;
614     static const size_t minimumPayloadSizeWithEightByteExtendedPayloadLength = 0x10000;
615     size_t overhead = hybiBaseFramingOverhead + hybiMaskingKeyLength;
616     if (payloadSize >= minimumPayloadSizeWithEightByteExtendedPayloadLength)
617         overhead += 8;
618     else if (payloadSize >= minimumPayloadSizeWithTwoByteExtendedPayloadLength)
619         overhead += 2;
620     return overhead;
621 }
622
623 void WebSocket::dispatchOrQueueEvent(Ref<Event>&& event)
624 {
625     if (m_shouldDelayEventFiring)
626         m_pendingEvents.append(WTF::move(event));
627     else
628         dispatchEvent(WTF::move(event));
629 }
630
631 }  // namespace WebCore
632
633 #endif