0

Pepper WebSocket API: Implement new design Chrome IPC.

This change implements new Chrome IPC for PPB_WebSocket.
After this change, all mode including out of process
will work with new design. It doesn't depend on old SRPC design any more.

BUG=87310,116317

Review URL: https://chromiumcodereview.appspot.com/10944005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@160783 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
toyoshim@chromium.org
2012-10-09 03:43:48 +00:00
parent 7f06df0de9
commit 9d5eadf3af
35 changed files with 1424 additions and 723 deletions

@ -746,6 +746,31 @@ TEST_PPAPI_IN_PROCESS_WITH_WS(WebSocket_UtilityGetProtocol)
TEST_PPAPI_IN_PROCESS_WITH_WS(WebSocket_UtilityTextSendReceive)
TEST_PPAPI_IN_PROCESS_WITH_WS(WebSocket_UtilityBinarySendReceive)
TEST_PPAPI_IN_PROCESS_WITH_WS(WebSocket_UtilityBufferedAmount)
TEST_PPAPI_OUT_OF_PROCESS(WebSocket_IsWebSocket)
TEST_PPAPI_OUT_OF_PROCESS(WebSocket_UninitializedPropertiesAccess)
TEST_PPAPI_OUT_OF_PROCESS(WebSocket_InvalidConnect)
TEST_PPAPI_OUT_OF_PROCESS(WebSocket_Protocols)
TEST_PPAPI_OUT_OF_PROCESS(WebSocket_GetURL)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_ValidConnect)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_InvalidClose)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_ValidClose)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_GetProtocol)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_TextSendReceive)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_BinarySendReceive)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_StressedSendReceive)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_BufferedAmount)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_AbortCalls)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_CcInterfaces)
TEST_PPAPI_OUT_OF_PROCESS(WebSocket_UtilityInvalidConnect)
TEST_PPAPI_OUT_OF_PROCESS(WebSocket_UtilityProtocols)
TEST_PPAPI_OUT_OF_PROCESS(WebSocket_UtilityGetURL)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_UtilityValidConnect)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_UtilityInvalidClose)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_UtilityValidClose)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_UtilityGetProtocol)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_UtilityTextSendReceive)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_UtilityBinarySendReceive)
TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(WebSocket_UtilityBufferedAmount)
TEST_PPAPI_NACL_VIA_HTTP(WebSocket_IsWebSocket)
TEST_PPAPI_NACL_VIA_HTTP(WebSocket_UninitializedPropertiesAccess)
TEST_PPAPI_NACL_VIA_HTTP(WebSocket_InvalidConnect)

@ -189,6 +189,8 @@
'renderer/pepper/pepper_plugin_delegate_impl.h',
'renderer/pepper/pepper_proxy_channel_delegate_impl.cc',
'renderer/pepper/pepper_proxy_channel_delegate_impl.h',
'renderer/pepper/pepper_websocket_host.cc',
'renderer/pepper/pepper_websocket_host.h',
'renderer/pepper/renderer_ppapi_host_impl.cc',
'renderer/pepper/renderer_ppapi_host_impl.h',
'renderer/plugin_channel_host.cc',

@ -13,6 +13,10 @@ class PpapiHost;
}
}
namespace WebKit {
class WebPluginContainer;
}
namespace content {
class RenderView;
@ -34,6 +38,11 @@ class RendererPpapiHost {
// instance is invalid.
virtual RenderView* GetRenderViewForInstance(PP_Instance instance) const = 0;
// Returns the WebPluginContainer for the given plugin instance, or NULL if
// the instance is invalid.
virtual WebKit::WebPluginContainer* GetContainerForInstance(
PP_Instance instance) const = 0;
// Returns true if the given instance is considered to be currently
// processing a user gesture or the plugin module has the "override user
// gesture" flag set (in which case it can always do things normally

@ -6,6 +6,7 @@
#include "base/logging.h"
#include "content/renderer/pepper/pepper_file_chooser_host.h"
#include "content/renderer/pepper/pepper_websocket_host.h"
#include "content/renderer/pepper/renderer_ppapi_host_impl.h"
#include "ppapi/host/resource_host.h"
#include "ppapi/proxy/ppapi_messages.h"
@ -33,6 +34,13 @@ scoped_ptr<ResourceHost> ContentRendererPepperHostFactory::CreateResourceHost(
if (!host_->IsValidInstance(instance))
return scoped_ptr<ResourceHost>();
// Stable interfaces.
switch (message.type()) {
case PpapiHostMsg_WebSocket_Create::ID:
return scoped_ptr<ResourceHost>(new PepperWebSocketHost(
host_, instance, params.pp_resource()));
}
// Resources for dev interfaces.
// TODO(brettw) when we support any public or private interfaces, put them in
// a separate switch above.

@ -34,6 +34,12 @@ RenderView* MockRendererPpapiHost::GetRenderViewForInstance(
return NULL;
}
WebKit::WebPluginContainer* MockRendererPpapiHost::GetContainerForInstance(
PP_Instance instance) const {
NOTIMPLEMENTED();
return NULL;
}
bool MockRendererPpapiHost::HasUserGesture(PP_Instance instance) const {
return has_user_gesture_;
}

@ -41,6 +41,8 @@ class MockRendererPpapiHost : public RendererPpapiHost {
virtual bool IsValidInstance(PP_Instance instance) const OVERRIDE;
virtual RenderView* GetRenderViewForInstance(
PP_Instance instance) const OVERRIDE;
virtual WebKit::WebPluginContainer* GetContainerForInstance(
PP_Instance instance) const OVERRIDE;
virtual bool HasUserGesture(PP_Instance instance) const OVERRIDE;
private:
@ -58,4 +60,3 @@ class MockRendererPpapiHost : public RendererPpapiHost {
} // namespace content
#endif // CONTENT_RENDERER_PEPPER_MOCK_RENDERER_PPAPI_HOST_H_

@ -17,6 +17,7 @@
#include "ppapi/proxy/ppapi_messages.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/ppapi_globals.h"
#include "ppapi/shared_impl/ppapi_permissions.h"
#include "ppapi/shared_impl/resource_tracker.h"
@ -64,4 +65,11 @@ PP_Resource PepperInProcessResourceCreation::CreateURLRequestInfo(
instance, data))->GetReference();
}
PP_Resource PepperInProcessResourceCreation::CreateWebSocket(
PP_Instance instance) {
return (new ppapi::proxy::WebSocketResource(
host_impl_->in_process_router()->GetPluginConnection(),
instance))->GetReference();
}
} // namespace content

@ -51,6 +51,8 @@ class PepperInProcessResourceCreation
virtual PP_Resource CreateURLRequestInfo(
PP_Instance instance,
const ::ppapi::URLRequestInfoData& data) OVERRIDE;
virtual PP_Resource CreateWebSocket(
PP_Instance instance) OVERRIDE;
private:
// Non-owning pointer to the host for the current plugin.

@ -0,0 +1,290 @@
// 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 "content/renderer/pepper/pepper_websocket_host.h"
#include <string>
#include "content/public/renderer/renderer_ppapi_host.h"
#include "net/base/net_util.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/ppb_websocket.h"
#include "ppapi/host/dispatch_host_message.h"
#include "ppapi/host/host_message_context.h"
#include "ppapi/host/ppapi_host.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURL.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebArrayBuffer.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/WebSocket.h"
using WebKit::WebArrayBuffer;
using WebKit::WebDocument;
using WebKit::WebString;
using WebKit::WebSocket;
using WebKit::WebURL;
namespace content {
PepperWebSocketHost::PepperWebSocketHost(
RendererPpapiHost* host,
PP_Instance instance,
PP_Resource resource)
: ResourceHost(host->GetPpapiHost(), instance, resource),
renderer_ppapi_host_(host),
connecting_(false),
initiating_close_(false),
accepting_close_(false),
error_was_received_(false) {
}
PepperWebSocketHost::~PepperWebSocketHost() {
if (websocket_.get())
websocket_->disconnect();
}
int32_t PepperWebSocketHost::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
IPC_BEGIN_MESSAGE_MAP(PepperWebSocketHost, msg)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_WebSocket_Connect,
OnHostMsgConnect)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_WebSocket_Close,
OnHostMsgClose)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_WebSocket_SendText,
OnHostMsgSendText)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_WebSocket_SendBinary,
OnHostMsgSendBinary)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_WebSocket_Fail,
OnHostMsgFail)
IPC_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
void PepperWebSocketHost::didConnect() {
std::string protocol;
if (websocket_.get())
protocol = websocket_->subprotocol().utf8();
connecting_ = false;
connect_reply_.params.set_result(PP_OK);
host()->SendReply(connect_reply_,
PpapiPluginMsg_WebSocket_ConnectReply(
url_,
protocol));
}
void PepperWebSocketHost::didReceiveMessage(const WebKit::WebString& message) {
// Dispose packets after receiving an error.
if (error_was_received_)
return;
// Send an IPC to transport received data.
std::string string_message = message.utf8();
host()->SendUnsolicitedReply(pp_resource(),
PpapiPluginMsg_WebSocket_ReceiveTextReply(
string_message));
}
void PepperWebSocketHost::didReceiveArrayBuffer(
const WebKit::WebArrayBuffer& binaryData) {
// Dispose packets after receiving an error.
if (error_was_received_)
return;
// Send an IPC to transport received data.
uint8_t* data = static_cast<uint8_t*>(binaryData.data());
std::vector<uint8_t> array_message(data, data + binaryData.byteLength());
host()->SendUnsolicitedReply(pp_resource(),
PpapiPluginMsg_WebSocket_ReceiveBinaryReply(
array_message));
}
void PepperWebSocketHost::didReceiveMessageError() {
// Records the error, then stops receiving any frames after this error.
// The error must be notified after all queued messages are read.
error_was_received_ = true;
// Send an IPC to report the error. After this IPC, ReceiveTextReply and
// ReceiveBinaryReply IPC are not sent anymore because |error_was_received_|
// blocks.
host()->SendUnsolicitedReply(pp_resource(),
PpapiPluginMsg_WebSocket_ErrorReply());
}
void PepperWebSocketHost::didUpdateBufferedAmount(
unsigned long buffered_amount) {
// Send an IPC to update buffered amount.
host()->SendUnsolicitedReply(pp_resource(),
PpapiPluginMsg_WebSocket_BufferedAmountReply(
buffered_amount));
}
void PepperWebSocketHost::didStartClosingHandshake() {
accepting_close_ = true;
// Send an IPC to notice that server starts closing handshake.
host()->SendUnsolicitedReply(pp_resource(),
PpapiPluginMsg_WebSocket_StateReply(
PP_WEBSOCKETREADYSTATE_CLOSING));
}
void PepperWebSocketHost::didClose(unsigned long unhandled_buffered_amount,
ClosingHandshakeCompletionStatus status,
unsigned short code,
const WebKit::WebString& reason) {
if (connecting_) {
connecting_ = false;
connect_reply_.params.set_result(PP_ERROR_FAILED);
host()->SendReply(
connect_reply_,
PpapiPluginMsg_WebSocket_ConnectReply(url_, std::string()));
}
// Set close_was_clean_.
bool was_clean =
(initiating_close_ || accepting_close_) &&
!unhandled_buffered_amount &&
status == WebSocketClient::ClosingHandshakeComplete;
if (initiating_close_) {
initiating_close_ = false;
close_reply_.params.set_result(PP_OK);
host()->SendReply(close_reply_, PpapiPluginMsg_WebSocket_CloseReply(
unhandled_buffered_amount,
was_clean,
code,
reason.utf8()));
} else {
accepting_close_ = false;
host()->SendUnsolicitedReply(pp_resource(),
PpapiPluginMsg_WebSocket_ClosedReply(
unhandled_buffered_amount,
was_clean,
code,
reason.utf8()));
}
// Disconnect.
if (websocket_.get())
websocket_->disconnect();
}
int32_t PepperWebSocketHost::OnHostMsgConnect(
ppapi::host::HostMessageContext* context,
const std::string& url,
const std::vector<std::string>& protocols) {
// Validate url and convert it to WebURL.
GURL gurl(url);
url_ = gurl.spec();
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;
if (!net::IsPortAllowedByDefault(gurl.IntPort()))
return PP_ERROR_BADARGUMENT;
WebURL web_url(gurl);
// Validate protocols.
std::string protocol_string;
for (std::vector<std::string>::const_iterator vector_it = protocols.begin();
vector_it != protocols.end();
++vector_it) {
// Check containing characters.
for (std::string::const_iterator string_it = vector_it->begin();
string_it != vector_it->end();
++string_it) {
uint8_t character = *string_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;
}
// Join protocols with the comma separator.
if (vector_it != protocols.begin())
protocol_string.append(",");
protocol_string.append(*vector_it);
}
// Convert protocols to WebString.
WebString web_protocols = WebString::fromUTF8(protocol_string);
// Create WebKit::WebSocket object and connect.
WebKit::WebPluginContainer* container =
renderer_ppapi_host_->GetContainerForInstance(pp_instance());
if (!container)
return PP_ERROR_BADARGUMENT;
// TODO(toyoshim) Remove following WebDocument object copy.
WebDocument document = container->element().document();
websocket_.reset(WebSocket::create(document, this));
DCHECK(websocket_.get());
if (!websocket_.get())
return PP_ERROR_NOTSUPPORTED;
// Set receiving binary object type.
websocket_->setBinaryType(WebSocket::BinaryTypeArrayBuffer);
websocket_->connect(web_url, web_protocols);
connect_reply_ = context->MakeReplyMessageContext();
connecting_ = true;
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperWebSocketHost::OnHostMsgClose(
ppapi::host::HostMessageContext* context,
int32_t code,
const std::string& reason) {
if (!websocket_.get())
return PP_ERROR_FAILED;
close_reply_ = context->MakeReplyMessageContext();
initiating_close_ = true;
WebString web_reason = WebString::fromUTF8(reason);
websocket_->close(code, web_reason);
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperWebSocketHost::OnHostMsgSendText(
ppapi::host::HostMessageContext* context,
const std::string& message) {
if (websocket_.get()) {
WebString web_message = WebString::fromUTF8(message);
websocket_->sendText(web_message);
}
return PP_OK;
}
int32_t PepperWebSocketHost::OnHostMsgSendBinary(
ppapi::host::HostMessageContext* context,
const std::vector<uint8_t>& message) {
if (websocket_.get()) {
WebArrayBuffer web_message = WebArrayBuffer::create(message.size(), 1);
memcpy(web_message.data(), &message.front(), message.size());
websocket_->sendArrayBuffer(web_message);
}
return PP_OK;
}
int32_t PepperWebSocketHost::OnHostMsgFail(
ppapi::host::HostMessageContext* context,
const std::string& message) {
if (websocket_.get())
websocket_->fail(WebString::fromUTF8(message));
return PP_OK;
}
} // namespace content

@ -0,0 +1,99 @@
// 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 CONTENT_RENDERER_PEPPER_PEPPER_WEBSOCKET_HOST_H_
#define CONTENT_RENDERER_PEPPER_PEPPER_WEBSOCKET_HOST_H_
#include <queue>
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "content/common/content_export.h"
#include "ppapi/host/host_message_context.h"
#include "ppapi/host/resource_host.h"
#include "ppapi/proxy/resource_message_params.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSocket.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSocketClient.h"
namespace ppapi {
class StringVar;
class Var;
} // namespace ppapi
namespace content {
class RendererPpapiHost;
class CONTENT_EXPORT PepperWebSocketHost
: public ppapi::host::ResourceHost,
public NON_EXPORTED_BASE(::WebKit::WebSocketClient) {
public:
explicit PepperWebSocketHost(RendererPpapiHost* host,
PP_Instance instance,
PP_Resource resource);
virtual ~PepperWebSocketHost();
virtual int32_t OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) OVERRIDE;
// WebSocketClient implementation.
virtual void didConnect();
virtual void didReceiveMessage(const WebKit::WebString& message);
virtual void didReceiveArrayBuffer(const WebKit::WebArrayBuffer& binaryData);
virtual void didReceiveMessageError();
virtual void didUpdateBufferedAmount(unsigned long buffered_amount);
virtual void didStartClosingHandshake();
virtual void didClose(unsigned long unhandled_buffered_amount,
ClosingHandshakeCompletionStatus status,
unsigned short code,
const WebKit::WebString& reason);
private:
// IPC message handlers.
int32_t OnHostMsgConnect(ppapi::host::HostMessageContext* context,
const std::string& url,
const std::vector<std::string>& protocols);
int32_t OnHostMsgClose(ppapi::host::HostMessageContext* context,
int32_t code,
const std::string& reason);
int32_t OnHostMsgSendText(ppapi::host::HostMessageContext* context,
const std::string& message);
int32_t OnHostMsgSendBinary(ppapi::host::HostMessageContext* context,
const std::vector<uint8_t>& message);
int32_t OnHostMsgFail(ppapi::host::HostMessageContext* context,
const std::string& message);
// Non-owning pointer.
RendererPpapiHost* renderer_ppapi_host_;
// IPC reply parameters.
ppapi::host::ReplyMessageContext connect_reply_;
ppapi::host::ReplyMessageContext close_reply_;
// The server URL to which this instance connects.
std::string url_;
// A flag to indicate if opening handshake is going on.
bool connecting_;
// A flag to indicate if client initiated closing handshake is performed.
bool initiating_close_;
// A flag to indicate if server initiated closing handshake is performed.
bool accepting_close_;
// Becomes true if any error is detected. Incoming data will be disposed
// if this variable is true.
bool error_was_received_;
// Keeps the WebKit side WebSocket object. This is used for calling WebKit
// side functions via WebKit API.
scoped_ptr<WebKit::WebSocket> websocket_;
DISALLOW_COPY_AND_ASSIGN(PepperWebSocketHost);
};
} // namespace content
#endif // CONTENT_RENDERER_PEPPER_PEPPER_WEBSOCKET_HOST_H_

@ -120,6 +120,14 @@ bool RendererPpapiHostImpl::IsValidInstance(
return !!GetAndValidateInstance(instance);
}
WebKit::WebPluginContainer* RendererPpapiHostImpl::GetContainerForInstance(
PP_Instance instance) const {
PluginInstance* instance_object = GetAndValidateInstance(instance);
if (!instance_object)
return NULL;
return instance_object->container();
}
bool RendererPpapiHostImpl::HasUserGesture(PP_Instance instance) const {
PluginInstance* instance_object = GetAndValidateInstance(instance);
if (!instance_object)

@ -86,6 +86,8 @@ class RendererPpapiHostImpl
virtual bool IsValidInstance(PP_Instance instance) const OVERRIDE;
virtual RenderView* GetRenderViewForInstance(
PP_Instance instance) const OVERRIDE;
virtual WebKit::WebPluginContainer* GetContainerForInstance(
PP_Instance instance) const OVERRIDE;
virtual bool HasUserGesture(PP_Instance instance) const OVERRIDE;
private:

@ -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());
}

@ -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);

@ -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',

@ -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

@ -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',

@ -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);

@ -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 */)

@ -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) {

@ -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;

@ -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

@ -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_

@ -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

@ -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();
}

@ -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)

@ -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)

@ -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;

@ -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;

@ -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

@ -252,8 +252,6 @@
'../plugins/ppapi/ppb_video_capture_impl.h',
'../plugins/ppapi/ppb_video_decoder_impl.cc',
'../plugins/ppapi/ppb_video_decoder_impl.h',
'../plugins/ppapi/ppb_websocket_impl.cc',
'../plugins/ppapi/ppb_websocket_impl.h',
'../plugins/ppapi/ppb_widget_impl.cc',
'../plugins/ppapi/ppb_widget_impl.h',
'../plugins/ppapi/ppb_x509_certificate_private_impl.cc',

@ -1,538 +0,0 @@
// 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 "webkit/plugins/ppapi/ppb_websocket_impl.h"
#include <set>
#include <string>
#include "base/basictypes.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/c/ppb_var_array_buffer.h"
#include "ppapi/shared_impl/var.h"
#include "ppapi/shared_impl/var_tracker.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURL.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebArrayBuffer.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/WebSocket.h"
#include "webkit/plugins/ppapi/host_array_buffer_var.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::ArrayBufferVar;
using ppapi::PpapiGlobals;
using ppapi::StringVar;
using ppapi::thunk::PPB_WebSocket_API;
using ppapi::TrackedCallback;
using ppapi::Var;
using ppapi::VarTracker;
using WebKit::WebArrayBuffer;
using WebKit::WebDocument;
using WebKit::WebString;
using WebKit::WebSocket;
using WebKit::WebSocketClient;
using WebKit::WebURL;
const uint32_t kMaxReasonSizeInBytes = 123;
const size_t kHybiBaseFramingOverhead = 2;
const size_t kHybiMaskingKeyLength = 4;
const size_t kMinimumPayloadSizeWithTwoByteExtendedPayloadLength = 126;
const size_t kMinimumPayloadSizeWithEightByteExtendedPayloadLength = 0x10000;
namespace {
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 = kHybiBaseFramingOverhead + kHybiMaskingKeyLength;
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 webkit {
namespace ppapi {
PPB_WebSocket_Impl::PPB_WebSocket_Impl(PP_Instance instance)
: Resource(::ppapi::OBJECT_IS_IMPL, instance),
state_(PP_WEBSOCKETREADYSTATE_INVALID),
error_was_received_(false),
receive_callback_var_(NULL),
wait_for_receive_(false),
close_code_(0),
close_was_clean_(PP_FALSE),
empty_string_(new StringVar("", 0)),
buffered_amount_(0),
buffered_amount_after_close_(0) {
}
PPB_WebSocket_Impl::~PPB_WebSocket_Impl() {
if (websocket_.get())
websocket_->disconnect();
}
// static
PP_Resource PPB_WebSocket_Impl::Create(PP_Instance instance) {
scoped_refptr<PPB_WebSocket_Impl> ws(new PPB_WebSocket_Impl(instance));
return ws->GetReference();
}
PPB_WebSocket_API* PPB_WebSocket_Impl::AsPPB_WebSocket_API() {
return this;
}
int32_t PPB_WebSocket_Impl::Connect(PP_Var url,
const PP_Var protocols[],
uint32_t protocol_count,
scoped_refptr<TrackedCallback> callback) {
// 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)
return PP_ERROR_INPROGRESS;
state_ = PP_WEBSOCKETREADYSTATE_CLOSED;
// 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());
url_ = new StringVar(gurl.spec());
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;
if (!net::IsPortAllowedByDefault(gurl.IntPort()))
return PP_ERROR_BADARGUMENT;
WebURL web_url(gurl);
// Validate protocols and convert it to WebString.
std::string protocol_string;
std::set<std::string> protocol_set;
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> 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());
// Check containing characters.
for (std::string::const_iterator it = protocol->value().begin();
it != protocol->value().end();
++it) {
uint8_t character = *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;
}
// Join protocols with the comma separator.
if (i != 0)
protocol_string.append(",");
protocol_string.append(protocol->value());
}
WebString web_protocols = WebString::fromUTF8(protocol_string);
// Create WebKit::WebSocket object and connect.
WebDocument document = plugin_instance->container()->element().document();
websocket_.reset(WebSocket::create(document, this));
DCHECK(websocket_.get());
if (!websocket_.get())
return PP_ERROR_NOTSUPPORTED;
// Set receiving binary object type.
websocket_->setBinaryType(WebSocket::BinaryTypeArrayBuffer);
websocket_->connect(web_url, web_protocols);
state_ = PP_WEBSOCKETREADYSTATE_CONNECTING;
// Install callback.
connect_callback_ = callback;
return PP_OK_COMPLETIONPENDING;
}
int32_t PPB_WebSocket_Impl::Close(uint16_t code,
PP_Var reason,
scoped_refptr<TrackedCallback> callback) {
// Check mandarory interfaces.
if (!websocket_.get())
return PP_ERROR_FAILED;
// Validate |code| and |reason|.
scoped_refptr<StringVar> reason_string;
WebString web_reason;
WebSocket::CloseEventCode event_code =
static_cast<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 = 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 = StringVar::FromPPVar(reason);
if (!reason_string ||
reason_string->value().size() > kMaxReasonSizeInBytes)
return PP_ERROR_BADARGUMENT;
web_reason = WebString::fromUTF8(reason_string->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 (state_ == PP_WEBSOCKETREADYSTATE_CONNECTING) {
state_ = PP_WEBSOCKETREADYSTATE_CLOSING;
// Need to do a "Post" to avoid reentering the plugin.
connect_callback_->PostAbort();
connect_callback_ = NULL;
websocket_->fail(
"WebSocket was closed before the connection was established.");
return PP_OK_COMPLETIONPENDING;
}
// Abort ongoing receive.
if (wait_for_receive_) {
wait_for_receive_ = false;
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;
websocket_->close(event_code, web_reason);
return PP_OK_COMPLETIONPENDING;
}
int32_t PPB_WebSocket_Impl::ReceiveMessage(
PP_Var* message,
scoped_refptr<TrackedCallback> callback) {
// 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|.
wait_for_receive_ = true;
receive_callback_var_ = message;
receive_callback_ = callback;
return PP_OK_COMPLETIONPENDING;
}
int32_t PPB_WebSocket_Impl::SendMessage(PP_Var message) {
// Check mandatory interfaces.
if (!websocket_.get())
return PP_ERROR_FAILED;
// 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 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;
} else if (message.type == PP_VARTYPE_ARRAY_BUFFER) {
// Convert message to WebArrayBuffer.
scoped_refptr<HostArrayBufferVar> host_message =
static_cast<HostArrayBufferVar*>(ArrayBufferVar::FromPPVar(message));
if (!host_message)
return PP_ERROR_BADARGUMENT;
WebArrayBuffer& web_message = host_message->webkit_buffer();
if (!websocket_->sendArrayBuffer(web_message))
return PP_ERROR_BADARGUMENT;
} else {
// TODO(toyoshim): Support Blob.
return PP_ERROR_NOTSUPPORTED;
}
return PP_OK;
}
uint64_t PPB_WebSocket_Impl::GetBufferedAmount() {
return SaturateAdd(buffered_amount_, buffered_amount_after_close_);
}
uint16_t PPB_WebSocket_Impl::GetCloseCode() {
return close_code_;
}
PP_Var PPB_WebSocket_Impl::GetCloseReason() {
if (!close_reason_)
return empty_string_->GetPPVar();
return close_reason_->GetPPVar();
}
PP_Bool PPB_WebSocket_Impl::GetCloseWasClean() {
return close_was_clean_;
}
PP_Var PPB_WebSocket_Impl::GetExtensions() {
// Check mandatory interfaces.
if (!websocket_.get())
return empty_string_->GetPPVar();
std::string extensions = websocket_->extensions().utf8();
return StringVar::StringToPPVar(extensions);
}
PP_Var PPB_WebSocket_Impl::GetProtocol() {
// Check mandatory interfaces.
if (!websocket_.get())
return empty_string_->GetPPVar();
std::string protocol = websocket_->subprotocol().utf8();
return StringVar::StringToPPVar(protocol);
}
PP_WebSocketReadyState PPB_WebSocket_Impl::GetReadyState() {
return state_;
}
PP_Var PPB_WebSocket_Impl::GetURL() {
if (!url_)
return empty_string_->GetPPVar();
return url_->GetPPVar();
}
void PPB_WebSocket_Impl::didConnect() {
DCHECK_EQ(PP_WEBSOCKETREADYSTATE_CONNECTING, state_);
state_ = PP_WEBSOCKETREADYSTATE_OPEN;
TrackedCallback::ClearAndRun(&connect_callback_, PP_OK);
}
void PPB_WebSocket_Impl::didReceiveMessage(const WebString& message) {
// Dispose packets after receiving an error or in invalid state.
if (error_was_received_ || !InValidStateToReceive(state_))
return;
// Append received data to queue.
std::string string = message.utf8();
received_messages_.push(scoped_refptr<Var>(new StringVar(string)));
if (!wait_for_receive_)
return;
TrackedCallback::ClearAndRun(&receive_callback_, DoReceive());
}
void PPB_WebSocket_Impl::didReceiveArrayBuffer(
const WebArrayBuffer& binaryData) {
// 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 HostArrayBufferVar(binaryData)));
if (!wait_for_receive_)
return;
TrackedCallback::ClearAndRun(&receive_callback_, DoReceive());
}
void PPB_WebSocket_Impl::didReceiveMessageError() {
// Ignore error notification in invalid state.
if (!InValidStateToReceive(state_))
return;
// Records the error, then stops receiving any frames after this error.
// The error will be notified after all queued messages are read via
// ReceiveMessage().
error_was_received_ = true;
if (!wait_for_receive_)
return;
// But, if no messages are queued and ReceiveMessage() is now on going.
// We must invoke the callback with error code here.
wait_for_receive_ = false;
receive_callback_var_ = NULL;
TrackedCallback::ClearAndRun(&receive_callback_, PP_ERROR_FAILED);
}
void PPB_WebSocket_Impl::didUpdateBufferedAmount(
unsigned long buffered_amount) {
if (state_ == PP_WEBSOCKETREADYSTATE_CLOSED)
return;
buffered_amount_ = buffered_amount;
}
void PPB_WebSocket_Impl::didStartClosingHandshake() {
state_ = PP_WEBSOCKETREADYSTATE_CLOSING;
}
void PPB_WebSocket_Impl::didClose(unsigned long unhandled_buffered_amount,
ClosingHandshakeCompletionStatus status,
unsigned short code,
const WebString& reason) {
// Store code and reason.
close_code_ = code;
close_reason_ = new StringVar(reason.utf8());
// Set close_was_clean_.
bool was_clean =
state_ == PP_WEBSOCKETREADYSTATE_CLOSING &&
!unhandled_buffered_amount &&
status == WebSocketClient::ClosingHandshakeComplete;
close_was_clean_ = was_clean ? PP_TRUE : PP_FALSE;
// Update buffered_amount_.
buffered_amount_ = unhandled_buffered_amount;
// Handle state transition and invoking callback.
DCHECK_NE(PP_WEBSOCKETREADYSTATE_CLOSED, state_);
PP_WebSocketReadyState state = state_;
state_ = PP_WEBSOCKETREADYSTATE_CLOSED;
// User handlers may release WebSocket PP_Resource in the following
// completion callbacks. Retain |this| here to assure that this object
// keep on being valid in this function.
scoped_refptr<PPB_WebSocket_Impl> retain_this(this);
if (state == PP_WEBSOCKETREADYSTATE_CONNECTING)
TrackedCallback::ClearAndRun(&connect_callback_, PP_ERROR_FAILED);
if (wait_for_receive_) {
wait_for_receive_ = false;
receive_callback_var_ = NULL;
TrackedCallback::ClearAndRun(&receive_callback_, PP_ERROR_FAILED);
}
if ((state == PP_WEBSOCKETREADYSTATE_CLOSING) && close_callback_.get())
TrackedCallback::ClearAndRun(&close_callback_, PP_OK);
// Disconnect.
if (websocket_.get())
websocket_->disconnect();
}
int32_t PPB_WebSocket_Impl::DoReceive() {
if (!receive_callback_var_)
return PP_OK;
*receive_callback_var_ = received_messages_.front()->GetPPVar();
received_messages_.pop();
receive_callback_var_ = NULL;
wait_for_receive_ = false;
return PP_OK;
}
} // namespace ppapi
} // namespace webkit

@ -1,155 +0,0 @@
// 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 WEBKIT_PLUGINS_PPAPI_PPB_WEBSOCKET_IMPL_H_
#define WEBKIT_PLUGINS_PPAPI_PPB_WEBSOCKET_IMPL_H_
#include <queue>
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "ppapi/shared_impl/resource.h"
#include "ppapi/shared_impl/tracked_callback.h"
#include "ppapi/thunk/ppb_websocket_api.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSocket.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSocketClient.h"
namespace ppapi {
class StringVar;
class Var;
} // namespace ppapi
namespace webkit {
namespace ppapi {
// All implementation is in this class for now. We should move some common
// implementation to shared_impl when we implement proxy interfaces.
class PPB_WebSocket_Impl : public ::ppapi::Resource,
public ::ppapi::thunk::PPB_WebSocket_API,
public ::WebKit::WebSocketClient {
public:
explicit PPB_WebSocket_Impl(PP_Instance instance);
virtual ~PPB_WebSocket_Impl();
static PP_Resource Create(PP_Instance instance);
// Resource overrides.
// Returns the pointer to the thunk::PPB_WebSocket_API if it's supported.
virtual ::ppapi::thunk::PPB_WebSocket_API* AsPPB_WebSocket_API() OVERRIDE;
// PPB_WebSocket_API implementation.
virtual int32_t Connect(
PP_Var url,
const PP_Var protocols[],
uint32_t protocol_count,
scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE;
virtual int32_t Close(
uint16_t code,
PP_Var reason,
scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE;
virtual int32_t ReceiveMessage(
PP_Var* message,
scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE;
virtual int32_t SendMessage(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;
// WebSocketClient implementation.
virtual void didConnect();
virtual void didReceiveMessage(const WebKit::WebString& message);
virtual void didReceiveArrayBuffer(const WebKit::WebArrayBuffer& binaryData);
virtual void didReceiveMessageError();
virtual void didUpdateBufferedAmount(unsigned long buffered_amount);
virtual void didStartClosingHandshake();
virtual void didClose(unsigned long unhandled_buffered_amount,
ClosingHandshakeCompletionStatus status,
unsigned short code,
const WebKit::WebString& reason);
private:
// Picks up a received message and moves it to user receiving buffer. This
// function is used in both ReceiveMessage for fast returning path and
// didReceiveMessage callback.
int32_t DoReceive();
// Keeps the WebKit side WebSocket object. This is used for calling WebKit
// side functions via WebKit API.
scoped_ptr<WebKit::WebSocket> websocket_;
// 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 completion callback for asynchronous Connect().
scoped_refptr< ::ppapi::TrackedCallback> connect_callback_;
// Keeps a completion callback for asynchronous ReceiveMessage().
scoped_refptr< ::ppapi::TrackedCallback> receive_callback_;
// 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_;
// Becomes true when asynchronous ReceiveMessage() is processed.
bool wait_for_receive_;
// Keeps received data until ReceiveMessage() requests.
std::queue< scoped_refptr< ::ppapi::Var> > received_messages_;
// Keeps a completion callback for asynchronous Close().
scoped_refptr< ::ppapi::TrackedCallback> close_callback_;
// 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< ::ppapi::StringVar> close_reason_;
// Becomes true when closing handshake is performed successfully. It can be
// read via GetCloseWasClean().
PP_Bool close_was_clean_;
// Keeps empty string for functions to return empty string.
scoped_refptr< ::ppapi::StringVar> empty_string_;
// Represents extensions described in the WebSocket API specification. It can
// be read via GetExtensions().
scoped_refptr< ::ppapi::StringVar> extensions_;
// Represents url described in the WebSocket API specification. It can be
// read via GetURL().
scoped_refptr< ::ppapi::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(PPB_WebSocket_Impl);
};
} // namespace ppapi
} // namespace webkit
#endif // WEBKIT_PLUGINS_PPAPI_PPB_WEBSOCKET_IMPL_H_

@ -33,7 +33,6 @@
#include "webkit/plugins/ppapi/ppb_url_loader_impl.h"
#include "webkit/plugins/ppapi/ppb_video_capture_impl.h"
#include "webkit/plugins/ppapi/ppb_video_decoder_impl.h"
#include "webkit/plugins/ppapi/ppb_websocket_impl.h"
#include "webkit/plugins/ppapi/ppb_x509_certificate_private_impl.h"
#include "webkit/plugins/ppapi/resource_helper.h"
@ -291,10 +290,6 @@ PP_Resource ResourceCreationImpl::CreateVideoDecoder(
return PPB_VideoDecoder_Impl::Create(instance, graphics3d_id, profile);
}
PP_Resource ResourceCreationImpl::CreateWebSocket(PP_Instance instance) {
return PPB_WebSocket_Impl::Create(instance);
}
PP_Resource ResourceCreationImpl::CreateWheelInputEvent(
PP_Instance instance,
PP_TimeTicks time_stamp,

@ -121,7 +121,6 @@ class WEBKIT_PLUGINS_EXPORT ResourceCreationImpl
PP_Instance instance,
PP_Resource graphics3d_id,
PP_VideoDecoder_Profile profile) OVERRIDE;
virtual PP_Resource CreateWebSocket(PP_Instance instance) OVERRIDE;
virtual PP_Resource CreateWheelInputEvent(
PP_Instance instance,
PP_TimeTicks time_stamp,