blob: 06db5afb6345bb4b78737cb830d21a1d507d32de [file] [log] [blame]
[email protected]aaa11b32012-03-08 12:30:131// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]eeb4e4a2011-07-19 16:22:062// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ppapi/proxy/ppb_graphics_3d_proxy.h"
6
brettw669d47b12015-02-13 21:17:387#include "base/numerics/safe_conversions.h"
avie029c4132015-12-23 06:45:228#include "build/build_config.h"
[email protected]eeb4e4a2011-07-19 16:22:069#include "gpu/command_buffer/client/gles2_implementation.h"
[email protected]84b44c552012-10-15 20:58:1710#include "gpu/command_buffer/common/command_buffer.h"
[email protected]eeb4e4a2011-07-19 16:22:0611#include "ppapi/c/pp_errors.h"
12#include "ppapi/proxy/enter_proxy.h"
13#include "ppapi/proxy/plugin_dispatcher.h"
[email protected]aaa11b32012-03-08 12:30:1314#include "ppapi/proxy/ppapi_command_buffer_proxy.h"
[email protected]eeb4e4a2011-07-19 16:22:0615#include "ppapi/proxy/ppapi_messages.h"
[email protected]84b44c552012-10-15 20:58:1716#include "ppapi/shared_impl/ppapi_globals.h"
[email protected]eeb4e4a2011-07-19 16:22:0617#include "ppapi/thunk/enter.h"
18#include "ppapi/thunk/resource_creation_api.h"
19#include "ppapi/thunk/thunk.h"
20
[email protected]eeb4e4a2011-07-19 16:22:0621using ppapi::thunk::EnterResourceNoLock;
22using ppapi::thunk::PPB_Graphics3D_API;
23using ppapi::thunk::ResourceCreationAPI;
24
[email protected]4d2efd22011-08-18 21:58:0225namespace ppapi {
[email protected]eeb4e4a2011-07-19 16:22:0626namespace proxy {
27
28namespace {
[email protected]84b44c552012-10-15 20:58:1729
avie029c4132015-12-23 06:45:2230const int32_t kCommandBufferSize = 1024 * 1024;
31const int32_t kTransferBufferSize = 1024 * 1024;
[email protected]eeb4e4a2011-07-19 16:22:0632
mazda9cfbcfb2014-11-12 17:07:1133#if !defined(OS_NACL)
penghuanga206fb492014-09-09 21:27:3234base::SharedMemoryHandle TransportSHMHandle(
35 Dispatcher* dispatcher,
36 const base::SharedMemoryHandle& handle) {
[email protected]eeb4e4a2011-07-19 16:22:0637 // Don't close the handle, it doesn't belong to us.
erikchen4fc32d52015-06-02 02:16:3838 return dispatcher->ShareSharedMemoryHandleWithRemote(handle);
[email protected]eeb4e4a2011-07-19 16:22:0639}
mazda9cfbcfb2014-11-12 17:07:1140#endif // !defined(OS_NACL)
[email protected]eeb4e4a2011-07-19 16:22:0641
[email protected]914f5262013-06-01 00:17:2242gpu::CommandBuffer::State GetErrorState() {
43 gpu::CommandBuffer::State error_state;
44 error_state.error = gpu::error::kGenericError;
[email protected]eeb4e4a2011-07-19 16:22:0645 return error_state;
46}
47
[email protected]eeb4e4a2011-07-19 16:22:0648} // namespace
49
50Graphics3D::Graphics3D(const HostResource& resource)
[email protected]4e46a732013-09-30 18:35:5951 : PPB_Graphics3D_Shared(resource) {
[email protected]eeb4e4a2011-07-19 16:22:0652}
53
54Graphics3D::~Graphics3D() {
[email protected]edccb562013-10-02 00:01:2055 DestroyGLES2Impl();
[email protected]eeb4e4a2011-07-19 16:22:0656}
57
penghuanga206fb492014-09-09 21:27:3258bool Graphics3D::Init(gpu::gles2::GLES2Implementation* share_gles2,
piman360175c2014-11-07 02:30:0159 const gpu::Capabilities& capabilities,
dyen12e45962015-09-18 00:13:5160 const SerializedHandle& shared_state,
61 uint64_t command_buffer_id) {
[email protected]7f8b26b2011-08-18 15:41:0162 PluginDispatcher* dispatcher = PluginDispatcher::GetForResource(this);
[email protected]eeb4e4a2011-07-19 16:22:0663 if (!dispatcher)
64 return false;
65
piman360175c2014-11-07 02:30:0166 command_buffer_.reset(new PpapiCommandBufferProxy(
dyen12e45962015-09-18 00:13:5167 host_resource(), dispatcher, capabilities, shared_state,
68 command_buffer_id));
[email protected]84b44c552012-10-15 20:58:1769
[email protected]9ed07f82012-05-29 21:54:5570 return CreateGLES2Impl(kCommandBufferSize, kTransferBufferSize,
71 share_gles2);
[email protected]eeb4e4a2011-07-19 16:22:0672}
73
[email protected]503b3a22011-12-12 23:29:4074PP_Bool Graphics3D::SetGetBuffer(int32_t /* transfer_buffer_id */) {
[email protected]eeb4e4a2011-07-19 16:22:0675 return PP_FALSE;
76}
77
[email protected]eeb4e4a2011-07-19 16:22:0678PP_Bool Graphics3D::Flush(int32_t put_offset) {
79 return PP_FALSE;
80}
81
[email protected]046fa1ac2014-04-01 09:06:4382scoped_refptr<gpu::Buffer> Graphics3D::CreateTransferBuffer(
83 uint32_t size,
84 int32_t* id) {
85 *id = -1;
86 return NULL;
[email protected]eeb4e4a2011-07-19 16:22:0687}
88
89PP_Bool Graphics3D::DestroyTransferBuffer(int32_t id) {
90 return PP_FALSE;
91}
92
[email protected]7fe4198b2014-03-18 21:52:3693gpu::CommandBuffer::State Graphics3D::WaitForTokenInRange(int32_t start,
94 int32_t end) {
95 return GetErrorState();
96}
97
98gpu::CommandBuffer::State Graphics3D::WaitForGetOffsetInRange(int32_t start,
99 int32_t end) {
[email protected]eeb4e4a2011-07-19 16:22:06100 return GetErrorState();
101}
102
[email protected]b096d032013-03-08 03:08:01103uint32_t Graphics3D::InsertSyncPoint() {
104 NOTREACHED();
105 return 0;
106}
107
[email protected]7035bc92014-07-01 00:27:22108uint32_t Graphics3D::InsertFutureSyncPoint() {
109 NOTREACHED();
110 return 0;
111}
112
113void Graphics3D::RetireSyncPoint(uint32_t sync_point) {
114 NOTREACHED();
115}
116
[email protected]eeb4e4a2011-07-19 16:22:06117gpu::CommandBuffer* Graphics3D::GetCommandBuffer() {
[email protected]4e46a732013-09-30 18:35:59118 return command_buffer_.get();
[email protected]eeb4e4a2011-07-19 16:22:06119}
120
[email protected]744e0792013-09-27 01:18:35121gpu::GpuControl* Graphics3D::GetGpuControl() {
122 return command_buffer_.get();
123}
124
avie029c4132015-12-23 06:45:22125int32_t Graphics3D::DoSwapBuffers() {
[email protected]561438abd2011-11-24 01:06:23126 gles2_impl()->SwapBuffers();
[email protected]eeb4e4a2011-07-19 16:22:06127 IPC::Message* msg = new PpapiHostMsg_PPBGraphics3D_SwapBuffers(
[email protected]ac4b54d2011-10-20 23:09:28128 API_ID_PPB_GRAPHICS_3D, host_resource());
[email protected]eeb4e4a2011-07-19 16:22:06129 msg->set_unblock(true);
[email protected]7f8b26b2011-08-18 15:41:01130 PluginDispatcher::GetForResource(this)->Send(msg);
[email protected]eeb4e4a2011-07-19 16:22:06131
[email protected]eeb4e4a2011-07-19 16:22:06132 return PP_OK_COMPLETIONPENDING;
133}
134
[email protected]5c966022011-09-13 18:09:37135PPB_Graphics3D_Proxy::PPB_Graphics3D_Proxy(Dispatcher* dispatcher)
136 : InterfaceProxy(dispatcher),
[email protected]a2f53dc2013-04-30 01:06:35137 callback_factory_(this) {
[email protected]eeb4e4a2011-07-19 16:22:06138}
139
140PPB_Graphics3D_Proxy::~PPB_Graphics3D_Proxy() {
141}
142
143// static
[email protected]eeb4e4a2011-07-19 16:22:06144PP_Resource PPB_Graphics3D_Proxy::CreateProxyResource(
145 PP_Instance instance,
[email protected]eeb4e4a2011-07-19 16:22:06146 PP_Resource share_context,
147 const int32_t* attrib_list) {
148 PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
149 if (!dispatcher)
150 return PP_ERROR_BADARGUMENT;
151
[email protected]9ed07f82012-05-29 21:54:55152 HostResource share_host;
153 gpu::gles2::GLES2Implementation* share_gles2 = NULL;
154 if (share_context != 0) {
155 EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true);
156 if (enter.failed())
157 return PP_ERROR_BADARGUMENT;
158
159 PPB_Graphics3D_Shared* share_graphics =
160 static_cast<PPB_Graphics3D_Shared*>(enter.object());
161 share_host = share_graphics->host_resource();
162 share_gles2 = share_graphics->gles2_impl();
163 }
[email protected]eeb4e4a2011-07-19 16:22:06164
165 std::vector<int32_t> attribs;
166 if (attrib_list) {
167 for (const int32_t* attr = attrib_list;
[email protected]0e50fc4d2011-08-01 15:33:51168 attr[0] != PP_GRAPHICS3DATTRIB_NONE;
169 attr += 2) {
170 attribs.push_back(attr[0]);
171 attribs.push_back(attr[1]);
[email protected]eeb4e4a2011-07-19 16:22:06172 }
[email protected]eeb4e4a2011-07-19 16:22:06173 }
[email protected]0e50fc4d2011-08-01 15:33:51174 attribs.push_back(PP_GRAPHICS3DATTRIB_NONE);
[email protected]eeb4e4a2011-07-19 16:22:06175
176 HostResource result;
piman360175c2014-11-07 02:30:01177 gpu::Capabilities capabilities;
penghuanga206fb492014-09-09 21:27:32178 ppapi::proxy::SerializedHandle shared_state;
dyen12e45962015-09-18 00:13:51179 uint64_t command_buffer_id = 0;
penghuanga206fb492014-09-09 21:27:32180 dispatcher->Send(new PpapiHostMsg_PPBGraphics3D_Create(API_ID_PPB_GRAPHICS_3D,
dyen12e45962015-09-18 00:13:51181 instance, share_host, attribs, &result, &capabilities, &shared_state,
182 &command_buffer_id));
penghuanga206fb492014-09-09 21:27:32183
[email protected]eeb4e4a2011-07-19 16:22:06184 if (result.is_null())
185 return 0;
186
[email protected]51e1aec2011-08-11 04:48:30187 scoped_refptr<Graphics3D> graphics_3d(new Graphics3D(result));
dyen12e45962015-09-18 00:13:51188 if (!graphics_3d->Init(share_gles2, capabilities, shared_state,
189 command_buffer_id)) {
[email protected]eeb4e4a2011-07-19 16:22:06190 return 0;
dyen12e45962015-09-18 00:13:51191 }
[email protected]7f8b26b2011-08-18 15:41:01192 return graphics_3d->GetReference();
[email protected]eeb4e4a2011-07-19 16:22:06193}
194
195bool PPB_Graphics3D_Proxy::OnMessageReceived(const IPC::Message& msg) {
196 bool handled = true;
197 IPC_BEGIN_MESSAGE_MAP(PPB_Graphics3D_Proxy, msg)
[email protected]11494c5c2012-11-13 02:50:57198#if !defined(OS_NACL)
[email protected]eeb4e4a2011-07-19 16:22:06199 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_Create,
200 OnMsgCreate)
[email protected]503b3a22011-12-12 23:29:40201 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_SetGetBuffer,
202 OnMsgSetGetBuffer)
[email protected]7fe4198b2014-03-18 21:52:36203 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_WaitForTokenInRange,
204 OnMsgWaitForTokenInRange)
205 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_WaitForGetOffsetInRange,
206 OnMsgWaitForGetOffsetInRange)
207 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_AsyncFlush, OnMsgAsyncFlush)
[email protected]eeb4e4a2011-07-19 16:22:06208 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_CreateTransferBuffer,
209 OnMsgCreateTransferBuffer)
210 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_DestroyTransferBuffer,
211 OnMsgDestroyTransferBuffer)
[email protected]eeb4e4a2011-07-19 16:22:06212 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_SwapBuffers,
213 OnMsgSwapBuffers)
[email protected]b096d032013-03-08 03:08:01214 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_InsertSyncPoint,
215 OnMsgInsertSyncPoint)
[email protected]7035bc92014-07-01 00:27:22216 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_InsertFutureSyncPoint,
217 OnMsgInsertFutureSyncPoint)
218 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_RetireSyncPoint,
219 OnMsgRetireSyncPoint)
[email protected]11494c5c2012-11-13 02:50:57220#endif // !defined(OS_NACL)
[email protected]eeb4e4a2011-07-19 16:22:06221
222 IPC_MESSAGE_HANDLER(PpapiMsg_PPBGraphics3D_SwapBuffersACK,
223 OnMsgSwapBuffersACK)
224 IPC_MESSAGE_UNHANDLED(handled = false)
225
226 IPC_END_MESSAGE_MAP()
227 // FIXME(brettw) handle bad messages!
228 return handled;
229}
230
[email protected]11494c5c2012-11-13 02:50:57231#if !defined(OS_NACL)
avie029c4132015-12-23 06:45:22232void PPB_Graphics3D_Proxy::OnMsgCreate(PP_Instance instance,
233 HostResource share_context,
234 const std::vector<int32_t>& attribs,
235 HostResource* result,
236 gpu::Capabilities* capabilities,
237 SerializedHandle* shared_state,
238 uint64_t* command_buffer_id) {
penghuanga206fb492014-09-09 21:27:32239 shared_state->set_null_shmem();
[email protected]48c1db72012-12-17 20:56:11240 if (attribs.empty() ||
241 attribs.back() != PP_GRAPHICS3DATTRIB_NONE ||
242 !(attribs.size() & 1))
[email protected]eeb4e4a2011-07-19 16:22:06243 return; // Bad message.
244
[email protected]5c966022011-09-13 18:09:37245 thunk::EnterResourceCreation enter(instance);
[email protected]9ed07f82012-05-29 21:54:55246
penghuanga206fb492014-09-09 21:27:32247 if (!enter.succeeded())
248 return;
249
erikchen4fc32d52015-06-02 02:16:38250 base::SharedMemoryHandle handle = base::SharedMemory::NULLHandle();
penghuanga206fb492014-09-09 21:27:32251 result->SetHostResource(
[email protected]9ed07f82012-05-29 21:54:55252 instance,
253 enter.functions()->CreateGraphics3DRaw(instance,
254 share_context.host_resource(),
penghuanga206fb492014-09-09 21:27:32255 &attribs.front(),
piman360175c2014-11-07 02:30:01256 capabilities,
dyen12e45962015-09-18 00:13:51257 &handle,
258 command_buffer_id));
penghuanga206fb492014-09-09 21:27:32259 if (!result->is_null()) {
260 shared_state->set_shmem(TransportSHMHandle(dispatcher(), handle),
261 sizeof(gpu::CommandBuffer::State));
[email protected]eeb4e4a2011-07-19 16:22:06262 }
263}
264
avie029c4132015-12-23 06:45:22265void PPB_Graphics3D_Proxy::OnMsgSetGetBuffer(const HostResource& context,
266 int32_t transfer_buffer_id) {
[email protected]503b3a22011-12-12 23:29:40267 EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
268 if (enter.succeeded())
269 enter.object()->SetGetBuffer(transfer_buffer_id);
[email protected]eeb4e4a2011-07-19 16:22:06270}
271
[email protected]7fe4198b2014-03-18 21:52:36272void PPB_Graphics3D_Proxy::OnMsgWaitForTokenInRange(
273 const HostResource& context,
avie029c4132015-12-23 06:45:22274 int32_t start,
275 int32_t end,
[email protected]7fe4198b2014-03-18 21:52:36276 gpu::CommandBuffer::State* state,
277 bool* success) {
[email protected]eeb4e4a2011-07-19 16:22:06278 EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
[email protected]571b35e82012-05-19 00:04:35279 if (enter.failed()) {
280 *success = false;
[email protected]eeb4e4a2011-07-19 16:22:06281 return;
[email protected]571b35e82012-05-19 00:04:35282 }
[email protected]7fe4198b2014-03-18 21:52:36283 *state = enter.object()->WaitForTokenInRange(start, end);
284 *success = true;
285}
286
287void PPB_Graphics3D_Proxy::OnMsgWaitForGetOffsetInRange(
288 const HostResource& context,
avie029c4132015-12-23 06:45:22289 int32_t start,
290 int32_t end,
[email protected]7fe4198b2014-03-18 21:52:36291 gpu::CommandBuffer::State* state,
292 bool* success) {
293 EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
294 if (enter.failed()) {
295 *success = false;
296 return;
297 }
298 *state = enter.object()->WaitForGetOffsetInRange(start, end);
[email protected]571b35e82012-05-19 00:04:35299 *success = true;
[email protected]eeb4e4a2011-07-19 16:22:06300}
301
302void PPB_Graphics3D_Proxy::OnMsgAsyncFlush(const HostResource& context,
avie029c4132015-12-23 06:45:22303 int32_t put_offset) {
[email protected]eeb4e4a2011-07-19 16:22:06304 EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
305 if (enter.succeeded())
306 enter.object()->Flush(put_offset);
307}
308
309void PPB_Graphics3D_Proxy::OnMsgCreateTransferBuffer(
310 const HostResource& context,
avie029c4132015-12-23 06:45:22311 uint32_t size,
312 int32_t* id,
penghuanga206fb492014-09-09 21:27:32313 SerializedHandle* transfer_buffer) {
[email protected]046fa1ac2014-04-01 09:06:43314 transfer_buffer->set_null_shmem();
[email protected]eeb4e4a2011-07-19 16:22:06315 EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
[email protected]046fa1ac2014-04-01 09:06:43316 if (enter.succeeded()) {
317 scoped_refptr<gpu::Buffer> buffer =
318 enter.object()->CreateTransferBuffer(size, id);
Daniel Cheng6d3ae972014-08-26 00:27:38319 if (!buffer.get())
[email protected]046fa1ac2014-04-01 09:06:43320 return;
[email protected]8a978df2014-04-02 23:54:09321 gpu::SharedMemoryBufferBacking* backing =
322 static_cast<gpu::SharedMemoryBufferBacking*>(buffer->backing());
323 DCHECK(backing && backing->shared_memory());
[email protected]046fa1ac2014-04-01 09:06:43324 transfer_buffer->set_shmem(
penghuanga206fb492014-09-09 21:27:32325 TransportSHMHandle(dispatcher(), backing->shared_memory()->handle()),
brettw669d47b12015-02-13 21:17:38326 base::checked_cast<uint32_t>(buffer->size()));
[email protected]046fa1ac2014-04-01 09:06:43327 } else {
[email protected]67c80782012-12-21 01:16:52328 *id = -1;
[email protected]046fa1ac2014-04-01 09:06:43329 }
[email protected]eeb4e4a2011-07-19 16:22:06330}
331
332void PPB_Graphics3D_Proxy::OnMsgDestroyTransferBuffer(
333 const HostResource& context,
avie029c4132015-12-23 06:45:22334 int32_t id) {
[email protected]eeb4e4a2011-07-19 16:22:06335 EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
336 if (enter.succeeded())
337 enter.object()->DestroyTransferBuffer(id);
338}
339
[email protected]eeb4e4a2011-07-19 16:22:06340void PPB_Graphics3D_Proxy::OnMsgSwapBuffers(const HostResource& context) {
[email protected]79d23c72011-08-08 19:40:40341 EnterHostFromHostResourceForceCallback<PPB_Graphics3D_API> enter(
342 context, callback_factory_,
[email protected]eeb4e4a2011-07-19 16:22:06343 &PPB_Graphics3D_Proxy::SendSwapBuffersACKToPlugin, context);
[email protected]eeb4e4a2011-07-19 16:22:06344 if (enter.succeeded())
[email protected]79d23c72011-08-08 19:40:40345 enter.SetResult(enter.object()->SwapBuffers(enter.callback()));
[email protected]eeb4e4a2011-07-19 16:22:06346}
[email protected]b096d032013-03-08 03:08:01347
348void PPB_Graphics3D_Proxy::OnMsgInsertSyncPoint(const HostResource& context,
avie029c4132015-12-23 06:45:22349 uint32_t* sync_point) {
[email protected]b096d032013-03-08 03:08:01350 *sync_point = 0;
351 EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
352 if (enter.succeeded())
353 *sync_point = enter.object()->InsertSyncPoint();
354}
[email protected]7035bc92014-07-01 00:27:22355
356void PPB_Graphics3D_Proxy::OnMsgInsertFutureSyncPoint(
357 const HostResource& context,
avie029c4132015-12-23 06:45:22358 uint32_t* sync_point) {
[email protected]7035bc92014-07-01 00:27:22359 *sync_point = 0;
360 EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
361 if (enter.succeeded())
362 *sync_point = enter.object()->InsertFutureSyncPoint();
363}
364
365void PPB_Graphics3D_Proxy::OnMsgRetireSyncPoint(const HostResource& context,
avie029c4132015-12-23 06:45:22366 uint32_t sync_point) {
[email protected]7035bc92014-07-01 00:27:22367 EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
368 if (enter.succeeded())
369 enter.object()->RetireSyncPoint(sync_point);
370}
[email protected]11494c5c2012-11-13 02:50:57371#endif // !defined(OS_NACL)
[email protected]eeb4e4a2011-07-19 16:22:06372
373void PPB_Graphics3D_Proxy::OnMsgSwapBuffersACK(const HostResource& resource,
374 int32_t pp_error) {
375 EnterPluginFromHostResource<PPB_Graphics3D_API> enter(resource);
376 if (enter.succeeded())
377 static_cast<Graphics3D*>(enter.object())->SwapBuffersACK(pp_error);
378}
379
[email protected]11494c5c2012-11-13 02:50:57380#if !defined(OS_NACL)
[email protected]eeb4e4a2011-07-19 16:22:06381void PPB_Graphics3D_Proxy::SendSwapBuffersACKToPlugin(
382 int32_t result,
383 const HostResource& context) {
384 dispatcher()->Send(new PpapiMsg_PPBGraphics3D_SwapBuffersACK(
[email protected]ac4b54d2011-10-20 23:09:28385 API_ID_PPB_GRAPHICS_3D, context, result));
[email protected]eeb4e4a2011-07-19 16:22:06386}
[email protected]11494c5c2012-11-13 02:50:57387#endif // !defined(OS_NACL)
[email protected]eeb4e4a2011-07-19 16:22:06388
389} // namespace proxy
[email protected]4d2efd22011-08-18 21:58:02390} // namespace ppapi