diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/Modules/websockets/WebSocket.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/Modules/websockets/WebSocket.cpp')
-rw-r--r-- | Source/WebCore/Modules/websockets/WebSocket.cpp | 416 |
1 files changed, 240 insertions, 176 deletions
diff --git a/Source/WebCore/Modules/websockets/WebSocket.cpp b/Source/WebCore/Modules/websockets/WebSocket.cpp index 72b192af3..bed936267 100644 --- a/Source/WebCore/Modules/websockets/WebSocket.cpp +++ b/Source/WebCore/Modules/websockets/WebSocket.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2015-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -35,35 +36,38 @@ #include "WebSocket.h" #include "Blob.h" -#include "BlobData.h" #include "CloseEvent.h" #include "ContentSecurityPolicy.h" #include "DOMWindow.h" #include "Document.h" #include "Event.h" -#include "EventException.h" #include "EventListener.h" #include "EventNames.h" #include "ExceptionCode.h" #include "Frame.h" #include "Logging.h" #include "MessageEvent.h" -#include "ScriptCallStack.h" +#include "ResourceLoadObserver.h" #include "ScriptController.h" #include "ScriptExecutionContext.h" #include "SecurityOrigin.h" +#include "SocketProvider.h" #include "ThreadableWebSocketChannel.h" #include "WebSocketChannel.h" +#include <inspector/ScriptCallStack.h> #include <runtime/ArrayBuffer.h> #include <runtime/ArrayBufferView.h> #include <wtf/HashSet.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> +#include <wtf/RunLoop.h> #include <wtf/StdLibExtras.h> #include <wtf/text/CString.h> #include <wtf/text/StringBuilder.h> #include <wtf/text/WTFString.h> +#if USE(WEB_THREAD) +#include "WebCoreThreadRun.h" +#endif + namespace WebCore { const size_t maxReasonSizeInBytes = 123; @@ -81,12 +85,12 @@ static inline bool isValidProtocolCharacter(UChar character) && character != '{' && character != '}'; } -static bool isValidProtocolString(const String& protocol) +static bool isValidProtocolString(StringView protocol) { if (protocol.isEmpty()) return false; - for (size_t i = 0; i < protocol.length(); ++i) { - if (!isValidProtocolCharacter(protocol[i])) + for (auto codeUnit : protocol.codeUnits()) { + if (!isValidProtocolCharacter(codeUnit)) return false; } return true; @@ -99,7 +103,7 @@ static String encodeProtocolString(const String& protocol) if (protocol[i] < 0x20 || protocol[i] > 0x7E) builder.append(String::format("\\u%04X", protocol[i])); else if (protocol[i] == 0x5c) - builder.append("\\\\"); + builder.appendLiteral("\\\\"); else builder.append(protocol[i]); } @@ -117,10 +121,10 @@ static String joinStrings(const Vector<String>& strings, const char* separator) return builder.toString(); } -static unsigned long saturateAdd(unsigned long a, unsigned long b) +static unsigned saturateAdd(unsigned a, unsigned b) { - if (std::numeric_limits<unsigned long>::max() - a < b) - return std::numeric_limits<unsigned long>::max(); + if (std::numeric_limits<unsigned>::max() - a < b) + return std::numeric_limits<unsigned>::max(); return a + b; } @@ -136,19 +140,16 @@ bool WebSocket::isAvailable() return webSocketsAvailable; } -const char* WebSocket::subProtocolSeperator() +const char* WebSocket::subprotocolSeparator() { return ", "; } WebSocket::WebSocket(ScriptExecutionContext& context) : ActiveDOMObject(&context) - , m_state(CONNECTING) - , m_bufferedAmount(0) - , m_bufferedAmountAfterClose(0) - , m_binaryType(BinaryTypeBlob) - , m_subprotocol("") - , m_extensions("") + , m_subprotocol(emptyString()) + , m_extensions(emptyString()) + , m_resumeTimer(*this, &WebSocket::resumeTimerFired) { } @@ -158,102 +159,95 @@ WebSocket::~WebSocket() m_channel->disconnect(); } -PassRefPtr<WebSocket> WebSocket::create(ScriptExecutionContext& context) -{ - RefPtr<WebSocket> webSocket(adoptRef(new WebSocket(context))); - webSocket->suspendIfNeeded(); - return webSocket.release(); -} - -PassRefPtr<WebSocket> WebSocket::create(ScriptExecutionContext& context, const String& url, ExceptionCode& ec) +ExceptionOr<Ref<WebSocket>> WebSocket::create(ScriptExecutionContext& context, const String& url) { - Vector<String> protocols; - return WebSocket::create(context, url, protocols, ec); + return create(context, url, Vector<String> { }); } -PassRefPtr<WebSocket> WebSocket::create(ScriptExecutionContext& context, const String& url, const Vector<String>& protocols, ExceptionCode& ec) +ExceptionOr<Ref<WebSocket>> WebSocket::create(ScriptExecutionContext& context, const String& url, const Vector<String>& protocols) { - if (url.isNull()) { - ec = SYNTAX_ERR; - return 0; - } + if (url.isNull()) + return Exception { SYNTAX_ERR }; - RefPtr<WebSocket> webSocket(adoptRef(new WebSocket(context))); - webSocket->suspendIfNeeded(); + auto socket = adoptRef(*new WebSocket(context)); + socket->suspendIfNeeded(); - webSocket->connect(context.completeURL(url), protocols, ec); - if (ec) - return 0; + auto result = socket->connect(context.completeURL(url), protocols); + if (result.hasException()) + return result.releaseException(); - return webSocket.release(); + return WTFMove(socket); } -PassRefPtr<WebSocket> WebSocket::create(ScriptExecutionContext& context, const String& url, const String& protocol, ExceptionCode& ec) +ExceptionOr<Ref<WebSocket>> WebSocket::create(ScriptExecutionContext& context, const String& url, const String& protocol) { - Vector<String> protocols; - protocols.append(protocol); - return WebSocket::create(context, url, protocols, ec); + return create(context, url, Vector<String> { 1, protocol }); } -void WebSocket::connect(const String& url, ExceptionCode& ec) +ExceptionOr<void> WebSocket::connect(const String& url) { - Vector<String> protocols; - connect(url, protocols, ec); + return connect(url, Vector<String> { }); } -void WebSocket::connect(const String& url, const String& protocol, ExceptionCode& ec) +ExceptionOr<void> WebSocket::connect(const String& url, const String& protocol) { - Vector<String> protocols; - protocols.append(protocol); - connect(url, protocols, ec); + return connect(url, Vector<String> { 1, protocol }); } -void WebSocket::connect(const String& url, const Vector<String>& protocols, ExceptionCode& ec) +ExceptionOr<void> WebSocket::connect(const String& url, const Vector<String>& protocols) { LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data()); m_url = URL(URL(), url); + ASSERT(scriptExecutionContext()); + auto& context = *scriptExecutionContext(); + if (!m_url.isValid()) { - scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Invalid url for WebSocket " + m_url.stringCenterEllipsizedToLength()); + context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Invalid url for WebSocket " + m_url.stringCenterEllipsizedToLength()); m_state = CLOSED; - ec = SYNTAX_ERR; - return; + return Exception { SYNTAX_ERR }; } if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { - scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Wrong url scheme for WebSocket " + m_url.stringCenterEllipsizedToLength()); + context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Wrong url scheme for WebSocket " + m_url.stringCenterEllipsizedToLength()); m_state = CLOSED; - ec = SYNTAX_ERR; - return; + return Exception { SYNTAX_ERR }; } if (m_url.hasFragmentIdentifier()) { - scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "URL has fragment component " + m_url.stringCenterEllipsizedToLength()); + context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, "URL has fragment component " + m_url.stringCenterEllipsizedToLength()); m_state = CLOSED; - ec = SYNTAX_ERR; - return; + return Exception { SYNTAX_ERR }; } + + ASSERT(context.contentSecurityPolicy()); + auto& contentSecurityPolicy = *context.contentSecurityPolicy(); + + contentSecurityPolicy.upgradeInsecureRequestIfNeeded(m_url, ContentSecurityPolicy::InsecureRequestType::Load); + if (!portAllowed(m_url)) { - scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "WebSocket port " + String::number(m_url.port()) + " blocked"); + String message; + if (m_url.port()) + message = makeString("WebSocket port ", String::number(m_url.port().value()), " blocked"); + else + message = ASCIILiteral("WebSocket without port blocked"); + context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, message); m_state = CLOSED; - ec = SECURITY_ERR; - return; + return Exception { SECURITY_ERR }; } // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved. - bool shouldBypassMainWorldContentSecurityPolicy = false; - if (scriptExecutionContext()->isDocument()) { - Document* document = toDocument(scriptExecutionContext()); - shouldBypassMainWorldContentSecurityPolicy = document->frame()->script().shouldBypassMainWorldContentSecurityPolicy(); - } - if (!shouldBypassMainWorldContentSecurityPolicy && !scriptExecutionContext()->contentSecurityPolicy()->allowConnectToSource(m_url)) { + if (!context.shouldBypassMainWorldContentSecurityPolicy() && !contentSecurityPolicy.allowConnectToSource(m_url)) { m_state = CLOSED; // FIXME: Should this be throwing an exception? - ec = SECURITY_ERR; - return; + return Exception { SECURITY_ERR }; } - m_channel = ThreadableWebSocketChannel::create(scriptExecutionContext(), this); + if (auto* provider = context.socketProvider()) + m_channel = ThreadableWebSocketChannel::create(*scriptExecutionContext(), *this, *provider); + + // Every ScriptExecutionContext should have a SocketProvider. + RELEASE_ASSERT(m_channel); // FIXME: There is a disagreement about restriction of subprotocols between WebSocket API and hybi-10 protocol // draft. The former simply says "only characters in the range U+0021 to U+007E are allowed," while the latter @@ -262,138 +256,155 @@ void WebSocket::connect(const String& url, const Vector<String>& protocols, Exce // // Here, we throw SYNTAX_ERR if the given protocols do not meet the latter criteria. This behavior does not // comply with WebSocket API specification, but it seems to be the only reasonable way to handle this conflict. - for (size_t i = 0; i < protocols.size(); ++i) { - if (!isValidProtocolString(protocols[i])) { - scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Wrong protocol for WebSocket '" + encodeProtocolString(protocols[i]) + "'"); + for (auto& protocol : protocols) { + if (!isValidProtocolString(protocol)) { + context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Wrong protocol for WebSocket '" + encodeProtocolString(protocol) + "'"); m_state = CLOSED; - ec = SYNTAX_ERR; - return; + return Exception { SYNTAX_ERR }; } } HashSet<String> visited; - for (size_t i = 0; i < protocols.size(); ++i) { - if (!visited.add(protocols[i]).isNewEntry) { - scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "WebSocket protocols contain duplicates: '" + encodeProtocolString(protocols[i]) + "'"); + for (auto& protocol : protocols) { + if (!visited.add(protocol).isNewEntry) { + context.addConsoleMessage(MessageSource::JS, MessageLevel::Error, "WebSocket protocols contain duplicates: '" + encodeProtocolString(protocol) + "'"); m_state = CLOSED; - ec = SYNTAX_ERR; - return; + return Exception { SYNTAX_ERR }; } } + if (is<Document>(context)) { + Document& document = downcast<Document>(context); + if (!document.frame()->loader().mixedContentChecker().canRunInsecureContent(document.securityOrigin(), m_url)) { + // Balanced by the call to ActiveDOMObject::unsetPendingActivity() in WebSocket::stop(). + ActiveDOMObject::setPendingActivity(this); + + // We must block this connection. Instead of throwing an exception, we indicate this + // using the error event. But since this code executes as part of the WebSocket's + // constructor, we have to wait until the constructor has completed before firing the + // event; otherwise, users can't connect to the event. +#if USE(WEB_THREAD) + ref(); + dispatch_async(dispatch_get_main_queue(), ^{ + WebThreadRun(^{ + dispatchOrQueueErrorEvent(); + stop(); + deref(); + }); + }); +#else + RunLoop::main().dispatch([this, protectedThis = makeRef(*this)]() { + dispatchOrQueueErrorEvent(); + stop(); + }); +#endif + return { }; + } else + ResourceLoadObserver::sharedObserver().logWebSocketLoading(document.frame(), m_url); + } + String protocolString; if (!protocols.isEmpty()) - protocolString = joinStrings(protocols, subProtocolSeperator()); + protocolString = joinStrings(protocols, subprotocolSeparator()); m_channel->connect(m_url, protocolString); ActiveDOMObject::setPendingActivity(this); + + return { }; } -void WebSocket::send(const String& message, ExceptionCode& ec) +ExceptionOr<void> WebSocket::send(const String& message) { LOG(Network, "WebSocket %p send() Sending String '%s'", this, message.utf8().data()); - if (m_state == CONNECTING) { - ec = INVALID_STATE_ERR; - return; - } + if (m_state == CONNECTING) + return Exception { INVALID_STATE_ERR }; // No exception is raised if the connection was once established but has subsequently been closed. if (m_state == CLOSING || m_state == CLOSED) { size_t payloadSize = message.utf8().length(); m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize); m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize)); - return; + return { }; } ASSERT(m_channel); - ThreadableWebSocketChannel::SendResult result = m_channel->send(message); - if (result == ThreadableWebSocketChannel::InvalidMessage) { - scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Websocket message contains invalid character(s)."); - ec = SYNTAX_ERR; - return; - } + m_channel->send(message); + return { }; } -void WebSocket::send(ArrayBuffer* binaryData, ExceptionCode& ec) +ExceptionOr<void> WebSocket::send(ArrayBuffer& binaryData) { - LOG(Network, "WebSocket %p send() Sending ArrayBuffer %p", this, binaryData); - ASSERT(binaryData); - if (m_state == CONNECTING) { - ec = INVALID_STATE_ERR; - return; - } + LOG(Network, "WebSocket %p send() Sending ArrayBuffer %p", this, &binaryData); + if (m_state == CONNECTING) + return Exception { INVALID_STATE_ERR }; if (m_state == CLOSING || m_state == CLOSED) { - unsigned payloadSize = binaryData->byteLength(); + unsigned payloadSize = binaryData.byteLength(); m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize); m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize)); - return; + return { }; } ASSERT(m_channel); - m_channel->send(*binaryData, 0, binaryData->byteLength()); + m_channel->send(binaryData, 0, binaryData.byteLength()); + return { }; } -void WebSocket::send(ArrayBufferView* arrayBufferView, ExceptionCode& ec) +ExceptionOr<void> WebSocket::send(ArrayBufferView& arrayBufferView) { - LOG(Network, "WebSocket %p send() Sending ArrayBufferView %p", this, arrayBufferView); - ASSERT(arrayBufferView); - if (m_state == CONNECTING) { - ec = INVALID_STATE_ERR; - return; - } + LOG(Network, "WebSocket %p send() Sending ArrayBufferView %p", this, &arrayBufferView); + + if (m_state == CONNECTING) + return Exception { INVALID_STATE_ERR }; if (m_state == CLOSING || m_state == CLOSED) { - unsigned payloadSize = arrayBufferView->byteLength(); + unsigned payloadSize = arrayBufferView.byteLength(); m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize); m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize)); - return; + return { }; } ASSERT(m_channel); - RefPtr<ArrayBuffer> arrayBuffer(arrayBufferView->buffer()); - m_channel->send(*arrayBuffer, arrayBufferView->byteOffset(), arrayBufferView->byteLength()); + m_channel->send(*arrayBufferView.unsharedBuffer(), arrayBufferView.byteOffset(), arrayBufferView.byteLength()); + return { }; } -void WebSocket::send(Blob* binaryData, ExceptionCode& ec) +ExceptionOr<void> WebSocket::send(Blob& binaryData) { - LOG(Network, "WebSocket %p send() Sending Blob '%s'", this, binaryData->url().stringCenterEllipsizedToLength().utf8().data()); - ASSERT(binaryData); - if (m_state == CONNECTING) { - ec = INVALID_STATE_ERR; - return; - } + LOG(Network, "WebSocket %p send() Sending Blob '%s'", this, binaryData.url().stringCenterEllipsizedToLength().utf8().data()); + if (m_state == CONNECTING) + return Exception { INVALID_STATE_ERR }; if (m_state == CLOSING || m_state == CLOSED) { - unsigned long payloadSize = static_cast<unsigned long>(binaryData->size()); + unsigned payloadSize = static_cast<unsigned>(binaryData.size()); m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize); m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize)); - return; + return { }; } ASSERT(m_channel); - m_channel->send(*binaryData); + m_channel->send(binaryData); + return { }; } -void WebSocket::close(int code, const String& reason, ExceptionCode& ec) +ExceptionOr<void> WebSocket::close(std::optional<unsigned short> optionalCode, const String& reason) { + int code = optionalCode ? optionalCode.value() : static_cast<int>(WebSocketChannel::CloseEventCodeNotSpecified); if (code == WebSocketChannel::CloseEventCodeNotSpecified) LOG(Network, "WebSocket %p close() without code and reason", this); else { LOG(Network, "WebSocket %p close() code=%d reason='%s'", this, code, reason.utf8().data()); - if (!(code == WebSocketChannel::CloseEventCodeNormalClosure || (WebSocketChannel::CloseEventCodeMinimumUserDefined <= code && code <= WebSocketChannel::CloseEventCodeMaximumUserDefined))) { - ec = INVALID_ACCESS_ERR; - return; - } + if (!(code == WebSocketChannel::CloseEventCodeNormalClosure || (WebSocketChannel::CloseEventCodeMinimumUserDefined <= code && code <= WebSocketChannel::CloseEventCodeMaximumUserDefined))) + return Exception { INVALID_ACCESS_ERR }; CString utf8 = reason.utf8(StrictConversionReplacingUnpairedSurrogatesWithFFFD); if (utf8.length() > maxReasonSizeInBytes) { - scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "WebSocket close message is too long."); - ec = SYNTAX_ERR; - return; + scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("WebSocket close message is too long.")); + return Exception { SYNTAX_ERR }; } } if (m_state == CLOSING || m_state == CLOSED) - return; + return { }; if (m_state == CONNECTING) { m_state = CLOSING; m_channel->fail("WebSocket is closed before the connection is established."); - return; + return { }; } m_state = CLOSING; if (m_channel) m_channel->close(code, reason); + return { }; } const URL& WebSocket::url() const @@ -406,7 +417,7 @@ WebSocket::State WebSocket::readyState() const return m_state; } -unsigned long WebSocket::bufferedAmount() const +unsigned WebSocket::bufferedAmount() const { return saturateAdd(m_bufferedAmount, m_bufferedAmountAfterClose); } @@ -424,26 +435,27 @@ String WebSocket::extensions() const String WebSocket::binaryType() const { switch (m_binaryType) { - case BinaryTypeBlob: - return "blob"; - case BinaryTypeArrayBuffer: - return "arraybuffer"; + case BinaryType::Blob: + return ASCIILiteral("blob"); + case BinaryType::ArrayBuffer: + return ASCIILiteral("arraybuffer"); } ASSERT_NOT_REACHED(); return String(); } -void WebSocket::setBinaryType(const String& binaryType) +ExceptionOr<void> WebSocket::setBinaryType(const String& binaryType) { if (binaryType == "blob") { - m_binaryType = BinaryTypeBlob; - return; + m_binaryType = BinaryType::Blob; + return { }; } if (binaryType == "arraybuffer") { - m_binaryType = BinaryTypeArrayBuffer; - return; + m_binaryType = BinaryType::ArrayBuffer; + return { }; } - scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "'" + binaryType + "' is not a valid value for binaryType; binaryType remains unchanged."); + scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "'" + binaryType + "' is not a valid value for binaryType; binaryType remains unchanged."); + return Exception { SYNTAX_ERR }; } EventTargetInterface WebSocket::eventTargetInterface() const @@ -464,21 +476,49 @@ void WebSocket::contextDestroyed() ActiveDOMObject::contextDestroyed(); } -bool WebSocket::canSuspend() const +bool WebSocket::canSuspendForDocumentSuspension() const { - return !m_channel; + return true; } -void WebSocket::suspend(ReasonForSuspension) +void WebSocket::suspend(ReasonForSuspension reason) { - if (m_channel) - m_channel->suspend(); + if (m_resumeTimer.isActive()) + m_resumeTimer.stop(); + + m_shouldDelayEventFiring = true; + + if (m_channel) { + if (reason == ActiveDOMObject::PageCache) { + // This will cause didClose() to be called. + m_channel->fail("WebSocket is closed due to suspension."); + } else + m_channel->suspend(); + } } void WebSocket::resume() { if (m_channel) m_channel->resume(); + else if (!m_pendingEvents.isEmpty() && !m_resumeTimer.isActive()) { + // Fire the pending events in a timer as we are not allowed to execute arbitrary JS from resume(). + m_resumeTimer.startOneShot(0); + } + + m_shouldDelayEventFiring = false; +} + +void WebSocket::resumeTimerFired() +{ + Ref<WebSocket> protectedThis(*this); + + ASSERT(!m_pendingEvents.isEmpty()); + + // Check m_shouldDelayEventFiring when iterating in case firing an event causes + // suspend() to be called. + while (!m_pendingEvents.isEmpty() && !m_shouldDelayEventFiring) + dispatchEvent(m_pendingEvents.takeFirst()); } void WebSocket::stop() @@ -486,18 +526,24 @@ void WebSocket::stop() bool pending = hasPendingActivity(); if (m_channel) m_channel->disconnect(); - m_channel = 0; + m_channel = nullptr; m_state = CLOSED; + m_pendingEvents.clear(); ActiveDOMObject::stop(); if (pending) ActiveDOMObject::unsetPendingActivity(this); } +const char* WebSocket::activeDOMObjectName() const +{ + return "WebSocket"; +} + void WebSocket::didConnect() { LOG(Network, "WebSocket %p didConnect()", this); if (m_state != CONNECTING) { - didClose(0, ClosingHandshakeIncomplete, WebSocketChannel::CloseEventCodeAbnormalClosure, ""); + didClose(0, ClosingHandshakeIncomplete, WebSocketChannel::CloseEventCodeAbnormalClosure, emptyString()); return; } ASSERT(scriptExecutionContext()); @@ -516,23 +562,16 @@ void WebSocket::didReceiveMessage(const String& msg) dispatchEvent(MessageEvent::create(msg, SecurityOrigin::create(m_url)->toString())); } -void WebSocket::didReceiveBinaryData(PassOwnPtr<Vector<char>> binaryData) +void WebSocket::didReceiveBinaryData(Vector<uint8_t>&& binaryData) { - LOG(Network, "WebSocket %p didReceiveBinaryData() %lu byte binary message", this, static_cast<unsigned long>(binaryData->size())); + LOG(Network, "WebSocket %p didReceiveBinaryData() %u byte binary message", this, static_cast<unsigned>(binaryData.size())); switch (m_binaryType) { - case BinaryTypeBlob: { - size_t size = binaryData->size(); - RefPtr<RawData> rawData = RawData::create(); - binaryData->swap(*rawData->mutableData()); - auto blobData = std::make_unique<BlobData>(); - blobData->appendData(rawData.release(), 0, BlobDataItem::toEndOfFile); - RefPtr<Blob> blob = Blob::create(std::move(blobData), size); - dispatchEvent(MessageEvent::create(blob.release(), SecurityOrigin::create(m_url)->toString())); + case BinaryType::Blob: + // FIXME: We just received the data from NetworkProcess, and are sending it back. This is inefficient. + dispatchEvent(MessageEvent::create(Blob::create(WTFMove(binaryData), emptyString()), SecurityOrigin::create(m_url)->toString())); break; - } - - case BinaryTypeArrayBuffer: - dispatchEvent(MessageEvent::create(ArrayBuffer::create(binaryData->data(), binaryData->size()), SecurityOrigin::create(m_url)->toString())); + case BinaryType::ArrayBuffer: + dispatchEvent(MessageEvent::create(ArrayBuffer::create(binaryData.data(), binaryData.size()), SecurityOrigin::create(m_url)->toString())); break; } } @@ -540,13 +579,14 @@ void WebSocket::didReceiveBinaryData(PassOwnPtr<Vector<char>> binaryData) void WebSocket::didReceiveMessageError() { LOG(Network, "WebSocket %p didReceiveErrorMessage()", this); + m_state = CLOSED; ASSERT(scriptExecutionContext()); - dispatchEvent(Event::create(eventNames().errorEvent, false, false)); + dispatchOrQueueErrorEvent(); } -void WebSocket::didUpdateBufferedAmount(unsigned long bufferedAmount) +void WebSocket::didUpdateBufferedAmount(unsigned bufferedAmount) { - LOG(Network, "WebSocket %p didUpdateBufferedAmount() New bufferedAmount is %lu", this, bufferedAmount); + LOG(Network, "WebSocket %p didUpdateBufferedAmount() New bufferedAmount is %u", this, bufferedAmount); if (m_state == CLOSED) return; m_bufferedAmount = bufferedAmount; @@ -558,7 +598,7 @@ void WebSocket::didStartClosingHandshake() m_state = CLOSING; } -void WebSocket::didClose(unsigned long unhandledBufferedAmount, ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason) +void WebSocket::didClose(unsigned unhandledBufferedAmount, ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason) { LOG(Network, "WebSocket %p didClose()", this); if (!m_channel) @@ -567,16 +607,23 @@ void WebSocket::didClose(unsigned long unhandledBufferedAmount, ClosingHandshake m_state = CLOSED; m_bufferedAmount = unhandledBufferedAmount; ASSERT(scriptExecutionContext()); - RefPtr<CloseEvent> event = CloseEvent::create(wasClean, code, reason); - dispatchEvent(event); + + dispatchOrQueueEvent(CloseEvent::create(wasClean, code, reason)); + if (m_channel) { m_channel->disconnect(); - m_channel = 0; + m_channel = nullptr; } if (hasPendingActivity()) ActiveDOMObject::unsetPendingActivity(this); } +void WebSocket::didUpgradeURL() +{ + ASSERT(m_url.protocolIs("ws")); + m_url.setProtocol("wss"); +} + size_t WebSocket::getFramingOverhead(size_t payloadSize) { static const size_t hybiBaseFramingOverhead = 2; // Every frame has at least two-byte header. @@ -591,6 +638,23 @@ size_t WebSocket::getFramingOverhead(size_t payloadSize) return overhead; } +void WebSocket::dispatchOrQueueErrorEvent() +{ + if (m_dispatchedErrorEvent) + return; + + m_dispatchedErrorEvent = true; + dispatchOrQueueEvent(Event::create(eventNames().errorEvent, false, false)); +} + +void WebSocket::dispatchOrQueueEvent(Ref<Event>&& event) +{ + if (m_shouldDelayEventFiring) + m_pendingEvents.append(WTFMove(event)); + else + dispatchEvent(event); +} + } // namespace WebCore #endif |