diff options
author | toyoshim@chromium.org <toyoshim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-26 03:45:30 +0000 |
---|---|---|
committer | toyoshim@chromium.org <toyoshim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-26 03:45:30 +0000 |
commit | 31c1a7e8aa6c07d5eb9c2ab4b303d20f33723d58 (patch) | |
tree | 5a8e7fa5a1032f9305ac3d7bb6379baa806b4f09 /webkit/plugins | |
parent | 7af90e75366a20a2c89b8dd757b09a6c5b1cb7e7 (diff) | |
download | chromium_src-31c1a7e8aa6c07d5eb9c2ab4b303d20f33723d58.zip chromium_src-31c1a7e8aa6c07d5eb9c2ab4b303d20f33723d58.tar.gz chromium_src-31c1a7e8aa6c07d5eb9c2ab4b303d20f33723d58.tar.bz2 |
WebSocket Pepper API: in process API implementation
This change enable in process Websocket Pepper API.
For now, some unit tests are disabled because they need external a
WebSocket server.
BUG=87310
TEST=ui_tests --gtest_filter='PPAPITest.WebSocket*'
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=111596
Review URL: http://codereview.chromium.org/8558017
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@111628 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/plugins')
-rw-r--r-- | webkit/plugins/ppapi/ppb_websocket_impl.cc | 332 | ||||
-rw-r--r-- | webkit/plugins/ppapi/ppb_websocket_impl.h | 52 |
2 files changed, 359 insertions, 25 deletions
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); }; |