// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/html_viewer/web_socket_handle_impl.h" #include #include "base/bind.h" #include "base/macros.h" #include "base/memory/scoped_vector.h" #include "components/html_viewer/blink_basic_type_converters.h" #include "mojo/services/network/public/cpp/web_socket_read_queue.h" #include "mojo/services/network/public/cpp/web_socket_write_queue.h" #include "mojo/services/network/public/interfaces/web_socket_factory.mojom.h" #include "third_party/WebKit/public/platform/WebSecurityOrigin.h" #include "third_party/WebKit/public/platform/WebSocketHandleClient.h" #include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/platform/WebVector.h" using blink::WebSecurityOrigin; using blink::WebSocketHandle; using blink::WebSocketHandleClient; using blink::WebString; using blink::WebURL; using blink::WebVector; using mojo::ConvertTo; using mojo::String; using mojo::WebSocket; using mojo::WebSocketReadQueue; namespace mojo { template<> struct TypeConverter { static WebSocket::MessageType Convert(WebSocketHandle::MessageType type) { DCHECK(type == WebSocketHandle::MessageTypeContinuation || type == WebSocketHandle::MessageTypeText || type == WebSocketHandle::MessageTypeBinary); typedef WebSocket::MessageType MessageType; COMPILE_ASSERT( static_cast(WebSocketHandle::MessageTypeContinuation) == WebSocket::MESSAGE_TYPE_CONTINUATION, enum_values_must_match_for_message_type); COMPILE_ASSERT( static_cast(WebSocketHandle::MessageTypeText) == WebSocket::MESSAGE_TYPE_TEXT, enum_values_must_match_for_message_type); COMPILE_ASSERT( static_cast(WebSocketHandle::MessageTypeBinary) == WebSocket::MESSAGE_TYPE_BINARY, enum_values_must_match_for_message_type); return static_cast(type); } }; template<> struct TypeConverter { static WebSocketHandle::MessageType Convert(WebSocket::MessageType type) { DCHECK(type == WebSocket::MESSAGE_TYPE_CONTINUATION || type == WebSocket::MESSAGE_TYPE_TEXT || type == WebSocket::MESSAGE_TYPE_BINARY); return static_cast(type); } }; } // namespace mojo namespace html_viewer { // This class forms a bridge from the mojo WebSocketClient interface and the // Blink WebSocketHandleClient interface. class WebSocketClientImpl : public mojo::WebSocketClient { public: WebSocketClientImpl(WebSocketHandleImpl* handle, blink::WebSocketHandleClient* client, mojo::InterfaceRequest request) : handle_(handle), client_(client), binding_(this, request.Pass()) {} ~WebSocketClientImpl() override {} private: // WebSocketClient methods: void DidConnect(const String& selected_subprotocol, const String& extensions, mojo::ScopedDataPipeConsumerHandle receive_stream) override { blink::WebSocketHandleClient* client = client_; WebSocketHandleImpl* handle = handle_; receive_stream_ = receive_stream.Pass(); read_queue_.reset(new WebSocketReadQueue(receive_stream_.get())); client->didConnect(handle, selected_subprotocol.To(), extensions.To()); // |handle| can be deleted here. } void DidReceiveData(bool fin, WebSocket::MessageType type, uint32_t num_bytes) override { read_queue_->Read(num_bytes, base::Bind(&WebSocketClientImpl::DidReadFromReceiveStream, base::Unretained(this), fin, type, num_bytes)); } void DidReceiveFlowControl(int64_t quota) override { client_->didReceiveFlowControl(handle_, quota); // |handle| can be deleted here. } void DidFail(const String& message) override { blink::WebSocketHandleClient* client = client_; WebSocketHandleImpl* handle = handle_; handle->Disconnect(); // deletes |this| client->didFail(handle, message.To()); // |handle| can be deleted here. } void DidClose(bool was_clean, uint16_t code, const String& reason) override { blink::WebSocketHandleClient* client = client_; WebSocketHandleImpl* handle = handle_; handle->Disconnect(); // deletes |this| client->didClose(handle, was_clean, code, reason.To()); // |handle| can be deleted here. } void DidReadFromReceiveStream(bool fin, WebSocket::MessageType type, uint32_t num_bytes, const char* data) { client_->didReceiveData(handle_, fin, ConvertTo(type), data, num_bytes); // |handle_| can be deleted here. } // |handle_| owns this object, so it is guaranteed to outlive us. WebSocketHandleImpl* handle_; blink::WebSocketHandleClient* client_; mojo::ScopedDataPipeConsumerHandle receive_stream_; scoped_ptr read_queue_; mojo::Binding binding_; DISALLOW_COPY_AND_ASSIGN(WebSocketClientImpl); }; WebSocketHandleImpl::WebSocketHandleImpl(mojo::WebSocketFactory* factory) : did_close_(false) { factory->CreateWebSocket(GetProxy(&web_socket_)); } WebSocketHandleImpl::~WebSocketHandleImpl() { if (!did_close_) { // The connection is abruptly disconnected by the renderer without // closing handshake. web_socket_->Close(WebSocket::kAbnormalCloseCode, String()); } } void WebSocketHandleImpl::connect(const WebURL& url, const WebVector& protocols, const WebSecurityOrigin& origin, WebSocketHandleClient* client) { // TODO(mpcomplete): Is this the right ownership model? Or should mojo own // |client_|? mojo::WebSocketClientPtr client_ptr; mojo::MessagePipe pipe; client_ptr.Bind( mojo::InterfacePtrInfo(pipe.handle0.Pass(), 0u)); mojo::InterfaceRequest request; request.Bind(pipe.handle1.Pass()); client_.reset(new WebSocketClientImpl(this, client, request.Pass())); mojo::DataPipe data_pipe; send_stream_ = data_pipe.producer_handle.Pass(); write_queue_.reset(new mojo::WebSocketWriteQueue(send_stream_.get())); web_socket_->Connect(url.string().utf8(), mojo::Array::From(protocols), origin.toString().utf8(), data_pipe.consumer_handle.Pass(), client_ptr.Pass()); } void WebSocketHandleImpl::send(bool fin, WebSocketHandle::MessageType type, const char* data, size_t size) { if (!client_) return; uint32_t size32 = static_cast(size); write_queue_->Write( data, size32, base::Bind(&WebSocketHandleImpl::DidWriteToSendStream, base::Unretained(this), fin, type, size32)); } void WebSocketHandleImpl::flowControl(int64_t quota) { if (!client_) return; web_socket_->FlowControl(quota); } void WebSocketHandleImpl::close(unsigned short code, const WebString& reason) { web_socket_->Close(code, reason.utf8()); } void WebSocketHandleImpl::DidWriteToSendStream( bool fin, WebSocketHandle::MessageType type, uint32_t num_bytes, const char* data) { web_socket_->Send(fin, ConvertTo(type), num_bytes); } void WebSocketHandleImpl::Disconnect() { did_close_ = true; client_.reset(); } } // namespace html_viewer