| Index: webkit/plugins/ppapi/ppb_websocket_impl.cc
|
| diff --git a/webkit/plugins/ppapi/ppb_websocket_impl.cc b/webkit/plugins/ppapi/ppb_websocket_impl.cc
|
| index f8c907c81d19b0657a23d022c6d92c640b5351ad..b304a54b68445ecfdae0b5cefc8da08e5736dc75 100644
|
| --- a/webkit/plugins/ppapi/ppb_websocket_impl.cc
|
| +++ b/webkit/plugins/ppapi/ppb_websocket_impl.cc
|
| @@ -4,19 +4,66 @@
|
|
|
| #include "webkit/plugins/ppapi/ppb_websocket_impl.h"
|
|
|
| +#include <string>
|
| +
|
| +#include "base/logging.h"
|
| +#include "googleurl/src/gurl.h"
|
| +#include "net/base/net_util.h"
|
| +#include "ppapi/c/pp_completion_callback.h"
|
| #include "ppapi/c/pp_errors.h"
|
| #include "ppapi/c/pp_var.h"
|
| +#include "ppapi/c/ppb_var.h"
|
| +#include "ppapi/shared_impl/var.h"
|
| +#include "ppapi/shared_impl/var_tracker.h"
|
| +#include "third_party/WebKit/Source/WebKit/chromium/public/WebData.h"
|
| +#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
|
| +#include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
|
| +#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h"
|
| +#include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h"
|
| +#include "third_party/WebKit/Source/WebKit/chromium/public/WebSocket.h"
|
| +#include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h"
|
| +#include "webkit/plugins/ppapi/host_globals.h"
|
| +#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
|
| +#include "webkit/plugins/ppapi/resource_helper.h"
|
|
|
| +using ppapi::PpapiGlobals;
|
| +using ppapi::StringVar;
|
| using ppapi::thunk::PPB_WebSocket_API;
|
| +using ppapi::VarTracker;
|
| +using WebKit::WebData;
|
| +using WebKit::WebDocument;
|
| +using WebKit::WebString;
|
| +using WebKit::WebSocket;
|
| +using WebKit::WebSocketClient;
|
| +using WebKit::WebURL;
|
| +
|
| +static const uint32_t kMaxReasonSizeInBytes = 123;
|
|
|
| namespace webkit {
|
| namespace ppapi {
|
|
|
| PPB_WebSocket_Impl::PPB_WebSocket_Impl(PP_Instance instance)
|
| - : Resource(instance) {
|
| + : Resource(instance),
|
| + state_(PP_WEBSOCKETREADYSTATE_INVALID_DEV),
|
| + receive_callback_var_(NULL),
|
| + wait_for_receive_(false),
|
| + close_code_(0),
|
| + close_was_clean_(PP_FALSE) {
|
| + empty_string_ = new StringVar(
|
| + PpapiGlobals::Get()->GetModuleForInstance(instance), "", 0);
|
| }
|
|
|
| PPB_WebSocket_Impl::~PPB_WebSocket_Impl() {
|
| + if (websocket_.get())
|
| + websocket_->disconnect();
|
| +
|
| + // Clean up received and unread messages
|
| + VarTracker* var_tracker = PpapiGlobals::Get()->GetVarTracker();
|
| + while (!received_messages_.empty()) {
|
| + PP_Var var = received_messages_.front();
|
| + received_messages_.pop();
|
| + var_tracker->ReleaseVar(var);
|
| + }
|
| }
|
|
|
| // static
|
| @@ -33,66 +80,303 @@ int32_t PPB_WebSocket_Impl::Connect(PP_Var url,
|
| const PP_Var protocols[],
|
| uint32_t protocol_count,
|
| PP_CompletionCallback callback) {
|
| - // TODO(toyoshim): Implement it.
|
| - return PP_ERROR_NOTSUPPORTED;
|
| + // Check mandatory interfaces.
|
| + PluginInstance* plugin_instance = ResourceHelper::GetPluginInstance(this);
|
| + DCHECK(plugin_instance);
|
| + if (!plugin_instance)
|
| + return PP_ERROR_FAILED;
|
| +
|
| + // Connect() can be called at most once.
|
| + if (websocket_.get())
|
| + return PP_ERROR_INPROGRESS;
|
| + if (state_ != PP_WEBSOCKETREADYSTATE_INVALID_DEV)
|
| + return PP_ERROR_INPROGRESS;
|
| + state_ = PP_WEBSOCKETREADYSTATE_CLOSED_DEV;
|
| +
|
| + // Validate |callback| (Doesn't support blocking callback)
|
| + if (!callback.func)
|
| + return PP_ERROR_BLOCKS_MAIN_THREAD;
|
| +
|
| + // Validate url and convert it to WebURL.
|
| + scoped_refptr<StringVar> url_string = StringVar::FromPPVar(url);
|
| + if (!url_string)
|
| + return PP_ERROR_BADARGUMENT;
|
| + GURL gurl(url_string->value());
|
| + if (!gurl.is_valid())
|
| + return PP_ERROR_BADARGUMENT;
|
| + if (!gurl.SchemeIs("ws") && !gurl.SchemeIs("wss"))
|
| + return PP_ERROR_BADARGUMENT;
|
| + if (gurl.has_ref())
|
| + return PP_ERROR_BADARGUMENT;
|
| + // TODO(toyoshim): Must check if the port is allowed by default.
|
| + // We could not just use net::IsPortAllowedByDefault() because it doesn't
|
| + // be exported over the shared library.
|
| + WebURL web_url(gurl);
|
| +
|
| + // Validate protocols and convert it to WebString.
|
| + // TODO(toyoshim): Detect duplicated protocols as error.
|
| + std::string protocol_string;
|
| + for (uint32_t i = 0; i < protocol_count; i++) {
|
| + // TODO(toyoshim): Similar function exist in WebKit::WebSocket.
|
| + // We must rearrange them into WebKit::WebChannel and share its protocol
|
| + // related implementation via WebKit API.
|
| + scoped_refptr<StringVar> string_var;
|
| + string_var = StringVar::FromPPVar(protocols[i]);
|
| + if (!string_var || !string_var->value().length())
|
| + return PP_ERROR_BADARGUMENT;
|
| + for (std::string::const_iterator it = string_var->value().begin();
|
| + it != string_var->value().end();
|
| + ++it) {
|
| + uint8_t character = static_cast<uint8_t>(*it);
|
| + // WebSocket specification says "(Subprotocol string must consist of)
|
| + // characters in the range U+0021 to U+007E not including separator
|
| + // characters as defined in [RFC2616]."
|
| + const uint8_t minimumProtocolCharacter = '!'; // U+0021.
|
| + const uint8_t maximumProtocolCharacter = '~'; // U+007E.
|
| + if (character < minimumProtocolCharacter ||
|
| + character > maximumProtocolCharacter ||
|
| + character == '"' || character == '(' || character == ')' ||
|
| + character == ',' || character == '/' ||
|
| + (character >= ':' && character <= '@') || // U+003A - U+0040
|
| + (character >= '[' && character <= ']') || // U+005B - u+005D
|
| + character == '{' || character == '}')
|
| + return PP_ERROR_BADARGUMENT;
|
| + }
|
| + if (i != 0)
|
| + protocol_string.append(",");
|
| + protocol_string.append(string_var->value());
|
| + }
|
| + WebString web_protocols = WebString::fromUTF8(protocol_string);
|
| +
|
| + // Create WebKit::WebSocket object.
|
| + WebDocument document = plugin_instance->container()->element().document();
|
| + websocket_.reset(WebSocket::create(document, this));
|
| + DCHECK(websocket_.get());
|
| + if (!websocket_.get())
|
| + return PP_ERROR_NOTSUPPORTED;
|
| +
|
| + websocket_->connect(web_url, web_protocols);
|
| + state_ = PP_WEBSOCKETREADYSTATE_CONNECTING_DEV;
|
| +
|
| + // Install callback.
|
| + connect_callback_ = callback;
|
| +
|
| + return PP_OK_COMPLETIONPENDING;
|
| }
|
|
|
| int32_t PPB_WebSocket_Impl::Close(uint16_t code,
|
| PP_Var reason,
|
| PP_CompletionCallback callback) {
|
| - // TODO(toyoshim): Implement it.
|
| - return PP_ERROR_NOTSUPPORTED;
|
| + // Check mandarory interfaces.
|
| + if (!websocket_.get())
|
| + return PP_ERROR_FAILED;
|
| +
|
| + // Validate |callback| (Doesn't support blocking callback)
|
| + if (!callback.func)
|
| + return PP_ERROR_BLOCKS_MAIN_THREAD;
|
| +
|
| + // Validate |code|.
|
| + if (code != WebSocket::CloseEventCodeNotSpecified) {
|
| + if (!(code == WebSocket::CloseEventCodeNormalClosure ||
|
| + (WebSocket::CloseEventCodeMinimumUserDefined <= code &&
|
| + code <= WebSocket::CloseEventCodeMaximumUserDefined)))
|
| + return PP_ERROR_NOACCESS;
|
| + }
|
| + // Validate |reason|.
|
| + // TODO(toyoshim): Returns PP_ERROR_BADARGUMENT if |reason| contains any
|
| + // surrogates.
|
| + scoped_refptr<StringVar> reason_string = StringVar::FromPPVar(reason);
|
| + if (!reason_string || reason_string->value().size() > kMaxReasonSizeInBytes)
|
| + return PP_ERROR_BADARGUMENT;
|
| +
|
| + // Check state.
|
| + if (state_ == PP_WEBSOCKETREADYSTATE_CLOSING_DEV ||
|
| + state_ == PP_WEBSOCKETREADYSTATE_CLOSED_DEV)
|
| + return PP_ERROR_INPROGRESS;
|
| +
|
| + // Install |callback|.
|
| + close_callback_ = callback;
|
| +
|
| + if (state_ == PP_WEBSOCKETREADYSTATE_CONNECTING_DEV) {
|
| + state_ = PP_WEBSOCKETREADYSTATE_CLOSING_DEV;
|
| + PP_RunAndClearCompletionCallback(&connect_callback_, PP_ERROR_ABORTED);
|
| + websocket_->fail(
|
| + "WebSocket was closed before the connection was established.");
|
| + return PP_OK_COMPLETIONPENDING;
|
| + }
|
| +
|
| + // TODO(toyoshim): Handle bufferedAmount here.
|
| +
|
| + state_ = PP_WEBSOCKETREADYSTATE_CLOSING_DEV;
|
| + WebString web_reason = WebString::fromUTF8(reason_string->value());
|
| + websocket_->close(code, web_reason);
|
| +
|
| + return PP_OK_COMPLETIONPENDING;
|
| }
|
|
|
| int32_t PPB_WebSocket_Impl::ReceiveMessage(PP_Var* message,
|
| PP_CompletionCallback callback) {
|
| - // TODO(toyoshim): Implement it.
|
| - return PP_ERROR_NOTSUPPORTED;
|
| + // Check state.
|
| + if (state_ == PP_WEBSOCKETREADYSTATE_INVALID_DEV ||
|
| + state_ == PP_WEBSOCKETREADYSTATE_CONNECTING_DEV)
|
| + return PP_ERROR_BADARGUMENT;
|
| +
|
| + // Just return received message if any received message is queued.
|
| + if (!received_messages_.empty())
|
| + return DoReceive();
|
| +
|
| + // Or retain |message| as buffer to store and install |callback|.
|
| + wait_for_receive_ = true;
|
| + receive_callback_var_ = message;
|
| + receive_callback_ = callback;
|
| +
|
| + return PP_OK_COMPLETIONPENDING;
|
| }
|
|
|
| int32_t PPB_WebSocket_Impl::SendMessage(PP_Var message) {
|
| - // TODO(toyoshim): Implement it.
|
| - return PP_ERROR_NOTSUPPORTED;
|
| + // Check mandatory interfaces.
|
| + if (!websocket_.get())
|
| + return PP_ERROR_FAILED;
|
| +
|
| + // Check state.
|
| + if (state_ == PP_WEBSOCKETREADYSTATE_INVALID_DEV ||
|
| + state_ == PP_WEBSOCKETREADYSTATE_CONNECTING_DEV)
|
| + return PP_ERROR_BADARGUMENT;
|
| +
|
| + if (state_ == PP_WEBSOCKETREADYSTATE_CLOSING_DEV ||
|
| + state_ == PP_WEBSOCKETREADYSTATE_CLOSED_DEV) {
|
| + // TODO(toyoshim): Handle bufferedAmount here.
|
| + }
|
| +
|
| + if (message.type != PP_VARTYPE_STRING) {
|
| + // TODO(toyoshim): Support binary data.
|
| + return PP_ERROR_NOTSUPPORTED;
|
| + }
|
| +
|
| + // Convert message to WebString.
|
| + scoped_refptr<StringVar> message_string = StringVar::FromPPVar(message);
|
| + if (!message_string)
|
| + return PP_ERROR_BADARGUMENT;
|
| + WebString web_message = WebString::fromUTF8(message_string->value());
|
| + if (!websocket_->sendText(web_message))
|
| + return PP_ERROR_BADARGUMENT;
|
| +
|
| + return PP_OK;
|
| }
|
|
|
| uint64_t PPB_WebSocket_Impl::GetBufferedAmount() {
|
| - // TODO(toyoshim): Implement it.
|
| + // TODO(toyoshim): Implement.
|
| return 0;
|
| }
|
|
|
| uint16_t PPB_WebSocket_Impl::GetCloseCode() {
|
| - // TODO(toyoshim): Implement it.
|
| - return 0;
|
| + return close_code_;
|
| }
|
|
|
| PP_Var PPB_WebSocket_Impl::GetCloseReason() {
|
| - // TODO(toyoshim): Implement it.
|
| - return PP_MakeUndefined();
|
| + if (!close_reason_)
|
| + return empty_string_->GetPPVar();
|
| + return close_reason_->GetPPVar();
|
| }
|
|
|
| PP_Bool PPB_WebSocket_Impl::GetCloseWasClean() {
|
| - // TODO(toyoshim): Implement it.
|
| - return PP_FALSE;
|
| + return close_was_clean_;
|
| }
|
|
|
| PP_Var PPB_WebSocket_Impl::GetExtensions() {
|
| - // TODO(toyoshim): Implement it.
|
| - return PP_MakeUndefined();
|
| + // TODO(toyoshim): For now, always returns empty string.
|
| + if (!extensions_)
|
| + return empty_string_->GetPPVar();
|
| + return extensions_->GetPPVar();
|
| }
|
|
|
| PP_Var PPB_WebSocket_Impl::GetProtocol() {
|
| - // TODO(toyoshim): Implement it.
|
| - return PP_MakeUndefined();
|
| + // TODO(toyoshim): Implement.
|
| + if (!protocol_)
|
| + return empty_string_->GetPPVar();
|
| + return protocol_->GetPPVar();
|
| }
|
|
|
| PP_WebSocketReadyState_Dev PPB_WebSocket_Impl::GetReadyState() {
|
| - // TODO(toyoshim): Implement it.
|
| - return PP_WEBSOCKETREADYSTATE_INVALID_DEV;
|
| + return state_;
|
| }
|
|
|
| PP_Var PPB_WebSocket_Impl::GetURL() {
|
| - // TODO(toyoshim): Implement it.
|
| - return PP_MakeUndefined();
|
| + // TODO(toyoshim): For now, always returns empty string.
|
| + if (!url_)
|
| + return empty_string_->GetPPVar();
|
| + return url_->GetPPVar();
|
| +}
|
| +
|
| +void PPB_WebSocket_Impl::didConnect() {
|
| + DCHECK_EQ(PP_WEBSOCKETREADYSTATE_CONNECTING_DEV, state_);
|
| + state_ = PP_WEBSOCKETREADYSTATE_OPEN_DEV;
|
| + PP_RunAndClearCompletionCallback(&connect_callback_, PP_OK);
|
| +}
|
| +
|
| +void PPB_WebSocket_Impl::didReceiveMessage(const WebString& message) {
|
| + // Append received data to queue.
|
| + std::string string = message.utf8();
|
| + PP_Var var = StringVar::StringToPPVar(
|
| + PpapiGlobals::Get()->GetModuleForInstance(pp_instance()), string);
|
| + received_messages_.push(var);
|
| +
|
| + if (!wait_for_receive_)
|
| + return;
|
| +
|
| + PP_RunAndClearCompletionCallback(&receive_callback_, DoReceive());
|
| +}
|
| +
|
| +void PPB_WebSocket_Impl::didReceiveBinaryData(const WebData& binaryData) {
|
| + DLOG(INFO) << "didReceiveBinaryData is not implemented yet.";
|
| + // TODO(toyoshim): Support to receive binary data.
|
| +}
|
| +
|
| +void PPB_WebSocket_Impl::didReceiveMessageError() {
|
| + // TODO(toyoshim): Must implement.
|
| + DLOG(INFO) << "didReceiveMessageError is not implemented yet.";
|
| +}
|
| +
|
| +void PPB_WebSocket_Impl::didStartClosingHandshake() {
|
| + // TODO(toyoshim): Must implement.
|
| + DLOG(INFO) << "didStartClosingHandshake is not implemented yet.";
|
| +}
|
| +
|
| +void PPB_WebSocket_Impl::didClose(unsigned long bufferedAmount,
|
| + ClosingHandshakeCompletionStatus status,
|
| + unsigned short code,
|
| + const WebString& reason) {
|
| + // Store code and reason.
|
| + close_code_ = code;
|
| + std::string reason_string = reason.utf8();
|
| + close_reason_ = new StringVar(
|
| + PpapiGlobals::Get()->GetModuleForInstance(pp_instance()), reason_string);
|
| +
|
| + // TODO(toyoshim): Set close_was_clean_.
|
| +
|
| + // Handle state transition and invoking callback.
|
| + DCHECK_NE(PP_WEBSOCKETREADYSTATE_CLOSED_DEV, state_);
|
| + PP_WebSocketReadyState_Dev state = state_;
|
| + state_ = PP_WEBSOCKETREADYSTATE_CLOSED_DEV;
|
| +
|
| + if (state == PP_WEBSOCKETREADYSTATE_CONNECTING_DEV)
|
| + PP_RunAndClearCompletionCallback(&connect_callback_, PP_OK);
|
| +
|
| + if (state == PP_WEBSOCKETREADYSTATE_CLOSING_DEV)
|
| + PP_RunAndClearCompletionCallback(&close_callback_, PP_OK);
|
| +}
|
| +
|
| +int32_t PPB_WebSocket_Impl::DoReceive() {
|
| + // TODO(toyoshim): Check state.
|
| +
|
| + if (!receive_callback_var_)
|
| + return PP_OK;
|
| +
|
| + *receive_callback_var_ = received_messages_.front();
|
| + received_messages_.pop();
|
| + receive_callback_var_ = NULL;
|
| + wait_for_receive_ = false;
|
| + return PP_OK;
|
| }
|
|
|
| } // namespace ppapi
|
|
|