summaryrefslogtreecommitdiffstats
path: root/ppapi
diff options
context:
space:
mode:
Diffstat (limited to 'ppapi')
-rw-r--r--ppapi/host/ppapi_host.cc6
-rw-r--r--ppapi/host/ppapi_host.h3
-rw-r--r--ppapi/ppapi_proxy.gypi2
-rw-r--r--ppapi/ppapi_shared.gypi1
-rw-r--r--ppapi/ppapi_tests.gypi1
-rw-r--r--ppapi/proxy/plugin_dispatcher.cc3
-rw-r--r--ppapi/proxy/ppapi_messages.h80
-rw-r--r--ppapi/proxy/resource_creation_proxy.cc10
-rw-r--r--ppapi/proxy/resource_creation_proxy.h2
-rw-r--r--ppapi/proxy/websocket_resource.cc509
-rw-r--r--ppapi/proxy/websocket_resource.h157
-rw-r--r--ppapi/proxy/websocket_resource_unittest.cc168
-rw-r--r--ppapi/tests/test_websocket.cc25
-rw-r--r--ppapi/thunk/interfaces_ppb_public_dev.h3
-rw-r--r--ppapi/thunk/interfaces_ppb_public_stable.h1
-rw-r--r--ppapi/thunk/ppb_websocket_api.h9
-rw-r--r--ppapi/thunk/resource_creation_api.h2
-rw-r--r--ppapi/thunk/thunk.h2
18 files changed, 963 insertions, 21 deletions
diff --git a/ppapi/host/ppapi_host.cc b/ppapi/host/ppapi_host.cc
index f13cea0..1361da2 100644
--- a/ppapi/host/ppapi_host.cc
+++ b/ppapi/host/ppapi_host.cc
@@ -79,6 +79,12 @@ void PpapiHost::SendReply(const ReplyMessageContext& context,
}
}
+void PpapiHost::SendUnsolicitedReply(PP_Resource resource,
+ const IPC::Message& msg) {
+ proxy::ResourceMessageReplyParams params(resource, 0);
+ Send(new PpapiPluginMsg_ResourceReply(params, msg));
+}
+
void PpapiHost::AddHostFactoryFilter(scoped_ptr<HostFactory> filter) {
host_factory_filters_.push_back(filter.release());
}
diff --git a/ppapi/host/ppapi_host.h b/ppapi/host/ppapi_host.h
index 79731e6..3597093 100644
--- a/ppapi/host/ppapi_host.h
+++ b/ppapi/host/ppapi_host.h
@@ -58,6 +58,9 @@ class PPAPI_HOST_EXPORT PpapiHost : public IPC::Sender, public IPC::Listener {
void SendReply(const ReplyMessageContext& context,
const IPC::Message& msg);
+ // Sends the given unsolicited reply message to the plugin.
+ void SendUnsolicitedReply(PP_Resource resource, const IPC::Message& msg);
+
// Adds the given host factory filter to the host. The PpapiHost will take
// ownership of the pointer.
void AddHostFactoryFilter(scoped_ptr<HostFactory> filter);
diff --git a/ppapi/ppapi_proxy.gypi b/ppapi/ppapi_proxy.gypi
index 56dbb8c..a483945 100644
--- a/ppapi/ppapi_proxy.gypi
+++ b/ppapi/ppapi_proxy.gypi
@@ -156,6 +156,8 @@
'proxy/url_request_info_resource.cc',
'proxy/url_request_info_resource.h',
'proxy/var_serialization_rules.h',
+ 'proxy/websocket_resource.cc',
+ 'proxy/websocket_resource.h',
],
'defines': [
'PPAPI_PROXY_IMPLEMENTATION',
diff --git a/ppapi/ppapi_shared.gypi b/ppapi/ppapi_shared.gypi
index 7832527..3bcec1d 100644
--- a/ppapi/ppapi_shared.gypi
+++ b/ppapi/ppapi_shared.gypi
@@ -286,7 +286,6 @@
'thunk/ppb_url_util_thunk.cc',
'thunk/ppb_video_capture_thunk.cc',
'thunk/ppb_video_decoder_thunk.cc',
- 'thunk/ppb_websocket_thunk.cc',
],
}],
# We exclude a few more things for nacl_win64, to avoid pulling in
diff --git a/ppapi/ppapi_tests.gypi b/ppapi/ppapi_tests.gypi
index ea156dd..9edc419 100644
--- a/ppapi/ppapi_tests.gypi
+++ b/ppapi/ppapi_tests.gypi
@@ -153,6 +153,7 @@
'proxy/ppp_messaging_proxy_unittest.cc',
'proxy/printing_resource_unittest.cc',
'proxy/serialized_var_unittest.cc',
+ 'proxy/websocket_resource_unittest.cc',
'shared_impl/resource_tracker_unittest.cc',
'shared_impl/tracked_callback_unittest.cc',
'shared_impl/var_tracker_unittest.cc',
diff --git a/ppapi/proxy/plugin_dispatcher.cc b/ppapi/proxy/plugin_dispatcher.cc
index 51a8ff2..51272fd 100644
--- a/ppapi/proxy/plugin_dispatcher.cc
+++ b/ppapi/proxy/plugin_dispatcher.cc
@@ -272,7 +272,8 @@ void PluginDispatcher::DispatchResourceReply(
Resource* resource = PpapiGlobals::Get()->GetResourceTracker()->GetResource(
reply_params.pp_resource());
if (!resource) {
- NOTREACHED();
+ if (reply_params.sequence())
+ NOTREACHED();
return;
}
resource->OnReplyReceived(reply_params, nested_msg);
diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h
index 8ac0886..3e9dca2 100644
--- a/ppapi/proxy/ppapi_messages.h
+++ b/ppapi/proxy/ppapi_messages.h
@@ -1582,3 +1582,83 @@ IPC_MESSAGE_CONTROL0(PpapiHostMsg_Printing_Create)
IPC_MESSAGE_CONTROL0(PpapiHostMsg_Printing_GetDefaultPrintSettings)
IPC_MESSAGE_CONTROL1(PpapiPluginMsg_Printing_GetDefaultPrintSettingsReply,
PP_PrintSettings_Dev /* print_settings */)
+
+// WebSocket ------------------------------------------------------------------
+
+IPC_MESSAGE_CONTROL0(PpapiHostMsg_WebSocket_Create)
+
+// Establishes the connection to a server. This message requires
+// WebSocket_ConnectReply as a reply message.
+IPC_MESSAGE_CONTROL2(PpapiHostMsg_WebSocket_Connect,
+ std::string /* url */,
+ std::vector<std::string> /* protocols */)
+
+// Closes established connection with graceful closing handshake. This message
+// requires WebSocket_CloseReply as a reply message.
+IPC_MESSAGE_CONTROL2(PpapiHostMsg_WebSocket_Close,
+ int32_t /* code */,
+ std::string /* reason */)
+
+// Sends a text frame to the server. No reply is defined.
+IPC_MESSAGE_CONTROL1(PpapiHostMsg_WebSocket_SendText,
+ std::string /* message */)
+
+// Sends a binary frame to the server. No reply is defined.
+IPC_MESSAGE_CONTROL1(PpapiHostMsg_WebSocket_SendBinary,
+ std::vector<uint8_t> /* message */)
+
+// Fails the connection. This message invokes RFC6455 defined
+// _Fail the WebSocket Connection_ operation. No reply is defined.
+IPC_MESSAGE_CONTROL1(PpapiHostMsg_WebSocket_Fail,
+ std::string /* message */)
+
+// This message is a reply to WebSocket_Connect. If the |url| and |protocols|
+// are invalid, WebSocket_ConnectReply is issued immediately and it contains
+// proper error code in its result. Otherwise, WebSocket_ConnectReply is sent
+// with valid |url|, |protocol|, and result PP_OK. |protocol| is not a passed
+// |protocols|, but a result of opening handshake negotiation. If the
+// connection can not be established successfully, WebSocket_ConnectReply is
+// not issued, but WebSocket_ClosedReply is sent instead.
+IPC_MESSAGE_CONTROL2(PpapiPluginMsg_WebSocket_ConnectReply,
+ std::string /* url */,
+ std::string /* protocol */)
+
+// This message is a reply to WebSocket_Close. If the operation fails,
+// WebSocket_CloseReply is issued immediately and it contains PP_ERROR_FAILED.
+// Otherwise, CloseReply will be issued after the closing handshake is
+// finished. All arguments will be valid iff the result is PP_OK and it means
+// that the client initiated closing handshake is finished gracefully.
+IPC_MESSAGE_CONTROL4(PpapiPluginMsg_WebSocket_CloseReply,
+ unsigned long /* buffered_amount */,
+ bool /* was_clean */,
+ unsigned short /* code */,
+ std::string /* reason */)
+
+// Unsolicited reply message to transmit a receiving text frame.
+IPC_MESSAGE_CONTROL1(PpapiPluginMsg_WebSocket_ReceiveTextReply,
+ std::string /* message */)
+
+// Unsolicited reply message to transmit a receiving binary frame.
+IPC_MESSAGE_CONTROL1(PpapiPluginMsg_WebSocket_ReceiveBinaryReply,
+ std::vector<uint8_t> /* message */)
+
+// Unsolicited reply message to notify a error on underlying network connetion.
+IPC_MESSAGE_CONTROL0(PpapiPluginMsg_WebSocket_ErrorReply)
+
+// Unsolicited reply message to update the buffered amount value.
+IPC_MESSAGE_CONTROL1(PpapiPluginMsg_WebSocket_BufferedAmountReply,
+ unsigned long /* buffered_amount */)
+
+// Unsolicited reply message to update |state| because of incoming external
+// events, e.g., protocol error, or unexpected network closure.
+IPC_MESSAGE_CONTROL1(PpapiPluginMsg_WebSocket_StateReply,
+ int32_t /* state */)
+
+// Unsolicited reply message to notify that the connection is closed without
+// any WebSocket_Close request. Server initiated closing handshake or
+// unexpected network errors will invoke this message.
+IPC_MESSAGE_CONTROL4(PpapiPluginMsg_WebSocket_ClosedReply,
+ unsigned long /* buffered_amount */,
+ bool /* was_clean */,
+ unsigned short /* code */,
+ std::string /* reason */)
diff --git a/ppapi/proxy/resource_creation_proxy.cc b/ppapi/proxy/resource_creation_proxy.cc
index 373b9d7..78123a6 100644
--- a/ppapi/proxy/resource_creation_proxy.cc
+++ b/ppapi/proxy/resource_creation_proxy.cc
@@ -39,6 +39,7 @@
#include "ppapi/proxy/ppb_x509_certificate_private_proxy.h"
#include "ppapi/proxy/printing_resource.h"
#include "ppapi/proxy/url_request_info_resource.h"
+#include "ppapi/proxy/websocket_resource.h"
#include "ppapi/shared_impl/api_id.h"
#include "ppapi/shared_impl/host_resource.h"
#include "ppapi/shared_impl/ppb_audio_config_shared.h"
@@ -256,6 +257,10 @@ PP_Resource ResourceCreationProxy::CreateUDPSocketPrivate(
return PPB_UDPSocket_Private_Proxy::CreateProxyResource(instance);
}
+PP_Resource ResourceCreationProxy::CreateWebSocket(PP_Instance instance) {
+ return (new WebSocketResource(GetConnection(), instance))->GetReference();
+}
+
PP_Resource ResourceCreationProxy::CreateX509CertificatePrivate(
PP_Instance instance) {
return PPB_X509Certificate_Private_Proxy::CreateProxyResource(instance);
@@ -351,11 +356,6 @@ PP_Resource ResourceCreationProxy::CreateVideoDecoder(
instance, context3d_id, profile);
}
-PP_Resource ResourceCreationProxy::CreateWebSocket(PP_Instance instance) {
- NOTIMPLEMENTED();
- return 0;
-}
-
#endif // !defined(OS_NACL)
bool ResourceCreationProxy::Send(IPC::Message* msg) {
diff --git a/ppapi/proxy/resource_creation_proxy.h b/ppapi/proxy/resource_creation_proxy.h
index 7ab07e0..4860cbf 100644
--- a/ppapi/proxy/resource_creation_proxy.h
+++ b/ppapi/proxy/resource_creation_proxy.h
@@ -122,6 +122,7 @@ class ResourceCreationProxy : public InterfaceProxy,
PP_Instance instance) OVERRIDE;
virtual PP_Resource CreateTCPSocketPrivate(PP_Instance instance) OVERRIDE;
virtual PP_Resource CreateUDPSocketPrivate(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateWebSocket(PP_Instance instance) OVERRIDE;
virtual PP_Resource CreateX509CertificatePrivate(
PP_Instance instance) OVERRIDE;
#if !defined(OS_NACL)
@@ -155,7 +156,6 @@ class ResourceCreationProxy : public InterfaceProxy,
PP_Instance instance,
PP_Resource context3d_id,
PP_VideoDecoder_Profile profile) OVERRIDE;
- virtual PP_Resource CreateWebSocket(PP_Instance instance) OVERRIDE;
#endif // !defined(OS_NACL)
virtual bool Send(IPC::Message* msg) OVERRIDE;
diff --git a/ppapi/proxy/websocket_resource.cc b/ppapi/proxy/websocket_resource.cc
new file mode 100644
index 0000000..de9ee2b
--- /dev/null
+++ b/ppapi/proxy/websocket_resource.cc
@@ -0,0 +1,509 @@
+// 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.
+
+#include "ppapi/proxy/websocket_resource.h"
+
+#include <set>
+#include <vector>
+
+#include "base/bind.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/proxy/ppapi_messages.h"
+#include "ppapi/shared_impl/ppapi_globals.h"
+#include "ppapi/shared_impl/var.h"
+#include "ppapi/shared_impl/var_tracker.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebSocket.h"
+
+namespace {
+
+const uint32_t kMaxReasonSizeInBytes = 123;
+const size_t kBaseFramingOverhead = 2;
+const size_t kMaskingKeyLength = 4;
+const size_t kMinimumPayloadSizeWithTwoByteExtendedPayloadLength = 126;
+const size_t kMinimumPayloadSizeWithEightByteExtendedPayloadLength = 0x10000;
+
+uint64_t SaturateAdd(uint64_t a, uint64_t b) {
+ if (kuint64max - a < b)
+ return kuint64max;
+ return a + b;
+}
+
+uint64_t GetFrameSize(uint64_t payload_size) {
+ uint64_t overhead = kBaseFramingOverhead + kMaskingKeyLength;
+ if (payload_size > kMinimumPayloadSizeWithEightByteExtendedPayloadLength)
+ overhead += 8;
+ else if (payload_size > kMinimumPayloadSizeWithTwoByteExtendedPayloadLength)
+ overhead += 2;
+ return SaturateAdd(payload_size, overhead);
+}
+
+bool InValidStateToReceive(PP_WebSocketReadyState state) {
+ return state == PP_WEBSOCKETREADYSTATE_OPEN ||
+ state == PP_WEBSOCKETREADYSTATE_CLOSING;
+}
+
+} // namespace
+
+
+namespace ppapi {
+namespace proxy {
+
+WebSocketResource::WebSocketResource(Connection connection,
+ PP_Instance instance)
+ : PluginResource(connection, instance),
+ state_(PP_WEBSOCKETREADYSTATE_INVALID),
+ error_was_received_(false),
+ receive_callback_var_(NULL),
+ empty_string_(new StringVar(std::string())),
+ close_code_(0),
+ close_reason_(NULL),
+ close_was_clean_(PP_FALSE),
+ extensions_(NULL),
+ protocol_(NULL),
+ url_(NULL),
+ buffered_amount_(0),
+ buffered_amount_after_close_(0) {
+}
+
+WebSocketResource::~WebSocketResource() {
+}
+
+thunk::PPB_WebSocket_API* WebSocketResource::AsPPB_WebSocket_API() {
+ return this;
+}
+
+int32_t WebSocketResource::Connect(
+ const PP_Var& url,
+ const PP_Var protocols[],
+ uint32_t protocol_count,
+ scoped_refptr<TrackedCallback> callback) {
+ if (TrackedCallback::IsPending(connect_callback_))
+ return PP_ERROR_INPROGRESS;
+
+ // Connect() can be called at most once.
+ if (state_ != PP_WEBSOCKETREADYSTATE_INVALID)
+ return PP_ERROR_INPROGRESS;
+ state_ = PP_WEBSOCKETREADYSTATE_CLOSED;
+
+ // Get the URL.
+ url_ = StringVar::FromPPVar(url);
+ if (!url_)
+ return PP_ERROR_BADARGUMENT;
+
+ // Get the protocols.
+ std::set<std::string> protocol_set;
+ std::vector<std::string> protocol_strings;
+ protocol_strings.reserve(protocol_count);
+ for (uint32_t i = 0; i < protocol_count; ++i) {
+ scoped_refptr<StringVar> protocol(StringVar::FromPPVar(protocols[i]));
+
+ // Check invalid and empty entries.
+ if (!protocol || !protocol->value().length())
+ return PP_ERROR_BADARGUMENT;
+
+ // Check duplicated protocol entries.
+ if (protocol_set.find(protocol->value()) != protocol_set.end())
+ return PP_ERROR_BADARGUMENT;
+ protocol_set.insert(protocol->value());
+
+ protocol_strings.push_back(protocol->value());
+ }
+
+ // Install callback.
+ connect_callback_ = callback;
+
+ // Create remote host in the renderer, then request to check the URL and
+ // establish the connection.
+ state_ = PP_WEBSOCKETREADYSTATE_CONNECTING;
+ SendCreateToRenderer(PpapiHostMsg_WebSocket_Create());
+ PpapiHostMsg_WebSocket_Connect msg(url_->value(), protocol_strings);
+ CallRenderer<PpapiPluginMsg_WebSocket_ConnectReply>(msg,
+ base::Bind(&WebSocketResource::OnPluginMsgConnectReply, this));
+
+ return PP_OK_COMPLETIONPENDING;
+}
+
+int32_t WebSocketResource::Close(uint16_t code,
+ const PP_Var& reason,
+ scoped_refptr<TrackedCallback> callback) {
+ if (TrackedCallback::IsPending(close_callback_))
+ return PP_ERROR_INPROGRESS;
+ if (state_ == PP_WEBSOCKETREADYSTATE_INVALID)
+ return PP_ERROR_FAILED;
+
+ // Validate |code| and |reason|.
+ scoped_refptr<StringVar> reason_string_var;
+ std::string reason_string;
+ WebKit::WebSocket::CloseEventCode event_code =
+ static_cast<WebKit::WebSocket::CloseEventCode>(code);
+ if (code == PP_WEBSOCKETSTATUSCODE_NOT_SPECIFIED) {
+ // PP_WEBSOCKETSTATUSCODE_NOT_SPECIFIED and CloseEventCodeNotSpecified are
+ // assigned to different values. A conversion is needed if
+ // PP_WEBSOCKETSTATUSCODE_NOT_SPECIFIED is specified.
+ event_code = WebKit::WebSocket::CloseEventCodeNotSpecified;
+ } else {
+ if (!(code == PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE ||
+ (PP_WEBSOCKETSTATUSCODE_USER_REGISTERED_MIN <= code &&
+ code <= PP_WEBSOCKETSTATUSCODE_USER_PRIVATE_MAX)))
+ // RFC 6455 limits applications to use reserved connection close code in
+ // section 7.4.2.. The WebSocket API (http://www.w3.org/TR/websockets/)
+ // defines this out of range error as InvalidAccessError in JavaScript.
+ return PP_ERROR_NOACCESS;
+
+ // |reason| must be ignored if it is PP_VARTYPE_UNDEFINED or |code| is
+ // PP_WEBSOCKETSTATUSCODE_NOT_SPECIFIED.
+ if (reason.type != PP_VARTYPE_UNDEFINED) {
+ // Validate |reason|.
+ reason_string_var = StringVar::FromPPVar(reason);
+ if (!reason_string_var ||
+ reason_string_var->value().size() > kMaxReasonSizeInBytes)
+ return PP_ERROR_BADARGUMENT;
+ reason_string = reason_string_var->value();
+ }
+ }
+
+ // Check state.
+ if (state_ == PP_WEBSOCKETREADYSTATE_CLOSING)
+ return PP_ERROR_INPROGRESS;
+ if (state_ == PP_WEBSOCKETREADYSTATE_CLOSED)
+ return PP_OK;
+
+ // Install |callback|.
+ close_callback_ = callback;
+
+ // Abort ongoing connect.
+ if (TrackedCallback::IsPending(connect_callback_)) {
+ state_ = PP_WEBSOCKETREADYSTATE_CLOSING;
+ // Need to do a "Post" to avoid reentering the plugin.
+ connect_callback_->PostAbort();
+ connect_callback_ = NULL;
+ PostToRenderer(PpapiHostMsg_WebSocket_Fail(
+ "WebSocket was closed before the connection was established."));
+ return PP_OK_COMPLETIONPENDING;
+ }
+
+ // Abort ongoing receive.
+ if (TrackedCallback::IsPending(receive_callback_)) {
+ receive_callback_var_ = NULL;
+ // Need to do a "Post" to avoid reentering the plugin.
+ receive_callback_->PostAbort();
+ receive_callback_ = NULL;
+ }
+
+ // Close connection.
+ state_ = PP_WEBSOCKETREADYSTATE_CLOSING;
+ PpapiHostMsg_WebSocket_Close msg(static_cast<int32_t>(event_code),
+ reason_string);
+ CallRenderer<PpapiPluginMsg_WebSocket_CloseReply>(msg,
+ base::Bind(&WebSocketResource::OnPluginMsgCloseReply, this));
+ return PP_OK_COMPLETIONPENDING;
+}
+
+int32_t WebSocketResource::ReceiveMessage(
+ PP_Var* message,
+ scoped_refptr<TrackedCallback> callback) {
+ if (TrackedCallback::IsPending(receive_callback_))
+ return PP_ERROR_INPROGRESS;
+
+ // Check state.
+ if (state_ == PP_WEBSOCKETREADYSTATE_INVALID ||
+ state_ == PP_WEBSOCKETREADYSTATE_CONNECTING)
+ return PP_ERROR_BADARGUMENT;
+
+ // Just return received message if any received message is queued.
+ if (!received_messages_.empty()) {
+ receive_callback_var_ = message;
+ return DoReceive();
+ }
+
+ // Check state again. In CLOSED state, no more messages will be received.
+ if (state_ == PP_WEBSOCKETREADYSTATE_CLOSED)
+ return PP_ERROR_BADARGUMENT;
+
+ // Returns PP_ERROR_FAILED after an error is received and received messages
+ // is exhausted.
+ if (error_was_received_)
+ return PP_ERROR_FAILED;
+
+ // Or retain |message| as buffer to store and install |callback|.
+ receive_callback_var_ = message;
+ receive_callback_ = callback;
+
+ return PP_OK_COMPLETIONPENDING;
+}
+
+int32_t WebSocketResource::SendMessage(const PP_Var& message) {
+ // Check state.
+ if (state_ == PP_WEBSOCKETREADYSTATE_INVALID ||
+ state_ == PP_WEBSOCKETREADYSTATE_CONNECTING)
+ return PP_ERROR_BADARGUMENT;
+
+ if (state_ == PP_WEBSOCKETREADYSTATE_CLOSING ||
+ state_ == PP_WEBSOCKETREADYSTATE_CLOSED) {
+ // Handle buffered_amount_after_close_.
+ uint64_t payload_size = 0;
+ if (message.type == PP_VARTYPE_STRING) {
+ scoped_refptr<StringVar> message_string = StringVar::FromPPVar(message);
+ if (message_string)
+ payload_size += message_string->value().length();
+ } else if (message.type == PP_VARTYPE_ARRAY_BUFFER) {
+ scoped_refptr<ArrayBufferVar> message_array_buffer =
+ ArrayBufferVar::FromPPVar(message);
+ if (message_array_buffer)
+ payload_size += message_array_buffer->ByteLength();
+ } else {
+ // TODO(toyoshim): Support Blob.
+ return PP_ERROR_NOTSUPPORTED;
+ }
+
+ buffered_amount_after_close_ =
+ SaturateAdd(buffered_amount_after_close_, GetFrameSize(payload_size));
+
+ return PP_ERROR_FAILED;
+ }
+
+ // Send the message.
+ if (message.type == PP_VARTYPE_STRING) {
+ // Convert message to std::string, then send it.
+ scoped_refptr<StringVar> message_string = StringVar::FromPPVar(message);
+ if (!message_string)
+ return PP_ERROR_BADARGUMENT;
+ PostToRenderer(PpapiHostMsg_WebSocket_SendText(message_string->value()));
+ } else if (message.type == PP_VARTYPE_ARRAY_BUFFER) {
+ // Convert message to std::vector<uint8_t>, then send it.
+ scoped_refptr<ArrayBufferVar> message_arraybuffer =
+ ArrayBufferVar::FromPPVar(message);
+ if (!message_arraybuffer)
+ return PP_ERROR_BADARGUMENT;
+ uint8_t* message_data = static_cast<uint8_t*>(message_arraybuffer->Map());
+ uint32 message_length = message_arraybuffer->ByteLength();
+ std::vector<uint8_t> message_vector(message_data,
+ message_data + message_length);
+ PostToRenderer(PpapiHostMsg_WebSocket_SendBinary(message_vector));
+ } else {
+ // TODO(toyoshim): Support Blob.
+ return PP_ERROR_NOTSUPPORTED;
+ }
+ return PP_OK;
+}
+
+uint64_t WebSocketResource::GetBufferedAmount() {
+ return SaturateAdd(buffered_amount_, buffered_amount_after_close_);
+}
+
+uint16_t WebSocketResource::GetCloseCode() {
+ return close_code_;
+}
+
+PP_Var WebSocketResource::GetCloseReason() {
+ if (!close_reason_)
+ return empty_string_->GetPPVar();
+ return close_reason_->GetPPVar();
+}
+
+PP_Bool WebSocketResource::GetCloseWasClean() {
+ return close_was_clean_;
+}
+
+PP_Var WebSocketResource::GetExtensions() {
+ return StringVar::StringToPPVar(std::string());
+}
+
+PP_Var WebSocketResource::GetProtocol() {
+ if (!protocol_)
+ return empty_string_->GetPPVar();
+ return protocol_->GetPPVar();
+}
+
+PP_WebSocketReadyState WebSocketResource::GetReadyState() {
+ return state_;
+}
+
+PP_Var WebSocketResource::GetURL() {
+ if (!url_)
+ return empty_string_->GetPPVar();
+ return url_->GetPPVar();
+}
+
+void WebSocketResource::OnReplyReceived(
+ const ResourceMessageReplyParams& params,
+ const IPC::Message& msg) {
+ if (params.sequence())
+ return PluginResource::OnReplyReceived(params, msg);
+
+ // TODO(toyoshim): Currently, following unsolicited reply IPCs are handled
+ // manually. We should introduce more useful mechanism for that.
+ switch (msg.type()) {
+ case PpapiPluginMsg_WebSocket_ReceiveTextReply::ID: {
+ PpapiPluginMsg_WebSocket_ReceiveTextReply::Schema::Param p;
+ if (PpapiPluginMsg_WebSocket_ReceiveTextReply::Read(&msg, &p))
+ OnPluginMsgReceiveTextReply(params, p.a);
+ else
+ NOTREACHED();
+ break;
+ }
+ case PpapiPluginMsg_WebSocket_ReceiveBinaryReply::ID: {
+ PpapiPluginMsg_WebSocket_ReceiveBinaryReply::Schema::Param p;
+ if (PpapiPluginMsg_WebSocket_ReceiveBinaryReply::Read(&msg, &p))
+ OnPluginMsgReceiveBinaryReply(params, p.a);
+ else
+ NOTREACHED();
+ break;
+ }
+ case PpapiPluginMsg_WebSocket_ErrorReply::ID: {
+ OnPluginMsgErrorReply(params);
+ break;
+ }
+ case PpapiPluginMsg_WebSocket_BufferedAmountReply::ID: {
+ PpapiPluginMsg_WebSocket_BufferedAmountReply::Schema::Param p;
+ if (PpapiPluginMsg_WebSocket_BufferedAmountReply::Read(&msg, &p))
+ OnPluginMsgBufferedAmountReply(params, p.a);
+ else
+ NOTREACHED();
+ break;
+ }
+ case PpapiPluginMsg_WebSocket_StateReply::ID: {
+ PpapiPluginMsg_WebSocket_StateReply::Schema::Param p;
+ if (PpapiPluginMsg_WebSocket_StateReply::Read(&msg, &p))
+ OnPluginMsgStateReply(params, p.a);
+ else
+ NOTREACHED();
+ break;
+ }
+ case PpapiPluginMsg_WebSocket_ClosedReply::ID: {
+ PpapiPluginMsg_WebSocket_ClosedReply::Schema::Param p;
+ if (PpapiPluginMsg_WebSocket_ClosedReply::Read(&msg, &p))
+ OnPluginMsgClosedReply(params, p.a, p.b, p.c, p.d);
+ else
+ NOTREACHED();
+ break;
+ }
+ default:
+ NOTREACHED();
+ }
+}
+
+void WebSocketResource::OnPluginMsgConnectReply(
+ const ResourceMessageReplyParams& params,
+ const std::string& url,
+ const std::string& protocol) {
+ if (!TrackedCallback::IsPending(connect_callback_))
+ return;
+
+ int32_t result = params.result();
+ if (result == PP_OK) {
+ state_ = PP_WEBSOCKETREADYSTATE_OPEN;
+ protocol_ = new StringVar(protocol);
+ url_ = new StringVar(url);
+ }
+ TrackedCallback::ClearAndRun(&connect_callback_, params.result());
+}
+
+void WebSocketResource::OnPluginMsgCloseReply(
+ const ResourceMessageReplyParams& params,
+ unsigned long buffered_amount,
+ bool was_clean,
+ unsigned short code,
+ const std::string& reason) {
+ // Set close related properties.
+ state_ = PP_WEBSOCKETREADYSTATE_CLOSED;
+ buffered_amount_ = buffered_amount;
+ close_was_clean_ = PP_FromBool(was_clean);
+ close_code_ = code;
+ close_reason_ = new StringVar(reason);
+
+ if (TrackedCallback::IsPending(receive_callback_)) {
+ receive_callback_var_ = NULL;
+ receive_callback_->PostRun(PP_ERROR_FAILED);
+ receive_callback_ = NULL;
+ }
+
+ if (TrackedCallback::IsPending(close_callback_)) {
+ close_callback_->PostRun(params.result());
+ close_callback_ = NULL;
+ }
+}
+
+void WebSocketResource::OnPluginMsgReceiveTextReply(
+ const ResourceMessageReplyParams& params,
+ const std::string& message) {
+ // Dispose packets after receiving an error or in invalid state.
+ if (error_was_received_ || !InValidStateToReceive(state_))
+ return;
+
+ // Append received data to queue.
+ received_messages_.push(scoped_refptr<Var>(new StringVar(message)));
+
+ if (!TrackedCallback::IsPending(receive_callback_))
+ return;
+
+ TrackedCallback::ClearAndRun(&receive_callback_, DoReceive());
+}
+
+void WebSocketResource::OnPluginMsgReceiveBinaryReply(
+ const ResourceMessageReplyParams& params,
+ const std::vector<uint8_t>& message) {
+ // Dispose packets after receiving an error or in invalid state.
+ if (error_was_received_ || !InValidStateToReceive(state_))
+ return;
+
+ // Append received data to queue.
+ scoped_refptr<Var> message_var(ArrayBufferVar::FromPPVar(
+ PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
+ message.size(),
+ &message.front())));
+ received_messages_.push(message_var);
+
+ if (!TrackedCallback::IsPending(receive_callback_))
+ return;
+
+ TrackedCallback::ClearAndRun(&receive_callback_, DoReceive());
+}
+
+void WebSocketResource::OnPluginMsgErrorReply(
+ const ResourceMessageReplyParams& params) {
+ error_was_received_ = true;
+
+ if (!TrackedCallback::IsPending(receive_callback_))
+ return;
+
+ // No more text or binary messages will be received. If there is ongoing
+ // ReceiveMessage(), we must invoke the callback with error code here.
+ receive_callback_var_ = NULL;
+ TrackedCallback::ClearAndRun(&receive_callback_, PP_ERROR_FAILED);
+}
+
+void WebSocketResource::OnPluginMsgBufferedAmountReply(
+ const ResourceMessageReplyParams& params,
+ unsigned long buffered_amount) {
+ buffered_amount_ = buffered_amount;
+}
+
+void WebSocketResource::OnPluginMsgStateReply(
+ const ResourceMessageReplyParams& params,
+ int32_t state) {
+ state_ = static_cast<PP_WebSocketReadyState>(state);
+}
+
+void WebSocketResource::OnPluginMsgClosedReply(
+ const ResourceMessageReplyParams& params,
+ unsigned long buffered_amount,
+ bool was_clean,
+ unsigned short code,
+ const std::string& reason) {
+ OnPluginMsgCloseReply(params, buffered_amount, was_clean, code, reason);
+}
+
+int32_t WebSocketResource::DoReceive() {
+ if (!receive_callback_var_)
+ return PP_OK;
+
+ *receive_callback_var_ = received_messages_.front()->GetPPVar();
+ received_messages_.pop();
+ receive_callback_var_ = NULL;
+ return PP_OK;
+}
+
+} // namespace proxy
+} // namespace ppapi
diff --git a/ppapi/proxy/websocket_resource.h b/ppapi/proxy/websocket_resource.h
new file mode 100644
index 0000000..49353e4
--- /dev/null
+++ b/ppapi/proxy/websocket_resource.h
@@ -0,0 +1,157 @@
+// 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.
+
+#ifndef PPAPI_PROXY_WEBSOCKET_RESOURCE_H_
+#define PPAPI_PROXY_WEBSOCKET_RESOURCE_H_
+
+#include <queue>
+
+#include "ppapi/c/ppb_websocket.h"
+#include "ppapi/proxy/plugin_resource.h"
+#include "ppapi/shared_impl/tracked_callback.h"
+#include "ppapi/thunk/ppb_websocket_api.h"
+
+namespace ppapi {
+
+class StringVar;
+class Var;
+
+namespace proxy {
+
+// This class contains protocol checks which doesn't affect security when it
+// run with untrusted code.
+class PPAPI_PROXY_EXPORT WebSocketResource
+ : public PluginResource,
+ public NON_EXPORTED_BASE(thunk::PPB_WebSocket_API) {
+ public:
+ WebSocketResource(Connection connection, PP_Instance instance);
+ virtual ~WebSocketResource();
+
+ // PluginResource implementation.
+ virtual thunk::PPB_WebSocket_API* AsPPB_WebSocket_API() OVERRIDE;
+
+ // PPB_WebSocket_API implementation.
+ virtual int32_t Connect(const PP_Var& url,
+ const PP_Var protocols[],
+ uint32_t protocol_count,
+ scoped_refptr<TrackedCallback> callback) OVERRIDE;
+ virtual int32_t Close(uint16_t code,
+ const PP_Var& reason,
+ scoped_refptr<TrackedCallback> callback) OVERRIDE;
+ virtual int32_t ReceiveMessage(
+ PP_Var* message,
+ scoped_refptr<TrackedCallback> callback) OVERRIDE;
+ virtual int32_t SendMessage(const PP_Var& message) OVERRIDE;
+ virtual uint64_t GetBufferedAmount() OVERRIDE;
+ virtual uint16_t GetCloseCode() OVERRIDE;
+ virtual PP_Var GetCloseReason() OVERRIDE;
+ virtual PP_Bool GetCloseWasClean() OVERRIDE;
+ virtual PP_Var GetExtensions() OVERRIDE;
+ virtual PP_Var GetProtocol() OVERRIDE;
+ virtual PP_WebSocketReadyState GetReadyState() OVERRIDE;
+ virtual PP_Var GetURL() OVERRIDE;
+
+ private:
+ // PluginResource override.
+ virtual void OnReplyReceived(const ResourceMessageReplyParams& params,
+ const IPC::Message& msg) OVERRIDE;
+
+ // IPC message handlers.
+ void OnPluginMsgConnectReply(const ResourceMessageReplyParams& params,
+ const std::string& url,
+ const std::string& protocol);
+ void OnPluginMsgCloseReply(const ResourceMessageReplyParams& params,
+ unsigned long buffered_amount,
+ bool was_clean,
+ unsigned short code,
+ const std::string& reason);
+ void OnPluginMsgReceiveTextReply(const ResourceMessageReplyParams& params,
+ const std::string& message);
+ void OnPluginMsgReceiveBinaryReply(const ResourceMessageReplyParams& params,
+ const std::vector<uint8_t>& message);
+ void OnPluginMsgErrorReply(const ResourceMessageReplyParams& params);
+ void OnPluginMsgBufferedAmountReply(const ResourceMessageReplyParams& params,
+ unsigned long buffered_amount);
+ void OnPluginMsgStateReply(const ResourceMessageReplyParams& params,
+ int32_t state);
+ void OnPluginMsgClosedReply(const ResourceMessageReplyParams& params,
+ unsigned long buffered_amount,
+ bool was_clean,
+ unsigned short code,
+ const std::string& reason);
+
+ // Picks up a received message and moves it to user receiving buffer. This
+ // function is used in both ReceiveMessage for fast returning path, and
+ // OnPluginMsgReceiveTextReply and OnPluginMsgReceiveBinaryReply for delayed
+ // callback invocations.
+ int32_t DoReceive();
+
+ // Holds user callbacks to invoke later.
+ scoped_refptr<TrackedCallback> connect_callback_;
+ scoped_refptr<TrackedCallback> close_callback_;
+ scoped_refptr<TrackedCallback> receive_callback_;
+
+ // Represents readyState described in the WebSocket API specification. It can
+ // be read via GetReadyState().
+ PP_WebSocketReadyState state_;
+
+ // Becomes true if any error is detected. Incoming data will be disposed
+ // if this variable is true, then ReceiveMessage() returns PP_ERROR_FAILED
+ // after returning all received data.
+ bool error_was_received_;
+
+ // Keeps a pointer to PP_Var which is provided via ReceiveMessage().
+ // Received data will be copied to this PP_Var on ready.
+ PP_Var* receive_callback_var_;
+
+ // Keeps received data until ReceiveMessage() requests.
+ std::queue<scoped_refptr<Var> > received_messages_;
+
+ // Keeps empty string for functions to return empty string.
+ scoped_refptr<StringVar> empty_string_;
+
+ // Keeps the status code field of closing handshake. It can be read via
+ // GetCloseCode().
+ uint16_t close_code_;
+
+ // Keeps the reason field of closing handshake. It can be read via
+ // GetCloseReason().
+ scoped_refptr<StringVar> close_reason_;
+
+ // Becomes true when closing handshake is performed successfully. It can be
+ // read via GetCloseWasClean().
+ PP_Bool close_was_clean_;
+
+ // Represents extensions described in the WebSocket API specification. It can
+ // be read via GetExtensions().
+ scoped_refptr<StringVar> extensions_;
+
+ // Represents protocol described in the WebSocket API specification. It can be
+ // read via GetProtocol().
+ scoped_refptr<StringVar> protocol_;
+
+ // Represents url described in the WebSocket API specification. It can be
+ // read via GetURL().
+ scoped_refptr<StringVar> url_;
+
+ // Keeps the number of bytes of application data that have been queued using
+ // SendMessage(). WebKit side implementation calculates the actual amount.
+ // This is a cached value which is notified through a WebKit callback.
+ // This value is used to calculate bufferedAmount in the WebSocket API
+ // specification. The calculated value can be read via GetBufferedAmount().
+ uint64_t buffered_amount_;
+
+ // Keeps the number of bytes of application data that have been ignored
+ // because the connection was already closed.
+ // This value is used to calculate bufferedAmount in the WebSocket API
+ // specification. The calculated value can be read via GetBufferedAmount().
+ uint64_t buffered_amount_after_close_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebSocketResource);
+};
+
+} // namespace proxy
+} // namespace ppapi
+
+#endif // PPAPI_PROXY_WEBSOCKET_RESOURCE_H_
diff --git a/ppapi/proxy/websocket_resource_unittest.cc b/ppapi/proxy/websocket_resource_unittest.cc
new file mode 100644
index 0000000..61e8925
--- /dev/null
+++ b/ppapi/proxy/websocket_resource_unittest.cc
@@ -0,0 +1,168 @@
+// 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.
+
+#include "base/memory/ref_counted.h"
+#include "base/message_loop.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/c/ppb_websocket.h"
+#include "ppapi/c/ppb_var.h"
+#include "ppapi/proxy/websocket_resource.h"
+#include "ppapi/proxy/ppapi_messages.h"
+#include "ppapi/proxy/ppapi_proxy_test.h"
+#include "ppapi/shared_impl/ppb_var_shared.h"
+#include "ppapi/shared_impl/scoped_pp_resource.h"
+#include "ppapi/shared_impl/scoped_pp_var.h"
+#include "ppapi/shared_impl/tracked_callback.h"
+#include "ppapi/shared_impl/var.h"
+#include "ppapi/thunk/thunk.h"
+
+namespace ppapi {
+namespace proxy {
+
+namespace {
+
+typedef PluginProxyTest WebSocketResourceTest;
+
+bool g_callback_called;
+int32_t g_callback_result;
+const PPB_Var* ppb_var_ = NULL;
+
+void Callback(void* user_data, int32_t result) {
+ g_callback_called = true;
+ g_callback_result = result;
+}
+
+PP_CompletionCallback MakeCallback() {
+ g_callback_called = false;
+ g_callback_result = PP_OK;
+ return PP_MakeCompletionCallback(Callback, NULL);
+}
+
+PP_Var MakeStringVar(const std::string& string) {
+ if (!ppb_var_)
+ ppb_var_ = ppapi::PPB_Var_Shared::GetVarInterface1_1();
+ return ppb_var_->VarFromUtf8(string.c_str(), string.length());
+}
+
+} // namespace
+
+
+// Does a test of Connect().
+TEST_F(WebSocketResourceTest, Connect) {
+ const PPB_WebSocket_1_0* websocket_iface =
+ thunk::GetPPB_WebSocket_1_0_Thunk();
+
+ std::string url("ws://ws.google.com");
+ std::string protocol0("x-foo");
+ std::string protocol1("x-bar");
+ PP_Var url_var = MakeStringVar(url);
+ PP_Var protocols[] = { MakeStringVar(protocol0), MakeStringVar(protocol1) };
+
+ ScopedPPResource res(ScopedPPResource::PassRef(),
+ websocket_iface->Create(pp_instance()));
+
+ int32_t result =
+ websocket_iface->Connect(res, url_var, protocols, 2, MakeCallback());
+ ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
+
+ // Should be sent a "Connect" message.
+ ResourceMessageCallParams params;
+ IPC::Message msg;
+ ASSERT_TRUE(sink().GetFirstResourceCallMatching(
+ PpapiHostMsg_WebSocket_Connect::ID, &params, &msg));
+ PpapiHostMsg_WebSocket_Connect::Schema::Param p;
+ PpapiHostMsg_WebSocket_Connect::Read(&msg, &p);
+ EXPECT_EQ(url, p.a);
+ EXPECT_EQ(protocol0, p.b[0]);
+ EXPECT_EQ(protocol1, p.b[1]);
+
+ // Synthesize a response.
+ ResourceMessageReplyParams reply_params(params.pp_resource(),
+ params.sequence());
+ reply_params.set_result(PP_OK);
+ ASSERT_TRUE(plugin_dispatcher()->OnMessageReceived(
+ PpapiPluginMsg_ResourceReply(reply_params,
+ PpapiPluginMsg_WebSocket_ConnectReply(url, protocol1))));
+
+ EXPECT_EQ(PP_OK, g_callback_result);
+ EXPECT_EQ(true, g_callback_called);
+}
+
+// Does a test for unsolicited replies.
+TEST_F(WebSocketResourceTest, UnsolicitedReplies) {
+ const PPB_WebSocket_1_0* websocket_iface =
+ thunk::GetPPB_WebSocket_1_0_Thunk();
+
+ ScopedPPResource res(ScopedPPResource::PassRef(),
+ websocket_iface->Create(pp_instance()));
+
+ // Check if BufferedAmountReply is handled.
+ ResourceMessageReplyParams reply_params(res, 0);
+ reply_params.set_result(PP_OK);
+ ASSERT_TRUE(plugin_dispatcher()->OnMessageReceived(
+ PpapiPluginMsg_ResourceReply(
+ reply_params,
+ PpapiPluginMsg_WebSocket_BufferedAmountReply(19760227u))));
+
+ uint64_t amount = websocket_iface->GetBufferedAmount(res);
+ EXPECT_EQ(19760227u, amount);
+
+ // Check if StateReply is handled.
+ ASSERT_TRUE(plugin_dispatcher()->OnMessageReceived(
+ PpapiPluginMsg_ResourceReply(
+ reply_params,
+ PpapiPluginMsg_WebSocket_StateReply(
+ static_cast<int32_t>(PP_WEBSOCKETREADYSTATE_CLOSING)))));
+
+ PP_WebSocketReadyState state = websocket_iface->GetReadyState(res);
+ EXPECT_EQ(PP_WEBSOCKETREADYSTATE_CLOSING, state);
+}
+
+TEST_F(WebSocketResourceTest, MessageError) {
+ const PPB_WebSocket_1_0* websocket_iface =
+ thunk::GetPPB_WebSocket_1_0_Thunk();
+
+ std::string url("ws://ws.google.com");
+ PP_Var url_var = MakeStringVar(url);
+
+ ScopedPPResource res(ScopedPPResource::PassRef(),
+ websocket_iface->Create(pp_instance()));
+
+ // Establish the connection virtually.
+ int32_t result =
+ websocket_iface->Connect(res, url_var, NULL, 0, MakeCallback());
+ ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
+
+ ResourceMessageCallParams params;
+ IPC::Message msg;
+ ASSERT_TRUE(sink().GetFirstResourceCallMatching(
+ PpapiHostMsg_WebSocket_Connect::ID, &params, &msg));
+
+ ResourceMessageReplyParams connect_reply_params(params.pp_resource(),
+ params.sequence());
+ connect_reply_params.set_result(PP_OK);
+ ASSERT_TRUE(plugin_dispatcher()->OnMessageReceived(
+ PpapiPluginMsg_ResourceReply(connect_reply_params,
+ PpapiPluginMsg_WebSocket_ConnectReply(url, std::string()))));
+
+ EXPECT_EQ(PP_OK, g_callback_result);
+ EXPECT_EQ(true, g_callback_called);
+
+ PP_Var message;
+ result = websocket_iface->ReceiveMessage(res, &message, MakeCallback());
+ EXPECT_EQ(false, g_callback_called);
+
+ // Synthesize a WebSocket_ErrorReply message.
+ ResourceMessageReplyParams error_reply_params(res, 0);
+ error_reply_params.set_result(PP_OK);
+ ASSERT_TRUE(plugin_dispatcher()->OnMessageReceived(
+ PpapiPluginMsg_ResourceReply(error_reply_params,
+ PpapiPluginMsg_WebSocket_ErrorReply())));
+
+ EXPECT_EQ(PP_ERROR_FAILED, g_callback_result);
+ EXPECT_EQ(true, g_callback_called);
+}
+
+} // namespace proxy
+} // namespace ppapi
diff --git a/ppapi/tests/test_websocket.cc b/ppapi/tests/test_websocket.cc
index 81ea021..2707b4f 100644
--- a/ppapi/tests/test_websocket.cc
+++ b/ppapi/tests/test_websocket.cc
@@ -1148,8 +1148,16 @@ std::string TestWebSocket::TestUtilityInvalidConnect() {
for (int i = 0; kInvalidURLs[i]; ++i) {
TestWebSocketAPI ws(instance_);
result = ws.Connect(pp::Var(std::string(kInvalidURLs[i])), protocols, 0U);
- ASSERT_EQ(PP_ERROR_BADARGUMENT, result);
- ASSERT_EQ(0U, ws.GetSeenEvents().size());
+ if (result == PP_OK_COMPLETIONPENDING) {
+ ws.WaitForClosed();
+ const std::vector<WebSocketEvent>& events = ws.GetSeenEvents();
+ ASSERT_EQ(WebSocketEvent::EVENT_ERROR, events[0].event_type);
+ ASSERT_EQ(WebSocketEvent::EVENT_CLOSE, events[1].event_type);
+ ASSERT_EQ(2U, ws.GetSeenEvents().size());
+ } else {
+ ASSERT_EQ(PP_ERROR_BADARGUMENT, result);
+ ASSERT_EQ(0U, ws.GetSeenEvents().size());
+ }
}
PASS();
@@ -1195,10 +1203,18 @@ std::string TestWebSocket::TestUtilityGetURL() {
TestWebSocketAPI websocket(instance_);
int32_t result = websocket.Connect(
pp::Var(std::string(kInvalidURLs[i])), protocols, 0U);
- ASSERT_EQ(PP_ERROR_BADARGUMENT, result);
+ if (result == PP_OK_COMPLETIONPENDING) {
+ websocket.WaitForClosed();
+ const std::vector<WebSocketEvent>& events = websocket.GetSeenEvents();
+ ASSERT_EQ(WebSocketEvent::EVENT_ERROR, events[0].event_type);
+ ASSERT_EQ(WebSocketEvent::EVENT_CLOSE, events[1].event_type);
+ ASSERT_EQ(2U, events.size());
+ } else {
+ ASSERT_EQ(PP_ERROR_BADARGUMENT, result);
+ ASSERT_EQ(0U, websocket.GetSeenEvents().size());
+ }
pp::Var url = websocket.GetURL();
ASSERT_TRUE(AreEqualWithString(url.pp_var(), kInvalidURLs[i]));
- ASSERT_EQ(0U, websocket.GetSeenEvents().size());
}
PASS();
@@ -1337,7 +1353,6 @@ std::string TestWebSocket::TestUtilityGetProtocol() {
ASSERT_EQ(WebSocketEvent::EVENT_OPEN, events[0].event_type);
ASSERT_EQ(WebSocketEvent::EVENT_MESSAGE, events[1].event_type);
ASSERT_TRUE(AreEqualWithString(events[1].var.pp_var(), protocol.c_str()));
- ASSERT_TRUE(events[1].was_clean);
PASS();
}
diff --git a/ppapi/thunk/interfaces_ppb_public_dev.h b/ppapi/thunk/interfaces_ppb_public_dev.h
index 98d3d46..9790192 100644
--- a/ppapi/thunk/interfaces_ppb_public_dev.h
+++ b/ppapi/thunk/interfaces_ppb_public_dev.h
@@ -17,7 +17,6 @@ UNPROXIED_API(PPB_DirectoryReader)
UNPROXIED_API(PPB_Scrollbar)
PROXIED_API(PPB_VideoCapture)
PROXIED_API(PPB_VideoDecoder)
-UNPROXIED_API(PPB_WebSocket)
UNPROXIED_API(PPB_Widget)
PROXIED_IFACE(PPB_AudioInput, PPB_AUDIO_INPUT_DEV_INTERFACE_0_1,
@@ -70,8 +69,6 @@ PROXIED_IFACE(PPB_VideoDecoder, PPB_VIDEODECODER_DEV_INTERFACE_0_16,
PPB_VideoDecoder_Dev_0_16)
PROXIED_IFACE(NoAPIName, PPB_VIEW_DEV_INTERFACE_0_1,
PPB_View_Dev_0_1)
-UNPROXIED_IFACE(PPB_WebSocket, PPB_WEBSOCKET_INTERFACE_1_0,
- PPB_WebSocket_1_0)
UNPROXIED_IFACE(PPB_Widget, PPB_WIDGET_DEV_INTERFACE_0_3, PPB_Widget_Dev_0_3)
UNPROXIED_IFACE(PPB_Widget, PPB_WIDGET_DEV_INTERFACE_0_4, PPB_Widget_Dev_0_4)
#endif // !defined(OS_NACL)
diff --git a/ppapi/thunk/interfaces_ppb_public_stable.h b/ppapi/thunk/interfaces_ppb_public_stable.h
index 635cb3a..f4ad0b9 100644
--- a/ppapi/thunk/interfaces_ppb_public_stable.h
+++ b/ppapi/thunk/interfaces_ppb_public_stable.h
@@ -77,6 +77,7 @@ PROXIED_IFACE(NoAPIName, PPB_URLREQUESTINFO_INTERFACE_1_0,
PPB_URLRequestInfo_1_0)
PROXIED_IFACE(PPB_URLResponseInfo, PPB_URLRESPONSEINFO_INTERFACE_1_0,
PPB_URLResponseInfo_1_0)
+PROXIED_IFACE(NoAPIName, PPB_WEBSOCKET_INTERFACE_1_0, PPB_WebSocket_1_0)
// Note: PPB_Var and PPB_VarArrayBuffer are special and registered manually.
PROXIED_IFACE(NoAPIName, PPB_VIEW_INTERFACE_1_0, PPB_View_1_0)
diff --git a/ppapi/thunk/ppb_websocket_api.h b/ppapi/thunk/ppb_websocket_api.h
index e5b7791..546ed99 100644
--- a/ppapi/thunk/ppb_websocket_api.h
+++ b/ppapi/thunk/ppb_websocket_api.h
@@ -8,6 +8,7 @@
#include "base/memory/ref_counted.h"
#include "ppapi/c/pp_completion_callback.h"
#include "ppapi/c/ppb_websocket.h"
+#include "ppapi/thunk/ppapi_thunk_export.h"
namespace ppapi {
@@ -19,14 +20,14 @@ namespace thunk {
// WebSocket API. See also following official specifications.
// - The WebSocket Protocol http://tools.ietf.org/html/rfc6455
// - The WebSocket API http://dev.w3.org/html5/websockets/
-class PPB_WebSocket_API {
+class PPAPI_THUNK_EXPORT PPB_WebSocket_API {
public:
virtual ~PPB_WebSocket_API() {}
// Connects to the specified WebSocket server with |protocols| argument
// defined by the WebSocket API. Returns an int32_t error code from
// pp_errors.h.
- virtual int32_t Connect(PP_Var url,
+ virtual int32_t Connect(const PP_Var& url,
const PP_Var protocols[],
uint32_t protocol_count,
scoped_refptr<TrackedCallback> callback) = 0;
@@ -34,7 +35,7 @@ class PPB_WebSocket_API {
// Closes the established connection with specified |code| and |reason|.
// Returns an int32_t error code from pp_errors.h.
virtual int32_t Close(uint16_t code,
- PP_Var reason,
+ const PP_Var& reason,
scoped_refptr<TrackedCallback> callback) = 0;
// Receives a message from the WebSocket server. Caller must keep specified
@@ -45,7 +46,7 @@ class PPB_WebSocket_API {
// Sends a message to the WebSocket server. Returns an int32_t error code
// from pp_errors.h.
- virtual int32_t SendMessage(PP_Var message) = 0;
+ virtual int32_t SendMessage(const PP_Var& message) = 0;
// Returns the bufferedAmount attribute of The WebSocket API.
virtual uint64_t GetBufferedAmount() = 0;
diff --git a/ppapi/thunk/resource_creation_api.h b/ppapi/thunk/resource_creation_api.h
index b0e1069..0832dc2 100644
--- a/ppapi/thunk/resource_creation_api.h
+++ b/ppapi/thunk/resource_creation_api.h
@@ -123,6 +123,7 @@ class ResourceCreationAPI {
virtual PP_Resource CreateTCPServerSocketPrivate(PP_Instance instance) = 0;
virtual PP_Resource CreateTCPSocketPrivate(PP_Instance instace) = 0;
virtual PP_Resource CreateUDPSocketPrivate(PP_Instance instace) = 0;
+ virtual PP_Resource CreateWebSocket(PP_Instance instance) = 0;
virtual PP_Resource CreateX509CertificatePrivate(PP_Instance instance) = 0;
#if !defined(OS_NACL)
virtual PP_Resource CreateAudioInput0_1(
@@ -154,7 +155,6 @@ class ResourceCreationAPI {
PP_Instance instance,
PP_Resource context3d_id,
PP_VideoDecoder_Profile profile) = 0;
- virtual PP_Resource CreateWebSocket(PP_Instance instance) = 0;
#endif // !defined(OS_NACL)
static const ApiID kApiID = API_ID_RESOURCE_CREATION;
diff --git a/ppapi/thunk/thunk.h b/ppapi/thunk/thunk.h
index 09086de..f40f806 100644
--- a/ppapi/thunk/thunk.h
+++ b/ppapi/thunk/thunk.h
@@ -76,6 +76,8 @@ PPAPI_THUNK_EXPORT const PPB_UDPSocket_Private_0_2*
GetPPB_UDPSocket_Private_0_2_Thunk();
PPAPI_THUNK_EXPORT const PPB_URLLoaderTrusted_0_3*
GetPPB_URLLoaderTrusted_0_3_Thunk();
+PPAPI_THUNK_EXPORT const PPB_WebSocket_1_0*
+ GetPPB_WebSocket_1_0_Thunk();
} // namespace thunk
} // namespace ppapi