mac: Fix DownloadItemController unit tests on Yosemite.
[chromium-blink-merge.git] / ppapi / proxy / udp_socket_resource_base.cc
blob8ed6d0299ed6b27d0613ca38e7019577e8495ca2
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 <algorithm>
8 #include <cstring>
10 #include "base/logging.h"
11 #include "ppapi/c/pp_bool.h"
12 #include "ppapi/c/pp_completion_callback.h"
13 #include "ppapi/c/pp_errors.h"
14 #include "ppapi/proxy/error_conversion.h"
15 #include "ppapi/proxy/plugin_globals.h"
16 #include "ppapi/proxy/ppapi_messages.h"
17 #include "ppapi/shared_impl/socket_option_data.h"
18 #include "ppapi/thunk/enter.h"
19 #include "ppapi/thunk/resource_creation_api.h"
21 namespace ppapi {
22 namespace proxy {
24 const int32_t UDPSocketResourceBase::kMaxReadSize = 128 * 1024;
25 const int32_t UDPSocketResourceBase::kMaxWriteSize = 128 * 1024;
26 const int32_t UDPSocketResourceBase::kMaxSendBufferSize =
27 1024 * UDPSocketResourceBase::kMaxWriteSize;
28 const int32_t UDPSocketResourceBase::kMaxReceiveBufferSize =
29 1024 * UDPSocketResourceBase::kMaxReadSize;
30 const size_t UDPSocketResourceBase::kPluginReceiveBufferSlots = 32u;
32 UDPSocketResourceBase::UDPSocketResourceBase(Connection connection,
33 PP_Instance instance,
34 bool private_api)
35 : PluginResource(connection, instance),
36 private_api_(private_api),
37 bind_called_(false),
38 bound_(false),
39 closed_(false),
40 read_buffer_(NULL),
41 bytes_to_read_(-1),
42 recvfrom_addr_resource_(NULL) {
43 recvfrom_addr_.size = 0;
44 memset(recvfrom_addr_.data, 0,
45 arraysize(recvfrom_addr_.data) * sizeof(*recvfrom_addr_.data));
46 bound_addr_.size = 0;
47 memset(bound_addr_.data, 0,
48 arraysize(bound_addr_.data) * sizeof(*bound_addr_.data));
50 if (private_api)
51 SendCreate(BROWSER, PpapiHostMsg_UDPSocket_CreatePrivate());
52 else
53 SendCreate(BROWSER, PpapiHostMsg_UDPSocket_Create());
55 PluginGlobals::Get()->resource_reply_thread_registrar()->HandleOnIOThread(
56 PpapiPluginMsg_UDPSocket_PushRecvResult::ID);
59 UDPSocketResourceBase::~UDPSocketResourceBase() {
62 int32_t UDPSocketResourceBase::SetOptionImpl(
63 PP_UDPSocket_Option name,
64 const PP_Var& value,
65 bool check_bind_state,
66 scoped_refptr<TrackedCallback> callback) {
67 if (closed_)
68 return PP_ERROR_FAILED;
70 SocketOptionData option_data;
71 switch (name) {
72 case PP_UDPSOCKET_OPTION_ADDRESS_REUSE:
73 case PP_UDPSOCKET_OPTION_BROADCAST: {
74 if ((check_bind_state || name == PP_UDPSOCKET_OPTION_ADDRESS_REUSE) &&
75 bind_called_) {
76 // SetOption should fail in this case in order to give predictable
77 // behavior while binding. Note that we use |bind_called_| rather
78 // than |bound_| since the latter is only set on successful completion
79 // of Bind().
80 return PP_ERROR_FAILED;
82 if (value.type != PP_VARTYPE_BOOL)
83 return PP_ERROR_BADARGUMENT;
84 option_data.SetBool(PP_ToBool(value.value.as_bool));
85 break;
87 case PP_UDPSOCKET_OPTION_SEND_BUFFER_SIZE:
88 case PP_UDPSOCKET_OPTION_RECV_BUFFER_SIZE: {
89 if (check_bind_state && !bound_)
90 return PP_ERROR_FAILED;
91 if (value.type != PP_VARTYPE_INT32)
92 return PP_ERROR_BADARGUMENT;
93 option_data.SetInt32(value.value.as_int);
94 break;
96 default: {
97 NOTREACHED();
98 return PP_ERROR_BADARGUMENT;
102 Call<PpapiPluginMsg_UDPSocket_SetOptionReply>(
103 BROWSER,
104 PpapiHostMsg_UDPSocket_SetOption(name, option_data),
105 base::Bind(&UDPSocketResourceBase::OnPluginMsgSetOptionReply,
106 base::Unretained(this),
107 callback),
108 callback);
109 return PP_OK_COMPLETIONPENDING;
112 int32_t UDPSocketResourceBase::BindImpl(
113 const PP_NetAddress_Private* addr,
114 scoped_refptr<TrackedCallback> callback) {
115 if (!addr)
116 return PP_ERROR_BADARGUMENT;
117 if (bound_ || closed_)
118 return PP_ERROR_FAILED;
119 if (TrackedCallback::IsPending(bind_callback_))
120 return PP_ERROR_INPROGRESS;
122 bind_called_ = true;
123 bind_callback_ = callback;
125 // Send the request, the browser will call us back via BindReply.
126 Call<PpapiPluginMsg_UDPSocket_BindReply>(
127 BROWSER,
128 PpapiHostMsg_UDPSocket_Bind(*addr),
129 base::Bind(&UDPSocketResourceBase::OnPluginMsgBindReply,
130 base::Unretained(this)),
131 callback);
132 return PP_OK_COMPLETIONPENDING;
135 PP_Bool UDPSocketResourceBase::GetBoundAddressImpl(
136 PP_NetAddress_Private* addr) {
137 if (!addr || !bound_ || closed_)
138 return PP_FALSE;
140 *addr = bound_addr_;
141 return PP_TRUE;
144 int32_t UDPSocketResourceBase::RecvFromImpl(
145 char* buffer,
146 int32_t num_bytes,
147 PP_Resource* addr,
148 scoped_refptr<TrackedCallback> callback) {
149 if (!buffer || num_bytes <= 0)
150 return PP_ERROR_BADARGUMENT;
151 if (!bound_)
152 return PP_ERROR_FAILED;
153 if (TrackedCallback::IsPending(recvfrom_callback_))
154 return PP_ERROR_INPROGRESS;
156 if (recv_buffers_.empty()) {
157 read_buffer_ = buffer;
158 bytes_to_read_ = std::min(num_bytes, kMaxReadSize);
159 recvfrom_addr_resource_ = addr;
160 recvfrom_callback_ = callback;
162 return PP_OK_COMPLETIONPENDING;
163 } else {
164 RecvBuffer& front = recv_buffers_.front();
166 if (num_bytes < static_cast<int32_t>(front.data.size()))
167 return PP_ERROR_MESSAGE_TOO_BIG;
169 int32_t result = SetRecvFromOutput(front.result, front.data, front.addr,
170 buffer, num_bytes, addr);
172 recv_buffers_.pop();
173 Post(BROWSER, PpapiHostMsg_UDPSocket_RecvSlotAvailable());
175 return result;
179 PP_Bool UDPSocketResourceBase::GetRecvFromAddressImpl(
180 PP_NetAddress_Private* addr) {
181 if (!addr)
182 return PP_FALSE;
183 *addr = recvfrom_addr_;
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 (TrackedCallback::IsPending(sendto_callback_))
197 return PP_ERROR_INPROGRESS;
199 if (num_bytes > kMaxWriteSize)
200 num_bytes = kMaxWriteSize;
202 sendto_callback_ = 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 PostAbortIfNecessary(&recvfrom_callback_);
225 PostAbortIfNecessary(&sendto_callback_);
227 read_buffer_ = NULL;
228 bytes_to_read_ = -1;
231 void UDPSocketResourceBase::OnReplyReceived(
232 const ResourceMessageReplyParams& params,
233 const IPC::Message& msg) {
234 PPAPI_BEGIN_MESSAGE_MAP(UDPSocketResourceBase, msg)
235 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
236 PpapiPluginMsg_UDPSocket_PushRecvResult,
237 OnPluginMsgPushRecvResult)
238 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(
239 PluginResource::OnReplyReceived(params, msg))
240 PPAPI_END_MESSAGE_MAP()
243 void UDPSocketResourceBase::PostAbortIfNecessary(
244 scoped_refptr<TrackedCallback>* callback) {
245 if (TrackedCallback::IsPending(*callback))
246 (*callback)->PostAbort();
249 void UDPSocketResourceBase::OnPluginMsgSetOptionReply(
250 scoped_refptr<TrackedCallback> callback,
251 const ResourceMessageReplyParams& params) {
252 if (TrackedCallback::IsPending(callback))
253 RunCallback(callback, params.result());
256 void UDPSocketResourceBase::OnPluginMsgBindReply(
257 const ResourceMessageReplyParams& params,
258 const PP_NetAddress_Private& bound_addr) {
259 // It is possible that |bind_callback_| is pending while |closed_| is true:
260 // CloseImpl() has been called, but a BindReply came earlier than the task to
261 // abort |bind_callback_|. We don't want to update |bound_| or |bound_addr_|
262 // in that case.
263 if (!TrackedCallback::IsPending(bind_callback_) || closed_)
264 return;
266 if (params.result() == PP_OK)
267 bound_ = true;
268 bound_addr_ = bound_addr;
269 RunCallback(bind_callback_, params.result());
272 void UDPSocketResourceBase::OnPluginMsgPushRecvResult(
273 const ResourceMessageReplyParams& params,
274 int32_t result,
275 const std::string& data,
276 const PP_NetAddress_Private& addr) {
277 // TODO(yzshen): Support passing in a non-const string ref, so that we can
278 // eliminate one copy when storing the data in the buffer.
280 DCHECK_LT(recv_buffers_.size(), kPluginReceiveBufferSlots);
282 if (!TrackedCallback::IsPending(recvfrom_callback_) || !read_buffer_) {
283 recv_buffers_.push(RecvBuffer());
284 RecvBuffer& back = recv_buffers_.back();
285 back.result = result;
286 back.data = data;
287 back.addr = addr;
289 return;
292 DCHECK_EQ(recv_buffers_.size(), 0u);
294 if (bytes_to_read_ < static_cast<int32_t>(data.size())) {
295 recv_buffers_.push(RecvBuffer());
296 RecvBuffer& back = recv_buffers_.back();
297 back.result = result;
298 back.data = data;
299 back.addr = addr;
301 result = PP_ERROR_MESSAGE_TOO_BIG;
302 } else {
303 result = SetRecvFromOutput(result, data, addr, read_buffer_, bytes_to_read_,
304 recvfrom_addr_resource_);
305 Post(BROWSER, PpapiHostMsg_UDPSocket_RecvSlotAvailable());
308 read_buffer_ = NULL;
309 bytes_to_read_ = -1;
310 recvfrom_addr_resource_ = NULL;
312 RunCallback(recvfrom_callback_, result);
315 void UDPSocketResourceBase::OnPluginMsgSendToReply(
316 const ResourceMessageReplyParams& params,
317 int32_t bytes_written) {
318 if (!TrackedCallback::IsPending(sendto_callback_))
319 return;
321 if (params.result() == PP_OK)
322 RunCallback(sendto_callback_, bytes_written);
323 else
324 RunCallback(sendto_callback_, params.result());
327 void UDPSocketResourceBase::RunCallback(scoped_refptr<TrackedCallback> callback,
328 int32_t pp_result) {
329 callback->Run(ConvertNetworkAPIErrorForCompatibility(pp_result,
330 private_api_));
333 int32_t UDPSocketResourceBase::SetRecvFromOutput(
334 int32_t browser_result,
335 const std::string& data,
336 const PP_NetAddress_Private& addr,
337 char* output_buffer,
338 int32_t num_bytes,
339 PP_Resource* output_addr) {
340 DCHECK_GE(num_bytes, static_cast<int32_t>(data.size()));
342 int32_t result = browser_result;
343 if (result == PP_OK && output_addr) {
344 thunk::EnterResourceCreationNoLock enter(pp_instance());
345 if (enter.succeeded()) {
346 *output_addr = enter.functions()->CreateNetAddressFromNetAddressPrivate(
347 pp_instance(), addr);
348 } else {
349 result = PP_ERROR_FAILED;
353 if (result == PP_OK && !data.empty())
354 memcpy(output_buffer, data.c_str(), data.size());
356 recvfrom_addr_ = addr;
358 return result == PP_OK ? static_cast<int32_t>(data.size()) : result;
361 } // namespace proxy
362 } // namespace ppapi