blob: ef08b04cce3b998e9f90a21c4e8b4c4bd0d92b68 [file] [log] [blame]
// 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 <vector>
#include "base/bind.h"
#include "base/macros.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"
#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.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<WebSocket::MessageType, WebSocketHandle::MessageType> {
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<MessageType>(WebSocketHandle::MessageTypeContinuation) ==
WebSocket::MESSAGE_TYPE_CONTINUATION,
enum_values_must_match_for_message_type);
COMPILE_ASSERT(
static_cast<MessageType>(WebSocketHandle::MessageTypeText) ==
WebSocket::MESSAGE_TYPE_TEXT,
enum_values_must_match_for_message_type);
COMPILE_ASSERT(
static_cast<MessageType>(WebSocketHandle::MessageTypeBinary) ==
WebSocket::MESSAGE_TYPE_BINARY,
enum_values_must_match_for_message_type);
return static_cast<WebSocket::MessageType>(type);
}
};
template<>
struct TypeConverter<WebSocketHandle::MessageType, WebSocket::MessageType> {
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<WebSocketHandle::MessageType>(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<mojo::WebSocketClient> 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<WebString>(),
extensions.To<WebString>());
// |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<WebString>());
// |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<WebString>());
// |handle| can be deleted here.
}
void DidReadFromReceiveStream(bool fin,
WebSocket::MessageType type,
uint32_t num_bytes,
const char* data) {
client_->didReceiveData(handle_,
fin,
ConvertTo<WebSocketHandle::MessageType>(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<WebSocketReadQueue> read_queue_;
mojo::Binding<mojo::WebSocketClient> 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<WebString>& 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<mojo::WebSocketClient>(pipe.handle0.Pass(), 0u));
mojo::InterfaceRequest<mojo::WebSocketClient> 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<String>::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<uint32_t>(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<WebSocket::MessageType>(type), num_bytes);
}
void WebSocketHandleImpl::Disconnect() {
did_close_ = true;
client_.reset();
}
} // namespace html_viewer