diff options
35 files changed, 1424 insertions, 723 deletions
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc index 5e215f5..c164b50 100644 --- a/chrome/test/ppapi/ppapi_browsertest.cc +++ b/chrome/test/ppapi/ppapi_browsertest.cc @@ -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) diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index 139b125..5e482da 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -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', diff --git a/content/public/renderer/renderer_ppapi_host.h b/content/public/renderer/renderer_ppapi_host.h index 8fda769..d6cc28f 100644 --- a/content/public/renderer/renderer_ppapi_host.h +++ b/content/public/renderer/renderer_ppapi_host.h @@ -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 diff --git a/content/renderer/pepper/content_renderer_pepper_host_factory.cc b/content/renderer/pepper/content_renderer_pepper_host_factory.cc index 538fd9b..6d77832 100644 --- a/content/renderer/pepper/content_renderer_pepper_host_factory.cc +++ b/content/renderer/pepper/content_renderer_pepper_host_factory.cc @@ -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. diff --git a/content/renderer/pepper/mock_renderer_ppapi_host.cc b/content/renderer/pepper/mock_renderer_ppapi_host.cc index 9a31ecd..bf7d269 100644 --- a/content/renderer/pepper/mock_renderer_ppapi_host.cc +++ b/content/renderer/pepper/mock_renderer_ppapi_host.cc @@ -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_; } diff --git a/content/renderer/pepper/mock_renderer_ppapi_host.h b/content/renderer/pepper/mock_renderer_ppapi_host.h index 471c2b1..115cb57 100644 --- a/content/renderer/pepper/mock_renderer_ppapi_host.h +++ b/content/renderer/pepper/mock_renderer_ppapi_host.h @@ -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_ - diff --git a/content/renderer/pepper/pepper_in_process_resource_creation.cc b/content/renderer/pepper/pepper_in_process_resource_creation.cc index 46b0eac..1854e9e 100644 --- a/content/renderer/pepper/pepper_in_process_resource_creation.cc +++ b/content/renderer/pepper/pepper_in_process_resource_creation.cc @@ -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 diff --git a/content/renderer/pepper/pepper_in_process_resource_creation.h b/content/renderer/pepper/pepper_in_process_resource_creation.h index 029311d..e5b3fa5 100644 --- a/content/renderer/pepper/pepper_in_process_resource_creation.h +++ b/content/renderer/pepper/pepper_in_process_resource_creation.h @@ -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. diff --git a/content/renderer/pepper/pepper_websocket_host.cc b/content/renderer/pepper/pepper_websocket_host.cc new file mode 100644 index 0000000..ec6341a --- /dev/null +++ b/content/renderer/pepper/pepper_websocket_host.cc @@ -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 diff --git a/content/renderer/pepper/pepper_websocket_host.h b/content/renderer/pepper/pepper_websocket_host.h new file mode 100644 index 0000000..8c6c8c9 --- /dev/null +++ b/content/renderer/pepper/pepper_websocket_host.h @@ -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_ diff --git a/content/renderer/pepper/renderer_ppapi_host_impl.cc b/content/renderer/pepper/renderer_ppapi_host_impl.cc index 0dde8e2..f3bbb9b 100644 --- a/content/renderer/pepper/renderer_ppapi_host_impl.cc +++ b/content/renderer/pepper/renderer_ppapi_host_impl.cc @@ -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) diff --git a/content/renderer/pepper/renderer_ppapi_host_impl.h b/content/renderer/pepper/renderer_ppapi_host_impl.h index ff0341d..3625de2 100644 --- a/content/renderer/pepper/renderer_ppapi_host_impl.h +++ b/content/renderer/pepper/renderer_ppapi_host_impl.h @@ -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: 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, ¶ms, &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, ¶ms, &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 diff --git a/webkit/glue/webkit_glue.gypi b/webkit/glue/webkit_glue.gypi index 0adca67..57388f6 100644 --- a/webkit/glue/webkit_glue.gypi +++ b/webkit/glue/webkit_glue.gypi @@ -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', diff --git a/webkit/plugins/ppapi/ppb_websocket_impl.cc b/webkit/plugins/ppapi/ppb_websocket_impl.cc deleted file mode 100644 index 2893fea..0000000 --- a/webkit/plugins/ppapi/ppb_websocket_impl.cc +++ /dev/null @@ -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 diff --git a/webkit/plugins/ppapi/ppb_websocket_impl.h b/webkit/plugins/ppapi/ppb_websocket_impl.h deleted file mode 100644 index 0bdaf4b..0000000 --- a/webkit/plugins/ppapi/ppb_websocket_impl.h +++ /dev/null @@ -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_ diff --git a/webkit/plugins/ppapi/resource_creation_impl.cc b/webkit/plugins/ppapi/resource_creation_impl.cc index 1b172bc..22ac0b2 100644 --- a/webkit/plugins/ppapi/resource_creation_impl.cc +++ b/webkit/plugins/ppapi/resource_creation_impl.cc @@ -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, diff --git a/webkit/plugins/ppapi/resource_creation_impl.h b/webkit/plugins/ppapi/resource_creation_impl.h index 8d445a7..77a7b99 100644 --- a/webkit/plugins/ppapi/resource_creation_impl.h +++ b/webkit/plugins/ppapi/resource_creation_impl.h @@ -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, |