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" | |
13 #include "ppapi/c/pp_errors.h" | 7 #include "ppapi/c/pp_errors.h" |
14 #include "ppapi/c/pp_var.h" | 8 #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" | |
29 | 9 |
30 using ppapi::PpapiGlobals; | |
31 using ppapi::StringVar; | |
32 using ppapi::thunk::PPB_WebSocket_API; | 10 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; | |
42 | 11 |
43 namespace webkit { | 12 namespace webkit { |
44 namespace ppapi { | 13 namespace ppapi { |
45 | 14 |
46 PPB_WebSocket_Impl::PPB_WebSocket_Impl(PP_Instance instance) | 15 PPB_WebSocket_Impl::PPB_WebSocket_Impl(PP_Instance instance) |
47 : Resource(instance), | 16 : 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( | |
54 PpapiGlobals::Get()->GetModuleForInstance(instance), "", 0); | |
55 } | 17 } |
56 | 18 |
57 PPB_WebSocket_Impl::~PPB_WebSocket_Impl() { | 19 PPB_WebSocket_Impl::~PPB_WebSocket_Impl() { |
58 if (websocket_.get()) | |
59 websocket_->disconnect(); | |
60 | |
61 // Clean up received and unread messages | |
62 VarTracker* var_tracker = PpapiGlobals::Get()->GetVarTracker(); | |
63 while (!received_messages_.empty()) { | |
64 PP_Var var = received_messages_.front(); | |
65 received_messages_.pop(); | |
66 var_tracker->ReleaseVar(var); | |
67 } | |
68 } | 20 } |
69 | 21 |
70 // static | 22 // static |
71 PP_Resource PPB_WebSocket_Impl::Create(PP_Instance instance) { | 23 PP_Resource PPB_WebSocket_Impl::Create(PP_Instance instance) { |
72 scoped_refptr<PPB_WebSocket_Impl> ws(new PPB_WebSocket_Impl(instance)); | 24 scoped_refptr<PPB_WebSocket_Impl> ws(new PPB_WebSocket_Impl(instance)); |
73 return ws->GetReference(); | 25 return ws->GetReference(); |
74 } | 26 } |
75 | 27 |
76 PPB_WebSocket_API* PPB_WebSocket_Impl::AsPPB_WebSocket_API() { | 28 PPB_WebSocket_API* PPB_WebSocket_Impl::AsPPB_WebSocket_API() { |
77 return this; | 29 return this; |
78 } | 30 } |
79 | 31 |
80 int32_t PPB_WebSocket_Impl::Connect(PP_Var url, | 32 int32_t PPB_WebSocket_Impl::Connect(PP_Var url, |
81 const PP_Var protocols[], | 33 const PP_Var protocols[], |
82 uint32_t protocol_count, | 34 uint32_t protocol_count, |
83 PP_CompletionCallback callback) { | 35 PP_CompletionCallback callback) { |
84 // Check mandatory interfaces. | 36 // TODO(toyoshim): Implement it. |
85 PluginInstance* plugin_instance = ResourceHelper::GetPluginInstance(this); | 37 return PP_ERROR_NOTSUPPORTED; |
86 DCHECK(plugin_instance); | |
87 if (!plugin_instance) | |
88 return PP_ERROR_FAILED; | |
89 | |
90 // Connect() can be called at most once. | |
91 if (websocket_.get()) | |
92 return PP_ERROR_INPROGRESS; | |
93 if (state_ != PP_WEBSOCKETREADYSTATE_INVALID_DEV) | |
94 return PP_ERROR_INPROGRESS; | |
95 state_ = PP_WEBSOCKETREADYSTATE_CLOSED_DEV; | |
96 | |
97 // Validate |callback| (Doesn't support blocking callback) | |
98 if (!callback.func) | |
99 return PP_ERROR_BLOCKS_MAIN_THREAD; | |
100 | |
101 // Validate url and convert it to WebURL. | |
102 scoped_refptr<StringVar> url_string = StringVar::FromPPVar(url); | |
103 if (!url_string) | |
104 return PP_ERROR_BADARGUMENT; | |
105 GURL gurl(url_string->value()); | |
106 if (!gurl.is_valid()) | |
107 return PP_ERROR_BADARGUMENT; | |
108 if (!gurl.SchemeIs("ws") && !gurl.SchemeIs("wss")) | |
109 return PP_ERROR_BADARGUMENT; | |
110 if (gurl.has_ref()) | |
111 return PP_ERROR_BADARGUMENT; | |
112 if (!net::IsPortAllowedByDefault(gurl.IntPort())) | |
113 return PP_ERROR_BADARGUMENT; | |
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; | |
165 } | 38 } |
166 | 39 |
167 int32_t PPB_WebSocket_Impl::Close(uint16_t code, | 40 int32_t PPB_WebSocket_Impl::Close(uint16_t code, |
168 PP_Var reason, | 41 PP_Var reason, |
169 PP_CompletionCallback callback) { | 42 PP_CompletionCallback callback) { |
170 // Check mandarory interfaces. | 43 // TODO(toyoshim): Implement it. |
171 if (!websocket_.get()) | 44 return PP_ERROR_NOTSUPPORTED; |
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; | |
215 } | 45 } |
216 | 46 |
217 int32_t PPB_WebSocket_Impl::ReceiveMessage(PP_Var* message, | 47 int32_t PPB_WebSocket_Impl::ReceiveMessage(PP_Var* message, |
218 PP_CompletionCallback callback) { | 48 PP_CompletionCallback callback) { |
219 // Check state. | 49 // TODO(toyoshim): Implement it. |
220 if (state_ == PP_WEBSOCKETREADYSTATE_INVALID_DEV || | 50 return PP_ERROR_NOTSUPPORTED; |
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; | |
234 } | 51 } |
235 | 52 |
236 int32_t PPB_WebSocket_Impl::SendMessage(PP_Var message) { | 53 int32_t PPB_WebSocket_Impl::SendMessage(PP_Var message) { |
237 // Check mandatory interfaces. | 54 // TODO(toyoshim): Implement it. |
238 if (!websocket_.get()) | 55 return PP_ERROR_NOTSUPPORTED; |
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; | |
265 } | 56 } |
266 | 57 |
267 uint64_t PPB_WebSocket_Impl::GetBufferedAmount() { | 58 uint64_t PPB_WebSocket_Impl::GetBufferedAmount() { |
268 // TODO(toyoshim): Implement. | 59 // TODO(toyoshim): Implement it. |
269 return 0; | 60 return 0; |
270 } | 61 } |
271 | 62 |
272 uint16_t PPB_WebSocket_Impl::GetCloseCode() { | 63 uint16_t PPB_WebSocket_Impl::GetCloseCode() { |
273 return close_code_; | 64 // TODO(toyoshim): Implement it. |
| 65 return 0; |
274 } | 66 } |
275 | 67 |
276 PP_Var PPB_WebSocket_Impl::GetCloseReason() { | 68 PP_Var PPB_WebSocket_Impl::GetCloseReason() { |
277 if (!close_reason_) | 69 // TODO(toyoshim): Implement it. |
278 return empty_string_->GetPPVar(); | 70 return PP_MakeUndefined(); |
279 return close_reason_->GetPPVar(); | |
280 } | 71 } |
281 | 72 |
282 PP_Bool PPB_WebSocket_Impl::GetCloseWasClean() { | 73 PP_Bool PPB_WebSocket_Impl::GetCloseWasClean() { |
283 return close_was_clean_; | 74 // TODO(toyoshim): Implement it. |
| 75 return PP_FALSE; |
284 } | 76 } |
285 | 77 |
286 PP_Var PPB_WebSocket_Impl::GetExtensions() { | 78 PP_Var PPB_WebSocket_Impl::GetExtensions() { |
287 // TODO(toyoshim): For now, always returns empty string. | 79 // TODO(toyoshim): Implement it. |
288 if (!extensions_) | 80 return PP_MakeUndefined(); |
289 return empty_string_->GetPPVar(); | |
290 return extensions_->GetPPVar(); | |
291 } | 81 } |
292 | 82 |
293 PP_Var PPB_WebSocket_Impl::GetProtocol() { | 83 PP_Var PPB_WebSocket_Impl::GetProtocol() { |
294 // TODO(toyoshim): Implement. | 84 // TODO(toyoshim): Implement it. |
295 if (!protocol_) | 85 return PP_MakeUndefined(); |
296 return empty_string_->GetPPVar(); | |
297 return protocol_->GetPPVar(); | |
298 } | 86 } |
299 | 87 |
300 PP_WebSocketReadyState_Dev PPB_WebSocket_Impl::GetReadyState() { | 88 PP_WebSocketReadyState_Dev PPB_WebSocket_Impl::GetReadyState() { |
301 return state_; | 89 // TODO(toyoshim): Implement it. |
| 90 return PP_WEBSOCKETREADYSTATE_INVALID_DEV; |
302 } | 91 } |
303 | 92 |
304 PP_Var PPB_WebSocket_Impl::GetURL() { | 93 PP_Var PPB_WebSocket_Impl::GetURL() { |
305 // TODO(toyoshim): For now, always returns empty string. | 94 // TODO(toyoshim): Implement it. |
306 if (!url_) | 95 return PP_MakeUndefined(); |
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; | |
380 } | 96 } |
381 | 97 |
382 } // namespace ppapi | 98 } // namespace ppapi |
383 } // namespace webkit | 99 } // namespace webkit |
OLD | NEW |