text: // Copyright (c) 2012 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.
  
  // An implementation of WebSocketStreamHandle.
  
  #include "webkit/child/websocketstreamhandle_impl.h"
  
  #include <vector>
  
  #include "base/compiler_specific.h"
  #include "base/logging.h"
  #include "base/memory/ref_counted.h"
  #include "base/memory/scoped_ptr.h"
  #include "base/strings/string16.h"
  #include "third_party/WebKit/public/platform/WebData.h"
  #include "third_party/WebKit/public/platform/WebSocketStreamError.h"
  #include "third_party/WebKit/public/platform/WebSocketStreamHandleClient.h"
  #include "third_party/WebKit/public/platform/WebURL.h"
  #include "webkit/child/webkitplatformsupport_impl.h"
  #include "webkit/child/websocketstreamhandle_bridge.h"
  #include "webkit/child/websocketstreamhandle_delegate.h"
  
  using blink::WebData;
  using blink::WebSocketStreamError;
  using blink::WebSocketStreamHandle;
  using blink::WebSocketStreamHandleClient;
  using blink::WebURL;
  
  namespace webkit_glue {
  
  // WebSocketStreamHandleImpl::Context -----------------------------------------
  
  class WebSocketStreamHandleImpl::Context
      : public base::RefCounted<Context>,
        public WebSocketStreamHandleDelegate {
   public:
    explicit Context(WebSocketStreamHandleImpl* handle);
  
    WebSocketStreamHandleClient* client() const { return client_; }
    void set_client(WebSocketStreamHandleClient* client) {
      client_ = client;
    }
  
    void Connect(const WebURL& url, WebKitPlatformSupportImpl* platform);
    bool Send(const WebData& data);
    void Close();
  
    // Must be called before |handle_| or |client_| is deleted.
    // Once detached, it never calls |client_| back.
    void Detach();
  
    // WebSocketStreamHandleDelegate methods:
    virtual void DidOpenStream(WebSocketStreamHandle*, int) OVERRIDE;
    virtual void DidSendData(WebSocketStreamHandle*, int) OVERRIDE;
    virtual void DidReceiveData(WebSocketStreamHandle*,
                                const char*,
                                int) OVERRIDE;
    virtual void DidClose(WebSocketStreamHandle*) OVERRIDE;
    virtual void DidFail(WebSocketStreamHandle*, int, const string16&) OVERRIDE;
  
   private:
    friend class base::RefCounted<Context>;
    virtual ~Context() {
      DCHECK(!handle_);
      DCHECK(!client_);
      DCHECK(!bridge_.get());
    }
  
    WebSocketStreamHandleImpl* handle_;
    WebSocketStreamHandleClient* client_;
    // |bridge_| is alive from Connect to DidClose, so Context must be alive
    // in the time period.
    scoped_refptr<WebSocketStreamHandleBridge> bridge_;
  
    DISALLOW_COPY_AND_ASSIGN(Context);
  };
  
  WebSocketStreamHandleImpl::Context::Context(WebSocketStreamHandleImpl* handle)
      : handle_(handle),
        client_(NULL) {
  }
  
  void WebSocketStreamHandleImpl::Context::Connect(
      const WebURL& url,
      WebKitPlatformSupportImpl* platform) {
    VLOG(1) << "Connect url=" << url;
    DCHECK(!bridge_.get());
    bridge_ = platform->CreateWebSocketStreamBridge(handle_, this);
    AddRef();  // Will be released by DidClose().
    bridge_->Connect(url);
  }
  
  bool WebSocketStreamHandleImpl::Context::Send(const WebData& data) {
    VLOG(1) << "Send data.size=" << data.size();
    DCHECK(bridge_.get());
    return bridge_->Send(
        std::vector<char>(data.data(), data.data() + data.size()));
  }
  
  void WebSocketStreamHandleImpl::Context::Close() {
    VLOG(1) << "Close";
    if (bridge_.get())
      bridge_->Close();
  }
  
  void WebSocketStreamHandleImpl::Context::Detach() {
    handle_ = NULL;
    client_ = NULL;
    // If Connect was called, |bridge_| is not NULL, so that this Context closes
    // the |bridge_| here.  Then |bridge_| will call back DidClose, and will
    // be released by itself.
    // Otherwise, |bridge_| is NULL.
    if (bridge_.get())
      bridge_->Close();
  }
  
  void WebSocketStreamHandleImpl::Context::DidOpenStream(
      WebSocketStreamHandle* web_handle, int max_amount_send_allowed) {
    VLOG(1) << "DidOpen";
    if (client_)
      client_->didOpenStream(handle_, max_amount_send_allowed);
  }
  
  void WebSocketStreamHandleImpl::Context::DidSendData(
      WebSocketStreamHandle* web_handle, int amount_sent) {
    if (client_)
      client_->didSendData(handle_, amount_sent);
  }
  
  void WebSocketStreamHandleImpl::Context::DidReceiveData(
      WebSocketStreamHandle* web_handle, const char* data, int size) {
    if (client_)
      client_->didReceiveData(handle_, WebData(data, size));
  }
  
  void WebSocketStreamHandleImpl::Context::DidClose(
      WebSocketStreamHandle* web_handle) {
    VLOG(1) << "DidClose";
    bridge_ = NULL;
    WebSocketStreamHandleImpl* handle = handle_;
    handle_ = NULL;
    if (client_) {
      WebSocketStreamHandleClient* client = client_;
      client_ = NULL;
      client->didClose(handle);
    }
    Release();
  }
  
  void WebSocketStreamHandleImpl::Context::DidFail(
      WebSocketStreamHandle* web_handle,
      int error_code,
      const string16& error_msg) {
    VLOG(1) << "DidFail";
    if (client_) {
      client_->didFail(
          handle_,
          WebSocketStreamError(error_code, error_msg));
    }
  }
  
  // WebSocketStreamHandleImpl ------------------------------------------------
  
  WebSocketStreamHandleImpl::WebSocketStreamHandleImpl(
      WebKitPlatformSupportImpl* platform)
      : context_(new Context(this)),
        platform_(platform) {
  }
  
  WebSocketStreamHandleImpl::~WebSocketStreamHandleImpl() {
    // We won't receive any events from |context_|.
    // |context_| is ref counted, and will be released when it received
    // DidClose.
    context_->Detach();
  }
  
  void WebSocketStreamHandleImpl::connect(
      const WebURL& url, WebSocketStreamHandleClient* client) {
    VLOG(1) << "connect url=" << url;
    DCHECK(!context_->client());
    context_->set_client(client);
  
    context_->Connect(url, platform_);
  }
  
  bool WebSocketStreamHandleImpl::send(const WebData& data) {
    return context_->Send(data);
  }
  
  void WebSocketStreamHandleImpl::close() {
    context_->Close();
  }
  
  }  // namespace webkit_glue
  