Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(283)

Side by Side Diff: webkit/plugins/ppapi/ppb_websocket_impl.cc

Issue 8558017: WebSocket Pepper API: in process API implementation (reland) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: work with internal server Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
OLDNEW
« webkit/plugins/ppapi/ppb_websocket_impl.h ('K') | « webkit/plugins/ppapi/ppb_websocket_impl.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698