| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ppapi/tests/test_var_deprecated.h" |
| |
| #include <stdint.h> |
| #include <string.h> |
| |
| #include <limits> |
| |
| #include "ppapi/c/dev/ppb_var_deprecated.h" |
| #include "ppapi/c/pp_var.h" |
| #include "ppapi/cpp/dev/scriptable_object_deprecated.h" |
| #include "ppapi/cpp/instance.h" |
| #include "ppapi/cpp/module.h" |
| #include "ppapi/cpp/private/var_private.h" |
| #include "ppapi/cpp/var.h" |
| #include "ppapi/tests/testing_instance.h" |
| |
| namespace { |
| |
| uint32_t kInvalidLength = static_cast<uint32_t>(-1); |
| |
| static const char kSetValueFunction[] = "SetValue"; |
| |
| // ScriptableObject used by the var tests. |
| class VarScriptableObject : public pp::deprecated::ScriptableObject { |
| public: |
| explicit VarScriptableObject(TestVarDeprecated* v) |
| : test_var_deprecated_(v) {} |
| |
| // pp::deprecated::ScriptableObject overrides. |
| bool HasMethod(const pp::Var& name, pp::Var* exception); |
| pp::Var Call(const pp::Var& name, |
| const std::vector<pp::Var>& args, |
| pp::Var* exception); |
| |
| private: |
| TestVarDeprecated* test_var_deprecated_; |
| }; |
| |
| bool VarScriptableObject::HasMethod(const pp::Var& name, pp::Var* exception) { |
| if (!name.is_string()) |
| return false; |
| return name.AsString() == kSetValueFunction; |
| } |
| |
| pp::Var VarScriptableObject::Call(const pp::Var& method_name, |
| const std::vector<pp::Var>& args, |
| pp::Var* exception) { |
| if (!method_name.is_string()) |
| return false; |
| std::string name = method_name.AsString(); |
| |
| if (name == kSetValueFunction) { |
| if (args.size() != 1) |
| *exception = pp::Var("Bad argument to SetValue(<value>)"); |
| else |
| test_var_deprecated_->set_var_from_page(pp::VarPrivate(args[0])); |
| } |
| |
| return pp::Var(); |
| } |
| |
| } // namespace |
| |
| REGISTER_TEST_CASE(VarDeprecated); |
| |
| bool TestVarDeprecated::Init() { |
| var_interface_ = static_cast<const PPB_Var_Deprecated*>( |
| pp::Module::Get()->GetBrowserInterface(PPB_VAR_DEPRECATED_INTERFACE)); |
| return var_interface_ && CheckTestingInterface(); |
| } |
| |
| void TestVarDeprecated::RunTests(const std::string& filter) { |
| RUN_TEST(BasicString, filter); |
| RUN_TEST(InvalidAndEmpty, filter); |
| RUN_TEST(InvalidUtf8, filter); |
| RUN_TEST(NullInputInUtf8Conversion, filter); |
| RUN_TEST(ValidUtf8, filter); |
| RUN_TEST(Utf8WithEmbeddedNulls, filter); |
| RUN_TEST(VarToUtf8ForWrongType, filter); |
| RUN_TEST(HasPropertyAndMethod, filter); |
| RUN_TEST(PassReference, filter); |
| } |
| |
| pp::deprecated::ScriptableObject* TestVarDeprecated::CreateTestObject() { |
| return new VarScriptableObject(this); |
| } |
| |
| std::string TestVarDeprecated::TestBasicString() { |
| uint32_t before_object = testing_interface_->GetLiveObjectsForInstance( |
| instance_->pp_instance()); |
| { |
| const char kStr[] = "Hello"; |
| const uint32_t kStrLen(sizeof(kStr) - 1); |
| PP_Var str = var_interface_->VarFromUtf8(pp::Module::Get()->pp_module(), |
| kStr, kStrLen); |
| ASSERT_EQ(PP_VARTYPE_STRING, str.type); |
| |
| // Reading back the string should work. |
| uint32_t len = 0; |
| const char* result = var_interface_->VarToUtf8(str, &len); |
| ASSERT_EQ(kStrLen, len); |
| ASSERT_EQ(0, strncmp(kStr, result, kStrLen)); |
| |
| // Destroy the string, readback should now fail. |
| var_interface_->Release(str); |
| result = var_interface_->VarToUtf8(str, &len); |
| ASSERT_EQ(0, len); |
| ASSERT_EQ(NULL, result); |
| } |
| |
| // Make sure nothing leaked. |
| ASSERT_TRUE(testing_interface_->GetLiveObjectsForInstance( |
| instance_->pp_instance()) == before_object); |
| |
| PASS(); |
| } |
| |
| std::string TestVarDeprecated::TestInvalidAndEmpty() { |
| PP_Var invalid_string; |
| invalid_string.type = PP_VARTYPE_STRING; |
| invalid_string.value.as_id = 31415926; |
| |
| // Invalid strings should give NULL as the return value. |
| uint32_t len = std::numeric_limits<uint32_t>::max(); |
| const char* result = var_interface_->VarToUtf8(invalid_string, &len); |
| ASSERT_EQ(0, len); |
| ASSERT_EQ(NULL, result); |
| |
| // Same with vars that are not strings. |
| len = std::numeric_limits<uint32_t>::max(); |
| pp::Var int_var(42); |
| result = var_interface_->VarToUtf8(int_var.pp_var(), &len); |
| ASSERT_EQ(0, len); |
| ASSERT_EQ(NULL, result); |
| |
| // Empty strings should return non-NULL. |
| pp::Var empty_string(""); |
| len = std::numeric_limits<uint32_t>::max(); |
| result = var_interface_->VarToUtf8(empty_string.pp_var(), &len); |
| ASSERT_EQ(0, len); |
| ASSERT_NE(NULL, result); |
| |
| PASS(); |
| } |
| |
| std::string TestVarDeprecated::TestInvalidUtf8() { |
| // utf8ăăăȘă (japanese for "is not utf8") in shift-jis encoding. |
| static const char kSjisString[] = "utf8\x82\xb6\x82\xe1\x82\xc8\x82\xa2"; |
| pp::Var sjis(kSjisString); |
| if (!sjis.is_null()) |
| return "Non-UTF8 string was permitted erroneously."; |
| |
| PASS(); |
| } |
| |
| std::string TestVarDeprecated::TestNullInputInUtf8Conversion() { |
| // This test talks directly to the C interface to access edge cases that |
| // cannot be exercised via the C++ interface. |
| PP_Var converted_string; |
| |
| // 0-length string should not dereference input string, and should produce |
| // an empty string. |
| converted_string = var_interface_->VarFromUtf8( |
| pp::Module::Get()->pp_module(), NULL, 0); |
| if (converted_string.type != PP_VARTYPE_STRING) { |
| return "Expected 0 length to return empty string."; |
| } |
| |
| // Now convert it back. |
| uint32_t length = kInvalidLength; |
| const char* result = NULL; |
| result = var_interface_->VarToUtf8(converted_string, &length); |
| if (length != 0) { |
| return "Expected 0 length string on conversion."; |
| } |
| if (result == NULL) { |
| return "Expected a non-null result for 0-lengthed string from VarToUtf8."; |
| } |
| var_interface_->Release(converted_string); |
| |
| // Should not crash, and make an empty string. |
| const char* null_string = NULL; |
| pp::Var null_var(null_string); |
| if (!null_var.is_string() || !null_var.AsString().empty()) { |
| return "Expected NULL input to make an empty string Var."; |
| } |
| |
| PASS(); |
| } |
| |
| std::string TestVarDeprecated::TestValidUtf8() { |
| // From UTF8 string -> PP_Var. |
| // Chinese for "I am utf8." |
| static const char kValidUtf8[] = "\xe6\x88\x91\xe6\x98\xafutf8."; |
| pp::Var converted_string(kValidUtf8); |
| |
| if (converted_string.is_null()) |
| return "Unable to convert valid utf8 to var."; |
| |
| // Since we're already here, test PP_Var back to UTF8 string. |
| std::string returned_string = converted_string.AsString(); |
| |
| // We need to check against 1 less than sizeof because the resulting string |
| // is technically not NULL terminated by API design. |
| if (returned_string.size() != sizeof(kValidUtf8) - 1) { |
| return "Unable to convert utf8 string back from var."; |
| } |
| if (returned_string != kValidUtf8) { |
| return "String mismatches on conversion back from PP_Var."; |
| } |
| |
| PASS(); |
| } |
| |
| std::string TestVarDeprecated::TestUtf8WithEmbeddedNulls() { |
| // From UTF8 string with embedded nulls -> PP_Var. |
| // Chinese for "also utf8." |
| static const char kUtf8WithEmbededNull[] = "\xe6\xb9\x9f\xe6\x98\xaf\0utf8."; |
| std::string orig_string(kUtf8WithEmbededNull, |
| sizeof(kUtf8WithEmbededNull) -1); |
| pp::Var converted_string(orig_string); |
| |
| if (converted_string.is_null()) |
| return "Unable to convert utf8 with embedded nulls to var."; |
| |
| // Since we're already here, test PP_Var back to UTF8 string. |
| std::string returned_string = converted_string.AsString(); |
| |
| if (returned_string.size() != orig_string.size()) { |
| return "Unable to convert utf8 with embedded nulls back from var."; |
| } |
| if (returned_string != orig_string) { |
| return "String mismatches on conversion back from PP_Var."; |
| } |
| |
| PASS(); |
| } |
| |
| std::string TestVarDeprecated::TestVarToUtf8ForWrongType() { |
| uint32_t length = kInvalidLength; |
| const char* result = NULL; |
| result = var_interface_->VarToUtf8(PP_MakeUndefined(), &length); |
| if (length != 0) { |
| return "Expected 0 on string conversion from Void var."; |
| } |
| if (result != NULL) { |
| return "Expected NULL on string conversion from Void var."; |
| } |
| |
| length = kInvalidLength; |
| result = NULL; |
| result = var_interface_->VarToUtf8(PP_MakeNull(), &length); |
| if (length != 0) { |
| return "Expected 0 on string conversion from Null var."; |
| } |
| if (result != NULL) { |
| return "Expected NULL on string conversion from Null var."; |
| } |
| |
| length = kInvalidLength; |
| result = NULL; |
| result = var_interface_->VarToUtf8(PP_MakeBool(PP_TRUE), &length); |
| if (length != 0) { |
| return "Expected 0 on string conversion from Bool var."; |
| } |
| if (result != NULL) { |
| return "Expected NULL on string conversion from Bool var."; |
| } |
| |
| length = kInvalidLength; |
| result = NULL; |
| result = var_interface_->VarToUtf8(PP_MakeInt32(1), &length); |
| if (length != 0) { |
| return "Expected 0 on string conversion from Int32 var."; |
| } |
| if (result != NULL) { |
| return "Expected NULL on string conversion from Int32 var."; |
| } |
| |
| length = kInvalidLength; |
| result = NULL; |
| result = var_interface_->VarToUtf8(PP_MakeDouble(1.0), &length); |
| if (length != 0) { |
| return "Expected 0 on string conversion from Double var."; |
| } |
| if (result != NULL) { |
| return "Expected NULL on string conversion from Double var."; |
| } |
| |
| PASS(); |
| } |
| |
| std::string TestVarDeprecated::TestHasPropertyAndMethod() { |
| pp::VarPrivate window = instance_->GetWindowObject(); |
| ASSERT_TRUE(window.is_object()); |
| |
| // Regular property. |
| pp::Var exception; |
| ASSERT_TRUE(window.HasProperty("scrollX", &exception)); |
| ASSERT_TRUE(exception.is_undefined()); |
| ASSERT_FALSE(window.HasMethod("scrollX", &exception)); |
| ASSERT_TRUE(exception.is_undefined()); |
| |
| // Regular method (also counts as HasProperty). |
| ASSERT_TRUE(window.HasProperty("find", &exception)); |
| ASSERT_TRUE(exception.is_undefined()); |
| ASSERT_TRUE(window.HasMethod("find", &exception)); |
| ASSERT_TRUE(exception.is_undefined()); |
| |
| // Nonexistant ones should return false and not set the exception. |
| ASSERT_FALSE(window.HasProperty("superEvilBit", &exception)); |
| ASSERT_TRUE(exception.is_undefined()); |
| ASSERT_FALSE(window.HasMethod("superEvilBit", &exception)); |
| ASSERT_TRUE(exception.is_undefined()); |
| |
| // Check exception and return false on invalid property name. |
| ASSERT_FALSE(window.HasProperty(3.14159, &exception)); |
| ASSERT_FALSE(exception.is_undefined()); |
| |
| exception = pp::Var(); |
| ASSERT_FALSE(window.HasMethod(3.14159, &exception)); |
| ASSERT_FALSE(exception.is_undefined()); |
| |
| // Try to use something not an object. |
| exception = pp::Var(); |
| pp::VarPrivate string_object("asdf"); |
| ASSERT_FALSE(string_object.HasProperty("find", &exception)); |
| ASSERT_FALSE(exception.is_undefined()); |
| exception = pp::Var(); |
| ASSERT_FALSE(string_object.HasMethod("find", &exception)); |
| ASSERT_FALSE(exception.is_undefined()); |
| |
| // Try to use an invalid object (need to use the C API). |
| PP_Var invalid_object; |
| invalid_object.type = PP_VARTYPE_OBJECT; |
| invalid_object.value.as_id = static_cast<int64_t>(-1234567); |
| PP_Var exception2 = PP_MakeUndefined(); |
| ASSERT_FALSE(var_interface_->HasProperty(invalid_object, |
| pp::Var("find").pp_var(), |
| &exception2)); |
| ASSERT_NE(PP_VARTYPE_UNDEFINED, exception2.type); |
| var_interface_->Release(exception2); |
| |
| exception2 = PP_MakeUndefined(); |
| ASSERT_FALSE(var_interface_->HasMethod(invalid_object, |
| pp::Var("find").pp_var(), |
| &exception2)); |
| ASSERT_NE(PP_VARTYPE_UNDEFINED, exception2.type); |
| var_interface_->Release(exception2); |
| |
| // Getting a valid property/method when the exception is set returns false. |
| exception = pp::Var("Bad something-or-other exception"); |
| ASSERT_FALSE(window.HasProperty("find", &exception)); |
| ASSERT_FALSE(exception.is_undefined()); |
| ASSERT_FALSE(window.HasMethod("find", &exception)); |
| ASSERT_FALSE(exception.is_undefined()); |
| |
| PASS(); |
| } |
| |
| // Tests that when the page sends an object to the plugin via a function call, |
| // that the refcounting works properly (bug 79813). |
| std::string TestVarDeprecated::TestPassReference() { |
| var_from_page_ = pp::Var(); |
| |
| // Send a JS object from the page to the plugin. |
| pp::Var exception; |
| pp::Var ret = instance_->ExecuteScript( |
| "document.getElementById('plugin').SetValue(function(arg) {" |
| "return 'works' + arg;" |
| "})", |
| &exception); |
| ASSERT_TRUE(exception.is_undefined()); |
| |
| // We should have gotten an object set for our var_from_page. |
| ASSERT_TRUE(var_from_page_.is_object()); |
| |
| // If the reference counting works, the object should be valid. We can test |
| // this by executing it (it was a function we defined above) and it should |
| // return "works" concatenated with the argument. |
| pp::VarPrivate function(var_from_page_); |
| pp::Var result = var_from_page_.Call(pp::Var(), "nice"); |
| ASSERT_TRUE(result.is_string()); |
| ASSERT_TRUE(result.AsString() == "worksnice"); |
| |
| // Reset var_from_page_ so it doesn't seem like a leak to the var leak |
| // checking code. |
| var_from_page_ = pp::Var(); |
| |
| PASS(); |
| } |
| |