summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/test/ui/ppapi_uitest.cc15
-rw-r--r--ppapi/tests/test_websocket.cc182
-rw-r--r--ppapi/tests/test_websocket.h14
-rw-r--r--webkit/plugins/ppapi/ppb_websocket_impl.cc332
-rw-r--r--webkit/plugins/ppapi/ppb_websocket_impl.h52
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);
};