diff options
-rw-r--r-- | chrome/test/ui/ppapi_uitest.cc | 15 | ||||
-rw-r--r-- | ppapi/tests/test_websocket.cc | 182 | ||||
-rw-r--r-- | ppapi/tests/test_websocket.h | 14 | ||||
-rw-r--r-- | webkit/plugins/ppapi/ppb_websocket_impl.cc | 332 | ||||
-rw-r--r-- | webkit/plugins/ppapi/ppb_websocket_impl.h | 52 |
5 files changed, 555 insertions, 40 deletions
diff --git a/chrome/test/ui/ppapi_uitest.cc b/chrome/test/ui/ppapi_uitest.cc index 5b2a831..68f42e2 100644 --- a/chrome/test/ui/ppapi_uitest.cc +++ b/chrome/test/ui/ppapi_uitest.cc @@ -231,6 +231,17 @@ class PPAPINaClTest : public PPAPITestBase { RunTestViaHTTP(#test_name); \ } +// Similar macros that test with WebSocket server +#define TEST_PPAPI_IN_PROCESS_WITH_WS(test_name) \ + TEST_F(PPAPITest, test_name) { \ + RunTestWithWebSocketServer(#test_name); \ + } +#define TEST_PPAPI_OUT_OF_PROCESS_WITH_WS(test_name) \ + TEST_F(OutOfProcessPPAPITest, test_name) { \ + RunTestWithWebSocketServer(#test_name); \ + } + + // NaCl based PPAPI tests #define TEST_PPAPI_NACL_VIA_HTTP(test_name) \ @@ -460,7 +471,9 @@ TEST_PPAPI_OUT_OF_PROCESS(Flash_MessageLoop) TEST_PPAPI_OUT_OF_PROCESS(Flash_GetLocalTimeZoneOffset) TEST_PPAPI_OUT_OF_PROCESS(Flash_GetCommandLineArgs) -TEST_PPAPI_IN_PROCESS(WebSocket_Create) +TEST_PPAPI_IN_PROCESS(WebSocket_InvalidConnect) TEST_PPAPI_IN_PROCESS(WebSocket_IsWebSocket) +TEST_PPAPI_IN_PROCESS_WITH_WS(WebSocket_ValidConnect) +TEST_PPAPI_IN_PROCESS_WITH_WS(WebSocket_TextSendReceive) #endif // ADDRESS_SANITIZER diff --git a/ppapi/tests/test_websocket.cc b/ppapi/tests/test_websocket.cc index eb83f60..2c85440 100644 --- a/ppapi/tests/test_websocket.cc +++ b/ppapi/tests/test_websocket.cc @@ -4,41 +4,197 @@ #include "ppapi/tests/test_websocket.h" +#include <string.h> + +#include "base/logging.h" #include "ppapi/c/dev/ppb_websocket_dev.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/c/pp_var.h" +#include "ppapi/c/pp_completion_callback.h" +#include "ppapi/c/ppb_core.h" +#include "ppapi/c/ppb_var.h" #include "ppapi/cpp/instance.h" #include "ppapi/cpp/module.h" +#include "ppapi/tests/test_utils.h" #include "ppapi/tests/testing_instance.h" +static const char kEchoServerURL[] = + "ws://localhost:8880/websocket/tests/hybi/echo"; + REGISTER_TEST_CASE(WebSocket); bool TestWebSocket::Init() { - websocket_interface_ = reinterpret_cast<PPB_WebSocket_Dev const*>( + websocket_interface_ = static_cast<const PPB_WebSocket_Dev*>( pp::Module::Get()->GetBrowserInterface(PPB_WEBSOCKET_DEV_INTERFACE)); - return !!websocket_interface_; + var_interface_ = static_cast<const PPB_Var*>( + pp::Module::Get()->GetBrowserInterface(PPB_VAR_INTERFACE)); + core_interface_ = static_cast<const PPB_Core*>( + pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE)); + if (!websocket_interface_ || !var_interface_ || !core_interface_) + return false; + + return true; } void TestWebSocket::RunTests(const std::string& filter) { - instance_->LogTest("Create", TestCreate()); - instance_->LogTest("IsWebSocket", TestIsWebSocket()); + RUN_TEST(IsWebSocket, filter); + RUN_TEST(InvalidConnect, filter); + RUN_TEST(ValidConnect, filter); + RUN_TEST(TextSendReceive, filter); } -std::string TestWebSocket::TestCreate() { - PP_Resource rsrc = websocket_interface_->Create(instance_->pp_instance()); - if (!rsrc) - return "Could not create websocket via C interface"; +PP_Var TestWebSocket::CreateVar(const char* string) { + return var_interface_->VarFromUtf8( + pp::Module::Get()->pp_module(), string, strlen(string)); +} - PASS(); +void TestWebSocket::ReleaseVar(const PP_Var& var) { + var_interface_->Release(var); +} + +bool TestWebSocket::AreEqual(const PP_Var& var, const char* string) { + if (var.type != PP_VARTYPE_STRING) + return false; + uint32_t utf8_length; + const char* utf8 = var_interface_->VarToUtf8(var, &utf8_length); + uint32_t string_length = strlen(string); + if (utf8_length != string_length) + return false; + if (strncmp(utf8, string, utf8_length)) + return false; + return true; +} + +PP_Resource TestWebSocket::Connect() { + PP_Var protocols[] = { PP_MakeUndefined() }; + PP_Resource ws = websocket_interface_->Create(instance_->pp_instance()); + if (!ws) + return 0; + PP_Var url = CreateVar(kEchoServerURL); + TestCompletionCallback callback(instance_->pp_instance(), force_async_); + int32_t result = websocket_interface_->Connect( + ws, url, protocols, 0, + static_cast<pp::CompletionCallback>(callback).pp_completion_callback()); + ReleaseVar(url); + if (force_async_ && result != PP_OK_COMPLETIONPENDING) { + core_interface_->ReleaseResource(ws); + return 0; + } + if (callback.WaitForResult() != PP_OK) { + core_interface_->ReleaseResource(ws); + return 0; + } + return ws; } std::string TestWebSocket::TestIsWebSocket() { // Test that a NULL resource isn't a websocket. pp::Resource null_resource; - if (websocket_interface_->IsWebSocket(null_resource.pp_resource())) - return "Null resource was reported as a valid websocket"; + PP_Bool result = + websocket_interface_->IsWebSocket(null_resource.pp_resource()); + ASSERT_FALSE(result); PP_Resource ws = websocket_interface_->Create(instance_->pp_instance()); - if (!websocket_interface_->IsWebSocket(ws)) - return "websocket was reported as an invalid websocket"; + ASSERT_TRUE(ws); + + result = websocket_interface_->IsWebSocket(ws); + ASSERT_TRUE(result); + + core_interface_->ReleaseResource(ws); PASS(); } + +std::string TestWebSocket::TestInvalidConnect() { + PP_Var protocols[] = { PP_MakeUndefined() }; + + PP_Resource ws = websocket_interface_->Create(instance_->pp_instance()); + ASSERT_TRUE(ws); + + TestCompletionCallback callback(instance_->pp_instance(), force_async_); + int32_t result = websocket_interface_->Connect( + ws, PP_MakeUndefined(), protocols, 1, + static_cast<pp::CompletionCallback>(callback).pp_completion_callback()); + ASSERT_EQ(PP_ERROR_BADARGUMENT, result); + + result = websocket_interface_->Connect( + ws, PP_MakeUndefined(), protocols, 1, + static_cast<pp::CompletionCallback>(callback).pp_completion_callback()); + ASSERT_EQ(PP_ERROR_INPROGRESS, result); + + core_interface_->ReleaseResource(ws); + + const char* invalid_urls[] = { + "http://www.google.com/invalid_scheme", + "ws://www.google.com/invalid#fragment", + // TODO(toyoshim): Add URL which has invalid port like + // ws://www.google.com:65535/invalid_port + NULL + }; + for (int i = 0; invalid_urls[i]; ++i) { + ws = websocket_interface_->Create(instance_->pp_instance()); + ASSERT_TRUE(ws); + PP_Var invalid_url = CreateVar(invalid_urls[i]); + result = websocket_interface_->Connect( + ws, invalid_url, protocols, 0, + static_cast<pp::CompletionCallback>( + callback).pp_completion_callback()); + ReleaseVar(invalid_url); + core_interface_->ReleaseResource(ws); + ASSERT_EQ(PP_ERROR_BADARGUMENT, result); + } + + // TODO(toyoshim): Add invalid protocols tests + + PASS(); +} + + +std::string TestWebSocket::TestValidConnect() { + PP_Resource ws = websocket_interface_->Create(instance_->pp_instance()); + PP_Var url = CreateVar(kEchoServerURL); + PP_Var protocols[] = { PP_MakeUndefined() }; + TestCompletionCallback callback(instance_->pp_instance(), force_async_); + int32_t result = websocket_interface_->Connect( + ws, url, protocols, 0, + static_cast<pp::CompletionCallback>(callback).pp_completion_callback()); + ReleaseVar(url); + ASSERT_EQ(PP_OK_COMPLETIONPENDING, result); + result = callback.WaitForResult(); + ASSERT_EQ(PP_OK, result); + core_interface_->ReleaseResource(ws); + + PASS(); +} + +// TODO(toyoshim): Add tests to call various interfaces before calling connect. + +std::string TestWebSocket::TestTextSendReceive() { + // Connect to test echo server. + PP_Resource ws = Connect(); + ASSERT_TRUE(ws); + + // Send 'hello pepper' text message. + const char* message = "hello pepper"; + PP_Var message_var = CreateVar(message); + int32_t result = websocket_interface_->SendMessage(ws, message_var); + ReleaseVar(message_var); + ASSERT_EQ(PP_OK, result); + + // Receive echoed 'hello pepper'. + TestCompletionCallback callback(instance_->pp_instance(), force_async_); + PP_Var received_message; + result = websocket_interface_->ReceiveMessage(ws, &received_message, + static_cast<pp::CompletionCallback>(callback).pp_completion_callback()); + ASSERT_FALSE(result != PP_OK && result != PP_OK_COMPLETIONPENDING); + if (result == PP_OK_COMPLETIONPENDING) + result = callback.WaitForResult(); + ASSERT_EQ(PP_OK, result); + ASSERT_TRUE(AreEqual(received_message, message)); + ReleaseVar(received_message); + core_interface_->ReleaseResource(ws); + + PASS(); +} + +// TODO(toyoshim): Add other function tests. diff --git a/ppapi/tests/test_websocket.h b/ppapi/tests/test_websocket.h index 60eeead..f6daf7b 100644 --- a/ppapi/tests/test_websocket.h +++ b/ppapi/tests/test_websocket.h @@ -9,6 +9,8 @@ #include "ppapi/tests/test_case.h" +struct PPB_Core; +struct PPB_Var; struct PPB_WebSocket_Dev; class TestWebSocket : public TestCase { @@ -20,11 +22,21 @@ class TestWebSocket : public TestCase { virtual void RunTests(const std::string& filter); private: - std::string TestCreate(); + PP_Var CreateVar(const char* string); + void ReleaseVar(const PP_Var& var); + bool AreEqual(const PP_Var& var, const char* string); + + PP_Resource Connect(); + std::string TestIsWebSocket(); + std::string TestInvalidConnect(); + std::string TestValidConnect(); + std::string TestTextSendReceive(); // Used by the tests that access the C API directly. const PPB_WebSocket_Dev* websocket_interface_; + const PPB_Var* var_interface_; + const PPB_Core* core_interface_; }; #endif // PAPPI_TESTS_TEST_WEBSOCKET_H_ diff --git a/webkit/plugins/ppapi/ppb_websocket_impl.cc b/webkit/plugins/ppapi/ppb_websocket_impl.cc index f8c907c..b304a54 100644 --- a/webkit/plugins/ppapi/ppb_websocket_impl.cc +++ b/webkit/plugins/ppapi/ppb_websocket_impl.cc @@ -4,19 +4,66 @@ #include "webkit/plugins/ppapi/ppb_websocket_impl.h" +#include <string> + +#include "base/logging.h" +#include "googleurl/src/gurl.h" +#include "net/base/net_util.h" +#include "ppapi/c/pp_completion_callback.h" #include "ppapi/c/pp_errors.h" #include "ppapi/c/pp_var.h" +#include "ppapi/c/ppb_var.h" +#include "ppapi/shared_impl/var.h" +#include "ppapi/shared_impl/var_tracker.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebData.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebSocket.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h" +#include "webkit/plugins/ppapi/host_globals.h" +#include "webkit/plugins/ppapi/ppapi_plugin_instance.h" +#include "webkit/plugins/ppapi/resource_helper.h" +using ppapi::PpapiGlobals; +using ppapi::StringVar; using ppapi::thunk::PPB_WebSocket_API; +using ppapi::VarTracker; +using WebKit::WebData; +using WebKit::WebDocument; +using WebKit::WebString; +using WebKit::WebSocket; +using WebKit::WebSocketClient; +using WebKit::WebURL; + +static const uint32_t kMaxReasonSizeInBytes = 123; namespace webkit { namespace ppapi { PPB_WebSocket_Impl::PPB_WebSocket_Impl(PP_Instance instance) - : Resource(instance) { + : Resource(instance), + state_(PP_WEBSOCKETREADYSTATE_INVALID_DEV), + receive_callback_var_(NULL), + wait_for_receive_(false), + close_code_(0), + close_was_clean_(PP_FALSE) { + empty_string_ = new StringVar( + PpapiGlobals::Get()->GetModuleForInstance(instance), "", 0); } PPB_WebSocket_Impl::~PPB_WebSocket_Impl() { + if (websocket_.get()) + websocket_->disconnect(); + + // Clean up received and unread messages + VarTracker* var_tracker = PpapiGlobals::Get()->GetVarTracker(); + while (!received_messages_.empty()) { + PP_Var var = received_messages_.front(); + received_messages_.pop(); + var_tracker->ReleaseVar(var); + } } // static @@ -33,66 +80,303 @@ int32_t PPB_WebSocket_Impl::Connect(PP_Var url, const PP_Var protocols[], uint32_t protocol_count, PP_CompletionCallback callback) { - // TODO(toyoshim): Implement it. - return PP_ERROR_NOTSUPPORTED; + // Check mandatory interfaces. + PluginInstance* plugin_instance = ResourceHelper::GetPluginInstance(this); + DCHECK(plugin_instance); + if (!plugin_instance) + return PP_ERROR_FAILED; + + // Connect() can be called at most once. + if (websocket_.get()) + return PP_ERROR_INPROGRESS; + if (state_ != PP_WEBSOCKETREADYSTATE_INVALID_DEV) + return PP_ERROR_INPROGRESS; + state_ = PP_WEBSOCKETREADYSTATE_CLOSED_DEV; + + // Validate |callback| (Doesn't support blocking callback) + if (!callback.func) + return PP_ERROR_BLOCKS_MAIN_THREAD; + + // Validate url and convert it to WebURL. + scoped_refptr<StringVar> url_string = StringVar::FromPPVar(url); + if (!url_string) + return PP_ERROR_BADARGUMENT; + GURL gurl(url_string->value()); + if (!gurl.is_valid()) + return PP_ERROR_BADARGUMENT; + if (!gurl.SchemeIs("ws") && !gurl.SchemeIs("wss")) + return PP_ERROR_BADARGUMENT; + if (gurl.has_ref()) + return PP_ERROR_BADARGUMENT; + // TODO(toyoshim): Must check if the port is allowed by default. + // We could not just use net::IsPortAllowedByDefault() because it doesn't + // be exported over the shared library. + WebURL web_url(gurl); + + // Validate protocols and convert it to WebString. + // TODO(toyoshim): Detect duplicated protocols as error. + std::string protocol_string; + for (uint32_t i = 0; i < protocol_count; i++) { + // TODO(toyoshim): Similar function exist in WebKit::WebSocket. + // We must rearrange them into WebKit::WebChannel and share its protocol + // related implementation via WebKit API. + scoped_refptr<StringVar> string_var; + string_var = StringVar::FromPPVar(protocols[i]); + if (!string_var || !string_var->value().length()) + return PP_ERROR_BADARGUMENT; + for (std::string::const_iterator it = string_var->value().begin(); + it != string_var->value().end(); + ++it) { + uint8_t character = static_cast<uint8_t>(*it); + // WebSocket specification says "(Subprotocol string must consist of) + // characters in the range U+0021 to U+007E not including separator + // characters as defined in [RFC2616]." + const uint8_t minimumProtocolCharacter = '!'; // U+0021. + const uint8_t maximumProtocolCharacter = '~'; // U+007E. + if (character < minimumProtocolCharacter || + character > maximumProtocolCharacter || + character == '"' || character == '(' || character == ')' || + character == ',' || character == '/' || + (character >= ':' && character <= '@') || // U+003A - U+0040 + (character >= '[' && character <= ']') || // U+005B - u+005D + character == '{' || character == '}') + return PP_ERROR_BADARGUMENT; + } + if (i != 0) + protocol_string.append(","); + protocol_string.append(string_var->value()); + } + WebString web_protocols = WebString::fromUTF8(protocol_string); + + // Create WebKit::WebSocket object. + WebDocument document = plugin_instance->container()->element().document(); + websocket_.reset(WebSocket::create(document, this)); + DCHECK(websocket_.get()); + if (!websocket_.get()) + return PP_ERROR_NOTSUPPORTED; + + websocket_->connect(web_url, web_protocols); + state_ = PP_WEBSOCKETREADYSTATE_CONNECTING_DEV; + + // Install callback. + connect_callback_ = callback; + + return PP_OK_COMPLETIONPENDING; } int32_t PPB_WebSocket_Impl::Close(uint16_t code, PP_Var reason, PP_CompletionCallback callback) { - // TODO(toyoshim): Implement it. - return PP_ERROR_NOTSUPPORTED; + // Check mandarory interfaces. + if (!websocket_.get()) + return PP_ERROR_FAILED; + + // Validate |callback| (Doesn't support blocking callback) + if (!callback.func) + return PP_ERROR_BLOCKS_MAIN_THREAD; + + // Validate |code|. + if (code != WebSocket::CloseEventCodeNotSpecified) { + if (!(code == WebSocket::CloseEventCodeNormalClosure || + (WebSocket::CloseEventCodeMinimumUserDefined <= code && + code <= WebSocket::CloseEventCodeMaximumUserDefined))) + return PP_ERROR_NOACCESS; + } + // Validate |reason|. + // TODO(toyoshim): Returns PP_ERROR_BADARGUMENT if |reason| contains any + // surrogates. + scoped_refptr<StringVar> reason_string = StringVar::FromPPVar(reason); + if (!reason_string || reason_string->value().size() > kMaxReasonSizeInBytes) + return PP_ERROR_BADARGUMENT; + + // Check state. + if (state_ == PP_WEBSOCKETREADYSTATE_CLOSING_DEV || + state_ == PP_WEBSOCKETREADYSTATE_CLOSED_DEV) + return PP_ERROR_INPROGRESS; + + // Install |callback|. + close_callback_ = callback; + + if (state_ == PP_WEBSOCKETREADYSTATE_CONNECTING_DEV) { + state_ = PP_WEBSOCKETREADYSTATE_CLOSING_DEV; + PP_RunAndClearCompletionCallback(&connect_callback_, PP_ERROR_ABORTED); + websocket_->fail( + "WebSocket was closed before the connection was established."); + return PP_OK_COMPLETIONPENDING; + } + + // TODO(toyoshim): Handle bufferedAmount here. + + state_ = PP_WEBSOCKETREADYSTATE_CLOSING_DEV; + WebString web_reason = WebString::fromUTF8(reason_string->value()); + websocket_->close(code, web_reason); + + return PP_OK_COMPLETIONPENDING; } int32_t PPB_WebSocket_Impl::ReceiveMessage(PP_Var* message, PP_CompletionCallback callback) { - // TODO(toyoshim): Implement it. - return PP_ERROR_NOTSUPPORTED; + // Check state. + if (state_ == PP_WEBSOCKETREADYSTATE_INVALID_DEV || + state_ == PP_WEBSOCKETREADYSTATE_CONNECTING_DEV) + return PP_ERROR_BADARGUMENT; + + // Just return received message if any received message is queued. + if (!received_messages_.empty()) + return DoReceive(); + + // Or retain |message| as buffer to store and install |callback|. + wait_for_receive_ = true; + receive_callback_var_ = message; + receive_callback_ = callback; + + return PP_OK_COMPLETIONPENDING; } int32_t PPB_WebSocket_Impl::SendMessage(PP_Var message) { - // TODO(toyoshim): Implement it. - return PP_ERROR_NOTSUPPORTED; + // Check mandatory interfaces. + if (!websocket_.get()) + return PP_ERROR_FAILED; + + // Check state. + if (state_ == PP_WEBSOCKETREADYSTATE_INVALID_DEV || + state_ == PP_WEBSOCKETREADYSTATE_CONNECTING_DEV) + return PP_ERROR_BADARGUMENT; + + if (state_ == PP_WEBSOCKETREADYSTATE_CLOSING_DEV || + state_ == PP_WEBSOCKETREADYSTATE_CLOSED_DEV) { + // TODO(toyoshim): Handle bufferedAmount here. + } + + if (message.type != PP_VARTYPE_STRING) { + // TODO(toyoshim): Support binary data. + return PP_ERROR_NOTSUPPORTED; + } + + // Convert message to WebString. + scoped_refptr<StringVar> message_string = StringVar::FromPPVar(message); + if (!message_string) + return PP_ERROR_BADARGUMENT; + WebString web_message = WebString::fromUTF8(message_string->value()); + if (!websocket_->sendText(web_message)) + return PP_ERROR_BADARGUMENT; + + return PP_OK; } uint64_t PPB_WebSocket_Impl::GetBufferedAmount() { - // TODO(toyoshim): Implement it. + // TODO(toyoshim): Implement. return 0; } uint16_t PPB_WebSocket_Impl::GetCloseCode() { - // TODO(toyoshim): Implement it. - return 0; + return close_code_; } PP_Var PPB_WebSocket_Impl::GetCloseReason() { - // TODO(toyoshim): Implement it. - return PP_MakeUndefined(); + if (!close_reason_) + return empty_string_->GetPPVar(); + return close_reason_->GetPPVar(); } PP_Bool PPB_WebSocket_Impl::GetCloseWasClean() { - // TODO(toyoshim): Implement it. - return PP_FALSE; + return close_was_clean_; } PP_Var PPB_WebSocket_Impl::GetExtensions() { - // TODO(toyoshim): Implement it. - return PP_MakeUndefined(); + // TODO(toyoshim): For now, always returns empty string. + if (!extensions_) + return empty_string_->GetPPVar(); + return extensions_->GetPPVar(); } PP_Var PPB_WebSocket_Impl::GetProtocol() { - // TODO(toyoshim): Implement it. - return PP_MakeUndefined(); + // TODO(toyoshim): Implement. + if (!protocol_) + return empty_string_->GetPPVar(); + return protocol_->GetPPVar(); } PP_WebSocketReadyState_Dev PPB_WebSocket_Impl::GetReadyState() { - // TODO(toyoshim): Implement it. - return PP_WEBSOCKETREADYSTATE_INVALID_DEV; + return state_; } PP_Var PPB_WebSocket_Impl::GetURL() { - // TODO(toyoshim): Implement it. - return PP_MakeUndefined(); + // TODO(toyoshim): For now, always returns empty string. + if (!url_) + return empty_string_->GetPPVar(); + return url_->GetPPVar(); +} + +void PPB_WebSocket_Impl::didConnect() { + DCHECK_EQ(PP_WEBSOCKETREADYSTATE_CONNECTING_DEV, state_); + state_ = PP_WEBSOCKETREADYSTATE_OPEN_DEV; + PP_RunAndClearCompletionCallback(&connect_callback_, PP_OK); +} + +void PPB_WebSocket_Impl::didReceiveMessage(const WebString& message) { + // Append received data to queue. + std::string string = message.utf8(); + PP_Var var = StringVar::StringToPPVar( + PpapiGlobals::Get()->GetModuleForInstance(pp_instance()), string); + received_messages_.push(var); + + if (!wait_for_receive_) + return; + + PP_RunAndClearCompletionCallback(&receive_callback_, DoReceive()); +} + +void PPB_WebSocket_Impl::didReceiveBinaryData(const WebData& binaryData) { + DLOG(INFO) << "didReceiveBinaryData is not implemented yet."; + // TODO(toyoshim): Support to receive binary data. +} + +void PPB_WebSocket_Impl::didReceiveMessageError() { + // TODO(toyoshim): Must implement. + DLOG(INFO) << "didReceiveMessageError is not implemented yet."; +} + +void PPB_WebSocket_Impl::didStartClosingHandshake() { + // TODO(toyoshim): Must implement. + DLOG(INFO) << "didStartClosingHandshake is not implemented yet."; +} + +void PPB_WebSocket_Impl::didClose(unsigned long bufferedAmount, + ClosingHandshakeCompletionStatus status, + unsigned short code, + const WebString& reason) { + // Store code and reason. + close_code_ = code; + std::string reason_string = reason.utf8(); + close_reason_ = new StringVar( + PpapiGlobals::Get()->GetModuleForInstance(pp_instance()), reason_string); + + // TODO(toyoshim): Set close_was_clean_. + + // Handle state transition and invoking callback. + DCHECK_NE(PP_WEBSOCKETREADYSTATE_CLOSED_DEV, state_); + PP_WebSocketReadyState_Dev state = state_; + state_ = PP_WEBSOCKETREADYSTATE_CLOSED_DEV; + + if (state == PP_WEBSOCKETREADYSTATE_CONNECTING_DEV) + PP_RunAndClearCompletionCallback(&connect_callback_, PP_OK); + + if (state == PP_WEBSOCKETREADYSTATE_CLOSING_DEV) + PP_RunAndClearCompletionCallback(&close_callback_, PP_OK); +} + +int32_t PPB_WebSocket_Impl::DoReceive() { + // TODO(toyoshim): Check state. + + if (!receive_callback_var_) + return PP_OK; + + *receive_callback_var_ = received_messages_.front(); + received_messages_.pop(); + receive_callback_var_ = NULL; + wait_for_receive_ = false; + return PP_OK; } } // namespace ppapi diff --git a/webkit/plugins/ppapi/ppb_websocket_impl.h b/webkit/plugins/ppapi/ppb_websocket_impl.h index a966171..7ae430b 100644 --- a/webkit/plugins/ppapi/ppb_websocket_impl.h +++ b/webkit/plugins/ppapi/ppb_websocket_impl.h @@ -5,8 +5,22 @@ #ifndef WEBKIT_PLUGINS_PPAPI_PPB_WEBSOCKET_IMPL_H_ #define WEBKIT_PLUGINS_PPAPI_PPB_WEBSOCKET_IMPL_H_ +#include <queue> + +#include "base/memory/scoped_ptr.h" #include "ppapi/shared_impl/resource.h" #include "ppapi/thunk/ppb_websocket_api.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebSocketClient.h" + +struct PPB_Var; + +namespace ppapi { +class StringVar; +} + +namespace WebKit { +class WebSocket; +} namespace webkit { namespace ppapi { @@ -14,7 +28,8 @@ 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 ::ppapi::thunk::PPB_WebSocket_API, + public ::WebKit::WebSocketClient { public: explicit PPB_WebSocket_Impl(PP_Instance instance); virtual ~PPB_WebSocket_Impl(); @@ -44,6 +59,41 @@ class PPB_WebSocket_Impl : public ::ppapi::Resource, virtual PP_WebSocketReadyState_Dev GetReadyState() OVERRIDE; virtual PP_Var GetURL() OVERRIDE; + // WebSocketClient implementation. + virtual void didConnect() OVERRIDE; + virtual void didReceiveMessage(const WebKit::WebString& message) OVERRIDE; + virtual void didReceiveBinaryData( + const WebKit::WebData& binaryData) OVERRIDE; + virtual void didReceiveMessageError() OVERRIDE; + virtual void didStartClosingHandshake() OVERRIDE; + virtual void didClose(unsigned long bufferedAmount, + ClosingHandshakeCompletionStatus status, + unsigned short code, + const WebKit::WebString& reason) OVERRIDE; + private: + int32_t DoReceive(); + + scoped_ptr<WebKit::WebSocket> websocket_; + PP_WebSocketReadyState_Dev state_; + + PP_CompletionCallback connect_callback_; + + PP_CompletionCallback receive_callback_; + PP_Var* receive_callback_var_; + bool wait_for_receive_; + // TODO(toyoshim): Use std::queue<Var> when it supports binary. + std::queue<PP_Var> received_messages_; + + PP_CompletionCallback close_callback_; + uint16_t close_code_; + scoped_refptr< ::ppapi::StringVar> close_reason_; + PP_Bool close_was_clean_; + + scoped_refptr< ::ppapi::StringVar> empty_string_; + scoped_refptr< ::ppapi::StringVar> extensions_; + scoped_refptr< ::ppapi::StringVar> protocol_; + scoped_refptr< ::ppapi::StringVar> url_; + DISALLOW_COPY_AND_ASSIGN(PPB_WebSocket_Impl); }; |