Roll src/third_party/WebKit ef7cfd7:80927b2 (svn 194570:194575)
[chromium-blink-merge.git] / ppapi / proxy / udp_socket_resource_base.cc
blobed231f8f6b3fd6f840d66c86edbbd97aeb949ebc
1 // Copyright 2013 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.
5 #include "ppapi/proxy/udp_socket_resource_base.h"
7 #include <cstring>
9 #include "base/logging.h"
10 #include "ppapi/c/pp_bool.h"
11 #include "ppapi/c/pp_errors.h"
12 #include "ppapi/proxy/error_conversion.h"
13 #include "ppapi/proxy/plugin_globals.h"
14 #include "ppapi/proxy/ppapi_messages.h"
15 #include "ppapi/shared_impl/socket_option_data.h"
16 #include "ppapi/thunk/enter.h"
18 namespace ppapi {
19 namespace proxy {
21 const int32_t UDPSocketResourceBase::kMaxWriteSize = 128 * 1024;
22 const int32_t UDPSocketResourceBase::kMaxSendBufferSize =
23 1024 * UDPSocketResourceBase::kMaxWriteSize;
24 const size_t UDPSocketResourceBase::kPluginSendBufferSlots = 8u;
26 namespace {
28 void RunCallback(scoped_refptr<TrackedCallback> callback,
29 int32_t pp_result,
30 bool private_api) {
31 callback->Run(ConvertNetworkAPIErrorForCompatibility(pp_result, private_api));
34 void PostAbortIfNecessary(const scoped_refptr<TrackedCallback>& callback) {
35 if (TrackedCallback::IsPending(callback))
36 callback->PostAbort();
39 } // namespace
41 UDPSocketResourceBase::UDPSocketResourceBase(Connection connection,
42 PP_Instance instance,
43 bool private_api)
44 : PluginResource(connection, instance),
45 private_api_(private_api),
46 bind_called_(false),
47 bound_(false),
48 closed_(false),
49 recv_filter_(PluginGlobals::Get()->udp_socket_filter()),
50 bound_addr_() {
51 recv_filter_->AddUDPResource(
52 pp_instance(), pp_resource(), private_api,
53 base::Bind(&UDPSocketResourceBase::SlotBecameAvailable, pp_resource()));
54 if (private_api)
55 SendCreate(BROWSER, PpapiHostMsg_UDPSocket_CreatePrivate());
56 else
57 SendCreate(BROWSER, PpapiHostMsg_UDPSocket_Create());
60 UDPSocketResourceBase::~UDPSocketResourceBase() {
61 CloseImpl();
64 int32_t UDPSocketResourceBase::SetOptionImpl(
65 PP_UDPSocket_Option name,
66 const PP_Var& value,
67 bool check_bind_state,
68 scoped_refptr<TrackedCallback> callback) {
69 if (closed_)
70 return PP_ERROR_FAILED;
72 // Check if socket is expected to be bound or not according to the option.
73 switch (name) {
74 case PP_UDPSOCKET_OPTION_ADDRESS_REUSE:
75 case PP_UDPSOCKET_OPTION_BROADCAST:
76 case PP_UDPSOCKET_OPTION_MULTICAST_LOOP:
77 case PP_UDPSOCKET_OPTION_MULTICAST_TTL: {
78 if ((check_bind_state || name == PP_UDPSOCKET_OPTION_ADDRESS_REUSE) &&
79 bind_called_) {
80 // SetOption should fail in this case in order to give predictable
81 // behavior while binding. Note that we use |bind_called_| rather
82 // than |bound_| since the latter is only set on successful completion
83 // of Bind().
84 return PP_ERROR_FAILED;
86 break;
88 case PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE:
89 case PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE: {
90 if (check_bind_state && !bound_)
91 return PP_ERROR_FAILED;
92 break;
96 SocketOptionData option_data;
97 switch (name) {
98 case PP_UDPSOCKET_OPTION_ADDRESS_REUSE:
99 case PP_UDPSOCKET_OPTION_BROADCAST:
100 case PP_UDPSOCKET_OPTION_MULTICAST_LOOP: {
101 if (value.type != PP_VARTYPE_BOOL)
102 return PP_ERROR_BADARGUMENT;
103 option_data.SetBool(PP_ToBool(value.value.as_bool));
104 break;
106 case PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE:
107 case PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE: {
108 if (value.type != PP_VARTYPE_INT32)
109 return PP_ERROR_BADARGUMENT;
110 option_data.SetInt32(value.value.as_int);
111 break;
113 case PP_UDPSOCKET_OPTION_MULTICAST_TTL: {
114 int32_t ival = value.value.as_int;
115 if (value.type != PP_VARTYPE_INT32 && (ival < 0 || ival > 255))
116 return PP_ERROR_BADARGUMENT;
117 option_data.SetInt32(ival);
118 break;
120 default: {
121 NOTREACHED();
122 return PP_ERROR_BADARGUMENT;
126 Call<PpapiPluginMsg_UDPSocket_SetOptionReply>(
127 BROWSER,
128 PpapiHostMsg_UDPSocket_SetOption(name, option_data),
129 base::Bind(&UDPSocketResourceBase::OnPluginMsgGeneralReply,
130 base::Unretained(this),
131 callback),
132 callback);
133 return PP_OK_COMPLETIONPENDING;
136 int32_t UDPSocketResourceBase::BindImpl(
137 const PP_NetAddress_Private* addr,
138 scoped_refptr<TrackedCallback> callback) {
139 if (!addr)
140 return PP_ERROR_BADARGUMENT;
141 if (bound_ || closed_)
142 return PP_ERROR_FAILED;
143 if (TrackedCallback::IsPending(bind_callback_))
144 return PP_ERROR_INPROGRESS;
146 bind_called_ = true;
147 bind_callback_ = callback;
149 // Send the request, the browser will call us back via BindReply.
150 Call<PpapiPluginMsg_UDPSocket_BindReply>(
151 BROWSER,
152 PpapiHostMsg_UDPSocket_Bind(*addr),
153 base::Bind(&UDPSocketResourceBase::OnPluginMsgBindReply,
154 base::Unretained(this)),
155 callback);
156 return PP_OK_COMPLETIONPENDING;
159 PP_Bool UDPSocketResourceBase::GetBoundAddressImpl(
160 PP_NetAddress_Private* addr) {
161 if (!addr || !bound_ || closed_)
162 return PP_FALSE;
164 *addr = bound_addr_;
165 return PP_TRUE;
168 int32_t UDPSocketResourceBase::RecvFromImpl(
169 char* buffer_out,
170 int32_t num_bytes,
171 PP_Resource* addr,
172 scoped_refptr<TrackedCallback> callback) {
173 if (!bound_)
174 return PP_ERROR_FAILED;
175 return recv_filter_->RequestData(pp_resource(), num_bytes, buffer_out, addr,
176 callback);
179 PP_Bool UDPSocketResourceBase::GetRecvFromAddressImpl(
180 PP_NetAddress_Private* addr) {
181 if (!addr)
182 return PP_FALSE;
183 *addr = recv_filter_->GetLastAddrPrivate(pp_resource());
184 return PP_TRUE;
187 int32_t UDPSocketResourceBase::SendToImpl(
188 const char* buffer,
189 int32_t num_bytes,
190 const PP_NetAddress_Private* addr,
191 scoped_refptr<TrackedCallback> callback) {
192 if (!buffer || num_bytes <= 0 || !addr)
193 return PP_ERROR_BADARGUMENT;
194 if (!bound_)
195 return PP_ERROR_FAILED;
196 if (sendto_callbacks_.size() == kPluginSendBufferSlots)
197 return PP_ERROR_INPROGRESS;
199 if (num_bytes > kMaxWriteSize)
200 num_bytes = kMaxWriteSize;
202 sendto_callbacks_.push(callback);
204 // Send the request, the browser will call us back via SendToReply.
205 Call<PpapiPluginMsg_UDPSocket_SendToReply>(
206 BROWSER,
207 PpapiHostMsg_UDPSocket_SendTo(std::string(buffer, num_bytes), *addr),
208 base::Bind(&UDPSocketResourceBase::OnPluginMsgSendToReply,
209 base::Unretained(this)),
210 callback);
211 return PP_OK_COMPLETIONPENDING;
214 void UDPSocketResourceBase::CloseImpl() {
215 if(closed_)
216 return;
218 bound_ = false;
219 closed_ = true;
221 Post(BROWSER, PpapiHostMsg_UDPSocket_Close());
223 PostAbortIfNecessary(bind_callback_);
224 while (!sendto_callbacks_.empty()) {
225 scoped_refptr<TrackedCallback> callback = sendto_callbacks_.front();
226 sendto_callbacks_.pop();
227 PostAbortIfNecessary(callback);
229 recv_filter_->RemoveUDPResource(pp_resource());
232 int32_t UDPSocketResourceBase::JoinGroupImpl(
233 const PP_NetAddress_Private *group,
234 scoped_refptr<TrackedCallback> callback) {
235 DCHECK(group);
237 Call<PpapiPluginMsg_UDPSocket_JoinGroupReply>(
238 BROWSER,
239 PpapiHostMsg_UDPSocket_JoinGroup(*group),
240 base::Bind(&UDPSocketResourceBase::OnPluginMsgGeneralReply,
241 base::Unretained(this),
242 callback),
243 callback);
244 return PP_OK_COMPLETIONPENDING;
247 int32_t UDPSocketResourceBase::LeaveGroupImpl(
248 const PP_NetAddress_Private *group,
249 scoped_refptr<TrackedCallback> callback) {
250 DCHECK(group);
252 Call<PpapiPluginMsg_UDPSocket_LeaveGroupReply>(
253 BROWSER,
254 PpapiHostMsg_UDPSocket_LeaveGroup(*group),
255 base::Bind(&UDPSocketResourceBase::OnPluginMsgGeneralReply,
256 base::Unretained(this),
257 callback),
258 callback);
259 return PP_OK_COMPLETIONPENDING;
262 void UDPSocketResourceBase::OnPluginMsgGeneralReply(
263 scoped_refptr<TrackedCallback> callback,
264 const ResourceMessageReplyParams& params) {
265 if (TrackedCallback::IsPending(callback))
266 RunCallback(callback, params.result(), private_api_);
269 void UDPSocketResourceBase::OnPluginMsgBindReply(
270 const ResourceMessageReplyParams& params,
271 const PP_NetAddress_Private& bound_addr) {
272 // It is possible that |bind_callback_| is pending while |closed_| is true:
273 // CloseImpl() has been called, but a BindReply came earlier than the task to
274 // abort |bind_callback_|. We don't want to update |bound_| or |bound_addr_|
275 // in that case.
276 if (!TrackedCallback::IsPending(bind_callback_) || closed_)
277 return;
279 if (params.result() == PP_OK)
280 bound_ = true;
281 bound_addr_ = bound_addr;
282 RunCallback(bind_callback_, params.result(), private_api_);
285 void UDPSocketResourceBase::OnPluginMsgSendToReply(
286 const ResourceMessageReplyParams& params,
287 int32_t bytes_written) {
288 // This can be empty if the socket was closed, but there are still tasks
289 // to be posted for this resource.
290 if (sendto_callbacks_.empty())
291 return;
293 scoped_refptr<TrackedCallback> callback = sendto_callbacks_.front();
294 sendto_callbacks_.pop();
295 if (!TrackedCallback::IsPending(callback))
296 return;
298 if (params.result() == PP_OK)
299 RunCallback(callback, bytes_written, private_api_);
300 else
301 RunCallback(callback, params.result(), private_api_);
304 // static
305 void UDPSocketResourceBase::SlotBecameAvailable(PP_Resource resource) {
306 ProxyLock::AssertAcquired();
307 thunk::EnterResourceNoLock<thunk::PPB_UDPSocket_API> enter(resource, false);
308 if (enter.failed())
309 return;
310 auto thiz(static_cast<UDPSocketResourceBase*>(enter.resource()));
312 if (!thiz->closed_)
313 thiz->Post(BROWSER, PpapiHostMsg_UDPSocket_RecvSlotAvailable());
316 } // namespace proxy
317 } // namespace ppapi