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 "ppapi/proxy/ppb_var_deprecated_proxy.h"
6
7#include <stdlib.h>  // For malloc
8
9#include "base/bind.h"
10#include "base/logging.h"
11#include "base/message_loop/message_loop.h"
12#include "ppapi/c/dev/ppb_var_deprecated.h"
13#include "ppapi/c/pp_var.h"
14#include "ppapi/c/ppb_core.h"
15#include "ppapi/c/ppb_var.h"
16#include "ppapi/proxy/host_dispatcher.h"
17#include "ppapi/proxy/plugin_dispatcher.h"
18#include "ppapi/proxy/plugin_globals.h"
19#include "ppapi/proxy/plugin_resource_tracker.h"
20#include "ppapi/proxy/plugin_var_tracker.h"
21#include "ppapi/proxy/ppapi_messages.h"
22#include "ppapi/proxy/ppp_class_proxy.h"
23#include "ppapi/proxy/proxy_object_var.h"
24#include "ppapi/proxy/serialized_var.h"
25#include "ppapi/shared_impl/ppb_var_shared.h"
26#include "ppapi/shared_impl/proxy_lock.h"
27#include "ppapi/shared_impl/var.h"
28
29namespace ppapi {
30namespace proxy {
31
32namespace {
33
34// Used to do get the set-up information for calling a var object. If the
35// exception is set, returns NULL. Otherwise, computes the dispatcher for the
36// given var object. If the var is not a valid object, returns NULL and sets
37// the exception.
38PluginDispatcher* CheckExceptionAndGetDispatcher(const PP_Var& object,
39                                                 PP_Var* exception) {
40  // If an exception is already set, we don't need to do anything, just return
41  // an error to the caller.
42  if (exception && exception->type != PP_VARTYPE_UNDEFINED)
43    return NULL;
44
45
46  if (object.type == PP_VARTYPE_OBJECT) {
47    // Get the dispatcher for the object.
48    PluginDispatcher* dispatcher =
49        PluginGlobals::Get()->plugin_var_tracker()->
50            DispatcherForPluginObject(object);
51    if (dispatcher)
52      return dispatcher;
53  }
54
55  // The object is invalid. This means we can't figure out which dispatcher
56  // to use, which is OK because the call will fail anyway. Set the exception.
57  if (exception) {
58    *exception = StringVar::StringToPPVar(
59        std::string("Attempting to use an invalid object"));
60  }
61  return NULL;
62}
63
64// PPB_Var_Deprecated plugin ---------------------------------------------------
65
66bool HasProperty(PP_Var var,
67                 PP_Var name,
68                 PP_Var* exception) {
69  ProxyAutoLock lock;
70  Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(var, exception);
71  if (!dispatcher)
72    return false;
73
74  ReceiveSerializedException se(dispatcher, exception);
75  PP_Bool result = PP_FALSE;
76  if (!se.IsThrown()) {
77    dispatcher->Send(new PpapiHostMsg_PPBVar_HasProperty(
78        API_ID_PPB_VAR_DEPRECATED,
79        SerializedVarSendInput(dispatcher, var),
80        SerializedVarSendInput(dispatcher, name), &se, &result));
81  }
82  return PP_ToBool(result);
83}
84
85bool HasMethod(PP_Var var,
86               PP_Var name,
87               PP_Var* exception) {
88  ProxyAutoLock lock;
89  Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(var, exception);
90  if (!dispatcher)
91    return false;
92
93  ReceiveSerializedException se(dispatcher, exception);
94  PP_Bool result = PP_FALSE;
95  if (!se.IsThrown()) {
96    dispatcher->Send(new PpapiHostMsg_PPBVar_HasMethodDeprecated(
97        API_ID_PPB_VAR_DEPRECATED,
98        SerializedVarSendInput(dispatcher, var),
99        SerializedVarSendInput(dispatcher, name), &se, &result));
100  }
101  return PP_ToBool(result);
102}
103
104PP_Var GetProperty(PP_Var var,
105                   PP_Var name,
106                   PP_Var* exception) {
107  ProxyAutoLock lock;
108  Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(var, exception);
109  if (!dispatcher)
110    return PP_MakeUndefined();
111
112  ReceiveSerializedException se(dispatcher, exception);
113  ReceiveSerializedVarReturnValue result;
114  if (!se.IsThrown()) {
115    dispatcher->Send(new PpapiHostMsg_PPBVar_GetProperty(
116        API_ID_PPB_VAR_DEPRECATED,
117        SerializedVarSendInput(dispatcher, var),
118        SerializedVarSendInput(dispatcher, name), &se, &result));
119  }
120  return result.Return(dispatcher);
121}
122
123void EnumerateProperties(PP_Var var,
124                         uint32_t* property_count,
125                         PP_Var** properties,
126                         PP_Var* exception) {
127  ProxyAutoLock lock;
128  Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(var, exception);
129  if (!dispatcher) {
130    *property_count = 0;
131    *properties = NULL;
132    return;
133  }
134
135  ReceiveSerializedVarVectorOutParam out_vector(dispatcher,
136                                                property_count, properties);
137  ReceiveSerializedException se(dispatcher, exception);
138  if (!se.IsThrown()) {
139    dispatcher->Send(new PpapiHostMsg_PPBVar_EnumerateProperties(
140        API_ID_PPB_VAR_DEPRECATED,
141        SerializedVarSendInput(dispatcher, var),
142        out_vector.OutParam(), &se));
143  }
144}
145
146void SetProperty(PP_Var var,
147                 PP_Var name,
148                 PP_Var value,
149                 PP_Var* exception) {
150  ProxyAutoLock lock;
151  Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(var, exception);
152  if (!dispatcher)
153    return;
154
155  ReceiveSerializedException se(dispatcher, exception);
156  if (!se.IsThrown()) {
157    dispatcher->Send(new PpapiHostMsg_PPBVar_SetPropertyDeprecated(
158        API_ID_PPB_VAR_DEPRECATED,
159        SerializedVarSendInput(dispatcher, var),
160        SerializedVarSendInput(dispatcher, name),
161        SerializedVarSendInput(dispatcher, value), &se));
162  }
163}
164
165void RemoveProperty(PP_Var var,
166                    PP_Var name,
167                    PP_Var* exception) {
168  ProxyAutoLock lock;
169  Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(var, exception);
170  if (!dispatcher)
171    return;
172
173  ReceiveSerializedException se(dispatcher, exception);
174  PP_Bool result = PP_FALSE;
175  if (!se.IsThrown()) {
176    dispatcher->Send(new PpapiHostMsg_PPBVar_DeleteProperty(
177        API_ID_PPB_VAR_DEPRECATED,
178        SerializedVarSendInput(dispatcher, var),
179        SerializedVarSendInput(dispatcher, name), &se, &result));
180  }
181}
182
183PP_Var Call(PP_Var object,
184            PP_Var method_name,
185            uint32_t argc,
186            PP_Var* argv,
187            PP_Var* exception) {
188  ProxyAutoLock lock;
189  Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(object, exception);
190  if (!dispatcher)
191    return PP_MakeUndefined();
192
193  ReceiveSerializedVarReturnValue result;
194  ReceiveSerializedException se(dispatcher, exception);
195  if (!se.IsThrown()) {
196    std::vector<SerializedVar> argv_vect;
197    SerializedVarSendInput::ConvertVector(dispatcher, argv, argc, &argv_vect);
198
199    dispatcher->Send(new PpapiHostMsg_PPBVar_CallDeprecated(
200        API_ID_PPB_VAR_DEPRECATED,
201        SerializedVarSendInput(dispatcher, object),
202        SerializedVarSendInput(dispatcher, method_name), argv_vect,
203        &se, &result));
204  }
205  return result.Return(dispatcher);
206}
207
208PP_Var Construct(PP_Var object,
209                 uint32_t argc,
210                 PP_Var* argv,
211                 PP_Var* exception) {
212  ProxyAutoLock lock;
213  Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(object, exception);
214  if (!dispatcher)
215    return PP_MakeUndefined();
216
217  ReceiveSerializedVarReturnValue result;
218  ReceiveSerializedException se(dispatcher, exception);
219  if (!se.IsThrown()) {
220    std::vector<SerializedVar> argv_vect;
221    SerializedVarSendInput::ConvertVector(dispatcher, argv, argc, &argv_vect);
222
223    dispatcher->Send(new PpapiHostMsg_PPBVar_Construct(
224        API_ID_PPB_VAR_DEPRECATED,
225        SerializedVarSendInput(dispatcher, object),
226        argv_vect, &se, &result));
227  }
228  return result.Return(dispatcher);
229}
230
231bool IsInstanceOf(PP_Var var,
232                  const PPP_Class_Deprecated* ppp_class,
233                  void** ppp_class_data) {
234  ProxyAutoLock lock;
235  Dispatcher* dispatcher = CheckExceptionAndGetDispatcher(var, NULL);
236  if (!dispatcher)
237    return false;
238
239  PP_Bool result = PP_FALSE;
240  int64 class_int = static_cast<int64>(reinterpret_cast<intptr_t>(ppp_class));
241  int64 class_data_int = 0;
242  dispatcher->Send(new PpapiHostMsg_PPBVar_IsInstanceOfDeprecated(
243      API_ID_PPB_VAR_DEPRECATED, SerializedVarSendInput(dispatcher, var),
244      class_int, &class_data_int, &result));
245  *ppp_class_data =
246      reinterpret_cast<void*>(static_cast<intptr_t>(class_data_int));
247  return PP_ToBool(result);
248}
249
250PP_Var CreateObject(PP_Instance instance,
251                    const PPP_Class_Deprecated* ppp_class,
252                    void* ppp_class_data) {
253  ProxyAutoLock lock;
254  Dispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
255  if (!dispatcher)
256    return PP_MakeUndefined();
257
258  PluginVarTracker* tracker = PluginGlobals::Get()->plugin_var_tracker();
259  if (tracker->IsPluginImplementedObjectAlive(ppp_class_data))
260    return PP_MakeUndefined();  // Object already exists with this user data.
261
262  ReceiveSerializedVarReturnValue result;
263  int64 class_int = static_cast<int64>(reinterpret_cast<intptr_t>(ppp_class));
264  int64 data_int =
265      static_cast<int64>(reinterpret_cast<intptr_t>(ppp_class_data));
266  dispatcher->Send(new PpapiHostMsg_PPBVar_CreateObjectDeprecated(
267      API_ID_PPB_VAR_DEPRECATED, instance, class_int, data_int,
268      &result));
269  PP_Var ret_var = result.Return(dispatcher);
270
271  // Register this object as being implemented by the plugin.
272  if (ret_var.type == PP_VARTYPE_OBJECT) {
273    tracker->PluginImplementedObjectCreated(instance, ret_var,
274                                            ppp_class, ppp_class_data);
275  }
276  return ret_var;
277}
278
279}  // namespace
280
281PPB_Var_Deprecated_Proxy::PPB_Var_Deprecated_Proxy(
282    Dispatcher* dispatcher)
283    : InterfaceProxy(dispatcher),
284      ppb_var_impl_(NULL),
285      task_factory_(this) {
286  if (!dispatcher->IsPlugin()) {
287    ppb_var_impl_ = static_cast<const PPB_Var_Deprecated*>(
288        dispatcher->local_get_interface()(PPB_VAR_DEPRECATED_INTERFACE));
289  }
290}
291
292PPB_Var_Deprecated_Proxy::~PPB_Var_Deprecated_Proxy() {
293}
294
295// static
296const PPB_Var_Deprecated* PPB_Var_Deprecated_Proxy::GetProxyInterface() {
297  static const PPB_Var_Deprecated var_deprecated_interface = {
298    ppapi::PPB_Var_Shared::GetVarInterface1_0()->AddRef,
299    ppapi::PPB_Var_Shared::GetVarInterface1_0()->Release,
300    ppapi::PPB_Var_Shared::GetVarInterface1_0()->VarFromUtf8,
301    ppapi::PPB_Var_Shared::GetVarInterface1_0()->VarToUtf8,
302    &HasProperty,
303    &HasMethod,
304    &GetProperty,
305    &EnumerateProperties,
306    &SetProperty,
307    &RemoveProperty,
308    &Call,
309    &Construct,
310    &IsInstanceOf,
311    &CreateObject
312  };
313  return &var_deprecated_interface;
314}
315
316bool PPB_Var_Deprecated_Proxy::OnMessageReceived(const IPC::Message& msg) {
317  if (!dispatcher()->permissions().HasPermission(PERMISSION_DEV))
318    return false;
319
320  // Prevent the dispatcher from going away during a call to Call or other
321  // function that could mutate the DOM. This must happen OUTSIDE of
322  // the message handlers since the SerializedVars use the dispatcher upon
323  // return of the function (converting the SerializedVarReturnValue/OutParam
324  // to a SerializedVar in the destructor).
325  ScopedModuleReference death_grip(dispatcher());
326
327  bool handled = true;
328  IPC_BEGIN_MESSAGE_MAP(PPB_Var_Deprecated_Proxy, msg)
329    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_AddRefObject, OnMsgAddRefObject)
330    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_ReleaseObject, OnMsgReleaseObject)
331    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_HasProperty,
332                        OnMsgHasProperty)
333    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_HasMethodDeprecated,
334                        OnMsgHasMethodDeprecated)
335    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_GetProperty,
336                        OnMsgGetProperty)
337    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_DeleteProperty,
338                        OnMsgDeleteProperty)
339    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_EnumerateProperties,
340                        OnMsgEnumerateProperties)
341    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_SetPropertyDeprecated,
342                        OnMsgSetPropertyDeprecated)
343    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_CallDeprecated,
344                        OnMsgCallDeprecated)
345    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_Construct,
346                        OnMsgConstruct)
347    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_IsInstanceOfDeprecated,
348                        OnMsgIsInstanceOfDeprecated)
349    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBVar_CreateObjectDeprecated,
350                        OnMsgCreateObjectDeprecated)
351    IPC_MESSAGE_UNHANDLED(handled = false)
352  IPC_END_MESSAGE_MAP()
353  // TODO(brettw) handle bad messages!
354  return handled;
355}
356
357void PPB_Var_Deprecated_Proxy::OnMsgAddRefObject(int64 object_id) {
358  PP_Var var = { PP_VARTYPE_OBJECT };
359  var.value.as_id = object_id;
360  ppb_var_impl_->AddRef(var);
361}
362
363void PPB_Var_Deprecated_Proxy::OnMsgReleaseObject(int64 object_id) {
364  // Ok, so this is super subtle.
365  // When the browser side sends a sync IPC message that returns a var, and the
366  // plugin wants to give ownership of that var to the browser, dropping all
367  // references, it may call ReleaseObject right after returning the result.
368  // However, the IPC system doesn't enforce strict ordering of messages in that
369  // case, where a message that is set to unblock (e.g. a sync message, or in
370  // our case all messages coming from the plugin) that is sent *after* the
371  // result may be dispatched on the browser side *before* the sync send
372  // returned (see ipc_sync_channel.cc). In this case, that means it could
373  // release the object before it is AddRef'ed on the browser side.
374  // To work around this, we post a task here, that will not execute before
375  // control goes back to the main message loop, that will ensure the sync send
376  // has returned and the browser side can take its reference before we Release.
377  // Note: if the instance is gone by the time the task is executed, then it
378  // will Release the objects itself and this Release will be a NOOP (aside of a
379  // spurious warning).
380  // TODO(piman): See if we can fix the IPC code to enforce strict ordering, and
381  // then remove this.
382  base::MessageLoop::current()->PostNonNestableTask(
383      FROM_HERE,
384      RunWhileLocked(base::Bind(&PPB_Var_Deprecated_Proxy::DoReleaseObject,
385                                task_factory_.GetWeakPtr(),
386                                object_id)));
387}
388
389void PPB_Var_Deprecated_Proxy::OnMsgHasProperty(
390    SerializedVarReceiveInput var,
391    SerializedVarReceiveInput name,
392    SerializedVarOutParam exception,
393    PP_Bool* result) {
394  SetAllowPluginReentrancy();
395  *result = PP_FromBool(ppb_var_impl_->HasProperty(
396      var.Get(dispatcher()),
397      name.Get(dispatcher()),
398      exception.OutParam(dispatcher())));
399}
400
401void PPB_Var_Deprecated_Proxy::OnMsgHasMethodDeprecated(
402    SerializedVarReceiveInput var,
403    SerializedVarReceiveInput name,
404    SerializedVarOutParam exception,
405    PP_Bool* result) {
406  SetAllowPluginReentrancy();
407  *result = PP_FromBool(ppb_var_impl_->HasMethod(
408      var.Get(dispatcher()),
409      name.Get(dispatcher()),
410      exception.OutParam(dispatcher())));
411}
412
413void PPB_Var_Deprecated_Proxy::OnMsgGetProperty(
414    SerializedVarReceiveInput var,
415    SerializedVarReceiveInput name,
416    SerializedVarOutParam exception,
417    SerializedVarReturnValue result) {
418  SetAllowPluginReentrancy();
419  result.Return(dispatcher(), ppb_var_impl_->GetProperty(
420      var.Get(dispatcher()), name.Get(dispatcher()),
421      exception.OutParam(dispatcher())));
422}
423
424void PPB_Var_Deprecated_Proxy::OnMsgEnumerateProperties(
425    SerializedVarReceiveInput var,
426    SerializedVarVectorOutParam props,
427    SerializedVarOutParam exception) {
428  SetAllowPluginReentrancy();
429  ppb_var_impl_->GetAllPropertyNames(var.Get(dispatcher()),
430      props.CountOutParam(), props.ArrayOutParam(dispatcher()),
431      exception.OutParam(dispatcher()));
432}
433
434void PPB_Var_Deprecated_Proxy::OnMsgSetPropertyDeprecated(
435    SerializedVarReceiveInput var,
436    SerializedVarReceiveInput name,
437    SerializedVarReceiveInput value,
438    SerializedVarOutParam exception) {
439  SetAllowPluginReentrancy();
440  ppb_var_impl_->SetProperty(var.Get(dispatcher()),
441                                name.Get(dispatcher()),
442                                value.Get(dispatcher()),
443                                exception.OutParam(dispatcher()));
444}
445
446void PPB_Var_Deprecated_Proxy::OnMsgDeleteProperty(
447    SerializedVarReceiveInput var,
448    SerializedVarReceiveInput name,
449    SerializedVarOutParam exception,
450    PP_Bool* result) {
451  SetAllowPluginReentrancy();
452  ppb_var_impl_->RemoveProperty(var.Get(dispatcher()),
453                                   name.Get(dispatcher()),
454                                   exception.OutParam(dispatcher()));
455  // This deprecated function doesn't actually return a value, but we re-use
456  // the message from the non-deprecated interface with the return value.
457  *result = PP_TRUE;
458}
459
460void PPB_Var_Deprecated_Proxy::OnMsgCallDeprecated(
461    SerializedVarReceiveInput object,
462    SerializedVarReceiveInput method_name,
463    SerializedVarVectorReceiveInput arg_vector,
464    SerializedVarOutParam exception,
465    SerializedVarReturnValue result) {
466  SetAllowPluginReentrancy();
467  uint32_t arg_count = 0;
468  PP_Var* args = arg_vector.Get(dispatcher(), &arg_count);
469  result.Return(dispatcher(), ppb_var_impl_->Call(
470      object.Get(dispatcher()),
471      method_name.Get(dispatcher()),
472      arg_count, args,
473      exception.OutParam(dispatcher())));
474}
475
476void PPB_Var_Deprecated_Proxy::OnMsgConstruct(
477    SerializedVarReceiveInput var,
478    SerializedVarVectorReceiveInput arg_vector,
479    SerializedVarOutParam exception,
480    SerializedVarReturnValue result) {
481  SetAllowPluginReentrancy();
482  uint32_t arg_count = 0;
483  PP_Var* args = arg_vector.Get(dispatcher(), &arg_count);
484  result.Return(dispatcher(), ppb_var_impl_->Construct(
485      var.Get(dispatcher()), arg_count, args,
486      exception.OutParam(dispatcher())));
487}
488
489void PPB_Var_Deprecated_Proxy::OnMsgIsInstanceOfDeprecated(
490    SerializedVarReceiveInput var,
491    int64 ppp_class,
492    int64* ppp_class_data,
493    PP_Bool* result) {
494  SetAllowPluginReentrancy();
495  *result = PPP_Class_Proxy::IsInstanceOf(ppb_var_impl_,
496                                          var.Get(dispatcher()),
497                                          ppp_class,
498                                          ppp_class_data);
499}
500
501void PPB_Var_Deprecated_Proxy::OnMsgCreateObjectDeprecated(
502    PP_Instance instance,
503    int64 ppp_class,
504    int64 class_data,
505    SerializedVarReturnValue result) {
506  SetAllowPluginReentrancy();
507  result.Return(dispatcher(), PPP_Class_Proxy::CreateProxiedObject(
508      ppb_var_impl_, dispatcher(), instance, ppp_class, class_data));
509}
510
511void PPB_Var_Deprecated_Proxy::SetAllowPluginReentrancy() {
512  if (dispatcher()->IsPlugin())
513    NOTREACHED();
514  else
515    static_cast<HostDispatcher*>(dispatcher())->set_allow_plugin_reentrancy();
516}
517
518void PPB_Var_Deprecated_Proxy::DoReleaseObject(int64 object_id) {
519  PP_Var var = { PP_VARTYPE_OBJECT };
520  var.value.as_id = object_id;
521  ppb_var_impl_->Release(var);
522}
523
524}  // namespace proxy
525}  // namespace ppapi
526