OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "webkit/plugins/ppapi/ppb_websocket_impl.h" | 5 #include "webkit/plugins/ppapi/ppb_websocket_impl.h" |
6 | 6 |
| 7 #include <string> |
| 8 |
| 9 #include "base/logging.h" |
| 10 #include "googleurl/src/gurl.h" |
| 11 #include "net/base/net_util.h" |
| 12 #include "ppapi/c/pp_completion_callback.h" |
7 #include "ppapi/c/pp_errors.h" | 13 #include "ppapi/c/pp_errors.h" |
8 #include "ppapi/c/pp_var.h" | 14 #include "ppapi/c/pp_var.h" |
| 15 #include "ppapi/c/ppb_var.h" |
| 16 #include "ppapi/shared_impl/var.h" |
| 17 #include "ppapi/shared_impl/var_tracker.h" |
| 18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebData.h" |
| 19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" |
| 20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" |
| 21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h" |
| 22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" |
| 23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSocket.h" |
| 24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h" |
| 25 #include "webkit/plugins/ppapi/host_globals.h" |
| 26 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h" |
| 27 #include "webkit/plugins/ppapi/resource_helper.h" |
9 | 28 |
| 29 using ppapi::PpapiGlobals; |
| 30 using ppapi::StringVar; |
10 using ppapi::thunk::PPB_WebSocket_API; | 31 using ppapi::thunk::PPB_WebSocket_API; |
| 32 using ppapi::VarTracker; |
| 33 using WebKit::WebData; |
| 34 using WebKit::WebDocument; |
| 35 using WebKit::WebString; |
| 36 using WebKit::WebSocket; |
| 37 using WebKit::WebSocketClient; |
| 38 using WebKit::WebURL; |
| 39 |
| 40 static const uint32_t kMaxReasonSizeInBytes = 123; |
11 | 41 |
12 namespace webkit { | 42 namespace webkit { |
13 namespace ppapi { | 43 namespace ppapi { |
14 | 44 |
15 PPB_WebSocket_Impl::PPB_WebSocket_Impl(PP_Instance instance) | 45 PPB_WebSocket_Impl::PPB_WebSocket_Impl(PP_Instance instance) |
16 : Resource(instance) { | 46 : Resource(instance), |
| 47 state_(PP_WEBSOCKETREADYSTATE_INVALID_DEV), |
| 48 receive_callback_var_(NULL), |
| 49 wait_for_receive_(false), |
| 50 close_code_(0), |
| 51 close_was_clean_(PP_FALSE) { |
| 52 empty_string_ = new StringVar( |
| 53 PpapiGlobals::Get()->GetModuleForInstance(instance), "", 0); |
17 } | 54 } |
18 | 55 |
19 PPB_WebSocket_Impl::~PPB_WebSocket_Impl() { | 56 PPB_WebSocket_Impl::~PPB_WebSocket_Impl() { |
| 57 if (websocket_.get()) |
| 58 websocket_->disconnect(); |
| 59 |
| 60 // Clean up received and unread messages |
| 61 VarTracker* var_tracker = PpapiGlobals::Get()->GetVarTracker(); |
| 62 while (!received_messages_.empty()) { |
| 63 PP_Var var = received_messages_.front(); |
| 64 received_messages_.pop(); |
| 65 var_tracker->ReleaseVar(var); |
| 66 } |
20 } | 67 } |
21 | 68 |
22 // static | 69 // static |
23 PP_Resource PPB_WebSocket_Impl::Create(PP_Instance instance) { | 70 PP_Resource PPB_WebSocket_Impl::Create(PP_Instance instance) { |
24 scoped_refptr<PPB_WebSocket_Impl> ws(new PPB_WebSocket_Impl(instance)); | 71 scoped_refptr<PPB_WebSocket_Impl> ws(new PPB_WebSocket_Impl(instance)); |
25 return ws->GetReference(); | 72 return ws->GetReference(); |
26 } | 73 } |
27 | 74 |
28 PPB_WebSocket_API* PPB_WebSocket_Impl::AsPPB_WebSocket_API() { | 75 PPB_WebSocket_API* PPB_WebSocket_Impl::AsPPB_WebSocket_API() { |
29 return this; | 76 return this; |
30 } | 77 } |
31 | 78 |
32 int32_t PPB_WebSocket_Impl::Connect(PP_Var url, | 79 int32_t PPB_WebSocket_Impl::Connect(PP_Var url, |
33 const PP_Var protocols[], | 80 const PP_Var protocols[], |
34 uint32_t protocol_count, | 81 uint32_t protocol_count, |
35 PP_CompletionCallback callback) { | 82 PP_CompletionCallback callback) { |
36 // TODO(toyoshim): Implement it. | 83 // Check mandatory interfaces. |
37 return PP_ERROR_NOTSUPPORTED; | 84 PluginInstance* plugin_instance = ResourceHelper::GetPluginInstance(this); |
| 85 DCHECK(plugin_instance); |
| 86 if (!plugin_instance) |
| 87 return PP_ERROR_FAILED; |
| 88 |
| 89 // Connect() can be called at most once. |
| 90 if (websocket_.get()) |
| 91 return PP_ERROR_INPROGRESS; |
| 92 if (state_ != PP_WEBSOCKETREADYSTATE_INVALID_DEV) |
| 93 return PP_ERROR_INPROGRESS; |
| 94 state_ = PP_WEBSOCKETREADYSTATE_CLOSED_DEV; |
| 95 |
| 96 // Validate |callback| (Doesn't support blocking callback) |
| 97 if (!callback.func) |
| 98 return PP_ERROR_BLOCKS_MAIN_THREAD; |
| 99 |
| 100 // Validate url and convert it to WebURL. |
| 101 scoped_refptr<StringVar> url_string = StringVar::FromPPVar(url); |
| 102 if (!url_string) |
| 103 return PP_ERROR_BADARGUMENT; |
| 104 GURL gurl(url_string->value()); |
| 105 if (!gurl.is_valid()) |
| 106 return PP_ERROR_BADARGUMENT; |
| 107 if (!gurl.SchemeIs("ws") && !gurl.SchemeIs("wss")) |
| 108 return PP_ERROR_BADARGUMENT; |
| 109 if (gurl.has_ref()) |
| 110 return PP_ERROR_BADARGUMENT; |
| 111 // TODO(toyoshim): Must check if the port is allowed by default. |
| 112 // We could not just use net::IsPortAllowedByDefault() because it doesn't |
| 113 // be exported over the shared library. |
| 114 WebURL web_url(gurl); |
| 115 |
| 116 // Validate protocols and convert it to WebString. |
| 117 // TODO(toyoshim): Detect duplicated protocols as error. |
| 118 std::string protocol_string; |
| 119 for (uint32_t i = 0; i < protocol_count; i++) { |
| 120 // TODO(toyoshim): Similar function exist in WebKit::WebSocket. |
| 121 // We must rearrange them into WebKit::WebChannel and share its protocol |
| 122 // related implementation via WebKit API. |
| 123 scoped_refptr<StringVar> string_var; |
| 124 string_var = StringVar::FromPPVar(protocols[i]); |
| 125 if (!string_var || !string_var->value().length()) |
| 126 return PP_ERROR_BADARGUMENT; |
| 127 for (std::string::const_iterator it = string_var->value().begin(); |
| 128 it != string_var->value().end(); |
| 129 ++it) { |
| 130 uint8_t character = static_cast<uint8_t>(*it); |
| 131 // WebSocket specification says "(Subprotocol string must consist of) |
| 132 // characters in the range U+0021 to U+007E not including separator |
| 133 // characters as defined in [RFC2616]." |
| 134 const uint8_t minimumProtocolCharacter = '!'; // U+0021. |
| 135 const uint8_t maximumProtocolCharacter = '~'; // U+007E. |
| 136 if (character < minimumProtocolCharacter || |
| 137 character > maximumProtocolCharacter || |
| 138 character == '"' || character == '(' || character == ')' || |
| 139 character == ',' || character == '/' || |
| 140 (character >= ':' && character <= '@') || // U+003A - U+0040 |
| 141 (character >= '[' && character <= ']') || // U+005B - u+005D |
| 142 character == '{' || character == '}') |
| 143 return PP_ERROR_BADARGUMENT; |
| 144 } |
| 145 if (i != 0) |
| 146 protocol_string.append(","); |
| 147 protocol_string.append(string_var->value()); |
| 148 } |
| 149 WebString web_protocols = WebString::fromUTF8(protocol_string); |
| 150 |
| 151 // Create WebKit::WebSocket object. |
| 152 WebDocument document = plugin_instance->container()->element().document(); |
| 153 websocket_.reset(WebSocket::create(document, this)); |
| 154 DCHECK(websocket_.get()); |
| 155 if (!websocket_.get()) |
| 156 return PP_ERROR_NOTSUPPORTED; |
| 157 |
| 158 websocket_->connect(web_url, web_protocols); |
| 159 state_ = PP_WEBSOCKETREADYSTATE_CONNECTING_DEV; |
| 160 |
| 161 // Install callback. |
| 162 connect_callback_ = callback; |
| 163 |
| 164 return PP_OK_COMPLETIONPENDING; |
38 } | 165 } |
39 | 166 |
40 int32_t PPB_WebSocket_Impl::Close(uint16_t code, | 167 int32_t PPB_WebSocket_Impl::Close(uint16_t code, |
41 PP_Var reason, | 168 PP_Var reason, |
42 PP_CompletionCallback callback) { | 169 PP_CompletionCallback callback) { |
43 // TODO(toyoshim): Implement it. | 170 // Check mandarory interfaces. |
44 return PP_ERROR_NOTSUPPORTED; | 171 if (!websocket_.get()) |
| 172 return PP_ERROR_FAILED; |
| 173 |
| 174 // Validate |callback| (Doesn't support blocking callback) |
| 175 if (!callback.func) |
| 176 return PP_ERROR_BLOCKS_MAIN_THREAD; |
| 177 |
| 178 // Validate |code|. |
| 179 if (code != WebSocket::CloseEventCodeNotSpecified) { |
| 180 if (!(code == WebSocket::CloseEventCodeNormalClosure || |
| 181 (WebSocket::CloseEventCodeMinimumUserDefined <= code && |
| 182 code <= WebSocket::CloseEventCodeMaximumUserDefined))) |
| 183 return PP_ERROR_NOACCESS; |
| 184 } |
| 185 // Validate |reason|. |
| 186 // TODO(toyoshim): Returns PP_ERROR_BADARGUMENT if |reason| contains any |
| 187 // surrogates. |
| 188 scoped_refptr<StringVar> reason_string = StringVar::FromPPVar(reason); |
| 189 if (!reason_string || reason_string->value().size() > kMaxReasonSizeInBytes) |
| 190 return PP_ERROR_BADARGUMENT; |
| 191 |
| 192 // Check state. |
| 193 if (state_ == PP_WEBSOCKETREADYSTATE_CLOSING_DEV || |
| 194 state_ == PP_WEBSOCKETREADYSTATE_CLOSED_DEV) |
| 195 return PP_ERROR_INPROGRESS; |
| 196 |
| 197 // Install |callback|. |
| 198 close_callback_ = callback; |
| 199 |
| 200 if (state_ == PP_WEBSOCKETREADYSTATE_CONNECTING_DEV) { |
| 201 state_ = PP_WEBSOCKETREADYSTATE_CLOSING_DEV; |
| 202 PP_RunAndClearCompletionCallback(&connect_callback_, PP_ERROR_ABORTED); |
| 203 websocket_->fail( |
| 204 "WebSocket was closed before the connection was established."); |
| 205 return PP_OK_COMPLETIONPENDING; |
| 206 } |
| 207 |
| 208 // TODO(toyoshim): Handle bufferedAmount here. |
| 209 |
| 210 state_ = PP_WEBSOCKETREADYSTATE_CLOSING_DEV; |
| 211 WebString web_reason = WebString::fromUTF8(reason_string->value()); |
| 212 websocket_->close(code, web_reason); |
| 213 |
| 214 return PP_OK_COMPLETIONPENDING; |
45 } | 215 } |
46 | 216 |
47 int32_t PPB_WebSocket_Impl::ReceiveMessage(PP_Var* message, | 217 int32_t PPB_WebSocket_Impl::ReceiveMessage(PP_Var* message, |
48 PP_CompletionCallback callback) { | 218 PP_CompletionCallback callback) { |
49 // TODO(toyoshim): Implement it. | 219 // Check state. |
50 return PP_ERROR_NOTSUPPORTED; | 220 if (state_ == PP_WEBSOCKETREADYSTATE_INVALID_DEV || |
| 221 state_ == PP_WEBSOCKETREADYSTATE_CONNECTING_DEV) |
| 222 return PP_ERROR_BADARGUMENT; |
| 223 |
| 224 // Just return received message if any received message is queued. |
| 225 if (!received_messages_.empty()) |
| 226 return DoReceive(); |
| 227 |
| 228 // Or retain |message| as buffer to store and install |callback|. |
| 229 wait_for_receive_ = true; |
| 230 receive_callback_var_ = message; |
| 231 receive_callback_ = callback; |
| 232 |
| 233 return PP_OK_COMPLETIONPENDING; |
51 } | 234 } |
52 | 235 |
53 int32_t PPB_WebSocket_Impl::SendMessage(PP_Var message) { | 236 int32_t PPB_WebSocket_Impl::SendMessage(PP_Var message) { |
54 // TODO(toyoshim): Implement it. | 237 // Check mandatory interfaces. |
55 return PP_ERROR_NOTSUPPORTED; | 238 if (!websocket_.get()) |
| 239 return PP_ERROR_FAILED; |
| 240 |
| 241 // Check state. |
| 242 if (state_ == PP_WEBSOCKETREADYSTATE_INVALID_DEV || |
| 243 state_ == PP_WEBSOCKETREADYSTATE_CONNECTING_DEV) |
| 244 return PP_ERROR_BADARGUMENT; |
| 245 |
| 246 if (state_ == PP_WEBSOCKETREADYSTATE_CLOSING_DEV || |
| 247 state_ == PP_WEBSOCKETREADYSTATE_CLOSED_DEV) { |
| 248 // TODO(toyoshim): Handle bufferedAmount here. |
| 249 } |
| 250 |
| 251 if (message.type != PP_VARTYPE_STRING) { |
| 252 // TODO(toyoshim): Support binary data. |
| 253 return PP_ERROR_NOTSUPPORTED; |
| 254 } |
| 255 |
| 256 // Convert message to WebString. |
| 257 scoped_refptr<StringVar> message_string = StringVar::FromPPVar(message); |
| 258 if (!message_string) |
| 259 return PP_ERROR_BADARGUMENT; |
| 260 WebString web_message = WebString::fromUTF8(message_string->value()); |
| 261 if (!websocket_->sendText(web_message)) |
| 262 return PP_ERROR_BADARGUMENT; |
| 263 |
| 264 return PP_OK; |
56 } | 265 } |
57 | 266 |
58 uint64_t PPB_WebSocket_Impl::GetBufferedAmount() { | 267 uint64_t PPB_WebSocket_Impl::GetBufferedAmount() { |
59 // TODO(toyoshim): Implement it. | 268 // TODO(toyoshim): Implement. |
60 return 0; | 269 return 0; |
61 } | 270 } |
62 | 271 |
63 uint16_t PPB_WebSocket_Impl::GetCloseCode() { | 272 uint16_t PPB_WebSocket_Impl::GetCloseCode() { |
64 // TODO(toyoshim): Implement it. | 273 return close_code_; |
65 return 0; | |
66 } | 274 } |
67 | 275 |
68 PP_Var PPB_WebSocket_Impl::GetCloseReason() { | 276 PP_Var PPB_WebSocket_Impl::GetCloseReason() { |
69 // TODO(toyoshim): Implement it. | 277 if (!close_reason_) |
70 return PP_MakeUndefined(); | 278 return empty_string_->GetPPVar(); |
| 279 return close_reason_->GetPPVar(); |
71 } | 280 } |
72 | 281 |
73 PP_Bool PPB_WebSocket_Impl::GetCloseWasClean() { | 282 PP_Bool PPB_WebSocket_Impl::GetCloseWasClean() { |
74 // TODO(toyoshim): Implement it. | 283 return close_was_clean_; |
75 return PP_FALSE; | |
76 } | 284 } |
77 | 285 |
78 PP_Var PPB_WebSocket_Impl::GetExtensions() { | 286 PP_Var PPB_WebSocket_Impl::GetExtensions() { |
79 // TODO(toyoshim): Implement it. | 287 // TODO(toyoshim): For now, always returns empty string. |
80 return PP_MakeUndefined(); | 288 if (!extensions_) |
| 289 return empty_string_->GetPPVar(); |
| 290 return extensions_->GetPPVar(); |
81 } | 291 } |
82 | 292 |
83 PP_Var PPB_WebSocket_Impl::GetProtocol() { | 293 PP_Var PPB_WebSocket_Impl::GetProtocol() { |
84 // TODO(toyoshim): Implement it. | 294 // TODO(toyoshim): Implement. |
85 return PP_MakeUndefined(); | 295 if (!protocol_) |
| 296 return empty_string_->GetPPVar(); |
| 297 return protocol_->GetPPVar(); |
86 } | 298 } |
87 | 299 |
88 PP_WebSocketReadyState_Dev PPB_WebSocket_Impl::GetReadyState() { | 300 PP_WebSocketReadyState_Dev PPB_WebSocket_Impl::GetReadyState() { |
89 // TODO(toyoshim): Implement it. | 301 return state_; |
90 return PP_WEBSOCKETREADYSTATE_INVALID_DEV; | |
91 } | 302 } |
92 | 303 |
93 PP_Var PPB_WebSocket_Impl::GetURL() { | 304 PP_Var PPB_WebSocket_Impl::GetURL() { |
94 // TODO(toyoshim): Implement it. | 305 // TODO(toyoshim): For now, always returns empty string. |
95 return PP_MakeUndefined(); | 306 if (!url_) |
| 307 return empty_string_->GetPPVar(); |
| 308 return url_->GetPPVar(); |
| 309 } |
| 310 |
| 311 void PPB_WebSocket_Impl::didConnect() { |
| 312 DCHECK_EQ(PP_WEBSOCKETREADYSTATE_CONNECTING_DEV, state_); |
| 313 state_ = PP_WEBSOCKETREADYSTATE_OPEN_DEV; |
| 314 PP_RunAndClearCompletionCallback(&connect_callback_, PP_OK); |
| 315 } |
| 316 |
| 317 void PPB_WebSocket_Impl::didReceiveMessage(const WebString& message) { |
| 318 // Append received data to queue. |
| 319 std::string string = message.utf8(); |
| 320 PP_Var var = StringVar::StringToPPVar( |
| 321 PpapiGlobals::Get()->GetModuleForInstance(pp_instance()), string); |
| 322 received_messages_.push(var); |
| 323 |
| 324 if (!wait_for_receive_) |
| 325 return; |
| 326 |
| 327 PP_RunAndClearCompletionCallback(&receive_callback_, DoReceive()); |
| 328 } |
| 329 |
| 330 void PPB_WebSocket_Impl::didReceiveBinaryData(const WebData& binaryData) { |
| 331 DLOG(INFO) << "didReceiveBinaryData is not implemented yet."; |
| 332 // TODO(toyoshim): Support to receive binary data. |
| 333 } |
| 334 |
| 335 void PPB_WebSocket_Impl::didReceiveMessageError() { |
| 336 // TODO(toyoshim): Must implement. |
| 337 DLOG(INFO) << "didReceiveMessageError is not implemented yet."; |
| 338 } |
| 339 |
| 340 void PPB_WebSocket_Impl::didStartClosingHandshake() { |
| 341 // TODO(toyoshim): Must implement. |
| 342 DLOG(INFO) << "didStartClosingHandshake is not implemented yet."; |
| 343 } |
| 344 |
| 345 void PPB_WebSocket_Impl::didClose(unsigned long bufferedAmount, |
| 346 ClosingHandshakeCompletionStatus status, |
| 347 unsigned short code, |
| 348 const WebString& reason) { |
| 349 // Store code and reason. |
| 350 close_code_ = code; |
| 351 std::string reason_string = reason.utf8(); |
| 352 close_reason_ = new StringVar( |
| 353 PpapiGlobals::Get()->GetModuleForInstance(pp_instance()), reason_string); |
| 354 |
| 355 // TODO(toyoshim): Set close_was_clean_. |
| 356 |
| 357 // Handle state transition and invoking callback. |
| 358 DCHECK_NE(PP_WEBSOCKETREADYSTATE_CLOSED_DEV, state_); |
| 359 PP_WebSocketReadyState_Dev state = state_; |
| 360 state_ = PP_WEBSOCKETREADYSTATE_CLOSED_DEV; |
| 361 |
| 362 if (state == PP_WEBSOCKETREADYSTATE_CONNECTING_DEV) |
| 363 PP_RunAndClearCompletionCallback(&connect_callback_, PP_OK); |
| 364 |
| 365 if (state == PP_WEBSOCKETREADYSTATE_CLOSING_DEV) |
| 366 PP_RunAndClearCompletionCallback(&close_callback_, PP_OK); |
| 367 } |
| 368 |
| 369 int32_t PPB_WebSocket_Impl::DoReceive() { |
| 370 // TODO(toyoshim): Check state. |
| 371 |
| 372 if (!receive_callback_var_) |
| 373 return PP_OK; |
| 374 |
| 375 *receive_callback_var_ = received_messages_.front(); |
| 376 received_messages_.pop(); |
| 377 receive_callback_var_ = NULL; |
| 378 wait_for_receive_ = false; |
| 379 return PP_OK; |
96 } | 380 } |
97 | 381 |
98 } // namespace ppapi | 382 } // namespace ppapi |
99 } // namespace webkit | 383 } // namespace webkit |
OLD | NEW |