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 "ppapi/proxy/ppb_tcp_socket_private_proxy.h" | 5 #include "ppapi/proxy/ppb_tcp_socket_private_proxy.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
yzshen1
2011/11/28 20:59:16
You could remove those includes that are not neede
ygorshenin
2011/11/29 18:30:09
Done.
| |
8 #include <cstring> | 8 #include <cstring> |
9 #include <map> | 9 #include <map> |
10 | 10 |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
yzshen1
2011/11/29 20:19:32
This is still needed.
yzshen1
2011/11/30 21:11:04
This is still needed, because DCHECK is used in th
ygorshenin
2011/12/01 10:58:31
Restored.
| |
12 #include "base/memory/scoped_ptr.h" | 12 #include "base/memory/scoped_ptr.h" |
13 #include "base/message_loop.h" | 13 #include "base/message_loop.h" |
14 #include "base/task.h" | 14 #include "base/task.h" |
15 #include "ppapi/c/pp_errors.h" | 15 #include "ppapi/c/pp_errors.h" |
16 #include "ppapi/proxy/plugin_dispatcher.h" | 16 #include "ppapi/proxy/plugin_dispatcher.h" |
17 #include "ppapi/proxy/plugin_resource_tracker.h" | 17 #include "ppapi/proxy/plugin_resource_tracker.h" |
18 #include "ppapi/proxy/ppapi_messages.h" | 18 #include "ppapi/proxy/ppapi_messages.h" |
19 #include "ppapi/shared_impl/resource.h" | 19 #include "ppapi/shared_impl/resource.h" |
20 #include "ppapi/thunk/ppb_tcp_socket_private_api.h" | 20 #include "ppapi/shared_impl/tcp_socket_impl.h" |
21 #include "ppapi/thunk/thunk.h" | 21 #include "ppapi/thunk/thunk.h" |
22 | 22 |
23 using ppapi::thunk::PPB_TCPSocket_Private_API; | |
24 | |
25 namespace ppapi { | 23 namespace ppapi { |
26 namespace proxy { | 24 namespace proxy { |
27 | 25 |
28 const int32_t kTCPSocketMaxReadSize = 1024 * 1024; | |
29 const int32_t kTCPSocketMaxWriteSize = 1024 * 1024; | |
30 | |
31 class TCPSocket; | |
32 | |
33 namespace { | 26 namespace { |
34 | 27 |
35 typedef std::map<uint32, TCPSocket*> IDToSocketMap; | 28 typedef std::map<uint32, TCPSocketImpl*> IDToSocketMap; |
36 IDToSocketMap* g_id_to_socket = NULL; | 29 IDToSocketMap* g_id_to_socket = NULL; |
37 | 30 |
38 class AbortCallbackTask : public Task { | 31 class AbortCallbackTask : public Task { |
39 public: | 32 public: |
40 explicit AbortCallbackTask(PP_CompletionCallback callback) | 33 explicit AbortCallbackTask(PP_CompletionCallback callback) |
41 : callback_(callback) {} | 34 : callback_(callback) {} |
42 virtual ~AbortCallbackTask() {} | 35 virtual ~AbortCallbackTask() {} |
43 virtual void Run() { | 36 virtual void Run() { |
44 if (callback_.func) | 37 if (callback_.func) |
45 PP_RunCompletionCallback(&callback_, PP_ERROR_ABORTED); | 38 PP_RunCompletionCallback(&callback_, PP_ERROR_ABORTED); |
46 } | 39 } |
47 | 40 |
48 private: | 41 private: |
49 PP_CompletionCallback callback_; | 42 PP_CompletionCallback callback_; |
50 }; | 43 }; |
51 | 44 |
52 } // namespace | 45 class TCPSocket : public TCPSocketImpl { |
53 | |
54 class TCPSocket : public PPB_TCPSocket_Private_API, | |
55 public Resource { | |
56 public: | 46 public: |
57 TCPSocket(const HostResource& resource, uint32 socket_id); | 47 TCPSocket(const HostResource& resource, uint32 socket_id); |
58 virtual ~TCPSocket(); | 48 virtual ~TCPSocket(); |
59 | 49 |
60 // Resource overrides. | 50 virtual void SendConnect(const std::string& host, uint16_t port) OVERRIDE; |
61 virtual PPB_TCPSocket_Private_API* AsPPB_TCPSocket_Private_API() OVERRIDE; | 51 virtual void SendConnectWithNetAddress( |
62 | 52 const PP_NetAddress_Private& addr) OVERRIDE; |
63 // PPB_TCPSocket_Private_API implementation. | 53 virtual void SendSSLHandshake(const std::string& server_name, |
64 virtual int32_t Connect(const char* host, | 54 uint16_t server_port) OVERRIDE; |
65 uint16_t port, | 55 virtual void SendRead(int32_t bytes_to_read) OVERRIDE; |
66 PP_CompletionCallback callback) OVERRIDE; | 56 virtual void SendWrite(const std::string& buffer) OVERRIDE; |
67 virtual int32_t ConnectWithNetAddress( | 57 virtual void SendDisconnect() OVERRIDE; |
68 const PP_NetAddress_Private* addr, | 58 virtual void PostAbort(PP_CompletionCallback callback) OVERRIDE; |
69 PP_CompletionCallback callback) OVERRIDE; | |
70 virtual PP_Bool GetLocalAddress(PP_NetAddress_Private* local_addr) OVERRIDE; | |
71 virtual PP_Bool GetRemoteAddress(PP_NetAddress_Private* remote_addr) OVERRIDE; | |
72 virtual int32_t SSLHandshake(const char* server_name, | |
73 uint16_t server_port, | |
74 PP_CompletionCallback callback) OVERRIDE; | |
75 virtual int32_t Read(char* buffer, | |
76 int32_t bytes_to_read, | |
77 PP_CompletionCallback callback) OVERRIDE; | |
78 virtual int32_t Write(const char* buffer, | |
79 int32_t bytes_to_write, | |
80 PP_CompletionCallback callback) OVERRIDE; | |
81 virtual void Disconnect() OVERRIDE; | |
82 | |
83 // Notifications from the proxy. | |
84 void OnConnectCompleted(bool succeeded, | |
85 const PP_NetAddress_Private& local_addr, | |
86 const PP_NetAddress_Private& remote_addr); | |
87 void OnSSLHandshakeCompleted(bool succeeded); | |
88 void OnReadCompleted(bool succeeded, const std::string& data); | |
89 void OnWriteCompleted(bool succeeded, int32_t bytes_written); | |
90 | 59 |
91 private: | 60 private: |
92 enum ConnectionState { | 61 void SendToBrowser(IPC::Message* msg); |
93 // Before a connection is successfully established (including a connect | |
94 // request is pending or a previous connect request failed). | |
95 BEFORE_CONNECT, | |
96 // A connection has been successfully established (including a request of | |
97 // initiating SSL is pending). | |
98 CONNECTED, | |
99 // An SSL connection has been successfully established. | |
100 SSL_CONNECTED, | |
101 // The connection has been ended. | |
102 DISCONNECTED | |
103 }; | |
104 | |
105 bool IsConnected() const; | |
106 | |
107 PluginDispatcher* GetDispatcher() const { | |
108 return PluginDispatcher::GetForResource(this); | |
109 } | |
110 | |
111 // Backend for both Connect() and ConnectWithNetAddress(). To keep things | |
112 // generic, the message is passed in (on error, it's deleted). | |
113 int32_t ConnectWithMessage(IPC::Message* msg, | |
114 PP_CompletionCallback callback); | |
115 | |
116 void PostAbortAndClearIfNecessary(PP_CompletionCallback* callback); | |
117 | |
118 uint32 socket_id_; | |
119 ConnectionState connection_state_; | |
120 | |
121 PP_CompletionCallback connect_callback_; | |
122 PP_CompletionCallback ssl_handshake_callback_; | |
123 PP_CompletionCallback read_callback_; | |
124 PP_CompletionCallback write_callback_; | |
125 | |
126 char* read_buffer_; | |
127 int32_t bytes_to_read_; | |
128 | |
129 PP_NetAddress_Private local_addr_; | |
130 PP_NetAddress_Private remote_addr_; | |
131 | 62 |
132 DISALLOW_COPY_AND_ASSIGN(TCPSocket); | 63 DISALLOW_COPY_AND_ASSIGN(TCPSocket); |
133 }; | 64 }; |
134 | 65 |
135 TCPSocket::TCPSocket(const HostResource& resource, uint32 socket_id) | 66 TCPSocket::TCPSocket(const HostResource& resource, uint32 socket_id) |
136 : Resource(resource), | 67 : TCPSocketImpl(resource, socket_id) { |
137 socket_id_(socket_id), | |
138 connection_state_(BEFORE_CONNECT), | |
139 connect_callback_(PP_BlockUntilComplete()), | |
140 ssl_handshake_callback_(PP_BlockUntilComplete()), | |
141 read_callback_(PP_BlockUntilComplete()), | |
142 write_callback_(PP_BlockUntilComplete()), | |
143 read_buffer_(NULL), | |
144 bytes_to_read_(-1) { | |
145 DCHECK(socket_id != 0); | |
146 | |
147 local_addr_.size = 0; | |
148 memset(local_addr_.data, 0, sizeof(local_addr_.data)); | |
149 remote_addr_.size = 0; | |
150 memset(remote_addr_.data, 0, sizeof(remote_addr_.data)); | |
151 | |
152 if (!g_id_to_socket) | 68 if (!g_id_to_socket) |
153 g_id_to_socket = new IDToSocketMap(); | 69 g_id_to_socket = new IDToSocketMap(); |
154 DCHECK(g_id_to_socket->find(socket_id) == g_id_to_socket->end()); | 70 DCHECK(g_id_to_socket->find(socket_id) == g_id_to_socket->end()); |
155 (*g_id_to_socket)[socket_id] = this; | 71 (*g_id_to_socket)[socket_id] = this; |
156 } | 72 } |
157 | 73 |
158 TCPSocket::~TCPSocket() { | 74 TCPSocket::~TCPSocket() { |
159 Disconnect(); | 75 Disconnect(); |
76 | |
dmichael (off chromium)
2011/11/28 20:38:43
nit: extra carriage return added
yzshen1
2011/11/28 20:59:16
1) Please remove the empty line.
ygorshenin
2011/11/29 18:30:09
Done.
ygorshenin
2011/11/29 18:30:09
Done.
| |
160 } | 77 } |
161 | 78 |
162 PPB_TCPSocket_Private_API* TCPSocket::AsPPB_TCPSocket_Private_API() { | 79 void TCPSocket::SendToBrowser(IPC::Message* msg) { |
yzshen1
2011/11/28 20:59:16
Please keep the definitions in the same order as t
ygorshenin
2011/11/29 18:30:09
Done.
| |
163 return this; | 80 PluginDispatcher::GetForResource(this)->SendToBrowser(msg); |
164 } | 81 } |
165 | 82 |
166 int32_t TCPSocket::Connect(const char* host, | 83 void TCPSocket::SendConnect(const std::string& host, uint16_t port) { |
167 uint16_t port, | 84 SendToBrowser(new PpapiHostMsg_PPBTCPSocket_Connect(socket_id_, host, port)); |
168 PP_CompletionCallback callback) { | |
169 if (!host) | |
170 return PP_ERROR_BADARGUMENT; | |
171 | |
172 return ConnectWithMessage( | |
173 new PpapiHostMsg_PPBTCPSocket_Connect(socket_id_, host, port), | |
174 callback); | |
175 } | 85 } |
176 | 86 |
177 int32_t TCPSocket::ConnectWithNetAddress( | 87 void TCPSocket::SendConnectWithNetAddress(const PP_NetAddress_Private& addr) { |
178 const PP_NetAddress_Private* addr, | 88 SendToBrowser( |
179 PP_CompletionCallback callback) { | 89 new PpapiHostMsg_PPBTCPSocket_ConnectWithNetAddress(socket_id_, addr)); |
180 if (!addr) | |
181 return PP_ERROR_BADARGUMENT; | |
182 | |
183 return ConnectWithMessage( | |
184 new PpapiHostMsg_PPBTCPSocket_ConnectWithNetAddress( | |
185 socket_id_, *addr), | |
186 callback); | |
187 } | 90 } |
188 | 91 |
189 PP_Bool TCPSocket::GetLocalAddress(PP_NetAddress_Private* local_addr) { | 92 void TCPSocket::SendSSLHandshake(const std::string& server_name, |
190 if (!IsConnected() || !local_addr) | 93 uint16_t server_port) { |
yzshen1
2011/11/28 20:59:16
Wrong indent.
ygorshenin
2011/11/29 18:30:09
Done.
| |
191 return PP_FALSE; | 94 SendToBrowser(new PpapiHostMsg_PPBTCPSocket_SSLHandshake( |
192 | 95 socket_id_, server_name, server_port)); |
193 *local_addr = local_addr_; | |
194 return PP_TRUE; | |
195 } | 96 } |
196 | 97 |
197 PP_Bool TCPSocket::GetRemoteAddress(PP_NetAddress_Private* remote_addr) { | 98 void TCPSocket::SendRead(int32_t bytes_to_read) { |
198 if (!IsConnected() || !remote_addr) | 99 SendToBrowser(new PpapiHostMsg_PPBTCPSocket_Read(socket_id_, bytes_to_read)); |
199 return PP_FALSE; | |
200 | |
201 *remote_addr = remote_addr_; | |
202 return PP_TRUE; | |
203 } | 100 } |
204 | 101 |
205 int32_t TCPSocket::SSLHandshake(const char* server_name, | 102 void TCPSocket::SendWrite(const std::string& buffer) { |
206 uint16_t server_port, | 103 SendToBrowser(new PpapiHostMsg_PPBTCPSocket_Write(socket_id_, buffer)); |
207 PP_CompletionCallback callback) { | |
208 if (!server_name) | |
209 return PP_ERROR_BADARGUMENT; | |
210 if (!callback.func) | |
211 return PP_ERROR_BLOCKS_MAIN_THREAD; | |
212 | |
213 if (connection_state_ != CONNECTED) | |
214 return PP_ERROR_FAILED; | |
215 if (ssl_handshake_callback_.func || read_callback_.func || | |
216 write_callback_.func) | |
217 return PP_ERROR_INPROGRESS; | |
218 | |
219 ssl_handshake_callback_ = callback; | |
220 | |
221 // Send the request, the browser will call us back via SSLHandshakeACK. | |
222 GetDispatcher()->SendToBrowser( | |
223 new PpapiHostMsg_PPBTCPSocket_SSLHandshake( | |
224 socket_id_, std::string(server_name), server_port)); | |
225 return PP_OK_COMPLETIONPENDING; | |
226 } | 104 } |
227 | 105 |
228 int32_t TCPSocket::Read(char* buffer, | 106 void TCPSocket::SendDisconnect() { |
229 int32_t bytes_to_read, | |
230 PP_CompletionCallback callback) { | |
231 if (!buffer || bytes_to_read <= 0) | |
232 return PP_ERROR_BADARGUMENT; | |
233 if (!callback.func) | |
234 return PP_ERROR_BLOCKS_MAIN_THREAD; | |
235 | |
236 if (!IsConnected()) | |
237 return PP_ERROR_FAILED; | |
238 if (read_callback_.func || ssl_handshake_callback_.func) | |
239 return PP_ERROR_INPROGRESS; | |
240 | |
241 read_buffer_ = buffer; | |
242 bytes_to_read_ = std::min(bytes_to_read, kTCPSocketMaxReadSize); | |
243 read_callback_ = callback; | |
244 | |
245 // Send the request, the browser will call us back via ReadACK. | |
246 GetDispatcher()->SendToBrowser( | |
247 new PpapiHostMsg_PPBTCPSocket_Read(socket_id_, bytes_to_read_)); | |
248 return PP_OK_COMPLETIONPENDING; | |
249 } | |
250 | |
251 int32_t TCPSocket::Write(const char* buffer, | |
252 int32_t bytes_to_write, | |
253 PP_CompletionCallback callback) { | |
254 if (!buffer || bytes_to_write <= 0) | |
255 return PP_ERROR_BADARGUMENT; | |
256 if (!callback.func) | |
257 return PP_ERROR_BLOCKS_MAIN_THREAD; | |
258 | |
259 if (!IsConnected()) | |
260 return PP_ERROR_FAILED; | |
261 if (write_callback_.func || ssl_handshake_callback_.func) | |
262 return PP_ERROR_INPROGRESS; | |
263 | |
264 if (bytes_to_write > kTCPSocketMaxWriteSize) | |
265 bytes_to_write = kTCPSocketMaxWriteSize; | |
266 | |
267 write_callback_ = callback; | |
268 | |
269 // Send the request, the browser will call us back via WriteACK. | |
270 GetDispatcher()->SendToBrowser( | |
271 new PpapiHostMsg_PPBTCPSocket_Write( | |
272 socket_id_, std::string(buffer, bytes_to_write))); | |
273 return PP_OK_COMPLETIONPENDING; | |
274 } | |
275 | |
276 void TCPSocket::Disconnect() { | |
277 if (connection_state_ == DISCONNECTED) | |
278 return; | |
279 | |
280 connection_state_ = DISCONNECTED; | |
281 // After removed from the mapping, this object won't receive any notifications | 107 // After removed from the mapping, this object won't receive any notifications |
282 // from the proxy. | 108 // from the proxy. |
283 DCHECK(g_id_to_socket->find(socket_id_) != g_id_to_socket->end()); | 109 DCHECK(g_id_to_socket->find(socket_id_) != g_id_to_socket->end()); |
284 g_id_to_socket->erase(socket_id_); | 110 g_id_to_socket->erase(socket_id_); |
285 | 111 SendToBrowser(new PpapiHostMsg_PPBTCPSocket_Disconnect(socket_id_)); |
286 GetDispatcher()->SendToBrowser( | |
287 new PpapiHostMsg_PPBTCPSocket_Disconnect(socket_id_)); | |
288 socket_id_ = 0; | |
289 | |
290 PostAbortAndClearIfNecessary(&connect_callback_); | |
291 PostAbortAndClearIfNecessary(&ssl_handshake_callback_); | |
292 PostAbortAndClearIfNecessary(&read_callback_); | |
293 PostAbortAndClearIfNecessary(&write_callback_); | |
294 read_buffer_ = NULL; | |
295 bytes_to_read_ = -1; | |
296 } | 112 } |
297 | 113 |
298 void TCPSocket::OnConnectCompleted( | 114 void TCPSocket::PostAbort(PP_CompletionCallback callback) { |
299 bool succeeded, | 115 MessageLoop::current()->PostTask(FROM_HERE, new AbortCallbackTask(callback)); |
dmichael (off chromium)
2011/11/28 20:38:43
It looks like you could pretty easily replace Abor
ygorshenin
2011/11/29 18:30:09
Added TODO comment.
| |
300 const PP_NetAddress_Private& local_addr, | |
301 const PP_NetAddress_Private& remote_addr) { | |
302 if (connection_state_ != BEFORE_CONNECT || !connect_callback_.func) { | |
303 NOTREACHED(); | |
304 return; | |
305 } | |
306 | |
307 if (succeeded) { | |
308 local_addr_ = local_addr; | |
309 remote_addr_ = remote_addr; | |
310 connection_state_ = CONNECTED; | |
311 } | |
312 PP_RunAndClearCompletionCallback(&connect_callback_, | |
313 succeeded ? PP_OK : PP_ERROR_FAILED); | |
314 } | 116 } |
315 | 117 |
316 void TCPSocket::OnSSLHandshakeCompleted(bool succeeded) { | 118 } // namespace |
317 if (connection_state_ != CONNECTED || !ssl_handshake_callback_.func) { | |
318 NOTREACHED(); | |
319 return; | |
320 } | |
321 | 119 |
322 if (succeeded) { | 120 //------------------------------------------------------------------------------ |
323 connection_state_ = SSL_CONNECTED; | |
324 PP_RunAndClearCompletionCallback(&ssl_handshake_callback_, PP_OK); | |
325 } else { | |
326 PP_RunAndClearCompletionCallback(&ssl_handshake_callback_, PP_ERROR_FAILED); | |
327 Disconnect(); | |
328 } | |
329 } | |
330 | |
331 void TCPSocket::OnReadCompleted(bool succeeded, const std::string& data) { | |
332 if (!read_callback_.func || !read_buffer_) { | |
333 NOTREACHED(); | |
334 return; | |
335 } | |
336 | |
337 if (succeeded) { | |
338 CHECK_LE(static_cast<int32_t>(data.size()), bytes_to_read_); | |
339 if (!data.empty()) | |
340 memcpy(read_buffer_, data.c_str(), data.size()); | |
341 } | |
342 read_buffer_ = NULL; | |
343 bytes_to_read_ = -1; | |
344 | |
345 PP_RunAndClearCompletionCallback( | |
346 &read_callback_, | |
347 succeeded ? static_cast<int32_t>(data.size()) : | |
348 static_cast<int32_t>(PP_ERROR_FAILED)); | |
349 } | |
350 | |
351 void TCPSocket::OnWriteCompleted(bool succeeded, int32_t bytes_written) { | |
352 if (!write_callback_.func || (succeeded && bytes_written < 0)) { | |
353 NOTREACHED(); | |
354 return; | |
355 } | |
356 | |
357 PP_RunAndClearCompletionCallback( | |
358 &write_callback_, | |
359 succeeded ? bytes_written : static_cast<int32_t>(PP_ERROR_FAILED)); | |
360 } | |
361 | |
362 bool TCPSocket::IsConnected() const { | |
363 return connection_state_ == CONNECTED || connection_state_ == SSL_CONNECTED; | |
364 } | |
365 | |
366 int32_t TCPSocket::ConnectWithMessage(IPC::Message* msg, | |
367 PP_CompletionCallback callback) { | |
368 scoped_ptr<IPC::Message> msg_deletor(msg); | |
369 if (!callback.func) | |
370 return PP_ERROR_BLOCKS_MAIN_THREAD; | |
371 if (connection_state_ != BEFORE_CONNECT) | |
372 return PP_ERROR_FAILED; | |
373 if (connect_callback_.func) | |
374 return PP_ERROR_INPROGRESS; // Can only have one pending request. | |
375 | |
376 connect_callback_ = callback; | |
377 // Send the request, the browser will call us back via ConnectACK. | |
378 GetDispatcher()->SendToBrowser(msg_deletor.release()); | |
379 return PP_OK_COMPLETIONPENDING; | |
380 } | |
381 | |
382 void TCPSocket::PostAbortAndClearIfNecessary( | |
383 PP_CompletionCallback* callback) { | |
384 DCHECK(callback); | |
385 | |
386 if (callback->func) { | |
387 MessageLoop::current()->PostTask(FROM_HERE, | |
388 new AbortCallbackTask(*callback)); | |
389 *callback = PP_BlockUntilComplete(); | |
390 } | |
391 } | |
392 | 121 |
393 PPB_TCPSocket_Private_Proxy::PPB_TCPSocket_Private_Proxy(Dispatcher* dispatcher) | 122 PPB_TCPSocket_Private_Proxy::PPB_TCPSocket_Private_Proxy(Dispatcher* dispatcher) |
394 : InterfaceProxy(dispatcher) { | 123 : InterfaceProxy(dispatcher) { |
395 } | 124 } |
396 | 125 |
397 PPB_TCPSocket_Private_Proxy::~PPB_TCPSocket_Private_Proxy() { | 126 PPB_TCPSocket_Private_Proxy::~PPB_TCPSocket_Private_Proxy() { |
398 } | 127 } |
399 | 128 |
400 // static | 129 // static |
401 PP_Resource PPB_TCPSocket_Private_Proxy::CreateProxyResource( | 130 PP_Resource PPB_TCPSocket_Private_Proxy::CreateProxyResource( |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
483 return; | 212 return; |
484 } | 213 } |
485 IDToSocketMap::iterator iter = g_id_to_socket->find(socket_id); | 214 IDToSocketMap::iterator iter = g_id_to_socket->find(socket_id); |
486 if (iter == g_id_to_socket->end()) | 215 if (iter == g_id_to_socket->end()) |
487 return; | 216 return; |
488 iter->second->OnWriteCompleted(succeeded, bytes_written); | 217 iter->second->OnWriteCompleted(succeeded, bytes_written); |
489 } | 218 } |
490 | 219 |
491 } // namespace proxy | 220 } // namespace proxy |
492 } // namespace ppapi | 221 } // namespace ppapi |
OLD | NEW |