1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// An implementation of WebSocketStreamHandle.
6
7#include "webkit/glue/websocketstreamhandle_impl.h"
8
9#include <vector>
10
11#include "base/compiler_specific.h"
12#include "base/logging.h"
13#include "base/memory/ref_counted.h"
14#include "base/memory/scoped_ptr.h"
15#include "third_party/WebKit/Source/WebKit/chromium/public/WebData.h"
16#include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h"
17#include "third_party/WebKit/Source/WebKit/chromium/public/WebSocketStreamHandleClient.h"
18#include "webkit/glue/websocketstreamhandle_bridge.h"
19#include "webkit/glue/websocketstreamhandle_delegate.h"
20
21namespace webkit_glue {
22
23// WebSocketStreamHandleImpl::Context -----------------------------------------
24
25class WebSocketStreamHandleImpl::Context
26    : public base::RefCounted<Context>,
27      public WebSocketStreamHandleDelegate {
28 public:
29  explicit Context(WebSocketStreamHandleImpl* handle);
30
31  WebKit::WebSocketStreamHandleClient* client() const { return client_; }
32  void set_client(WebKit::WebSocketStreamHandleClient* client) {
33    client_ = client;
34  }
35
36  void Connect(const WebKit::WebURL& url);
37  bool Send(const WebKit::WebData& data);
38  void Close();
39
40  // Must be called before |handle_| or |client_| is deleted.
41  // Once detached, it never calls |client_| back.
42  void Detach();
43
44  // WebSocketStreamHandleDelegate methods:
45  virtual void DidOpenStream(WebKit::WebSocketStreamHandle*, int);
46  virtual void DidSendData(WebKit::WebSocketStreamHandle*, int);
47  virtual void DidReceiveData(
48      WebKit::WebSocketStreamHandle*, const char*, int);
49  virtual void DidClose(WebKit::WebSocketStreamHandle*);
50
51 private:
52  friend class base::RefCounted<Context>;
53  ~Context() {
54    DCHECK(!handle_);
55    DCHECK(!client_);
56    DCHECK(!bridge_);
57  }
58
59  WebSocketStreamHandleImpl* handle_;
60  WebKit::WebSocketStreamHandleClient* client_;
61  // |bridge_| is alive from Connect to DidClose, so Context must be alive
62  // in the time period.
63  scoped_refptr<WebSocketStreamHandleBridge> bridge_;
64
65  DISALLOW_COPY_AND_ASSIGN(Context);
66};
67
68WebSocketStreamHandleImpl::Context::Context(WebSocketStreamHandleImpl* handle)
69    : handle_(handle),
70      client_(NULL),
71      bridge_(NULL) {
72}
73
74void WebSocketStreamHandleImpl::Context::Connect(const WebKit::WebURL& url) {
75  VLOG(1) << "Connect url=" << url;
76  DCHECK(!bridge_);
77  bridge_ = WebSocketStreamHandleBridge::Create(handle_, this);
78  AddRef();  // Will be released by DidClose().
79  bridge_->Connect(url);
80}
81
82bool WebSocketStreamHandleImpl::Context::Send(const WebKit::WebData& data) {
83  VLOG(1) << "Send data.size=" << data.size();
84  DCHECK(bridge_);
85  return bridge_->Send(
86      std::vector<char>(data.data(), data.data() + data.size()));
87}
88
89void WebSocketStreamHandleImpl::Context::Close() {
90  VLOG(1) << "Close";
91  if (bridge_)
92    bridge_->Close();
93}
94
95void WebSocketStreamHandleImpl::Context::Detach() {
96  handle_ = NULL;
97  client_ = NULL;
98  // If Connect was called, |bridge_| is not NULL, so that this Context closes
99  // the |bridge_| here.  Then |bridge_| will call back DidClose, and will
100  // be released by itself.
101  // Otherwise, |bridge_| is NULL.
102  if (bridge_)
103    bridge_->Close();
104}
105
106void WebSocketStreamHandleImpl::Context::DidOpenStream(
107    WebKit::WebSocketStreamHandle* web_handle, int max_amount_send_allowed) {
108  VLOG(1) << "DidOpen";
109  if (client_)
110    client_->didOpenStream(handle_, max_amount_send_allowed);
111}
112
113void WebSocketStreamHandleImpl::Context::DidSendData(
114    WebKit::WebSocketStreamHandle* web_handle, int amount_sent) {
115  if (client_)
116    client_->didSendData(handle_, amount_sent);
117}
118
119void WebSocketStreamHandleImpl::Context::DidReceiveData(
120    WebKit::WebSocketStreamHandle* web_handle, const char* data, int size) {
121  if (client_)
122    client_->didReceiveData(handle_, WebKit::WebData(data, size));
123}
124
125void WebSocketStreamHandleImpl::Context::DidClose(
126    WebKit::WebSocketStreamHandle* web_handle) {
127  VLOG(1) << "DidClose";
128  bridge_ = NULL;
129  WebSocketStreamHandleImpl* handle = handle_;
130  handle_ = NULL;
131  if (client_) {
132    WebKit::WebSocketStreamHandleClient* client = client_;
133    client_ = NULL;
134    client->didClose(handle);
135  }
136  Release();
137}
138
139// WebSocketStreamHandleImpl ------------------------------------------------
140
141WebSocketStreamHandleImpl::WebSocketStreamHandleImpl()
142    : ALLOW_THIS_IN_INITIALIZER_LIST(context_(new Context(this))) {
143}
144
145WebSocketStreamHandleImpl::~WebSocketStreamHandleImpl() {
146  // We won't receive any events from |context_|.
147  // |context_| is ref counted, and will be released when it received
148  // DidClose.
149  context_->Detach();
150}
151
152void WebSocketStreamHandleImpl::connect(
153    const WebKit::WebURL& url, WebKit::WebSocketStreamHandleClient* client) {
154  VLOG(1) << "connect url=" << url;
155  DCHECK(!context_->client());
156  context_->set_client(client);
157
158  context_->Connect(url);
159}
160
161bool WebSocketStreamHandleImpl::send(const WebKit::WebData& data) {
162  return context_->Send(data);
163}
164
165void WebSocketStreamHandleImpl::close() {
166  context_->Close();
167}
168
169}  // namespace webkit_glue
170