OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "webkit/plugins/ppapi/ppb_transport_impl.h" | |
6 | |
7 #include "base/message_loop.h" | |
8 #include "base/string_util.h" | |
9 #include "net/base/io_buffer.h" | |
10 #include "net/base/net_errors.h" | |
11 #include "net/base/net_util.h" | |
12 #include "net/socket/socket.h" | |
13 #include "ppapi/c/dev/ppb_transport_dev.h" | |
14 #include "ppapi/c/pp_completion_callback.h" | |
15 #include "ppapi/c/pp_errors.h" | |
16 #include "ppapi/shared_impl/callback_tracker.h" | |
17 #include "ppapi/shared_impl/ppapi_globals.h" | |
18 #include "ppapi/shared_impl/var.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 "webkit/plugins/ppapi/common.h" | |
23 #include "webkit/plugins/ppapi/plugin_module.h" | |
24 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h" | |
25 #include "webkit/plugins/ppapi/resource_helper.h" | |
26 | |
27 using ppapi::StringVar; | |
28 using ppapi::thunk::PPB_Transport_API; | |
29 using ppapi::TrackedCallback; | |
30 using webkit_glue::P2PTransport; | |
31 | |
32 namespace webkit { | |
33 namespace ppapi { | |
34 | |
35 namespace { | |
36 | |
37 const char kUdpProtocolName[] = "udp"; | |
38 const char kTcpProtocolName[] = "tcp"; | |
39 | |
40 const int kMinBufferSize = 1024; | |
41 const int kMaxBufferSize = 1024 * 1024; | |
42 const int kMinAckDelay = 10; | |
43 const int kMaxAckDelay = 1000; | |
44 | |
45 int MapNetError(int result) { | |
46 if (result > 0) | |
47 return result; | |
48 | |
49 switch (result) { | |
50 case net::OK: | |
51 return PP_OK; | |
52 case net::ERR_IO_PENDING: | |
53 return PP_OK_COMPLETIONPENDING; | |
54 case net::ERR_INVALID_ARGUMENT: | |
55 return PP_ERROR_BADARGUMENT; | |
56 default: | |
57 return PP_ERROR_FAILED; | |
58 } | |
59 } | |
60 | |
61 WebKit::WebFrame* GetFrameForResource(const ::ppapi::Resource* resource) { | |
62 PluginInstance* plugin_instance = | |
63 ResourceHelper::GetPluginInstance(resource); | |
64 if (!plugin_instance) | |
65 return NULL; | |
66 return plugin_instance->container()->element().document().frame(); | |
67 } | |
68 | |
69 } // namespace | |
70 | |
71 PPB_Transport_Impl::PPB_Transport_Impl(PP_Instance instance) | |
72 : Resource(::ppapi::OBJECT_IS_IMPL, instance), | |
73 type_(PP_TRANSPORTTYPE_DATAGRAM), | |
74 started_(false), | |
75 writable_(false) { | |
76 } | |
77 | |
78 PPB_Transport_Impl::~PPB_Transport_Impl() { | |
79 } | |
80 | |
81 // static | |
82 PP_Resource PPB_Transport_Impl::Create(PP_Instance instance, | |
83 const char* name, | |
84 PP_TransportType type) { | |
85 scoped_refptr<PPB_Transport_Impl> t(new PPB_Transport_Impl(instance)); | |
86 if (!t->Init(name, type)) | |
87 return 0; | |
88 return t->GetReference(); | |
89 } | |
90 | |
91 PPB_Transport_API* PPB_Transport_Impl::AsPPB_Transport_API() { | |
92 return this; | |
93 } | |
94 | |
95 bool PPB_Transport_Impl::Init(const char* name, PP_TransportType type) { | |
96 name_ = name; | |
97 | |
98 if (type != PP_TRANSPORTTYPE_DATAGRAM && type != PP_TRANSPORTTYPE_STREAM) { | |
99 LOG(WARNING) << "Unknown transport type: " << type; | |
100 return false; | |
101 } | |
102 type_ = type; | |
103 | |
104 PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); | |
105 if (!plugin_delegate) | |
106 return false; | |
107 p2p_transport_.reset(plugin_delegate->CreateP2PTransport()); | |
108 return p2p_transport_.get() != NULL; | |
109 } | |
110 | |
111 PP_Bool PPB_Transport_Impl::IsWritable() { | |
112 if (!p2p_transport_.get()) | |
113 return PP_FALSE; | |
114 | |
115 return PP_FromBool(writable_); | |
116 } | |
117 | |
118 int32_t PPB_Transport_Impl::SetProperty(PP_TransportProperty property, | |
119 PP_Var value) { | |
120 // SetProperty() may be called only before Connect(). | |
121 if (started_) | |
122 return PP_ERROR_FAILED; | |
123 | |
124 switch (property) { | |
125 case PP_TRANSPORTPROPERTY_STUN_SERVER: { | |
126 StringVar* value_str = StringVar::FromPPVar(value); | |
127 if (!value_str) | |
128 return PP_ERROR_BADARGUMENT; | |
129 if (!net::ParseHostAndPort(value_str->value(), &config_.stun_server, | |
130 &config_.stun_server_port)) { | |
131 return PP_ERROR_BADARGUMENT; | |
132 } | |
133 break; | |
134 } | |
135 | |
136 case PP_TRANSPORTPROPERTY_RELAY_SERVER: { | |
137 StringVar* value_str = StringVar::FromPPVar(value); | |
138 if (!value_str) | |
139 return PP_ERROR_BADARGUMENT; | |
140 if (!net::ParseHostAndPort(value_str->value(), &config_.relay_server, | |
141 &config_.relay_server_port)) { | |
142 return PP_ERROR_BADARGUMENT; | |
143 } | |
144 break; | |
145 } | |
146 | |
147 case PP_TRANSPORTPROPERTY_RELAY_USERNAME: { | |
148 StringVar* value_str = StringVar::FromPPVar(value); | |
149 if (!value_str) | |
150 return PP_ERROR_BADARGUMENT; | |
151 config_.relay_username = value_str->value(); | |
152 break; | |
153 } | |
154 | |
155 case PP_TRANSPORTPROPERTY_RELAY_PASSWORD: { | |
156 StringVar* value_str = StringVar::FromPPVar(value); | |
157 if (!value_str) | |
158 return PP_ERROR_BADARGUMENT; | |
159 config_.relay_password = value_str->value(); | |
160 break; | |
161 } | |
162 | |
163 case PP_TRANSPORTPROPERTY_RELAY_MODE: { | |
164 switch (value.value.as_int) { | |
165 case PP_TRANSPORTRELAYMODE_TURN: | |
166 config_.legacy_relay = false; | |
167 break; | |
168 case PP_TRANSPORTRELAYMODE_GOOGLE: | |
169 config_.legacy_relay = true; | |
170 break; | |
171 default: | |
172 return PP_ERROR_BADARGUMENT; | |
173 } | |
174 break; | |
175 } | |
176 | |
177 case PP_TRANSPORTPROPERTY_TCP_RECEIVE_WINDOW: { | |
178 if (type_ != PP_TRANSPORTTYPE_STREAM) | |
179 return PP_ERROR_BADARGUMENT; | |
180 | |
181 int32_t int_value = value.value.as_int; | |
182 if (value.type != PP_VARTYPE_INT32 || int_value < kMinBufferSize || | |
183 int_value > kMaxBufferSize) { | |
184 return PP_ERROR_BADARGUMENT; | |
185 } | |
186 config_.tcp_receive_window = int_value; | |
187 break; | |
188 } | |
189 | |
190 case PP_TRANSPORTPROPERTY_TCP_SEND_WINDOW: { | |
191 if (type_ != PP_TRANSPORTTYPE_STREAM) | |
192 return PP_ERROR_BADARGUMENT; | |
193 | |
194 int32_t int_value = value.value.as_int; | |
195 if (value.type != PP_VARTYPE_INT32 || int_value < kMinBufferSize || | |
196 int_value > kMaxBufferSize) { | |
197 return PP_ERROR_BADARGUMENT; | |
198 } | |
199 config_.tcp_send_window = int_value; | |
200 break; | |
201 } | |
202 | |
203 case PP_TRANSPORTPROPERTY_TCP_NO_DELAY: { | |
204 if (type_ != PP_TRANSPORTTYPE_STREAM) | |
205 return PP_ERROR_BADARGUMENT; | |
206 | |
207 if (value.type != PP_VARTYPE_BOOL) | |
208 return PP_ERROR_BADARGUMENT; | |
209 config_.tcp_no_delay = PP_ToBool(value.value.as_bool); | |
210 break; | |
211 } | |
212 | |
213 case PP_TRANSPORTPROPERTY_TCP_ACK_DELAY: { | |
214 if (type_ != PP_TRANSPORTTYPE_STREAM) | |
215 return PP_ERROR_BADARGUMENT; | |
216 | |
217 int32_t int_value = value.value.as_int; | |
218 if (value.type != PP_VARTYPE_INT32 || int_value < kMinAckDelay || | |
219 int_value > kMaxAckDelay) { | |
220 return PP_ERROR_BADARGUMENT; | |
221 } | |
222 config_.tcp_ack_delay_ms = int_value; | |
223 break; | |
224 } | |
225 | |
226 case PP_TRANSPORTPROPERTY_DISABLE_TCP_TRANSPORT: { | |
227 if (value.type != PP_VARTYPE_BOOL) | |
228 return PP_ERROR_BADARGUMENT; | |
229 config_.disable_tcp_transport = PP_ToBool(value.value.as_bool); | |
230 break; | |
231 } | |
232 | |
233 default: | |
234 return PP_ERROR_BADARGUMENT; | |
235 } | |
236 | |
237 return PP_OK; | |
238 } | |
239 | |
240 int32_t PPB_Transport_Impl::Connect(PP_CompletionCallback callback) { | |
241 if (!callback.func) | |
242 return PP_ERROR_BLOCKS_MAIN_THREAD; | |
243 if (!p2p_transport_.get()) | |
244 return PP_ERROR_FAILED; | |
245 | |
246 // Connect() has already been called. | |
247 if (started_) | |
248 return PP_ERROR_INPROGRESS; | |
249 | |
250 P2PTransport::Protocol protocol = (type_ == PP_TRANSPORTTYPE_STREAM) ? | |
251 P2PTransport::PROTOCOL_TCP : P2PTransport::PROTOCOL_UDP; | |
252 | |
253 if (!p2p_transport_->Init( | |
254 GetFrameForResource(this), name_, protocol, config_, this)) { | |
255 return PP_ERROR_FAILED; | |
256 } | |
257 | |
258 started_ = true; | |
259 | |
260 PluginModule* plugin_module = ResourceHelper::GetPluginModule(this); | |
261 if (!plugin_module) | |
262 return PP_ERROR_FAILED; | |
263 | |
264 connect_callback_ = new TrackedCallback(this, callback); | |
265 return PP_OK_COMPLETIONPENDING; | |
266 } | |
267 | |
268 int32_t PPB_Transport_Impl::GetNextAddress(PP_Var* address, | |
269 PP_CompletionCallback callback) { | |
270 if (!callback.func) | |
271 return PP_ERROR_BLOCKS_MAIN_THREAD; | |
272 if (!p2p_transport_.get()) | |
273 return PP_ERROR_FAILED; | |
274 | |
275 if (TrackedCallback::IsPending(next_address_callback_)) | |
276 return PP_ERROR_INPROGRESS; | |
277 | |
278 PluginModule* plugin_module = ResourceHelper::GetPluginModule(this); | |
279 if (!plugin_module) | |
280 return PP_ERROR_FAILED; | |
281 | |
282 if (!local_candidates_.empty()) { | |
283 *address = StringVar::StringToPPVar(local_candidates_.front()); | |
284 local_candidates_.pop_front(); | |
285 return PP_OK; | |
286 } | |
287 | |
288 next_address_callback_ = new TrackedCallback(this, callback); | |
289 return PP_OK_COMPLETIONPENDING; | |
290 } | |
291 | |
292 int32_t PPB_Transport_Impl::ReceiveRemoteAddress(PP_Var address) { | |
293 if (!p2p_transport_.get()) | |
294 return PP_ERROR_FAILED; | |
295 | |
296 StringVar* address_str = StringVar::FromPPVar(address); | |
297 if (!address_str) | |
298 return PP_ERROR_BADARGUMENT; | |
299 | |
300 return p2p_transport_->AddRemoteCandidate(address_str->value()) ? | |
301 PP_OK : PP_ERROR_FAILED; | |
302 } | |
303 | |
304 int32_t PPB_Transport_Impl::Recv(void* data, uint32_t len, | |
305 PP_CompletionCallback callback) { | |
306 if (!callback.func) | |
307 return PP_ERROR_BLOCKS_MAIN_THREAD; | |
308 if (!p2p_transport_.get()) | |
309 return PP_ERROR_FAILED; | |
310 | |
311 if (TrackedCallback::IsPending(recv_callback_)) | |
312 return PP_ERROR_INPROGRESS; | |
313 | |
314 net::Socket* channel = p2p_transport_->GetChannel(); | |
315 if (!channel) | |
316 return PP_ERROR_FAILED; | |
317 | |
318 PluginModule* plugin_module = ResourceHelper::GetPluginModule(this); | |
319 if (!plugin_module) | |
320 return PP_ERROR_FAILED; | |
321 | |
322 scoped_refptr<net::IOBuffer> buffer = | |
323 new net::WrappedIOBuffer(static_cast<const char*>(data)); | |
324 int result = MapNetError( | |
325 channel->Read(buffer, len, base::Bind(&PPB_Transport_Impl::OnRead, | |
326 base::Unretained(this)))); | |
327 if (result == PP_OK_COMPLETIONPENDING) | |
328 recv_callback_ = new TrackedCallback(this, callback); | |
329 | |
330 return result; | |
331 } | |
332 | |
333 int32_t PPB_Transport_Impl::Send(const void* data, uint32_t len, | |
334 PP_CompletionCallback callback) { | |
335 if (!callback.func) | |
336 return PP_ERROR_BLOCKS_MAIN_THREAD; | |
337 if (!p2p_transport_.get()) | |
338 return PP_ERROR_FAILED; | |
339 | |
340 if (TrackedCallback::IsPending(send_callback_)) | |
341 return PP_ERROR_INPROGRESS; | |
342 | |
343 net::Socket* channel = p2p_transport_->GetChannel(); | |
344 if (!channel) | |
345 return PP_ERROR_FAILED; | |
346 | |
347 PluginModule* plugin_module = ResourceHelper::GetPluginModule(this); | |
348 if (!plugin_module) | |
349 return PP_ERROR_FAILED; | |
350 | |
351 scoped_refptr<net::IOBuffer> buffer = | |
352 new net::WrappedIOBuffer(static_cast<const char*>(data)); | |
353 int result = MapNetError( | |
354 channel->Write(buffer, len, base::Bind(&PPB_Transport_Impl::OnWritten, | |
355 base::Unretained(this)))); | |
356 if (result == PP_OK_COMPLETIONPENDING) | |
357 send_callback_ = new TrackedCallback(this, callback); | |
358 | |
359 return result; | |
360 } | |
361 | |
362 int32_t PPB_Transport_Impl::Close() { | |
363 if (!p2p_transport_.get()) | |
364 return PP_ERROR_FAILED; | |
365 | |
366 p2p_transport_.reset(); | |
367 | |
368 ::ppapi::PpapiGlobals::Get()->GetCallbackTrackerForInstance( | |
369 pp_instance())->PostAbortForResource(pp_resource()); | |
370 return PP_OK; | |
371 } | |
372 | |
373 void PPB_Transport_Impl::OnCandidateReady(const std::string& address) { | |
374 // Store the candidate first before calling the callback. | |
375 local_candidates_.push_back(address); | |
376 | |
377 if (TrackedCallback::IsPending(next_address_callback_)) | |
378 TrackedCallback::ClearAndRun(&next_address_callback_, PP_OK); | |
379 } | |
380 | |
381 void PPB_Transport_Impl::OnStateChange(webkit_glue::P2PTransport::State state) { | |
382 writable_ = (state | webkit_glue::P2PTransport::STATE_WRITABLE) != 0; | |
383 if (writable_ && TrackedCallback::IsPending(connect_callback_)) | |
384 TrackedCallback::ClearAndRun(&connect_callback_, PP_OK); | |
385 } | |
386 | |
387 void PPB_Transport_Impl::OnError(int error) { | |
388 writable_ = false; | |
389 if (TrackedCallback::IsPending(connect_callback_)) | |
390 TrackedCallback::ClearAndRun(&connect_callback_, PP_ERROR_FAILED); | |
391 } | |
392 | |
393 void PPB_Transport_Impl::OnRead(int result) { | |
394 DCHECK(TrackedCallback::IsPending(recv_callback_)); | |
395 TrackedCallback::ClearAndRun(&recv_callback_, MapNetError(result)); | |
396 } | |
397 | |
398 void PPB_Transport_Impl::OnWritten(int result) { | |
399 DCHECK(TrackedCallback::IsPending(send_callback_)); | |
400 TrackedCallback::ClearAndRun(&send_callback_, MapNetError(result)); | |
401 } | |
402 | |
403 } // namespace ppapi | |
404 } // namespace webkit | |
OLD | NEW |