diff options
Diffstat (limited to 'WebCore/websockets')
-rw-r--r-- | WebCore/websockets/ThreadableWebSocketChannel.cpp | 74 | ||||
-rw-r--r-- | WebCore/websockets/ThreadableWebSocketChannel.h | 69 | ||||
-rw-r--r-- | WebCore/websockets/ThreadableWebSocketChannelClientWrapper.h | 127 | ||||
-rw-r--r-- | WebCore/websockets/WebSocket.cpp | 36 | ||||
-rw-r--r-- | WebCore/websockets/WebSocket.h | 20 | ||||
-rw-r--r-- | WebCore/websockets/WebSocket.idl | 1 | ||||
-rw-r--r-- | WebCore/websockets/WebSocketChannel.cpp | 8 | ||||
-rw-r--r-- | WebCore/websockets/WebSocketChannel.h | 28 | ||||
-rw-r--r-- | WebCore/websockets/WebSocketChannelClient.h | 6 | ||||
-rw-r--r-- | WebCore/websockets/WebSocketHandshake.cpp | 64 | ||||
-rw-r--r-- | WebCore/websockets/WebSocketHandshake.h | 6 | ||||
-rw-r--r-- | WebCore/websockets/WorkerThreadableWebSocketChannel.cpp | 362 | ||||
-rw-r--r-- | WebCore/websockets/WorkerThreadableWebSocketChannel.h | 155 |
13 files changed, 895 insertions, 61 deletions
diff --git a/WebCore/websockets/ThreadableWebSocketChannel.cpp b/WebCore/websockets/ThreadableWebSocketChannel.cpp new file mode 100644 index 0000000..28b9263 --- /dev/null +++ b/WebCore/websockets/ThreadableWebSocketChannel.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_SOCKETS) + +#include "ThreadableWebSocketChannel.h" + +#include "PlatformString.h" +#include "ScriptExecutionContext.h" +#include "ThreadableWebSocketChannelClientWrapper.h" +#include "WebSocketChannel.h" +#include "WebSocketChannelClient.h" +#include "WorkerContext.h" +#include "WorkerRunLoop.h" +#include "WorkerThread.h" +#include "WorkerThreadableWebSocketChannel.h" + +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +static const char webSocketChannelMode[] = "webSocketChannelMode"; + +PassRefPtr<ThreadableWebSocketChannel> ThreadableWebSocketChannel::create(ScriptExecutionContext* context, WebSocketChannelClient* client, const KURL& url, const String& protocol) +{ + ASSERT(context); + ASSERT(client); + +#if ENABLE(WORKERS) + if (context->isWorkerContext()) { + WorkerContext* workerContext = static_cast<WorkerContext*>(context); + WorkerRunLoop& runLoop = workerContext->thread()->runLoop(); + String mode = webSocketChannelMode; + mode.append(String::number(runLoop.createUniqueId())); + return WorkerThreadableWebSocketChannel::create(workerContext, client, mode, url, protocol); + } +#endif // ENABLE(WORKERS) + + ASSERT(context->isDocument()); + return WebSocketChannel::create(context, client, url, protocol); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_SOCKETS) diff --git a/WebCore/websockets/ThreadableWebSocketChannel.h b/WebCore/websockets/ThreadableWebSocketChannel.h new file mode 100644 index 0000000..74ea4b4 --- /dev/null +++ b/WebCore/websockets/ThreadableWebSocketChannel.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ThreadableWebSocketChannel_h +#define ThreadableWebSocketChannel_h + +#if ENABLE(WEB_SOCKETS) + +#include <wtf/Noncopyable.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class KURL; +class ScriptExecutionContext; +class String; +class WebSocketChannelClient; + +class ThreadableWebSocketChannel : public Noncopyable { +public: + static PassRefPtr<ThreadableWebSocketChannel> create(ScriptExecutionContext*, WebSocketChannelClient*, const KURL&, const String& protocol); + + virtual void connect() = 0; + virtual bool send(const String& message) = 0; + virtual unsigned long bufferedAmount() const = 0; + virtual void close() = 0; + virtual void disconnect() = 0; // Will suppress didClose(). + + void ref() { refThreadableWebSocketChannel(); } + void deref() { derefThreadableWebSocketChannel(); } + +protected: + virtual ~ThreadableWebSocketChannel() { } + virtual void refThreadableWebSocketChannel() = 0; + virtual void derefThreadableWebSocketChannel() = 0; +}; + +} // namespace WebCore + +#endif // ENABLE(WEB_SOCKETS) + +#endif // ThreadableWebSocketChannel_h diff --git a/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.h b/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.h new file mode 100644 index 0000000..8bf51fa --- /dev/null +++ b/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ThreadableWebSocketChannelClientWrapper_h +#define ThreadableWebSocketChannelClientWrapper_h + +#if ENABLE(WEB_SOCKETS) + +#include "WebSocketChannelClient.h" +#include <wtf/PassRefPtr.h> +#include <wtf/Threading.h> + +namespace WebCore { + +class String; + +class ThreadableWebSocketChannelClientWrapper : public ThreadSafeShared<ThreadableWebSocketChannelClientWrapper> { +public: + static PassRefPtr<ThreadableWebSocketChannelClientWrapper> create(WebSocketChannelClient* client) + { + return adoptRef(new ThreadableWebSocketChannelClientWrapper(client)); + } + + void clearSyncMethodDone() + { + m_syncMethodDone = false; + } + void setSyncMethodDone() + { + m_syncMethodDone = true; + } + + bool syncMethodDone() const + { + return m_syncMethodDone; + } + + bool sent() const + { + return m_sent; + } + void setSent(bool sent) + { + m_sent = sent; + m_syncMethodDone = true; + } + + unsigned long bufferedAmount() const + { + return m_bufferedAmount; + } + void setBufferedAmount(unsigned long bufferedAmount) + { + m_bufferedAmount = bufferedAmount; + m_syncMethodDone = true; + } + + void clearClient() + { + m_client = 0; + } + + void didConnect() + { + if (m_client) + m_client->didConnect(); + } + + void didReceiveMessage(const String& msg) + { + if (m_client) + m_client->didReceiveMessage(msg); + } + + void didClose() + { + if (m_client) + m_client->didClose(); + } + +protected: + ThreadableWebSocketChannelClientWrapper(WebSocketChannelClient* client) + : m_client(client) + , m_syncMethodDone(false) + , m_sent(false) + , m_bufferedAmount(0) + { + } + + WebSocketChannelClient* m_client; + bool m_syncMethodDone; + bool m_sent; + unsigned long m_bufferedAmount; +}; + +} // namespace WebCore + +#endif // ENABLE(WEB_SOCKETS) + +#endif // ThreadableWebSocketChannelClientWrapper_h diff --git a/WebCore/websockets/WebSocket.cpp b/WebCore/websockets/WebSocket.cpp index ee78174..0435490 100644 --- a/WebCore/websockets/WebSocket.cpp +++ b/WebCore/websockets/WebSocket.cpp @@ -43,12 +43,14 @@ #include "Logging.h" #include "MessageEvent.h" #include "ScriptExecutionContext.h" +#include "StringBuilder.h" +#include "ThreadableWebSocketChannel.h" #include "WebSocketChannel.h" #include <wtf/StdLibExtras.h> namespace WebCore { -static bool isValidProtocolString(const WebCore::String& protocol) +static bool isValidProtocolString(const String& protocol) { if (protocol.isNull()) return true; @@ -62,6 +64,20 @@ static bool isValidProtocolString(const WebCore::String& protocol) return true; } +static String encodeProtocolString(const String& protocol) +{ + StringBuilder builder; + for (size_t i = 0; i < protocol.length(); i++) { + if (protocol[i] < 0x20 || protocol[i] > 0x7E) + builder.append(String::format("\\u%04X", protocol[i])); + else if (protocol[i] == 0x5c) + builder.append("\\\\"); + else + builder.append(protocol[i]); + } + return builder.toString(); +} + #if USE(V8) static bool webSocketsAvailable = false; @@ -101,32 +117,39 @@ void WebSocket::connect(const KURL& url, const String& protocol, ExceptionCode& m_url = url; m_protocol = protocol; + if (!m_url.isValid()) { + scriptExecutionContext()->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Invalid url for WebSocket " + url.string(), 0, scriptExecutionContext()->securityOrigin()->toString()); + m_state = CLOSED; + ec = SYNTAX_ERR; + return; + } + if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { - LOG(Network, "Wrong url scheme for WebSocket %s", url.string().utf8().data()); + scriptExecutionContext()->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong url scheme for WebSocket " + url.string(), 0, scriptExecutionContext()->securityOrigin()->toString()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (m_url.hasFragmentIdentifier()) { - LOG(Network, "URL has fragment component %s", url.string().utf8().data()); + scriptExecutionContext()->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "URL has fragment component " + url.string(), 0, scriptExecutionContext()->securityOrigin()->toString()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!isValidProtocolString(m_protocol)) { - LOG(Network, "Wrong protocol for WebSocket %s", m_protocol.utf8().data()); + scriptExecutionContext()->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong protocol for WebSocket '" + encodeProtocolString(m_protocol) + "'", 0, scriptExecutionContext()->securityOrigin()->toString()); m_state = CLOSED; ec = SYNTAX_ERR; return; } if (!portAllowed(url)) { - LOG(Network, "WebSocket port %d blocked", url.port()); + scriptExecutionContext()->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, String::format("WebSocket port %d blocked", url.port()), 0, scriptExecutionContext()->securityOrigin()->toString()); m_state = CLOSED; ec = SECURITY_ERR; return; } - m_channel = WebSocketChannel::create(scriptExecutionContext(), this, m_url, m_protocol); + m_channel = ThreadableWebSocketChannel::create(scriptExecutionContext(), this, m_url, m_protocol); m_channel->connect(); } @@ -192,7 +215,6 @@ void WebSocket::didReceiveMessage(const String& msg) if (m_state != OPEN || !scriptExecutionContext()) return; RefPtr<MessageEvent> evt = MessageEvent::create(); - // FIXME: origin, lastEventId, source, messagePort. evt->initMessageEvent(eventNames().messageEvent, false, false, SerializedScriptValue::create(msg), "", "", 0, 0); dispatchEvent(evt); } diff --git a/WebCore/websockets/WebSocket.h b/WebCore/websockets/WebSocket.h index 9ecbed7..18e2b25 100644 --- a/WebCore/websockets/WebSocket.h +++ b/WebCore/websockets/WebSocket.h @@ -46,7 +46,7 @@ namespace WebCore { class String; - class WebSocketChannel; + class ThreadableWebSocketChannel; class WebSocket : public RefCounted<WebSocket>, public EventTarget, public ActiveDOMObject, public WebSocketChannelClient { public: @@ -83,20 +83,12 @@ namespace WebCore { virtual ScriptExecutionContext* scriptExecutionContext() const; - // ActiveDOMObject - // virtual bool hasPendingActivity() const; - // virtual void contextDestroyed(); - // virtual bool canSuspend() const; - // virtual void suspend(); - // virtual void resume(); - // virtual void stop(); - using RefCounted<WebSocket>::ref; using RefCounted<WebSocket>::deref; // WebSocketChannelClient virtual void didConnect(); - virtual void didReceiveMessage(const String& msg); + virtual void didReceiveMessage(const String& message); virtual void didClose(); private: @@ -111,7 +103,7 @@ namespace WebCore { void dispatchMessageEvent(Event*); void dispatchCloseEvent(Event*); - RefPtr<WebSocketChannel> m_channel; + RefPtr<ThreadableWebSocketChannel> m_channel; State m_state; KURL m_url; @@ -119,8 +111,8 @@ namespace WebCore { EventTargetData m_eventTargetData; }; -} // namespace WebCore +} // namespace WebCore -#endif // ENABLE(WEB_SOCKETS) +#endif // ENABLE(WEB_SOCKETS) -#endif // WebSocket_h +#endif // WebSocket_h diff --git a/WebCore/websockets/WebSocket.idl b/WebCore/websockets/WebSocket.idl index c662940..c9552d9 100644 --- a/WebCore/websockets/WebSocket.idl +++ b/WebCore/websockets/WebSocket.idl @@ -32,6 +32,7 @@ module websockets { interface [ Conditional=WEB_SOCKETS, + CustomConstructor, EventTarget, NoStaticTables ] WebSocket { diff --git a/WebCore/websockets/WebSocketChannel.cpp b/WebCore/websockets/WebSocketChannel.cpp index a222b4d..5c0f4c3 100644 --- a/WebCore/websockets/WebSocketChannel.cpp +++ b/WebCore/websockets/WebSocketChannel.cpp @@ -118,7 +118,7 @@ void WebSocketChannel::didOpen(SocketStreamHandle* handle) ASSERT(handle == m_handle); const CString& handshakeMessage = m_handshake.clientHandshakeMessage(); if (!handle->send(handshakeMessage.data(), handshakeMessage.length())) { - LOG(Network, "Error in sending handshake message."); + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Error sending handshake message.", 0, m_handshake.clientOrigin()); handle->close(); } } @@ -150,7 +150,7 @@ void WebSocketChannel::didReceiveData(SocketStreamHandle* handle, const char* da handle->close(); return; } - if (m_handshake.mode() != WebSocketHandshake::Connected) { + if (m_handshake.mode() == WebSocketHandshake::Incomplete) { int headerLength = m_handshake.readServerHandshake(m_buffer, m_bufferSize); if (headerLength <= 0) return; @@ -179,6 +179,8 @@ void WebSocketChannel::didReceiveData(SocketStreamHandle* handle, const char* da return; LOG(Network, "remaining in read buf %ul", m_bufferSize); } + if (m_handshake.mode() != WebSocketHandshake::Connected) + return; const char* nextFrame = m_buffer; const char* p = m_buffer; @@ -246,7 +248,7 @@ bool WebSocketChannel::appendToBuffer(const char* data, int len) m_bufferSize += len; return true; } - LOG(Network, "Too long WebSocket frame %d", m_bufferSize + len); + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, String::format("WebSocket frame (at %d bytes) is too long.", m_bufferSize + len), 0, m_handshake.clientOrigin()); return false; } diff --git a/WebCore/websockets/WebSocketChannel.h b/WebCore/websockets/WebSocketChannel.h index 14b1e8c..7ec826c 100644 --- a/WebCore/websockets/WebSocketChannel.h +++ b/WebCore/websockets/WebSocketChannel.h @@ -34,6 +34,7 @@ #if ENABLE(WEB_SOCKETS) #include "SocketStreamHandleClient.h" +#include "ThreadableWebSocketChannel.h" #include "WebSocketHandshake.h" #include <wtf/RefCounted.h> @@ -45,16 +46,16 @@ namespace WebCore { class SocketStreamError; class WebSocketChannelClient; - class WebSocketChannel : public RefCounted<WebSocketChannel>, public SocketStreamHandleClient { + class WebSocketChannel : public RefCounted<WebSocketChannel>, public SocketStreamHandleClient, public ThreadableWebSocketChannel { public: static PassRefPtr<WebSocketChannel> create(ScriptExecutionContext* context, WebSocketChannelClient* client, const KURL& url, const String& protocol) { return adoptRef(new WebSocketChannel(context, client, url, protocol)); } virtual ~WebSocketChannel(); - void connect(); - bool send(const String& msg); - unsigned long bufferedAmount() const; - void close(); - void disconnect(); // Will suppress didClose(). + virtual void connect(); + virtual bool send(const String& message); + virtual unsigned long bufferedAmount() const; + virtual void close(); + virtual void disconnect(); // Will suppress didClose(). virtual void didOpen(SocketStreamHandle*); virtual void didClose(SocketStreamHandle*); @@ -63,8 +64,15 @@ namespace WebCore { virtual void didReceiveAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&); virtual void didCancelAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&); + using RefCounted<WebSocketChannel>::ref; + using RefCounted<WebSocketChannel>::deref; + + protected: + virtual void refThreadableWebSocketChannel() { ref(); } + virtual void derefThreadableWebSocketChannel() { deref(); } + private: - WebSocketChannel(ScriptExecutionContext*, WebSocketChannelClient*, const KURL&, const String&); + WebSocketChannel(ScriptExecutionContext*, WebSocketChannelClient*, const KURL&, const String& protocol); bool appendToBuffer(const char* data, int len); void skipBuffer(int len); @@ -78,8 +86,8 @@ namespace WebCore { unsigned long m_unhandledBufferSize; }; -} // namespace WebCore +} // namespace WebCore -#endif // ENABLE(WEB_SOCKETS) +#endif // ENABLE(WEB_SOCKETS) -#endif // WebSocketChannel_h +#endif // WebSocketChannel_h diff --git a/WebCore/websockets/WebSocketChannelClient.h b/WebCore/websockets/WebSocketChannelClient.h index 463cada..163070f 100644 --- a/WebCore/websockets/WebSocketChannelClient.h +++ b/WebCore/websockets/WebSocketChannelClient.h @@ -46,8 +46,8 @@ namespace WebCore { WebSocketChannelClient() { } }; -} // namespace WebCore +} // namespace WebCore -#endif // ENABLE(WEB_SOCKETS) +#endif // ENABLE(WEB_SOCKETS) -#endif // WebSocketChannelClient_h +#endif // WebSocketChannelClient_h diff --git a/WebCore/websockets/WebSocketHandshake.cpp b/WebCore/websockets/WebSocketHandshake.cpp index d1da443..883f84b 100644 --- a/WebCore/websockets/WebSocketHandshake.cpp +++ b/WebCore/websockets/WebSocketHandshake.cpp @@ -76,9 +76,14 @@ static String extractResponseCode(const char* header, int len) static String resourceName(const KURL& url) { - if (url.query().isNull()) - return url.path(); - return url.path() + "?" + url.query(); + String name = url.path(); + if (name.isEmpty()) + name = "/"; + if (!url.query().isNull()) + name += "?" + url.query(); + ASSERT(!name.isEmpty()); + ASSERT(!name.contains(' ')); + return name; } WebSocketHandshake::WebSocketHandshake(const KURL& url, const String& protocol, ScriptExecutionContext* context) @@ -216,19 +221,22 @@ int WebSocketHandshake::readServerHandshake(const char* header, size_t len) else { const String& code = extractResponseCode(header, len); if (code.isNull()) { - LOG(Network, "short server handshake: %s", header); + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Short server handshake: " + String(header, len), 0, clientOrigin()); return -1; } if (code.isEmpty()) { - LOG(Network, "no response code found: %s", header); + m_mode = Failed; + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "No response code found: " + String(header, len), 0, clientOrigin()); return len; } LOG(Network, "response code: %s", code.utf8().data()); if (code == "401") { - LOG(Network, "Authentication required"); + m_mode = Failed; + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Authentication required, but not implemented yet.", 0, clientOrigin()); return len; } else { - LOG(Network, "Mismatch server handshake: %s", header); + m_mode = Failed; + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected response code:" + code, 0, clientOrigin()); return len; } } @@ -237,19 +245,25 @@ int WebSocketHandshake::readServerHandshake(const char* header, size_t len) if (m_mode == Normal) { size_t headerSize = end - p; - if (headerSize < sizeof(webSocketUpgradeHeader) - 1) + if (headerSize < sizeof(webSocketUpgradeHeader) - 1) { + m_mode = Incomplete; return 0; + } if (memcmp(p, webSocketUpgradeHeader, sizeof(webSocketUpgradeHeader) - 1)) { - LOG(Network, "Bad upgrade header: %s", p); + m_mode = Failed; + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Bad Upgrade header: " + String(p, end - p), 0, clientOrigin()); return p - header + sizeof(webSocketUpgradeHeader) - 1; } p += sizeof(webSocketUpgradeHeader) - 1; headerSize = end - p; - if (headerSize < sizeof(webSocketConnectionHeader) - 1) + if (headerSize < sizeof(webSocketConnectionHeader) - 1) { + m_mode = Incomplete; return -1; + } if (memcmp(p, webSocketConnectionHeader, sizeof(webSocketConnectionHeader) - 1)) { - LOG(Network, "Bad connection header: %s", p); + m_mode = Failed; + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Bad Connection header: " + String(p, end - p), 0, clientOrigin()); return p - header + sizeof(webSocketConnectionHeader) - 1; } p += sizeof(webSocketConnectionHeader) - 1; @@ -257,6 +271,7 @@ int WebSocketHandshake::readServerHandshake(const char* header, size_t len) if (!strnstr(p, "\r\n\r\n", end - p)) { // Just hasn't been received fully yet. + m_mode = Incomplete; return -1; } HTTPHeaderMap headers; @@ -340,7 +355,8 @@ void WebSocketHandshake::setServerSetCookie2(const String& setCookie2) KURL WebSocketHandshake::httpURLForAuthenticationAndCookies() const { KURL url = m_url.copy(); - url.setProtocol(m_secure ? "https" : "http"); + bool couldSetProtocol = url.setProtocol(m_secure ? "https" : "http"); + ASSERT_UNUSED(couldSetProtocol, couldSetProtocol); return url; } @@ -358,13 +374,13 @@ const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* e if (name.isEmpty()) { if (p + 1 < end && *(p + 1) == '\n') return p + 2; - LOG(Network, "CR doesn't follow LF p=%p end=%p", p, end); + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "CR doesn't follow LF at " + String(p, end - p), 0, clientOrigin()); return 0; } - LOG(Network, "Unexpected CR in name"); + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected CR in name at " + String(p, end - p), 0, clientOrigin()); return 0; case '\n': - LOG(Network, "Unexpected LF in name"); + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected LF in name at " + String(p, end - p), 0, clientOrigin()); return 0; case ':': break; @@ -388,7 +404,7 @@ const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* e case '\r': break; case '\n': - LOG(Network, "Unexpected LF in value"); + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected LF in value at " + String(p, end - p), 0, clientOrigin()); return 0; default: value.append(*p); @@ -399,7 +415,7 @@ const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* e } } if (p >= end || *p != '\n') { - LOG(Network, "CR doesn't follow LF after value p=%p end=%p", p, end); + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "CR doesn't follow LF after value at " + String(p, end - p), 0, clientOrigin()); return 0; } AtomicString nameStr(String::fromUTF8(name.data(), name.size())); @@ -441,19 +457,25 @@ void WebSocketHandshake::checkResponseHeaders() { ASSERT(m_mode == Normal); m_mode = Failed; - if (m_wsOrigin.isNull() || m_wsLocation.isNull()) + if (m_wsOrigin.isNull()) { + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: 'websocket-origin' header is missing", 0, clientOrigin()); return; + } + if (m_wsLocation.isNull()) { + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: 'websocket-location' header is missing", 0, clientOrigin()); + return; + } if (clientOrigin() != m_wsOrigin) { - LOG(Network, "Mismatch origin: %s != %s", clientOrigin().utf8().data(), m_wsOrigin.utf8().data()); + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: origin mismatch: " + clientOrigin() + " != " + m_wsOrigin, 0, clientOrigin()); return; } if (clientLocation() != m_wsLocation) { - LOG(Network, "Mismatch location: %s != %s", clientLocation().utf8().data(), m_wsLocation.utf8().data()); + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: location mismatch: " + clientLocation() + " != " + m_wsLocation, 0, clientOrigin()); return; } if (!m_clientProtocol.isEmpty() && m_clientProtocol != m_wsProtocol) { - LOG(Network, "Mismatch protocol: %s != %s", m_clientProtocol.utf8().data(), m_wsProtocol.utf8().data()); + m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: protocol mismatch: " + m_clientProtocol + " != " + m_wsProtocol, 0, clientOrigin()); return; } m_mode = Connected; diff --git a/WebCore/websockets/WebSocketHandshake.h b/WebCore/websockets/WebSocketHandshake.h index d5dbe68..bda320a 100644 --- a/WebCore/websockets/WebSocketHandshake.h +++ b/WebCore/websockets/WebSocketHandshake.h @@ -106,8 +106,8 @@ namespace WebCore { String m_setCookie2; }; -} // namespace WebCore +} // namespace WebCore -#endif // ENABLE(WEB_SOCKETS) +#endif // ENABLE(WEB_SOCKETS) -#endif // WebSocketHandshake_h +#endif // WebSocketHandshake_h diff --git a/WebCore/websockets/WorkerThreadableWebSocketChannel.cpp b/WebCore/websockets/WorkerThreadableWebSocketChannel.cpp new file mode 100644 index 0000000..8db81b7 --- /dev/null +++ b/WebCore/websockets/WorkerThreadableWebSocketChannel.cpp @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2009, 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEB_SOCKETS) && ENABLE(WORKERS) + +#include "WorkerThreadableWebSocketChannel.h" + +#include "GenericWorkerTask.h" +#include "PlatformString.h" +#include "ScriptExecutionContext.h" +#include "ThreadableWebSocketChannelClientWrapper.h" +#include "WebSocketChannel.h" +#include "WebSocketChannelClient.h" +#include "WorkerContext.h" +#include "WorkerLoaderProxy.h" +#include "WorkerRunLoop.h" +#include "WorkerThread.h" + +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +WorkerThreadableWebSocketChannel::WorkerThreadableWebSocketChannel(WorkerContext* context, WebSocketChannelClient* client, const String& taskMode, const KURL& url, const String& protocol) + : m_workerContext(context) + , m_workerClientWrapper(ThreadableWebSocketChannelClientWrapper::create(client)) + , m_bridge(new Bridge(m_workerClientWrapper, m_workerContext, taskMode, url, protocol)) +{ +} + +WorkerThreadableWebSocketChannel::~WorkerThreadableWebSocketChannel() +{ + if (m_bridge) + m_bridge->disconnect(); +} + +void WorkerThreadableWebSocketChannel::connect() +{ + if (m_bridge) + m_bridge->connect(); +} + +bool WorkerThreadableWebSocketChannel::send(const String& message) +{ + if (!m_bridge) + return false; + return m_bridge->send(message); +} + +unsigned long WorkerThreadableWebSocketChannel::bufferedAmount() const +{ + if (!m_bridge) + return 0; + return m_bridge->bufferedAmount(); +} + +void WorkerThreadableWebSocketChannel::close() +{ + if (!m_bridge) + m_bridge->close(); +} + +void WorkerThreadableWebSocketChannel::disconnect() +{ + m_bridge->disconnect(); + m_bridge.clear(); +} + +WorkerThreadableWebSocketChannel::Peer::Peer(RefPtr<ThreadableWebSocketChannelClientWrapper> clientWrapper, WorkerLoaderProxy& loaderProxy, ScriptExecutionContext* context, const String& taskMode, const KURL& url, const String& protocol) + : m_workerClientWrapper(clientWrapper) + , m_loaderProxy(loaderProxy) + , m_mainWebSocketChannel(WebSocketChannel::create(context, this, url, protocol)) + , m_taskMode(taskMode) +{ + ASSERT(isMainThread()); +} + +WorkerThreadableWebSocketChannel::Peer::~Peer() +{ + ASSERT(isMainThread()); + if (m_mainWebSocketChannel) + m_mainWebSocketChannel->disconnect(); +} + +void WorkerThreadableWebSocketChannel::Peer::connect() +{ + ASSERT(isMainThread()); + if (!m_mainWebSocketChannel) + return; + m_mainWebSocketChannel->connect(); +} + +static void workerContextDidSend(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, bool sent) +{ + ASSERT_UNUSED(context, context->isWorkerContext()); + workerClientWrapper->setSent(sent); +} + +void WorkerThreadableWebSocketChannel::Peer::send(const String& message) +{ + ASSERT(isMainThread()); + if (!m_mainWebSocketChannel || !m_workerClientWrapper) + return; + bool sent = m_mainWebSocketChannel->send(message); + m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidSend, m_workerClientWrapper, sent), m_taskMode); +} + +static void workerContextDidGetBufferedAmount(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, unsigned long bufferedAmount) +{ + ASSERT_UNUSED(context, context->isWorkerContext()); + workerClientWrapper->setBufferedAmount(bufferedAmount); +} + +void WorkerThreadableWebSocketChannel::Peer::bufferedAmount() +{ + ASSERT(isMainThread()); + if (!m_mainWebSocketChannel || !m_workerClientWrapper) + return; + unsigned long bufferedAmount = m_mainWebSocketChannel->bufferedAmount(); + m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidGetBufferedAmount, m_workerClientWrapper, bufferedAmount), m_taskMode); +} + +void WorkerThreadableWebSocketChannel::Peer::close() +{ + ASSERT(isMainThread()); + if (!m_mainWebSocketChannel) + return; + m_mainWebSocketChannel->close(); + m_mainWebSocketChannel = 0; +} + +void WorkerThreadableWebSocketChannel::Peer::disconnect() +{ + ASSERT(isMainThread()); + if (!m_mainWebSocketChannel) + return; + m_mainWebSocketChannel->disconnect(); + m_mainWebSocketChannel = 0; +} + +static void workerContextDidConnect(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper) +{ + ASSERT_UNUSED(context, context->isWorkerContext()); + workerClientWrapper->didConnect(); +} + +void WorkerThreadableWebSocketChannel::Peer::didConnect() +{ + ASSERT(isMainThread()); + m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidConnect, m_workerClientWrapper), m_taskMode); +} + +static void workerContextDidReceiveMessage(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, const String& message) +{ + ASSERT_UNUSED(context, context->isWorkerContext()); + workerClientWrapper->didReceiveMessage(message); +} + +void WorkerThreadableWebSocketChannel::Peer::didReceiveMessage(const String& message) +{ + ASSERT(isMainThread()); + m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidReceiveMessage, m_workerClientWrapper, message), m_taskMode); +} + +static void workerContextDidClose(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper) +{ + ASSERT_UNUSED(context, context->isWorkerContext()); + workerClientWrapper->didClose(); +} + +void WorkerThreadableWebSocketChannel::Peer::didClose() +{ + ASSERT(isMainThread()); + m_mainWebSocketChannel = 0; + m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidClose, m_workerClientWrapper), m_taskMode); +} + +void WorkerThreadableWebSocketChannel::Bridge::setWebSocketChannel(ScriptExecutionContext* context, Bridge* thisPtr, Peer* peer, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper) +{ + ASSERT_UNUSED(context, context->isWorkerContext()); + thisPtr->m_peer = peer; + workerClientWrapper->setSyncMethodDone(); +} + +void WorkerThreadableWebSocketChannel::Bridge::mainThreadCreateWebSocketChannel(ScriptExecutionContext* context, Bridge* thisPtr, RefPtr<ThreadableWebSocketChannelClientWrapper> clientWrapper, const String& taskMode, const KURL& url, const String& protocol) +{ + ASSERT(isMainThread()); + ASSERT_UNUSED(context, context->isDocument()); + + Peer* peer = Peer::create(clientWrapper, thisPtr->m_loaderProxy, context, taskMode, url, protocol); + thisPtr->m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&Bridge::setWebSocketChannel, thisPtr, peer, clientWrapper), taskMode); +} + +WorkerThreadableWebSocketChannel::Bridge::Bridge(PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, PassRefPtr<WorkerContext> workerContext, const String& taskMode, const KURL& url, const String& protocol) + : m_workerClientWrapper(workerClientWrapper) + , m_workerContext(workerContext) + , m_loaderProxy(m_workerContext->thread()->workerLoaderProxy()) + , m_taskMode(taskMode) + , m_peer(0) +{ + ASSERT(m_workerClientWrapper.get()); + setMethodNotCompleted(); + m_loaderProxy.postTaskToLoader(createCallbackTask(&Bridge::mainThreadCreateWebSocketChannel, this, m_workerClientWrapper, m_taskMode, url, protocol)); + waitForMethodCompletion(); + ASSERT(m_peer); +} + +WorkerThreadableWebSocketChannel::Bridge::~Bridge() +{ + disconnect(); +} + +void WorkerThreadableWebSocketChannel::mainThreadConnect(ScriptExecutionContext* context, Peer* peer) +{ + ASSERT(isMainThread()); + ASSERT_UNUSED(context, context->isDocument()); + ASSERT(peer); + + peer->connect(); +} + +void WorkerThreadableWebSocketChannel::Bridge::connect() +{ + ASSERT(m_workerClientWrapper); + ASSERT(m_peer); + m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadConnect, m_peer)); +} + +void WorkerThreadableWebSocketChannel::mainThreadSend(ScriptExecutionContext* context, Peer* peer, const String& message) +{ + ASSERT(isMainThread()); + ASSERT_UNUSED(context, context->isDocument()); + ASSERT(peer); + + peer->send(message); +} + +bool WorkerThreadableWebSocketChannel::Bridge::send(const String& message) +{ + if (!m_workerClientWrapper) + return false; + ASSERT(m_peer); + setMethodNotCompleted(); + m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadSend, m_peer, message)); + waitForMethodCompletion(); + ThreadableWebSocketChannelClientWrapper* clientWrapper = m_workerClientWrapper.get(); + return clientWrapper && clientWrapper->sent(); +} + +void WorkerThreadableWebSocketChannel::mainThreadBufferedAmount(ScriptExecutionContext* context, Peer* peer) +{ + ASSERT(isMainThread()); + ASSERT_UNUSED(context, context->isDocument()); + ASSERT(peer); + + peer->bufferedAmount(); +} + +unsigned long WorkerThreadableWebSocketChannel::Bridge::bufferedAmount() +{ + if (!m_workerClientWrapper) + return 0; + ASSERT(m_peer); + setMethodNotCompleted(); + m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadBufferedAmount, m_peer)); + waitForMethodCompletion(); + ThreadableWebSocketChannelClientWrapper* clientWrapper = m_workerClientWrapper.get(); + if (clientWrapper) + return clientWrapper->bufferedAmount(); + return 0; +} + +void WorkerThreadableWebSocketChannel::mainThreadClose(ScriptExecutionContext* context, Peer* peer) +{ + ASSERT(isMainThread()); + ASSERT_UNUSED(context, context->isDocument()); + ASSERT(peer); + + peer->close(); +} + +void WorkerThreadableWebSocketChannel::Bridge::close() +{ + ASSERT(m_peer); + m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadClose, m_peer)); +} + +void WorkerThreadableWebSocketChannel::mainThreadDestroy(ScriptExecutionContext* context, Peer* peer) +{ + ASSERT(isMainThread()); + ASSERT_UNUSED(context, context->isDocument()); + ASSERT(peer); + + delete peer; +} + +void WorkerThreadableWebSocketChannel::Bridge::disconnect() +{ + clearClientWrapper(); + if (m_peer) { + Peer* peer = m_peer; + m_peer = 0; + m_loaderProxy.postTaskToLoader(createCallbackTask(&mainThreadDestroy, peer)); + } + m_workerContext = 0; +} + +void WorkerThreadableWebSocketChannel::Bridge::clearClientWrapper() +{ + m_workerClientWrapper->clearClient(); +} + +void WorkerThreadableWebSocketChannel::Bridge::setMethodNotCompleted() +{ + ASSERT(m_workerClientWrapper); + m_workerClientWrapper->clearSyncMethodDone(); +} + +void WorkerThreadableWebSocketChannel::Bridge::waitForMethodCompletion() +{ + if (!m_workerContext) + return; + WorkerRunLoop& runLoop = m_workerContext->thread()->runLoop(); + MessageQueueWaitResult result = MessageQueueMessageReceived; + ThreadableWebSocketChannelClientWrapper* clientWrapper = m_workerClientWrapper.get(); + while (clientWrapper && !clientWrapper->syncMethodDone() && result != MessageQueueTerminated) { + result = runLoop.runInMode(m_workerContext.get(), m_taskMode); + clientWrapper = m_workerClientWrapper.get(); + } +} + +} // namespace WebCore + +#endif // ENABLE(WEB_SOCKETS) diff --git a/WebCore/websockets/WorkerThreadableWebSocketChannel.h b/WebCore/websockets/WorkerThreadableWebSocketChannel.h new file mode 100644 index 0000000..1106f43 --- /dev/null +++ b/WebCore/websockets/WorkerThreadableWebSocketChannel.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2009, 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WorkerThreadableWebSocketChannel_h +#define WorkerThreadableWebSocketChannel_h + +#if ENABLE(WEB_SOCKETS) && ENABLE(WORKERS) + +#include "PlatformString.h" +#include "ThreadableWebSocketChannel.h" +#include "WebSocketChannelClient.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/Threading.h> + +namespace WebCore { + +class KURL; +class ScriptExecutionContext; +class ThreadableWebSocketChannelClientWrapper; +class WorkerContext; +class WorkerLoaderProxy; +class WorkerRunLoop; + +class WorkerThreadableWebSocketChannel : public RefCounted<WorkerThreadableWebSocketChannel>, public ThreadableWebSocketChannel { +public: + static PassRefPtr<ThreadableWebSocketChannel> create(WorkerContext* workerContext, WebSocketChannelClient* client, const String& taskMode, const KURL& url, const String& protocol) + { + return adoptRef(new WorkerThreadableWebSocketChannel(workerContext, client, taskMode, url, protocol)); + } + virtual ~WorkerThreadableWebSocketChannel(); + + virtual void connect(); + virtual bool send(const String& message); + virtual unsigned long bufferedAmount() const; + virtual void close(); + virtual void disconnect(); // Will suppress didClose(). + + using RefCounted<WorkerThreadableWebSocketChannel>::ref; + using RefCounted<WorkerThreadableWebSocketChannel>::deref; + +protected: + virtual void refThreadableWebSocketChannel() { ref(); } + virtual void derefThreadableWebSocketChannel() { deref(); } + +private: + // Generated by the bridge. The Peer and its bridge should have identical + // lifetimes. + class Peer : public WebSocketChannelClient, public Noncopyable { + public: + static Peer* create(RefPtr<ThreadableWebSocketChannelClientWrapper> clientWrapper, WorkerLoaderProxy& loaderProxy, ScriptExecutionContext* context, const String& taskMode, const KURL& url, const String& protocol) + { + return new Peer(clientWrapper, loaderProxy, context, taskMode, url, protocol); + } + ~Peer(); + + void connect(); + void send(const String& message); + void bufferedAmount(); + void close(); + void disconnect(); + + virtual void didConnect(); + virtual void didReceiveMessage(const String& message); + virtual void didClose(); + + private: + Peer(RefPtr<ThreadableWebSocketChannelClientWrapper>, WorkerLoaderProxy&, ScriptExecutionContext*, const String& taskMode, const KURL&, const String& protocol); + + RefPtr<ThreadableWebSocketChannelClientWrapper> m_workerClientWrapper; + WorkerLoaderProxy& m_loaderProxy; + RefPtr<ThreadableWebSocketChannel> m_mainWebSocketChannel; + String m_taskMode; + }; + + // Bridge for Peer. Running on the worker thread. + class Bridge : public RefCounted<Bridge> { + public: + Bridge(PassRefPtr<ThreadableWebSocketChannelClientWrapper>, PassRefPtr<WorkerContext>, const String& taskMode, const KURL&, const String& protocol); + ~Bridge(); + void connect(); + bool send(const String& message); + unsigned long bufferedAmount(); + void close(); + void disconnect(); + + using RefCounted<Bridge>::ref; + using RefCounted<Bridge>::deref; + + private: + static void setWebSocketChannel(ScriptExecutionContext*, Bridge* thisPtr, Peer*, RefPtr<ThreadableWebSocketChannelClientWrapper>); + + // Executed on the main thread to create a Peer for this bridge. + static void mainThreadCreateWebSocketChannel(ScriptExecutionContext*, Bridge* thisPtr, RefPtr<ThreadableWebSocketChannelClientWrapper>, const String& taskMode, const KURL&, const String& protocol); + + // Executed on the worker context's thread. + void clearClientWrapper(); + + void setMethodNotCompleted(); + void waitForMethodCompletion(); + + RefPtr<ThreadableWebSocketChannelClientWrapper> m_workerClientWrapper; + RefPtr<WorkerContext> m_workerContext; + WorkerLoaderProxy& m_loaderProxy; + String m_taskMode; + Peer* m_peer; + }; + + WorkerThreadableWebSocketChannel(WorkerContext*, WebSocketChannelClient*, const String& taskMode, const KURL&, const String& protocol); + + static void mainThreadConnect(ScriptExecutionContext*, Peer*); + static void mainThreadSend(ScriptExecutionContext*, Peer*, const String& message); + static void mainThreadBufferedAmount(ScriptExecutionContext*, Peer*); + static void mainThreadClose(ScriptExecutionContext*, Peer*); + static void mainThreadDestroy(ScriptExecutionContext*, Peer*); + + RefPtr<WorkerContext> m_workerContext; + RefPtr<ThreadableWebSocketChannelClientWrapper> m_workerClientWrapper; + RefPtr<Bridge> m_bridge; +}; + +} // namespace WebCore + +#endif // ENABLE(WEB_SOCKETS) + +#endif // WorkerThreadableWebSocketChannel_h |