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