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 "third_party/WebKit/Source/WebKit/chromium/public/WebData.h" | |
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" | |
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" | |
20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h" | |
21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" | |
22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSocket.h" | |
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h" | |
24 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h" | |
25 #include "webkit/plugins/ppapi/resource_helper.h" | |
9 | 26 |
10 using ppapi::thunk::PPB_WebSocket_API; | 27 using ppapi::thunk::PPB_WebSocket_API; |
28 using WebKit::WebData; | |
29 using WebKit::WebDocument; | |
30 using WebKit::WebString; | |
31 using WebKit::WebSocket; | |
32 using WebKit::WebSocketClient; | |
33 using WebKit::WebURL; | |
34 | |
35 static const uint32_t kMaxReasonSizeInBytes = 123; | |
11 | 36 |
12 namespace webkit { | 37 namespace webkit { |
13 namespace ppapi { | 38 namespace ppapi { |
14 | 39 |
15 PPB_WebSocket_Impl::PPB_WebSocket_Impl(PP_Instance instance) | 40 PPB_WebSocket_Impl::PPB_WebSocket_Impl(PP_Instance instance) |
16 : Resource(instance) { | 41 : Resource(instance), |
42 state_(PP_WEBSOCKETREADYSTATE_INVALID_DEV), | |
43 receive_callback_var_(NULL), | |
44 wait_for_receive_(false), | |
45 close_code_(0), | |
46 close_reason_(PP_MakeUndefined()), | |
47 close_was_clean_(PP_FALSE), | |
48 extensions_(PP_MakeUndefined()), | |
49 protocol_(PP_MakeUndefined()), | |
50 url_(PP_MakeUndefined()) { | |
51 var_interface_ = reinterpret_cast<const PPB_Var*>( | |
52 pp::Module::Get()->GetBrowserInterface(PPB_VAR_INTERFACE)); | |
dmichael (off chromium)
2011/11/23 17:41:24
In implementation code, we should access the VarTr
Takashi Toyoshima
2011/11/24 03:55:49
Done.
| |
53 DCHECK(var_interface_); | |
54 if (!var_interface_) | |
55 empty_var_ = PP_MakeUndefined(); | |
dmichael (off chromium)
2011/11/23 17:41:24
And maybe empty_var_ should be a scoped_refptr<Str
Takashi Toyoshima
2011/11/24 03:55:49
Done.
| |
56 else | |
57 empty_var_ = var_interface_->VarFromUtf8( | |
58 pp::Module::Get()->pp_module(), "", 0); | |
17 } | 59 } |
18 | 60 |
19 PPB_WebSocket_Impl::~PPB_WebSocket_Impl() { | 61 PPB_WebSocket_Impl::~PPB_WebSocket_Impl() { |
62 if (websocket_.get()) | |
63 websocket_->disconnect(); | |
64 | |
65 if (!var_interface_) | |
66 return; | |
67 | |
68 if (empty_var_.type == PP_VARTYPE_STRING) | |
69 var_interface_->Release(empty_var_); | |
70 | |
71 // Clean up received and unread messages | |
72 while (!received_messages_.empty()) { | |
73 PP_Var var = received_messages_.front(); | |
74 received_messages_.pop(); | |
75 var_interface_->Release(var); | |
dmichael (off chromium)
2011/11/23 17:41:24
*if* each received_message is really just a string
Takashi Toyoshima
2011/11/24 03:55:49
For now, it is true.
But, we must handle binary da
dmichael (off chromium)
2011/11/24 04:32:36
The binary data will be ArrayBuffer and Blob, and
Takashi Toyoshima
2011/11/25 05:42:48
Oh, it sounds very nice that ArrayBuffer and Blob
| |
76 } | |
20 } | 77 } |
21 | 78 |
22 // static | 79 // static |
23 PP_Resource PPB_WebSocket_Impl::Create(PP_Instance instance) { | 80 PP_Resource PPB_WebSocket_Impl::Create(PP_Instance instance) { |
24 scoped_refptr<PPB_WebSocket_Impl> ws(new PPB_WebSocket_Impl(instance)); | 81 scoped_refptr<PPB_WebSocket_Impl> ws(new PPB_WebSocket_Impl(instance)); |
25 return ws->GetReference(); | 82 return ws->GetReference(); |
26 } | 83 } |
27 | 84 |
28 PPB_WebSocket_API* PPB_WebSocket_Impl::AsPPB_WebSocket_API() { | 85 PPB_WebSocket_API* PPB_WebSocket_Impl::AsPPB_WebSocket_API() { |
29 return this; | 86 return this; |
30 } | 87 } |
31 | 88 |
32 int32_t PPB_WebSocket_Impl::Connect(PP_Var url, | 89 int32_t PPB_WebSocket_Impl::Connect(PP_Var url, |
33 const PP_Var protocols[], | 90 const PP_Var protocols[], |
34 uint32_t protocol_count, | 91 uint32_t protocol_count, |
35 PP_CompletionCallback callback) { | 92 PP_CompletionCallback callback) { |
dmichael (off chromium)
2011/11/23 18:29:57
If callback is a blocking completion callback, you
Takashi Toyoshima
2011/11/24 03:55:49
I'd like to valid after changing state_, because I
| |
36 // TODO(toyoshim): Implement it. | 93 // Check mandatory interfaces. |
37 return PP_ERROR_NOTSUPPORTED; | 94 if (!var_interface_) |
95 return PP_ERROR_FAILED; | |
96 | |
97 PluginInstance* plugin_instance = ResourceHelper::GetPluginInstance(this); | |
98 DCHECK(plugin_instance); | |
99 if (!plugin_instance) | |
100 return PP_ERROR_FAILED; | |
101 | |
102 // Connect() can be called at most once. | |
103 if (websocket_.get()) | |
104 return PP_ERROR_INPROGRESS; | |
105 if (state_ != PP_WEBSOCKETREADYSTATE_INVALID_DEV) | |
106 return PP_ERROR_INPROGRESS; | |
107 state_ = PP_WEBSOCKETREADYSTATE_CLOSED_DEV; | |
108 | |
109 // Type of |url| and |protocols| must be PP_VARTYPE_STRING. | |
110 if (url.type != PP_VARTYPE_STRING) | |
111 return PP_ERROR_BADARGUMENT; | |
112 for (uint32_t i = 0; i < protocol_count; i++) | |
113 if (protocols[i].type != PP_VARTYPE_STRING) | |
114 return PP_ERROR_BADARGUMENT; | |
115 | |
116 // Validate url and convert it to WebURL. | |
117 uint32_t utf8_length; | |
118 const char* utf8 = var_interface_->VarToUtf8(url, &utf8_length); | |
119 std::string url_string(utf8, utf8_length); | |
120 | |
121 GURL gurl(url_string); | |
122 if (!gurl.is_valid()) | |
123 return PP_ERROR_BADARGUMENT; | |
124 if (!gurl.SchemeIs("ws") && !gurl.SchemeIs("wss")) | |
125 return PP_ERROR_BADARGUMENT; | |
126 if (gurl.has_ref()) | |
127 return PP_ERROR_BADARGUMENT; | |
128 if (!net::IsPortAllowedByDefault(gurl.IntPort())) | |
129 return PP_ERROR_BADARGUMENT; | |
130 WebURL web_url(gurl); | |
131 | |
132 // Validate protocols and convert it to WebString. | |
133 // TODO(toyoshim): Detect duplicated protocols as error. | |
134 std::string protocol_string; | |
135 for (uint32_t i = 0; i < protocol_count; i++) { | |
136 // TODO(toyoshim): Similar function exist in WebKit::WebSocket. | |
137 // We must rearrange them into WebKit::WebChannel and share its protocol | |
138 // related implementation via WebKit API. | |
139 utf8 = var_interface_->VarToUtf8(protocols[i], &utf8_length); | |
140 if (!utf8_length) | |
141 return PP_ERROR_BADARGUMENT; | |
142 for (uint32_t caret = 0; caret < utf8_length; caret++) { | |
143 uint8_t character = static_cast<uint8_t>(utf8[caret]); | |
144 // WebSocket specification says "(Subprotocol string must consist of) | |
145 // characters in the range U+0021 to U+007E not including separator | |
146 // characters as defined in [RFC2616]." | |
147 const uint8_t minimumProtocolCharacter = '!'; // U+0021. | |
148 const uint8_t maximumProtocolCharacter = '~'; // U+007E. | |
149 if (character < minimumProtocolCharacter || | |
150 character > maximumProtocolCharacter || | |
151 character == '"' || character == '(' || character == ')' || | |
152 character == ',' || character == '/' || | |
153 (character >= ':' && character <= '@') || // U+003A - U+0040 | |
154 (character >= '[' && character <= ']') || // U+005B - u+005D | |
155 character == '{' || character == '}') | |
156 return PP_ERROR_BADARGUMENT; | |
157 } | |
158 if (i != 0) | |
159 protocol_string.append(","); | |
160 protocol_string.append(utf8, utf8_length); | |
161 } | |
162 WebString web_protocols = | |
163 WebString::fromUTF8(protocol_string.data(), protocol_string.length()); | |
164 | |
165 // Create WebKit::WebSocket object. | |
166 WebDocument document = plugin_instance->container()->element().document(); | |
167 websocket_.reset(WebSocket::create(document, this)); | |
168 DCHECK(websocket_.get()); | |
169 if (!websocket_.get()) | |
170 return PP_ERROR_NOTSUPPORTED; | |
171 | |
172 websocket_->connect(web_url, web_protocols); | |
173 state_ = PP_WEBSOCKETREADYSTATE_CONNECTING_DEV; | |
174 | |
175 // Install callback. | |
176 connect_callback_ = callback; | |
177 | |
178 return PP_OK_COMPLETIONPENDING; | |
38 } | 179 } |
39 | 180 |
40 int32_t PPB_WebSocket_Impl::Close(uint16_t code, | 181 int32_t PPB_WebSocket_Impl::Close(uint16_t code, |
41 PP_Var reason, | 182 PP_Var reason, |
42 PP_CompletionCallback callback) { | 183 PP_CompletionCallback callback) { |
dmichael (off chromium)
2011/11/23 18:29:57
Same as above, you should not allow a blocking com
Takashi Toyoshima
2011/11/24 03:55:49
Done.
| |
43 // TODO(toyoshim): Implement it. | 184 // Check mandarory interfaces. |
44 return PP_ERROR_NOTSUPPORTED; | 185 if (!var_interface_) |
186 return PP_ERROR_FAILED; | |
187 | |
188 if (!websocket_.get()) | |
189 return PP_ERROR_FAILED; | |
190 | |
191 // Validate |code|. | |
192 if (code != WebSocket::CloseEventCodeNotSpecified) { | |
193 if (!(code == WebSocket::CloseEventCodeNormalClosure || | |
194 (WebSocket::CloseEventCodeMinimumUserDefined <= code && | |
195 code <= WebSocket::CloseEventCodeMaximumUserDefined))) | |
196 return PP_ERROR_NOACCESS; | |
197 } | |
198 // Validate |reason|. | |
199 // TODO(toyoshim): Returns PP_ERROR_BADARGUMENT if |reason| contains any | |
200 // surrogates. | |
201 if (reason.type != PP_VARTYPE_STRING) | |
202 return PP_ERROR_BADARGUMENT; | |
203 uint32_t utf8_length; | |
204 const char* utf8 = var_interface_->VarToUtf8(reason, &utf8_length); | |
205 if (utf8_length > kMaxReasonSizeInBytes) | |
206 return PP_ERROR_BADARGUMENT; | |
207 | |
208 // Check state. | |
209 if (state_ == PP_WEBSOCKETREADYSTATE_CLOSING_DEV || | |
210 state_ == PP_WEBSOCKETREADYSTATE_CLOSED_DEV) | |
211 return PP_ERROR_INPROGRESS; | |
212 | |
213 // Install |callback|. | |
214 close_callback_ = callback; | |
215 | |
216 if (state_ == PP_WEBSOCKETREADYSTATE_CONNECTING_DEV) { | |
217 state_ = PP_WEBSOCKETREADYSTATE_CLOSING_DEV; | |
218 websocket_->fail( | |
219 "WebSocket is closed before the connection is established."); | |
dmichael (off chromium)
2011/11/23 18:29:57
is->was, is->was ?
Takashi Toyoshima
2011/11/24 03:55:49
OK, I fixed.
Yuta,
I think WebCore::WebSocket has
| |
220 return PP_OK_COMPLETIONPENDING; | |
221 } | |
222 | |
223 // TODO(toyoshim): Handle bufferedAmount here. | |
224 | |
225 state_ = PP_WEBSOCKETREADYSTATE_CLOSING_DEV; | |
226 WebString web_reason = WebString::fromUTF8(utf8, utf8_length); | |
227 websocket_->close(code, web_reason); | |
228 | |
229 return PP_OK_COMPLETIONPENDING; | |
45 } | 230 } |
46 | 231 |
47 int32_t PPB_WebSocket_Impl::ReceiveMessage(PP_Var* message, | 232 int32_t PPB_WebSocket_Impl::ReceiveMessage(PP_Var* message, |
48 PP_CompletionCallback callback) { | 233 PP_CompletionCallback callback) { |
49 // TODO(toyoshim): Implement it. | 234 // Check mandatory interfaces. |
50 return PP_ERROR_NOTSUPPORTED; | 235 if (!var_interface_) |
236 return PP_ERROR_FAILED; | |
237 | |
238 // Check state. | |
239 if (state_ == PP_WEBSOCKETREADYSTATE_INVALID_DEV || | |
240 state_ == PP_WEBSOCKETREADYSTATE_CONNECTING_DEV) | |
241 return PP_ERROR_BADARGUMENT; | |
242 | |
243 // Just return received message if any received message is queued. | |
244 if (!received_messages_.empty()) | |
245 return DoReceive(); | |
246 | |
247 // Or retain |message| as buffer to store and install |callbak|. | |
dmichael (off chromium)
2011/11/23 18:29:57
nit: callbak->callback
Takashi Toyoshima
2011/11/24 03:55:49
Done.
| |
248 wait_for_receive_ = true; | |
249 receive_callback_var_ = message; | |
250 receive_callback_ = callback; | |
dmichael (off chromium)
2011/11/23 18:29:57
I don't see where these 3 get cleared. Should that
Takashi Toyoshima
2011/11/24 03:55:49
Done.
| |
251 | |
252 return PP_OK_COMPLETIONPENDING; | |
51 } | 253 } |
52 | 254 |
53 int32_t PPB_WebSocket_Impl::SendMessage(PP_Var message) { | 255 int32_t PPB_WebSocket_Impl::SendMessage(PP_Var message) { |
54 // TODO(toyoshim): Implement it. | 256 // Check mandatory interfaces. |
55 return PP_ERROR_NOTSUPPORTED; | 257 if (!var_interface_) |
258 return PP_ERROR_FAILED; | |
259 | |
260 if (!websocket_.get()) | |
261 return PP_ERROR_FAILED; | |
262 | |
263 // Check state. | |
264 if (state_ == PP_WEBSOCKETREADYSTATE_INVALID_DEV || | |
265 state_ == PP_WEBSOCKETREADYSTATE_CONNECTING_DEV) | |
266 return PP_ERROR_BADARGUMENT; | |
267 | |
268 if (state_ == PP_WEBSOCKETREADYSTATE_CLOSING_DEV || | |
269 state_ == PP_WEBSOCKETREADYSTATE_CLOSED_DEV) { | |
270 // TODO(toyoshim): Handle bufferedAmount here. | |
271 } | |
272 | |
273 if (message.type != PP_VARTYPE_STRING) { | |
274 // TODO(toyoshim): Support binary data. | |
275 return PP_ERROR_NOTSUPPORTED; | |
276 } | |
277 | |
278 // Convert message to WebString. | |
279 // TODO(toyoshim): Check message is valid utf8. | |
280 uint32_t utf8_length; | |
281 const char* utf8 = var_interface_->VarToUtf8(message, &utf8_length); | |
282 WebString web_message = WebString::fromUTF8(utf8, utf8_length); | |
283 if (!websocket_->sendText(web_message)) | |
284 return PP_ERROR_BADARGUMENT; | |
285 | |
286 return PP_OK; | |
56 } | 287 } |
57 | 288 |
58 uint64_t PPB_WebSocket_Impl::GetBufferedAmount() { | 289 uint64_t PPB_WebSocket_Impl::GetBufferedAmount() { |
59 // TODO(toyoshim): Implement it. | 290 // TODO(toyoshim): Implement. |
60 return 0; | 291 return 0; |
61 } | 292 } |
62 | 293 |
63 uint16_t PPB_WebSocket_Impl::GetCloseCode() { | 294 uint16_t PPB_WebSocket_Impl::GetCloseCode() { |
64 // TODO(toyoshim): Implement it. | 295 return close_code_; |
65 return 0; | |
66 } | 296 } |
67 | 297 |
68 PP_Var PPB_WebSocket_Impl::GetCloseReason() { | 298 PP_Var PPB_WebSocket_Impl::GetCloseReason() { |
69 // TODO(toyoshim): Implement it. | 299 return ReturnVarString(close_reason_); |
70 return PP_MakeUndefined(); | |
71 } | 300 } |
72 | 301 |
73 PP_Bool PPB_WebSocket_Impl::GetCloseWasClean() { | 302 PP_Bool PPB_WebSocket_Impl::GetCloseWasClean() { |
74 // TODO(toyoshim): Implement it. | 303 return close_was_clean_; |
75 return PP_FALSE; | |
76 } | 304 } |
77 | 305 |
78 PP_Var PPB_WebSocket_Impl::GetExtensions() { | 306 PP_Var PPB_WebSocket_Impl::GetExtensions() { |
79 // TODO(toyoshim): Implement it. | 307 // TODO(toyoshim): For now, always returns empty string. |
80 return PP_MakeUndefined(); | 308 return ReturnVarString(extensions_); |
81 } | 309 } |
82 | 310 |
83 PP_Var PPB_WebSocket_Impl::GetProtocol() { | 311 PP_Var PPB_WebSocket_Impl::GetProtocol() { |
84 // TODO(toyoshim): Implement it. | 312 // TODO(toyoshim): Implement. |
85 return PP_MakeUndefined(); | 313 return ReturnVarString(protocol_); |
86 } | 314 } |
87 | 315 |
88 PP_WebSocketReadyState_Dev PPB_WebSocket_Impl::GetReadyState() { | 316 PP_WebSocketReadyState_Dev PPB_WebSocket_Impl::GetReadyState() { |
89 // TODO(toyoshim): Implement it. | 317 return state_; |
90 return PP_WEBSOCKETREADYSTATE_INVALID_DEV; | |
91 } | 318 } |
92 | 319 |
93 PP_Var PPB_WebSocket_Impl::GetURL() { | 320 PP_Var PPB_WebSocket_Impl::GetURL() { |
94 // TODO(toyoshim): Implement it. | 321 return ReturnVarString(url_); |
95 return PP_MakeUndefined(); | 322 } |
323 | |
324 void PPB_WebSocket_Impl::didConnect() { | |
325 DCHECK_EQ(PP_WEBSOCKETREADYSTATE_CONNECTING_DEV, state_); | |
326 state_ = PP_WEBSOCKETREADYSTATE_OPEN_DEV; | |
327 PP_RunAndClearCompletionCallback(&connect_callback_, PP_OK); | |
328 } | |
329 | |
330 void PPB_WebSocket_Impl::didReceiveMessage(const WebString& message) { | |
331 // Check mandatory interface and states. | |
332 if (!var_interface_) | |
333 return; | |
334 | |
335 // Append received data to queue. | |
336 std::string string = message.utf8(); | |
337 PP_Var var = var_interface_->VarFromUtf8( | |
338 pp::Module::Get()->pp_module(), string.data(), string.length()); | |
dmichael (off chromium)
2011/11/23 17:41:24
Does the message really just represent a string? O
Takashi Toyoshima
2011/11/24 03:55:49
In the future, we must handle binary data in didRe
| |
339 received_messages_.push(var); | |
340 | |
341 if (!wait_for_receive_) | |
342 return; | |
343 | |
344 PP_RunAndClearCompletionCallback(&receive_callback_, DoReceive()); | |
345 } | |
346 | |
347 void PPB_WebSocket_Impl::didReceiveBinaryData(const WebData& binaryData) { | |
348 DLOG(WARNING) << "Not implemented yet."; | |
349 // TODO(toyoshim): Support to receive binary data. | |
350 } | |
351 | |
352 void PPB_WebSocket_Impl::didReceiveMessageError() { | |
353 // TODO(toyoshim): Must implement. | |
354 puts("didReceiveMessageError"); | |
dmichael (off chromium)
2011/11/23 18:29:57
please don't use stdio directly. DLOG(INFO)?
Takashi Toyoshima
2011/11/24 03:55:49
Sorry, I miss removing them.
| |
355 } | |
356 | |
357 void PPB_WebSocket_Impl::didStartClosingHandshake() { | |
358 // TODO(toyoshim): Must implement. | |
359 puts("didStartClosingHandshake"); | |
360 } | |
361 | |
362 void PPB_WebSocket_Impl::didClose(unsigned long bufferedAmount, | |
363 ClosingHandshakeCompletionStatus status, | |
364 unsigned short code, | |
365 const WebString& reason) { | |
366 // Store code and reason. | |
367 close_code_ = code; | |
368 std::string reason_string = reason.utf8(); | |
369 close_reason_ = var_interface_->VarFromUtf8( | |
370 pp::Module::Get()->pp_module(), | |
371 reason_string.data(), | |
372 reason_string.length()); | |
373 | |
374 // TODO(toyoshim): Set close_was_clean_. | |
375 | |
376 if (state_ == PP_WEBSOCKETREADYSTATE_CONNECTING_DEV) | |
377 PP_RunAndClearCompletionCallback(&connect_callback_, PP_OK); | |
378 | |
379 DCHECK_NE(PP_WEBSOCKETREADYSTATE_CLOSED_DEV, state_); | |
380 state_ = PP_WEBSOCKETREADYSTATE_CLOSED_DEV; | |
dmichael (off chromium)
2011/11/23 18:29:57
I don't see where you ever invoke close_callback_?
Takashi Toyoshima
2011/11/24 03:55:49
Oh, sorry.
I must invoke around here.
| |
381 } | |
382 | |
383 int32_t PPB_WebSocket_Impl::DoReceive() { | |
384 // TODO(toyoshim): Check state. | |
385 | |
386 if (!receive_callback_var_) | |
387 return PP_OK; | |
388 | |
389 *receive_callback_var_ = received_messages_.front(); | |
390 received_messages_.pop(); | |
391 return PP_OK; | |
392 } | |
393 | |
394 PP_Var PPB_WebSocket_Impl::ReturnVarString(PP_Var var) { | |
395 if (!var_interface_) | |
396 return PP_MakeUndefined(); | |
397 | |
398 if (var.type == PP_VARTYPE_STRING) { | |
399 var_interface_->AddRef(var); | |
400 return var; | |
401 } | |
402 var_interface_->AddRef(empty_var_); | |
403 return empty_var_; | |
96 } | 404 } |
97 | 405 |
98 } // namespace ppapi | 406 } // namespace ppapi |
99 } // namespace webkit | 407 } // namespace webkit |
OLD | NEW |