#include "core/frame/LocalFrame.h"
#include "core/frame/csp/ContentSecurityPolicy.h"
#include "core/inspector/ScriptCallStack.h"
-#include "heap/Handle.h"
#include "modules/websockets/CloseEvent.h"
#include "platform/Logging.h"
#include "platform/blob/BlobData.h"
+#include "platform/heap/Handle.h"
#include "platform/weborigin/KnownPorts.h"
#include "platform/weborigin/SecurityOrigin.h"
+#include "public/platform/Platform.h"
#include "wtf/ArrayBuffer.h"
#include "wtf/ArrayBufferView.h"
+#include "wtf/Assertions.h"
#include "wtf/HashSet.h"
#include "wtf/PassOwnPtr.h"
#include "wtf/StdLibExtras.h"
WebSocket::EventQueue::~EventQueue() { stop(); }
-void WebSocket::EventQueue::dispatch(PassRefPtr<Event> event)
+void WebSocket::EventQueue::dispatch(PassRefPtrWillBeRawPtr<Event> event)
{
switch (m_state) {
case Active:
if (m_state != Active)
return;
- RefPtr<EventQueue> protect(this);
+ RefPtrWillBeRawPtr<EventQueue> protect(this);
- Deque<RefPtr<Event> > events;
+ WillBeHeapDeque<RefPtrWillBeMember<Event> > events;
events.swap(m_events);
while (!events.isEmpty()) {
if (m_state == Stopped || m_state == Suspended)
dispatchQueuedEvents();
}
+void WebSocket::EventQueue::trace(Visitor* visitor)
+{
+ visitor->trace(m_events);
+}
+
const size_t maxReasonSizeInBytes = 123;
-static inline bool isValidProtocolCharacter(UChar character)
+static inline bool isValidSubprotocolCharacter(UChar character)
{
- // Hybi-10 says "(Subprotocol string must consist of) characters in the range U+0021 to U+007E not including
- // separator characters as defined in [RFC2616]."
const UChar minimumProtocolCharacter = '!'; // U+0021.
const UChar maximumProtocolCharacter = '~'; // U+007E.
- return character >= minimumProtocolCharacter && character <= maximumProtocolCharacter
- && character != '"' && character != '(' && character != ')' && character != ',' && character != '/'
+ // Set to true if character does not matches "separators" ABNF defined in
+ // RFC2616. SP and HT are excluded since the range check excludes them.
+ bool isNotSeparator = character != '"' && character != '(' && character != ')' && character != ',' && character != '/'
&& !(character >= ':' && character <= '@') // U+003A - U+0040 (':', ';', '<', '=', '>', '?', '@').
&& !(character >= '[' && character <= ']') // U+005B - U+005D ('[', '\\', ']').
&& character != '{' && character != '}';
+ return character >= minimumProtocolCharacter && character <= maximumProtocolCharacter && isNotSeparator;
}
-static bool isValidProtocolString(const String& protocol)
+static bool isValidSubprotocolString(const String& protocol)
{
if (protocol.isEmpty())
return false;
for (size_t i = 0; i < protocol.length(); ++i) {
- if (!isValidProtocolCharacter(protocol[i]))
+ if (!isValidSubprotocolCharacter(protocol[i]))
return false;
}
return true;
}
-static String encodeProtocolString(const String& protocol)
+static String encodeSubprotocolString(const String& protocol)
{
StringBuilder builder;
for (size_t i = 0; i < protocol.length(); i++) {
executionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, message);
}
-PassRefPtr<WebSocket> WebSocket::create(ExecutionContext* context, const String& url, ExceptionState& exceptionState)
+PassRefPtrWillBeRawPtr<WebSocket> WebSocket::create(ExecutionContext* context, const String& url, ExceptionState& exceptionState)
{
Vector<String> protocols;
return create(context, url, protocols, exceptionState);
}
-PassRefPtr<WebSocket> WebSocket::create(ExecutionContext* context, const String& url, const Vector<String>& protocols, ExceptionState& exceptionState)
+PassRefPtrWillBeRawPtr<WebSocket> WebSocket::create(ExecutionContext* context, const String& url, const Vector<String>& protocols, ExceptionState& exceptionState)
{
if (url.isNull()) {
exceptionState.throwDOMException(SyntaxError, "Failed to create a WebSocket: the provided URL is invalid.");
return nullptr;
}
- RefPtr<WebSocket> webSocket(adoptRef(new WebSocket(context)));
+ RefPtrWillBeRawPtr<WebSocket> webSocket(adoptRefWillBeRefCountedGarbageCollected(new WebSocket(context)));
webSocket->suspendIfNeeded();
- webSocket->connect(context->completeURL(url), protocols, exceptionState);
+ webSocket->connect(url, protocols, exceptionState);
if (exceptionState.hadException())
return nullptr;
return webSocket.release();
}
-PassRefPtr<WebSocket> WebSocket::create(ExecutionContext* context, const String& url, const String& protocol, ExceptionState& exceptionState)
+PassRefPtrWillBeRawPtr<WebSocket> WebSocket::create(ExecutionContext* context, const String& url, const String& protocol, ExceptionState& exceptionState)
{
Vector<String> protocols;
protocols.append(protocol);
exceptionState.throwDOMException(SyntaxError, "The URL's scheme must be either 'ws' or 'wss'. '" + m_url.protocol() + "' is not allowed.");
return;
}
- if (MixedContentChecker::isMixedContent(executionContext()->securityOrigin(), m_url)) {
- // FIXME: Throw an exception and close the connection.
- String message = "Connecting to a non-secure WebSocket server from a secure origin is deprecated.";
- executionContext()->addConsoleMessage(JSMessageSource, WarningMessageLevel, message);
- }
+
if (m_url.hasFragmentIdentifier()) {
m_state = CLOSED;
exceptionState.throwDOMException(SyntaxError, "The URL contains a fragment identifier ('" + m_url.fragmentIdentifier() + "'). Fragment identifiers are not allowed in WebSocket URLs.");
m_channel = WebSocketChannel::create(executionContext(), this);
- // 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
- // imposes a stricter rule: "the elements MUST be non-empty strings with characters as defined in [RFC2616],
- // and MUST all be unique strings."
- //
- // Here, we throw SyntaxError 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])) {
+ if (!isValidSubprotocolString(protocols[i])) {
m_state = CLOSED;
- exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeProtocolString(protocols[i]) + "' is invalid.");
+ exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeSubprotocolString(protocols[i]) + "' is invalid.");
releaseChannel();
return;
}
for (size_t i = 0; i < protocols.size(); ++i) {
if (!visited.add(protocols[i]).isNewEntry) {
m_state = CLOSED;
- exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeProtocolString(protocols[i]) + "' is duplicated.");
+ exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeSubprotocolString(protocols[i]) + "' is duplicated.");
releaseChannel();
return;
}
if (!protocols.isEmpty())
protocolString = joinStrings(protocols, subProtocolSeperator());
- m_channel->connect(m_url, protocolString);
+ if (!m_channel->connect(m_url, protocolString)) {
+ m_state = CLOSED;
+ exceptionState.throwSecurityError("An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.");
+ releaseChannel();
+ return;
+ }
}
-void WebSocket::handleSendResult(WebSocketChannel::SendResult result, ExceptionState& exceptionState)
+void WebSocket::handleSendResult(WebSocketChannel::SendResult result, ExceptionState& exceptionState, WebSocketSendType dataType)
{
switch (result) {
case WebSocketChannel::InvalidMessage:
logError("WebSocket send() failed.");
return;
case WebSocketChannel::SendSuccess:
+ blink::Platform::current()->histogramEnumeration("WebCore.WebSocket.SendType", dataType, WebSocketSendTypeMax);
return;
}
ASSERT_NOT_REACHED();
return;
}
ASSERT(m_channel);
- handleSendResult(m_channel->send(message), exceptionState);
+ handleSendResult(m_channel->send(message), exceptionState, WebSocketSendTypeString);
}
void WebSocket::send(ArrayBuffer* binaryData, ExceptionState& exceptionState)
return;
}
ASSERT(m_channel);
- handleSendResult(m_channel->send(*binaryData, 0, binaryData->byteLength()), exceptionState);
+ handleSendResult(m_channel->send(*binaryData, 0, binaryData->byteLength()), exceptionState, WebSocketSendTypeArrayBuffer);
}
void WebSocket::send(ArrayBufferView* arrayBufferView, ExceptionState& exceptionState)
}
ASSERT(m_channel);
RefPtr<ArrayBuffer> arrayBuffer(arrayBufferView->buffer());
- handleSendResult(m_channel->send(*arrayBuffer, arrayBufferView->byteOffset(), arrayBufferView->byteLength()), exceptionState);
+ handleSendResult(m_channel->send(*arrayBuffer, arrayBufferView->byteOffset(), arrayBufferView->byteLength()), exceptionState, WebSocketSendTypeArrayBufferView);
}
void WebSocket::send(Blob* binaryData, ExceptionState& exceptionState)
return;
}
ASSERT(m_channel);
- handleSendResult(m_channel->send(binaryData->blobDataHandle()), exceptionState);
+ handleSendResult(m_channel->send(binaryData->blobDataHandle()), exceptionState, WebSocketSendTypeBlob);
}
void WebSocket::close(unsigned short code, const String& reason, ExceptionState& exceptionState)
}
case BinaryTypeArrayBuffer:
- m_eventQueue->dispatch(MessageEvent::create(ArrayBuffer::create(binaryData->data(), binaryData->size()), SecurityOrigin::create(m_url)->toString()));
+ RefPtr<ArrayBuffer> arrayBuffer = ArrayBuffer::create(binaryData->data(), binaryData->size());
+ if (!arrayBuffer) {
+ // Failed to allocate an ArrayBuffer. We need to crash the renderer
+ // since there's no way defined in the spec to tell this to the
+ // user.
+ CRASH();
+ }
+ m_eventQueue->dispatch(MessageEvent::create(arrayBuffer.release(), SecurityOrigin::create(m_url)->toString()));
break;
}
}
return overhead;
}
+void WebSocket::trace(Visitor* visitor)
+{
+ visitor->trace(m_channel);
+ visitor->trace(m_eventQueue);
+}
+
} // namespace WebCore