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