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 "content/renderer/pepper/ppb_var_deprecated_impl.h"
6
7#include <limits>
8
9#include "content/renderer/pepper/host_globals.h"
10#include "content/renderer/pepper/message_channel.h"
11#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
12#include "content/renderer/pepper/pepper_try_catch.h"
13#include "content/renderer/pepper/plugin_module.h"
14#include "content/renderer/pepper/plugin_object.h"
15#include "content/renderer/pepper/v8object_var.h"
16#include "ppapi/c/dev/ppb_var_deprecated.h"
17#include "ppapi/c/ppb_var.h"
18#include "ppapi/shared_impl/ppb_var_shared.h"
19#include "third_party/WebKit/public/web/WebDocument.h"
20#include "third_party/WebKit/public/web/WebElement.h"
21#include "third_party/WebKit/public/web/WebLocalFrame.h"
22#include "third_party/WebKit/public/web/WebPluginContainer.h"
23#include "third_party/WebKit/public/web/WebScopedUserGesture.h"
24
25using ppapi::V8ObjectVar;
26using ppapi::PpapiGlobals;
27using ppapi::ScopedPPVar;
28using ppapi::ScopedPPVarArray;
29using ppapi::StringVar;
30using ppapi::Var;
31
32namespace content {
33
34namespace {
35
36const char kInvalidIdentifierException[] = "Error: Invalid identifier.";
37const char kInvalidObjectException[] = "Error: Invalid object";
38const char kUnableToCallMethodException[] = "Error: Unable to call method";
39
40class ObjectAccessor {
41 public:
42  ObjectAccessor(PP_Var var)
43      : object_var_(V8ObjectVar::FromPPVar(var).get()),
44        instance_(object_var_ ? object_var_->instance() : NULL) {
45  }
46
47  // Check if the object is valid. If it isn't, set an exception and return
48  // false.
49  bool IsValid(PP_Var* exception) {
50    // If we already have an exception, then the call is invalid according to
51    // the unittests.
52    if (exception && exception->type != PP_VARTYPE_UNDEFINED)
53      return false;
54    if (instance_)
55      return true;
56    if (exception)
57      *exception = ppapi::StringVar::StringToPPVar(kInvalidObjectException);
58    return false;
59  }
60  // Lazily grab the object so that the handle is created in the current handle
61  // scope.
62  v8::Handle<v8::Object> GetObject() { return object_var_->GetHandle(); }
63  PepperPluginInstanceImpl* instance() { return instance_; }
64
65 private:
66  V8ObjectVar* object_var_;
67  PepperPluginInstanceImpl* instance_;
68};
69
70bool IsValidIdentifer(PP_Var identifier, PP_Var* exception) {
71  if (identifier.type == PP_VARTYPE_INT32 ||
72      identifier.type == PP_VARTYPE_STRING) {
73    return true;
74  }
75  if (exception)
76    *exception = ppapi::StringVar::StringToPPVar(kInvalidIdentifierException);
77  return false;
78}
79
80bool HasPropertyDeprecated(PP_Var var, PP_Var name, PP_Var* exception) {
81  ObjectAccessor accessor(var);
82  if (!accessor.IsValid(exception) || !IsValidIdentifer(name, exception))
83    return false;
84
85  PepperTryCatchVar try_catch(accessor.instance(), exception);
86  v8::Handle<v8::Value> v8_name = try_catch.ToV8(name);
87  if (try_catch.HasException())
88    return false;
89
90  bool result = accessor.GetObject()->Has(v8_name);
91  if (try_catch.HasException())
92    return false;
93  return result;
94}
95
96bool HasMethodDeprecated(PP_Var var, PP_Var name, PP_Var* exception) {
97  ObjectAccessor accessor(var);
98  if (!accessor.IsValid(exception) || !IsValidIdentifer(name, exception))
99    return false;
100
101  PepperTryCatchVar try_catch(accessor.instance(), exception);
102  v8::Handle<v8::Value> v8_name = try_catch.ToV8(name);
103  if (try_catch.HasException())
104    return false;
105
106  bool result = accessor.GetObject()->Has(v8_name) &&
107      accessor.GetObject()->Get(v8_name)->IsFunction();
108  if (try_catch.HasException())
109    return false;
110  return result;
111}
112
113PP_Var GetProperty(PP_Var var, PP_Var name, PP_Var* exception) {
114  ObjectAccessor accessor(var);
115  if (!accessor.IsValid(exception) || !IsValidIdentifer(name, exception))
116    return PP_MakeUndefined();
117
118  PepperTryCatchVar try_catch(accessor.instance(), exception);
119  v8::Handle<v8::Value> v8_name = try_catch.ToV8(name);
120  if (try_catch.HasException())
121    return PP_MakeUndefined();
122
123  ScopedPPVar result_var = try_catch.FromV8(accessor.GetObject()->Get(v8_name));
124  if (try_catch.HasException())
125    return PP_MakeUndefined();
126
127  return result_var.Release();
128}
129
130void EnumerateProperties(PP_Var var,
131                         uint32_t* property_count,
132                         PP_Var** properties,
133                         PP_Var* exception) {
134  ObjectAccessor accessor(var);
135  if (!accessor.IsValid(exception))
136    return;
137
138  PepperTryCatchVar try_catch(accessor.instance(), exception);
139
140  *properties = NULL;
141  *property_count = 0;
142
143  v8::Local<v8::Array> identifiers = accessor.GetObject()->GetPropertyNames();
144  if (try_catch.HasException())
145    return;
146  ScopedPPVarArray identifier_vars(identifiers->Length());
147  for (uint32_t i = 0; i < identifiers->Length(); ++i) {
148    ScopedPPVar var = try_catch.FromV8(identifiers->Get(i));
149    if (try_catch.HasException())
150      return;
151    identifier_vars.Set(i, var);
152  }
153
154  size_t size = identifier_vars.size();
155  *properties = identifier_vars.Release(
156      ScopedPPVarArray::PassPPBMemoryAllocatedArray());
157  *property_count = size;
158}
159
160void SetPropertyDeprecated(PP_Var var,
161                           PP_Var name,
162                           PP_Var value,
163                           PP_Var* exception) {
164  ObjectAccessor accessor(var);
165  if (!accessor.IsValid(exception) || !IsValidIdentifer(name, exception))
166    return;
167
168  PepperTryCatchVar try_catch(accessor.instance(), exception);
169  v8::Handle<v8::Value> v8_name = try_catch.ToV8(name);
170  v8::Handle<v8::Value> v8_value = try_catch.ToV8(value);
171
172  if (try_catch.HasException())
173    return;
174
175  accessor.GetObject()->Set(v8_name, v8_value);
176  try_catch.HasException();  // Ensure an exception gets set if one occured.
177}
178
179void DeletePropertyDeprecated(PP_Var var, PP_Var name, PP_Var* exception) {
180  ObjectAccessor accessor(var);
181  if (!accessor.IsValid(exception) || !IsValidIdentifer(name, exception))
182    return;
183
184  PepperTryCatchVar try_catch(accessor.instance(), exception);
185  v8::Handle<v8::Value> v8_name = try_catch.ToV8(name);
186
187  if (try_catch.HasException())
188    return;
189
190  accessor.GetObject()->Delete(v8_name);
191  try_catch.HasException();  // Ensure an exception gets set if one occured.
192}
193
194PP_Var CallDeprecatedInternal(PP_Var var,
195                              PP_Var method_name,
196                              uint32_t argc,
197                              PP_Var* argv,
198                              PP_Var* exception) {
199  ObjectAccessor accessor(var);
200  if (!accessor.IsValid(exception))
201    return PP_MakeUndefined();
202
203  // If the method name is undefined, set it to the empty string to trigger
204  // calling |var| as a function.
205  ScopedPPVar scoped_name(method_name);
206  if (method_name.type == PP_VARTYPE_UNDEFINED) {
207    scoped_name = ScopedPPVar(ScopedPPVar::PassRef(),
208                                StringVar::StringToPPVar(""));
209  }
210
211  PepperTryCatchVar try_catch(accessor.instance(), exception);
212  v8::Handle<v8::Value> v8_method_name = try_catch.ToV8(scoped_name.get());
213  if (try_catch.HasException())
214    return PP_MakeUndefined();
215
216  if (!v8_method_name->IsString()) {
217    try_catch.SetException(kUnableToCallMethodException);
218    return PP_MakeUndefined();
219  }
220
221  v8::Handle<v8::Object> function = accessor.GetObject();
222  v8::Handle<v8::Object> recv =
223      accessor.instance()->GetMainWorldContext()->Global();
224  if (v8_method_name.As<v8::String>()->Length() != 0) {
225    function = function->Get(v8_method_name)->ToObject();
226    recv = accessor.GetObject();
227  }
228
229  if (try_catch.HasException())
230    return PP_MakeUndefined();
231
232  if (!function->IsFunction()) {
233    try_catch.SetException(kUnableToCallMethodException);
234    return PP_MakeUndefined();
235  }
236
237  scoped_ptr<v8::Handle<v8::Value>[] > converted_args(
238      new v8::Handle<v8::Value>[argc]);
239  for (uint32_t i = 0; i < argc; ++i) {
240    converted_args[i] = try_catch.ToV8(argv[i]);
241    if (try_catch.HasException())
242      return PP_MakeUndefined();
243  }
244
245  blink::WebPluginContainer* container = accessor.instance()->container();
246  blink::WebLocalFrame* frame = NULL;
247  if (container)
248    frame = container->element().document().frame();
249
250  if (!frame) {
251    try_catch.SetException("No frame to execute script in.");
252    return PP_MakeUndefined();
253  }
254
255  v8::Handle<v8::Value> result = frame->callFunctionEvenIfScriptDisabled(
256      function.As<v8::Function>(), recv, argc, converted_args.get());
257  ScopedPPVar result_var = try_catch.FromV8(result);
258
259  if (try_catch.HasException())
260    return PP_MakeUndefined();
261
262  return result_var.Release();
263}
264
265PP_Var CallDeprecated(PP_Var var,
266                      PP_Var method_name,
267                      uint32_t argc,
268                      PP_Var* argv,
269                      PP_Var* exception) {
270  ObjectAccessor accessor(var);
271  if (accessor.instance() && accessor.instance()->IsProcessingUserGesture()) {
272    blink::WebScopedUserGesture user_gesture(
273        accessor.instance()->CurrentUserGestureToken());
274    return CallDeprecatedInternal(var, method_name, argc, argv, exception);
275  }
276  return CallDeprecatedInternal(var, method_name, argc, argv, exception);
277}
278
279PP_Var Construct(PP_Var var, uint32_t argc, PP_Var* argv, PP_Var* exception) {
280  // Deprecated.
281  NOTREACHED();
282  return PP_MakeUndefined();
283}
284
285bool IsInstanceOfDeprecated(PP_Var var,
286                            const PPP_Class_Deprecated* ppp_class,
287                            void** ppp_class_data) {
288  scoped_refptr<V8ObjectVar> object(V8ObjectVar::FromPPVar(var));
289  if (!object.get())
290    return false;  // Not an object at all.
291
292  v8::HandleScope handle_scope(object->instance()->GetIsolate());
293  v8::Handle<v8::Context> context = object->instance()->GetMainWorldContext();
294  if (context.IsEmpty())
295    return false;
296  v8::Context::Scope context_scope(context);
297  PluginObject* plugin_object = PluginObject::FromV8Object(
298      object->instance()->GetIsolate(), object->GetHandle());
299  if (plugin_object && plugin_object->ppp_class() == ppp_class) {
300    if (ppp_class_data)
301      *ppp_class_data = plugin_object->ppp_class_data();
302    return true;
303  }
304
305  return false;
306}
307
308PP_Var CreateObjectDeprecated(PP_Instance pp_instance,
309                              const PPP_Class_Deprecated* ppp_class,
310                              void* ppp_class_data) {
311  PepperPluginInstanceImpl* instance =
312      HostGlobals::Get()->GetInstance(pp_instance);
313  if (!instance) {
314    DLOG(ERROR) << "Create object passed an invalid instance.";
315    return PP_MakeNull();
316  }
317  return PluginObject::Create(instance, ppp_class, ppp_class_data);
318}
319
320PP_Var CreateObjectWithModuleDeprecated(PP_Module pp_module,
321                                        const PPP_Class_Deprecated* ppp_class,
322                                        void* ppp_class_data) {
323  PluginModule* module = HostGlobals::Get()->GetModule(pp_module);
324  if (!module)
325    return PP_MakeNull();
326  return PluginObject::Create(
327      module->GetSomeInstance(), ppp_class, ppp_class_data);
328}
329
330}  // namespace
331
332// static
333const PPB_Var_Deprecated* PPB_Var_Deprecated_Impl::GetVarDeprecatedInterface() {
334  static const PPB_Var_Deprecated var_deprecated_interface = {
335      ppapi::PPB_Var_Shared::GetVarInterface1_0()->AddRef,
336      ppapi::PPB_Var_Shared::GetVarInterface1_0()->Release,
337      ppapi::PPB_Var_Shared::GetVarInterface1_0()->VarFromUtf8,
338      ppapi::PPB_Var_Shared::GetVarInterface1_0()->VarToUtf8,
339      &HasPropertyDeprecated,
340      &HasMethodDeprecated,
341      &GetProperty,
342      &EnumerateProperties,
343      &SetPropertyDeprecated,
344      &DeletePropertyDeprecated,
345      &CallDeprecated,
346      &Construct,
347      &IsInstanceOfDeprecated,
348      &CreateObjectDeprecated,
349      &CreateObjectWithModuleDeprecated, };
350
351  return &var_deprecated_interface;
352}
353
354}  // namespace content
355