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
3// found in the LICENSE file.
4
5#include "ppapi/tests/test_var_deprecated.h"
6
7#include <string.h>
8
9#include <limits>
10
11#include "ppapi/c/pp_var.h"
12#include "ppapi/c/dev/ppb_var_deprecated.h"
13#include "ppapi/cpp/dev/scriptable_object_deprecated.h"
14#include "ppapi/cpp/instance.h"
15#include "ppapi/cpp/module.h"
16#include "ppapi/cpp/private/var_private.h"
17#include "ppapi/cpp/var.h"
18#include "ppapi/tests/testing_instance.h"
19
20namespace {
21
22uint32_t kInvalidLength = static_cast<uint32_t>(-1);
23
24static const char kSetValueFunction[] = "SetValue";
25
26// ScriptableObject used by the var tests.
27class VarScriptableObject : public pp::deprecated::ScriptableObject {
28 public:
29  VarScriptableObject(TestVarDeprecated* v) : test_var_deprecated_(v) {}
30
31  // pp::deprecated::ScriptableObject overrides.
32  bool HasMethod(const pp::Var& name, pp::Var* exception);
33  pp::Var Call(const pp::Var& name,
34               const std::vector<pp::Var>& args,
35               pp::Var* exception);
36
37 private:
38  TestVarDeprecated* test_var_deprecated_;
39};
40
41bool VarScriptableObject::HasMethod(const pp::Var& name, pp::Var* exception) {
42  if (!name.is_string())
43    return false;
44  return name.AsString() == kSetValueFunction;
45}
46
47pp::Var VarScriptableObject::Call(const pp::Var& method_name,
48                                  const std::vector<pp::Var>& args,
49                                  pp::Var* exception) {
50  if (!method_name.is_string())
51    return false;
52  std::string name = method_name.AsString();
53
54  if (name == kSetValueFunction) {
55    if (args.size() != 1)
56      *exception = pp::Var("Bad argument to SetValue(<value>)");
57    else
58      test_var_deprecated_->set_var_from_page(pp::VarPrivate(args[0]));
59  }
60
61  return pp::Var();
62}
63
64}  // namespace
65
66REGISTER_TEST_CASE(VarDeprecated);
67
68bool TestVarDeprecated::Init() {
69  var_interface_ = static_cast<const PPB_Var_Deprecated*>(
70      pp::Module::Get()->GetBrowserInterface(PPB_VAR_DEPRECATED_INTERFACE));
71  return var_interface_ && CheckTestingInterface();
72}
73
74void TestVarDeprecated::RunTests(const std::string& filter) {
75  RUN_TEST(BasicString, filter);
76  RUN_TEST(InvalidAndEmpty, filter);
77  RUN_TEST(InvalidUtf8, filter);
78  RUN_TEST(NullInputInUtf8Conversion, filter);
79  RUN_TEST(ValidUtf8, filter);
80  RUN_TEST(Utf8WithEmbeddedNulls, filter);
81  RUN_TEST(VarToUtf8ForWrongType, filter);
82  RUN_TEST(HasPropertyAndMethod, filter);
83  RUN_TEST(PassReference, filter);
84}
85
86pp::deprecated::ScriptableObject* TestVarDeprecated::CreateTestObject() {
87  return new VarScriptableObject(this);
88}
89
90std::string TestVarDeprecated::TestBasicString() {
91  uint32_t before_object = testing_interface_->GetLiveObjectsForInstance(
92      instance_->pp_instance());
93  {
94    const char kStr[] = "Hello";
95    const uint32_t kStrLen(sizeof(kStr) - 1);
96    PP_Var str = var_interface_->VarFromUtf8(pp::Module::Get()->pp_module(),
97                                             kStr, kStrLen);
98    ASSERT_EQ(PP_VARTYPE_STRING, str.type);
99
100    // Reading back the string should work.
101    uint32_t len = 0;
102    const char* result = var_interface_->VarToUtf8(str, &len);
103    ASSERT_EQ(kStrLen, len);
104    ASSERT_EQ(0, strncmp(kStr, result, kStrLen));
105
106    // Destroy the string, readback should now fail.
107    var_interface_->Release(str);
108    result = var_interface_->VarToUtf8(str, &len);
109    ASSERT_EQ(0, len);
110    ASSERT_EQ(NULL, result);
111  }
112
113  // Make sure nothing leaked.
114  ASSERT_TRUE(testing_interface_->GetLiveObjectsForInstance(
115      instance_->pp_instance()) == before_object);
116
117  PASS();
118}
119
120std::string TestVarDeprecated::TestInvalidAndEmpty() {
121  PP_Var invalid_string;
122  invalid_string.type = PP_VARTYPE_STRING;
123  invalid_string.value.as_id = 31415926;
124
125  // Invalid strings should give NULL as the return value.
126  uint32_t len = std::numeric_limits<uint32_t>::max();
127  const char* result = var_interface_->VarToUtf8(invalid_string, &len);
128  ASSERT_EQ(0, len);
129  ASSERT_EQ(NULL, result);
130
131  // Same with vars that are not strings.
132  len = std::numeric_limits<uint32_t>::max();
133  pp::Var int_var(42);
134  result = var_interface_->VarToUtf8(int_var.pp_var(), &len);
135  ASSERT_EQ(0, len);
136  ASSERT_EQ(NULL, result);
137
138  // Empty strings should return non-NULL.
139  pp::Var empty_string("");
140  len = std::numeric_limits<uint32_t>::max();
141  result = var_interface_->VarToUtf8(empty_string.pp_var(), &len);
142  ASSERT_EQ(0, len);
143  ASSERT_NE(NULL, result);
144
145  PASS();
146}
147
148std::string TestVarDeprecated::TestInvalidUtf8() {
149  // utf8じゃない (japanese for "is not utf8") in shift-jis encoding.
150  static const char kSjisString[] = "utf8\x82\xb6\x82\xe1\x82\xc8\x82\xa2";
151  pp::Var sjis(kSjisString);
152  if (!sjis.is_null())
153    return "Non-UTF8 string was permitted erroneously.";
154
155  PASS();
156}
157
158std::string TestVarDeprecated::TestNullInputInUtf8Conversion() {
159  // This test talks directly to the C interface to access edge cases that
160  // cannot be exercised via the C++ interface.
161  PP_Var converted_string;
162
163  // 0-length string should not dereference input string, and should produce
164  // an empty string.
165  converted_string = var_interface_->VarFromUtf8(
166      pp::Module::Get()->pp_module(), NULL, 0);
167  if (converted_string.type != PP_VARTYPE_STRING) {
168    return "Expected 0 length to return empty string.";
169  }
170
171  // Now convert it back.
172  uint32_t length = kInvalidLength;
173  const char* result = NULL;
174  result = var_interface_->VarToUtf8(converted_string, &length);
175  if (length != 0) {
176    return "Expected 0 length string on conversion.";
177  }
178  if (result == NULL) {
179    return "Expected a non-null result for 0-lengthed string from VarToUtf8.";
180  }
181  var_interface_->Release(converted_string);
182
183  // Should not crash, and make an empty string.
184  const char* null_string = NULL;
185  pp::Var null_var(null_string);
186  if (!null_var.is_string() || null_var.AsString() != "") {
187    return "Expected NULL input to make an empty string Var.";
188  }
189
190  PASS();
191}
192
193std::string TestVarDeprecated::TestValidUtf8() {
194  // From UTF8 string -> PP_Var.
195  // Chinese for "I am utf8."
196  static const char kValidUtf8[] = "\xe6\x88\x91\xe6\x98\xafutf8.";
197  pp::Var converted_string(kValidUtf8);
198
199  if (converted_string.is_null())
200    return "Unable to convert valid utf8 to var.";
201
202  // Since we're already here, test PP_Var back to UTF8 string.
203  std::string returned_string = converted_string.AsString();
204
205  // We need to check against 1 less than sizeof because the resulting string
206  // is technically not NULL terminated by API design.
207  if (returned_string.size() != sizeof(kValidUtf8) - 1) {
208    return "Unable to convert utf8 string back from var.";
209  }
210  if (returned_string != kValidUtf8) {
211    return "String mismatches on conversion back from PP_Var.";
212  }
213
214  PASS();
215}
216
217std::string TestVarDeprecated::TestUtf8WithEmbeddedNulls() {
218  // From UTF8 string with embedded nulls -> PP_Var.
219  // Chinese for "also utf8."
220  static const char kUtf8WithEmbededNull[] = "\xe6\xb9\x9f\xe6\x98\xaf\0utf8.";
221  std::string orig_string(kUtf8WithEmbededNull,
222                          sizeof(kUtf8WithEmbededNull) -1);
223  pp::Var converted_string(orig_string);
224
225  if (converted_string.is_null())
226    return "Unable to convert utf8 with embedded nulls to var.";
227
228  // Since we're already here, test PP_Var back to UTF8 string.
229  std::string returned_string = converted_string.AsString();
230
231  if (returned_string.size() != orig_string.size()) {
232    return "Unable to convert utf8 with embedded nulls back from var.";
233  }
234  if (returned_string != orig_string) {
235    return "String mismatches on conversion back from PP_Var.";
236  }
237
238  PASS();
239}
240
241std::string TestVarDeprecated::TestVarToUtf8ForWrongType() {
242  uint32_t length = kInvalidLength;
243  const char* result = NULL;
244  result = var_interface_->VarToUtf8(PP_MakeUndefined(), &length);
245  if (length != 0) {
246    return "Expected 0 on string conversion from Void var.";
247  }
248  if (result != NULL) {
249    return "Expected NULL on string conversion from Void var.";
250  }
251
252  length = kInvalidLength;
253  result = NULL;
254  result = var_interface_->VarToUtf8(PP_MakeNull(), &length);
255  if (length != 0) {
256    return "Expected 0 on string conversion from Null var.";
257  }
258  if (result != NULL) {
259    return "Expected NULL on string conversion from Null var.";
260  }
261
262  length = kInvalidLength;
263  result = NULL;
264  result = var_interface_->VarToUtf8(PP_MakeBool(PP_TRUE), &length);
265  if (length != 0) {
266    return "Expected 0 on string conversion from Bool var.";
267  }
268  if (result != NULL) {
269    return "Expected NULL on string conversion from Bool var.";
270  }
271
272  length = kInvalidLength;
273  result = NULL;
274  result = var_interface_->VarToUtf8(PP_MakeInt32(1), &length);
275  if (length != 0) {
276    return "Expected 0 on string conversion from Int32 var.";
277  }
278  if (result != NULL) {
279    return "Expected NULL on string conversion from Int32 var.";
280  }
281
282  length = kInvalidLength;
283  result = NULL;
284  result = var_interface_->VarToUtf8(PP_MakeDouble(1.0), &length);
285  if (length != 0) {
286    return "Expected 0 on string conversion from Double var.";
287  }
288  if (result != NULL) {
289    return "Expected NULL on string conversion from Double var.";
290  }
291
292  PASS();
293}
294
295std::string TestVarDeprecated::TestHasPropertyAndMethod() {
296  pp::VarPrivate window = instance_->GetWindowObject();
297  ASSERT_TRUE(window.is_object());
298
299  // Regular property.
300  pp::Var exception;
301  ASSERT_TRUE(window.HasProperty("scrollX", &exception));
302  ASSERT_TRUE(exception.is_undefined());
303  ASSERT_FALSE(window.HasMethod("scrollX", &exception));
304  ASSERT_TRUE(exception.is_undefined());
305
306  // Regular method (also counts as HasProperty).
307  ASSERT_TRUE(window.HasProperty("find", &exception));
308  ASSERT_TRUE(exception.is_undefined());
309  ASSERT_TRUE(window.HasMethod("find", &exception));
310  ASSERT_TRUE(exception.is_undefined());
311
312  // Nonexistant ones should return false and not set the exception.
313  ASSERT_FALSE(window.HasProperty("superEvilBit", &exception));
314  ASSERT_TRUE(exception.is_undefined());
315  ASSERT_FALSE(window.HasMethod("superEvilBit", &exception));
316  ASSERT_TRUE(exception.is_undefined());
317
318  // Check exception and return false on invalid property name.
319  ASSERT_FALSE(window.HasProperty(3.14159, &exception));
320  ASSERT_FALSE(exception.is_undefined());
321
322  exception = pp::Var();
323  ASSERT_FALSE(window.HasMethod(3.14159, &exception));
324  ASSERT_FALSE(exception.is_undefined());
325
326  // Try to use something not an object.
327  exception = pp::Var();
328  pp::VarPrivate string_object("asdf");
329  ASSERT_FALSE(string_object.HasProperty("find", &exception));
330  ASSERT_FALSE(exception.is_undefined());
331  exception = pp::Var();
332  ASSERT_FALSE(string_object.HasMethod("find", &exception));
333  ASSERT_FALSE(exception.is_undefined());
334
335  // Try to use an invalid object (need to use the C API).
336  PP_Var invalid_object;
337  invalid_object.type = PP_VARTYPE_OBJECT;
338  invalid_object.value.as_id = static_cast<int64_t>(-1234567);
339  PP_Var exception2 = PP_MakeUndefined();
340  ASSERT_FALSE(var_interface_->HasProperty(invalid_object,
341                                           pp::Var("find").pp_var(),
342                                           &exception2));
343  ASSERT_NE(PP_VARTYPE_UNDEFINED, exception2.type);
344  var_interface_->Release(exception2);
345
346  exception2 = PP_MakeUndefined();
347  ASSERT_FALSE(var_interface_->HasMethod(invalid_object,
348                                         pp::Var("find").pp_var(),
349                                         &exception2));
350  ASSERT_NE(PP_VARTYPE_UNDEFINED, exception2.type);
351  var_interface_->Release(exception2);
352
353  // Getting a valid property/method when the exception is set returns false.
354  exception = pp::Var("Bad something-or-other exception");
355  ASSERT_FALSE(window.HasProperty("find", &exception));
356  ASSERT_FALSE(exception.is_undefined());
357  ASSERT_FALSE(window.HasMethod("find", &exception));
358  ASSERT_FALSE(exception.is_undefined());
359
360  PASS();
361}
362
363// Tests that when the page sends an object to the plugin via a function call,
364// that the refcounting works properly (bug 79813).
365std::string TestVarDeprecated::TestPassReference() {
366  var_from_page_ = pp::Var();
367
368  // Send a JS object from the page to the plugin.
369  pp::Var exception;
370  pp::Var ret = instance_->ExecuteScript(
371      "document.getElementById('plugin').SetValue(function(arg) {"
372          "return 'works' + arg;"
373      "})",
374      &exception);
375  ASSERT_TRUE(exception.is_undefined());
376
377  // We should have gotten an object set for our var_from_page.
378  ASSERT_TRUE(var_from_page_.is_object());
379
380  // If the reference counting works, the object should be valid. We can test
381  // this by executing it (it was a function we defined above) and it should
382  // return "works" concatenated with the argument.
383  pp::VarPrivate function(var_from_page_);
384  pp::Var result = var_from_page_.Call(pp::Var(), "nice");
385  ASSERT_TRUE(result.is_string());
386  ASSERT_TRUE(result.AsString() == "worksnice");
387
388  // Reset var_from_page_ so it doesn't seem like a leak to the var leak
389  // checking code.
390  var_from_page_ = pp::Var();
391
392  PASS();
393}
394
395