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 20 namespace { 21 22 uint32_t kInvalidLength = static_cast<uint32_t>(-1); 23 24 static const char kSetValueFunction[] = "SetValue"; 25 26 // ScriptableObject used by the var tests. 27 class 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 41 bool VarScriptableObject::HasMethod(const pp::Var& name, pp::Var* exception) { 42 if (!name.is_string()) 43 return false; 44 return name.AsString() == kSetValueFunction; 45 } 46 47 pp::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 66 REGISTER_TEST_CASE(VarDeprecated); 67 68 bool 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 74 void 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 86 pp::deprecated::ScriptableObject* TestVarDeprecated::CreateTestObject() { 87 return new VarScriptableObject(this); 88 } 89 90 std::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 120 std::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 148 std::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 158 std::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 193 std::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 217 std::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 241 std::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 295 std::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). 365 std::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