| OLD | NEW |
| (Empty) |
| 1 // Copyright 2017 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 "extensions/renderer/api_signature.h" | |
| 6 | |
| 7 #include "base/memory/ptr_util.h" | |
| 8 #include "base/values.h" | |
| 9 #include "extensions/renderer/api_binding_test.h" | |
| 10 #include "extensions/renderer/api_binding_test_util.h" | |
| 11 #include "extensions/renderer/api_invocation_errors.h" | |
| 12 #include "extensions/renderer/api_type_reference_map.h" | |
| 13 #include "extensions/renderer/argument_spec.h" | |
| 14 #include "extensions/renderer/argument_spec_builder.h" | |
| 15 #include "gin/converter.h" | |
| 16 | |
| 17 namespace extensions { | |
| 18 namespace { | |
| 19 | |
| 20 using SpecVector = std::vector<std::unique_ptr<ArgumentSpec>>; | |
| 21 | |
| 22 std::unique_ptr<APISignature> OneString() { | |
| 23 SpecVector specs; | |
| 24 specs.push_back(ArgumentSpecBuilder(ArgumentType::STRING, "string").Build()); | |
| 25 return base::MakeUnique<APISignature>(std::move(specs)); | |
| 26 } | |
| 27 | |
| 28 std::unique_ptr<APISignature> StringAndInt() { | |
| 29 SpecVector specs; | |
| 30 specs.push_back(ArgumentSpecBuilder(ArgumentType::STRING, "string").Build()); | |
| 31 specs.push_back(ArgumentSpecBuilder(ArgumentType::INTEGER, "int").Build()); | |
| 32 return base::MakeUnique<APISignature>(std::move(specs)); | |
| 33 } | |
| 34 | |
| 35 std::unique_ptr<APISignature> StringOptionalIntAndBool() { | |
| 36 SpecVector specs; | |
| 37 specs.push_back(ArgumentSpecBuilder(ArgumentType::STRING, "string").Build()); | |
| 38 specs.push_back( | |
| 39 ArgumentSpecBuilder(ArgumentType::INTEGER, "int").MakeOptional().Build()); | |
| 40 specs.push_back(ArgumentSpecBuilder(ArgumentType::BOOLEAN, "bool").Build()); | |
| 41 return base::MakeUnique<APISignature>(std::move(specs)); | |
| 42 } | |
| 43 | |
| 44 std::unique_ptr<APISignature> OneObject() { | |
| 45 SpecVector specs; | |
| 46 specs.push_back( | |
| 47 ArgumentSpecBuilder(ArgumentType::OBJECT, "obj") | |
| 48 .AddProperty("prop1", | |
| 49 ArgumentSpecBuilder(ArgumentType::STRING).Build()) | |
| 50 .AddProperty( | |
| 51 "prop2", | |
| 52 ArgumentSpecBuilder(ArgumentType::STRING).MakeOptional().Build()) | |
| 53 .Build()); | |
| 54 return base::MakeUnique<APISignature>(std::move(specs)); | |
| 55 } | |
| 56 | |
| 57 std::unique_ptr<APISignature> NoArgs() { | |
| 58 return base::MakeUnique<APISignature>(SpecVector()); | |
| 59 } | |
| 60 | |
| 61 std::unique_ptr<APISignature> IntAndCallback() { | |
| 62 SpecVector specs; | |
| 63 specs.push_back(ArgumentSpecBuilder(ArgumentType::INTEGER, "int").Build()); | |
| 64 specs.push_back( | |
| 65 ArgumentSpecBuilder(ArgumentType::FUNCTION, "callback").Build()); | |
| 66 return base::MakeUnique<APISignature>(std::move(specs)); | |
| 67 } | |
| 68 | |
| 69 std::unique_ptr<APISignature> IntAndOptionalCallback() { | |
| 70 SpecVector specs; | |
| 71 specs.push_back(ArgumentSpecBuilder(ArgumentType::INTEGER, "int").Build()); | |
| 72 specs.push_back(ArgumentSpecBuilder(ArgumentType::FUNCTION, "callback") | |
| 73 .MakeOptional() | |
| 74 .Build()); | |
| 75 return base::MakeUnique<APISignature>(std::move(specs)); | |
| 76 } | |
| 77 | |
| 78 std::unique_ptr<APISignature> OptionalIntAndCallback() { | |
| 79 SpecVector specs; | |
| 80 specs.push_back( | |
| 81 ArgumentSpecBuilder(ArgumentType::INTEGER, "int").MakeOptional().Build()); | |
| 82 specs.push_back( | |
| 83 ArgumentSpecBuilder(ArgumentType::FUNCTION, "callback").Build()); | |
| 84 return base::MakeUnique<APISignature>(std::move(specs)); | |
| 85 } | |
| 86 | |
| 87 std::unique_ptr<APISignature> OptionalCallback() { | |
| 88 SpecVector specs; | |
| 89 specs.push_back(ArgumentSpecBuilder(ArgumentType::FUNCTION, "callback") | |
| 90 .MakeOptional() | |
| 91 .Build()); | |
| 92 return base::MakeUnique<APISignature>(std::move(specs)); | |
| 93 } | |
| 94 | |
| 95 std::unique_ptr<APISignature> IntAnyOptionalObjectOptionalCallback() { | |
| 96 SpecVector specs; | |
| 97 specs.push_back(ArgumentSpecBuilder(ArgumentType::INTEGER, "int").Build()); | |
| 98 specs.push_back(ArgumentSpecBuilder(ArgumentType::ANY, "any").Build()); | |
| 99 specs.push_back( | |
| 100 ArgumentSpecBuilder(ArgumentType::OBJECT, "obj") | |
| 101 .AddProperty( | |
| 102 "prop", | |
| 103 ArgumentSpecBuilder(ArgumentType::INTEGER).MakeOptional().Build()) | |
| 104 .MakeOptional() | |
| 105 .Build()); | |
| 106 specs.push_back(ArgumentSpecBuilder(ArgumentType::FUNCTION, "callback") | |
| 107 .MakeOptional() | |
| 108 .Build()); | |
| 109 return base::MakeUnique<APISignature>(std::move(specs)); | |
| 110 } | |
| 111 | |
| 112 std::unique_ptr<APISignature> RefObj() { | |
| 113 SpecVector specs; | |
| 114 specs.push_back( | |
| 115 ArgumentSpecBuilder(ArgumentType::REF, "obj").SetRef("refObj").Build()); | |
| 116 return base::MakeUnique<APISignature>(std::move(specs)); | |
| 117 } | |
| 118 | |
| 119 std::unique_ptr<APISignature> RefEnum() { | |
| 120 SpecVector specs; | |
| 121 specs.push_back( | |
| 122 ArgumentSpecBuilder(ArgumentType::REF, "enum").SetRef("refEnum").Build()); | |
| 123 return base::MakeUnique<APISignature>(std::move(specs)); | |
| 124 } | |
| 125 | |
| 126 } // namespace | |
| 127 | |
| 128 class APISignatureTest : public APIBindingTest { | |
| 129 public: | |
| 130 APISignatureTest() | |
| 131 : type_refs_(APITypeReferenceMap::InitializeTypeCallback()) {} | |
| 132 ~APISignatureTest() override = default; | |
| 133 | |
| 134 void SetUp() override { | |
| 135 APIBindingTest::SetUp(); | |
| 136 | |
| 137 std::unique_ptr<ArgumentSpec> ref_obj_spec = | |
| 138 ArgumentSpecBuilder(ArgumentType::OBJECT) | |
| 139 .AddProperty("prop1", | |
| 140 ArgumentSpecBuilder(ArgumentType::STRING).Build()) | |
| 141 .AddProperty("prop2", ArgumentSpecBuilder(ArgumentType::INTEGER) | |
| 142 .MakeOptional() | |
| 143 .Build()) | |
| 144 .Build(); | |
| 145 type_refs_.AddSpec("refObj", std::move(ref_obj_spec)); | |
| 146 | |
| 147 type_refs_.AddSpec("refEnum", ArgumentSpecBuilder(ArgumentType::STRING) | |
| 148 .SetEnums({"alpha", "beta"}) | |
| 149 .Build()); | |
| 150 } | |
| 151 | |
| 152 void ExpectPass(const APISignature& signature, | |
| 153 base::StringPiece arg_values, | |
| 154 base::StringPiece expected_parsed_args, | |
| 155 bool expect_callback) { | |
| 156 RunTest(signature, arg_values, expected_parsed_args, expect_callback, true, | |
| 157 std::string()); | |
| 158 } | |
| 159 | |
| 160 void ExpectFailure(const APISignature& signature, | |
| 161 base::StringPiece arg_values, | |
| 162 const std::string& expected_error) { | |
| 163 RunTest(signature, arg_values, base::StringPiece(), false, false, | |
| 164 expected_error); | |
| 165 } | |
| 166 | |
| 167 private: | |
| 168 void RunTest(const APISignature& signature, | |
| 169 base::StringPiece arg_values, | |
| 170 base::StringPiece expected_parsed_args, | |
| 171 bool expect_callback, | |
| 172 bool should_succeed, | |
| 173 const std::string& expected_error) { | |
| 174 SCOPED_TRACE(arg_values); | |
| 175 v8::Local<v8::Context> context = MainContext(); | |
| 176 v8::Local<v8::Value> v8_args = V8ValueFromScriptSource(context, arg_values); | |
| 177 ASSERT_FALSE(v8_args.IsEmpty()); | |
| 178 ASSERT_TRUE(v8_args->IsArray()); | |
| 179 std::vector<v8::Local<v8::Value>> vector_args; | |
| 180 ASSERT_TRUE(gin::ConvertFromV8(isolate(), v8_args, &vector_args)); | |
| 181 | |
| 182 std::unique_ptr<base::ListValue> result; | |
| 183 v8::Local<v8::Function> callback; | |
| 184 std::string error; | |
| 185 bool success = signature.ParseArgumentsToJSON( | |
| 186 context, vector_args, type_refs_, &result, &callback, &error); | |
| 187 EXPECT_EQ(should_succeed, success); | |
| 188 ASSERT_EQ(should_succeed, !!result); | |
| 189 EXPECT_EQ(expect_callback, !callback.IsEmpty()); | |
| 190 if (should_succeed) { | |
| 191 EXPECT_EQ(ReplaceSingleQuotes(expected_parsed_args), | |
| 192 ValueToString(*result)); | |
| 193 } else { | |
| 194 EXPECT_EQ(expected_error, error); | |
| 195 } | |
| 196 } | |
| 197 | |
| 198 APITypeReferenceMap type_refs_; | |
| 199 | |
| 200 DISALLOW_COPY_AND_ASSIGN(APISignatureTest); | |
| 201 }; | |
| 202 | |
| 203 TEST_F(APISignatureTest, BasicSignatureParsing) { | |
| 204 using namespace api_errors; | |
| 205 | |
| 206 v8::HandleScope handle_scope(isolate()); | |
| 207 | |
| 208 { | |
| 209 auto signature = OneString(); | |
| 210 ExpectPass(*signature, "['foo']", "['foo']", false); | |
| 211 ExpectPass(*signature, "['']", "['']", false); | |
| 212 ExpectFailure( | |
| 213 *signature, "[1]", | |
| 214 ArgumentError("string", InvalidType(kTypeString, kTypeInteger))); | |
| 215 ExpectFailure(*signature, "[]", MissingRequiredArgument("string")); | |
| 216 ExpectFailure( | |
| 217 *signature, "[{}]", | |
| 218 ArgumentError("string", InvalidType(kTypeString, kTypeObject))); | |
| 219 ExpectFailure(*signature, "['foo', 'bar']", TooManyArguments()); | |
| 220 } | |
| 221 | |
| 222 { | |
| 223 auto signature = StringAndInt(); | |
| 224 ExpectPass(*signature, "['foo', 42]", "['foo',42]", false); | |
| 225 ExpectPass(*signature, "['foo', -1]", "['foo',-1]", false); | |
| 226 ExpectFailure( | |
| 227 *signature, "[1]", | |
| 228 ArgumentError("string", InvalidType(kTypeString, kTypeInteger))); | |
| 229 ExpectFailure(*signature, "['foo'];", MissingRequiredArgument("int")); | |
| 230 ExpectFailure( | |
| 231 *signature, "[1, 'foo']", | |
| 232 ArgumentError("string", InvalidType(kTypeString, kTypeInteger))); | |
| 233 ExpectFailure(*signature, "['foo', 'foo']", | |
| 234 ArgumentError("int", InvalidType(kTypeInteger, kTypeString))); | |
| 235 ExpectFailure(*signature, "['foo', '1']", | |
| 236 ArgumentError("int", InvalidType(kTypeInteger, kTypeString))); | |
| 237 ExpectFailure(*signature, "['foo', 2.3]", | |
| 238 ArgumentError("int", InvalidType(kTypeInteger, kTypeDouble))); | |
| 239 } | |
| 240 | |
| 241 { | |
| 242 auto signature = StringOptionalIntAndBool(); | |
| 243 ExpectPass(*signature, "['foo', 42, true]", "['foo',42,true]", false); | |
| 244 ExpectPass(*signature, "['foo', true]", "['foo',null,true]", false); | |
| 245 ExpectFailure( | |
| 246 *signature, "['foo', 'bar', true]", | |
| 247 ArgumentError("bool", InvalidType(kTypeBoolean, kTypeString))); | |
| 248 } | |
| 249 | |
| 250 { | |
| 251 auto signature = OneObject(); | |
| 252 ExpectPass(*signature, "[{prop1: 'foo'}]", "[{'prop1':'foo'}]", false); | |
| 253 ExpectFailure(*signature, | |
| 254 "[{ get prop1() { throw new Error('Badness'); } }]", | |
| 255 ArgumentError("obj", ScriptThrewError())); | |
| 256 } | |
| 257 | |
| 258 { | |
| 259 auto signature = NoArgs(); | |
| 260 ExpectPass(*signature, "[]", "[]", false); | |
| 261 ExpectFailure(*signature, "[0]", TooManyArguments()); | |
| 262 ExpectFailure(*signature, "['']", TooManyArguments()); | |
| 263 ExpectFailure(*signature, "[null]", TooManyArguments()); | |
| 264 ExpectFailure(*signature, "[undefined]", TooManyArguments()); | |
| 265 } | |
| 266 | |
| 267 { | |
| 268 auto signature = IntAndCallback(); | |
| 269 ExpectPass(*signature, "[1, function() {}]", "[1]", true); | |
| 270 ExpectFailure( | |
| 271 *signature, "[function() {}]", | |
| 272 ArgumentError("int", InvalidType(kTypeInteger, kTypeFunction))); | |
| 273 ExpectFailure(*signature, "[1]", MissingRequiredArgument("callback")); | |
| 274 } | |
| 275 | |
| 276 { | |
| 277 auto signature = OptionalIntAndCallback(); | |
| 278 ExpectPass(*signature, "[1, function() {}]", "[1]", true); | |
| 279 ExpectPass(*signature, "[function() {}]", "[null]", true); | |
| 280 ExpectFailure(*signature, "[1]", MissingRequiredArgument("callback")); | |
| 281 } | |
| 282 | |
| 283 { | |
| 284 auto signature = OptionalCallback(); | |
| 285 ExpectPass(*signature, "[function() {}]", "[]", true); | |
| 286 ExpectPass(*signature, "[]", "[]", false); | |
| 287 ExpectPass(*signature, "[undefined]", "[]", false); | |
| 288 ExpectFailure( | |
| 289 *signature, "[0]", | |
| 290 ArgumentError("callback", InvalidType(kTypeFunction, kTypeInteger))); | |
| 291 } | |
| 292 | |
| 293 { | |
| 294 auto signature = IntAnyOptionalObjectOptionalCallback(); | |
| 295 ExpectPass(*signature, "[4, {foo: 'bar'}, function() {}]", | |
| 296 "[4,{'foo':'bar'},null]", true); | |
| 297 ExpectPass(*signature, "[4, {foo: 'bar'}]", "[4,{'foo':'bar'},null]", | |
| 298 false); | |
| 299 ExpectPass(*signature, "[4, {foo: 'bar'}, {}]", "[4,{'foo':'bar'},{}]", | |
| 300 false); | |
| 301 ExpectFailure(*signature, "[4, function() {}]", | |
| 302 ArgumentError("any", UnserializableValue())); | |
| 303 ExpectFailure(*signature, "[4]", MissingRequiredArgument("any")); | |
| 304 } | |
| 305 } | |
| 306 | |
| 307 TEST_F(APISignatureTest, TypeRefsTest) { | |
| 308 using namespace api_errors; | |
| 309 | |
| 310 v8::HandleScope handle_scope(isolate()); | |
| 311 | |
| 312 { | |
| 313 auto signature = RefObj(); | |
| 314 ExpectPass(*signature, "[{prop1: 'foo'}]", "[{'prop1':'foo'}]", false); | |
| 315 ExpectPass(*signature, "[{prop1: 'foo', prop2: 2}]", | |
| 316 "[{'prop1':'foo','prop2':2}]", false); | |
| 317 ExpectFailure( | |
| 318 *signature, "[{prop1: 'foo', prop2: 'a'}]", | |
| 319 ArgumentError("obj", PropertyError("prop2", InvalidType(kTypeInteger, | |
| 320 kTypeString)))); | |
| 321 } | |
| 322 | |
| 323 { | |
| 324 auto signature = RefEnum(); | |
| 325 ExpectPass(*signature, "['alpha']", "['alpha']", false); | |
| 326 ExpectPass(*signature, "['beta']", "['beta']", false); | |
| 327 ExpectFailure(*signature, "['gamma']", | |
| 328 ArgumentError("enum", InvalidEnumValue({"alpha", "beta"}))); | |
| 329 } | |
| 330 } | |
| 331 | |
| 332 TEST_F(APISignatureTest, ExpectedSignature) { | |
| 333 EXPECT_EQ("string string", OneString()->GetExpectedSignature()); | |
| 334 EXPECT_EQ("string string, integer int", | |
| 335 StringAndInt()->GetExpectedSignature()); | |
| 336 EXPECT_EQ("string string, optional integer int, boolean bool", | |
| 337 StringOptionalIntAndBool()->GetExpectedSignature()); | |
| 338 EXPECT_EQ("object obj", OneObject()->GetExpectedSignature()); | |
| 339 EXPECT_EQ("", NoArgs()->GetExpectedSignature()); | |
| 340 EXPECT_EQ("integer int, function callback", | |
| 341 IntAndCallback()->GetExpectedSignature()); | |
| 342 EXPECT_EQ("optional integer int, function callback", | |
| 343 OptionalIntAndCallback()->GetExpectedSignature()); | |
| 344 EXPECT_EQ("optional function callback", | |
| 345 OptionalCallback()->GetExpectedSignature()); | |
| 346 EXPECT_EQ( | |
| 347 "integer int, any any, optional object obj, optional function callback", | |
| 348 IntAnyOptionalObjectOptionalCallback()->GetExpectedSignature()); | |
| 349 EXPECT_EQ("refObj obj", RefObj()->GetExpectedSignature()); | |
| 350 EXPECT_EQ("refEnum enum", RefEnum()->GetExpectedSignature()); | |
| 351 } | |
| 352 | |
| 353 TEST_F(APISignatureTest, ParseIgnoringSchema) { | |
| 354 v8::HandleScope handle_scope(isolate()); | |
| 355 v8::Local<v8::Context> context = MainContext(); | |
| 356 | |
| 357 auto string_to_v8_vector = [context](base::StringPiece args) { | |
| 358 v8::Local<v8::Value> v8_args = V8ValueFromScriptSource(context, args); | |
| 359 EXPECT_FALSE(v8_args.IsEmpty()); | |
| 360 EXPECT_TRUE(v8_args->IsArray()); | |
| 361 std::vector<v8::Local<v8::Value>> vector_args; | |
| 362 EXPECT_TRUE( | |
| 363 gin::ConvertFromV8(context->GetIsolate(), v8_args, &vector_args)); | |
| 364 return vector_args; | |
| 365 }; | |
| 366 | |
| 367 { | |
| 368 // Test with providing an optional callback. | |
| 369 auto signature = IntAndOptionalCallback(); | |
| 370 std::vector<v8::Local<v8::Value>> v8_args = | |
| 371 string_to_v8_vector("[1, function() {}]"); | |
| 372 v8::Local<v8::Function> callback; | |
| 373 std::unique_ptr<base::ListValue> parsed; | |
| 374 EXPECT_TRUE(signature->ConvertArgumentsIgnoringSchema(context, v8_args, | |
| 375 &parsed, &callback)); | |
| 376 ASSERT_TRUE(parsed); | |
| 377 EXPECT_EQ("[1]", ValueToString(*parsed)); | |
| 378 EXPECT_FALSE(callback.IsEmpty()); | |
| 379 } | |
| 380 | |
| 381 { | |
| 382 // Test with omitting the optional callback. | |
| 383 auto signature = IntAndOptionalCallback(); | |
| 384 std::vector<v8::Local<v8::Value>> v8_args = | |
| 385 string_to_v8_vector("[1, null]"); | |
| 386 v8::Local<v8::Function> callback; | |
| 387 std::unique_ptr<base::ListValue> parsed; | |
| 388 EXPECT_TRUE(signature->ConvertArgumentsIgnoringSchema(context, v8_args, | |
| 389 &parsed, &callback)); | |
| 390 ASSERT_TRUE(parsed); | |
| 391 EXPECT_EQ("[1]", ValueToString(*parsed)); | |
| 392 EXPECT_TRUE(callback.IsEmpty()); | |
| 393 } | |
| 394 | |
| 395 { | |
| 396 // Test with providing something completely different than the spec, which | |
| 397 // is (unfortunately) allowed and used. | |
| 398 auto signature = OneString(); | |
| 399 std::vector<v8::Local<v8::Value>> v8_args = | |
| 400 string_to_v8_vector("[{not: 'a string'}]"); | |
| 401 v8::Local<v8::Function> callback; | |
| 402 std::unique_ptr<base::ListValue> parsed; | |
| 403 EXPECT_TRUE(signature->ConvertArgumentsIgnoringSchema(context, v8_args, | |
| 404 &parsed, &callback)); | |
| 405 ASSERT_TRUE(parsed); | |
| 406 EXPECT_EQ(R"([{"not":"a string"}])", ValueToString(*parsed)); | |
| 407 EXPECT_TRUE(callback.IsEmpty()); | |
| 408 } | |
| 409 } | |
| 410 | |
| 411 } // namespace extensions | |
| OLD | NEW |