api_request_handler_unittest.cc 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901
  1. // Copyright 2016 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. #include "extensions/renderer/bindings/api_request_handler.h"
  5. #include "base/bind.h"
  6. #include "base/callback_helpers.h"
  7. #include "base/strings/strcat.h"
  8. #include "base/strings/stringprintf.h"
  9. #include "base/test/bind.h"
  10. #include "base/values.h"
  11. #include "extensions/renderer/bindings/api_binding_test.h"
  12. #include "extensions/renderer/bindings/api_binding_test_util.h"
  13. #include "extensions/renderer/bindings/exception_handler.h"
  14. #include "extensions/renderer/bindings/test_interaction_provider.h"
  15. #include "extensions/renderer/bindings/test_js_runner.h"
  16. #include "extensions/renderer/v8_helpers.h"
  17. #include "gin/converter.h"
  18. #include "gin/function_template.h"
  19. #include "gin/public/context_holder.h"
  20. #include "gin/public/isolate_holder.h"
  21. #include "testing/gmock/include/gmock/gmock.h"
  22. #include "third_party/abseil-cpp/absl/types/optional.h"
  23. namespace extensions {
  24. namespace {
  25. const char kEchoArgs[] =
  26. "(function() { this.result = Array.from(arguments); })";
  27. const char kMethod[] = "method";
  28. // TODO(devlin): We should probably hoist this up to e.g. api_binding_types.h.
  29. using ArgumentList = std::vector<v8::Local<v8::Value>>;
  30. // TODO(devlin): Should we move some parts of api_binding_unittest.cc to here?
  31. } // namespace
  32. class APIRequestHandlerTest : public APIBindingTest {
  33. public:
  34. APIRequestHandlerTest(const APIRequestHandlerTest&) = delete;
  35. APIRequestHandlerTest& operator=(const APIRequestHandlerTest&) = delete;
  36. std::unique_ptr<APIRequestHandler> CreateRequestHandler() {
  37. return std::make_unique<APIRequestHandler>(
  38. base::DoNothing(),
  39. APILastError(APILastError::GetParent(), binding::AddConsoleError()),
  40. exception_handler(), interaction_provider());
  41. }
  42. void SaveUserActivationState(v8::Local<v8::Context> context,
  43. absl::optional<bool>* ran_with_user_gesture) {
  44. *ran_with_user_gesture =
  45. interaction_provider()->HasActiveInteraction(context);
  46. }
  47. protected:
  48. APIRequestHandlerTest() {}
  49. ~APIRequestHandlerTest() override {}
  50. std::unique_ptr<TestJSRunner::Scope> CreateTestJSRunner() override {
  51. return std::make_unique<TestJSRunner::Scope>(
  52. std::make_unique<TestJSRunner>(base::BindRepeating(
  53. &APIRequestHandlerTest::SetDidRunJS, base::Unretained(this))));
  54. }
  55. InteractionProvider* interaction_provider() {
  56. if (!interaction_provider_)
  57. interaction_provider_ = std::make_unique<TestInteractionProvider>();
  58. return interaction_provider_.get();
  59. }
  60. ExceptionHandler* exception_handler() {
  61. if (!exception_handler_) {
  62. exception_handler_ =
  63. std::make_unique<ExceptionHandler>(binding::AddConsoleError());
  64. }
  65. return exception_handler_.get();
  66. }
  67. bool did_run_js() const { return did_run_js_; }
  68. private:
  69. void SetDidRunJS() { did_run_js_ = true; }
  70. bool did_run_js_ = false;
  71. std::unique_ptr<TestInteractionProvider> interaction_provider_;
  72. std::unique_ptr<ExceptionHandler> exception_handler_;
  73. };
  74. // Tests adding a request to the request handler, and then triggering the
  75. // response.
  76. TEST_F(APIRequestHandlerTest, AddRequestAndCompleteRequestTest) {
  77. v8::HandleScope handle_scope(isolate());
  78. v8::Local<v8::Context> context = MainContext();
  79. std::unique_ptr<APIRequestHandler> request_handler = CreateRequestHandler();
  80. EXPECT_TRUE(request_handler->GetPendingRequestIdsForTesting().empty());
  81. v8::Local<v8::Function> function = FunctionFromString(context, kEchoArgs);
  82. ASSERT_FALSE(function.IsEmpty());
  83. request_handler->StartRequest(
  84. context, kMethod, std::make_unique<base::ListValue>(),
  85. binding::AsyncResponseType::kCallback, function,
  86. v8::Local<v8::Function>(), binding::ResultModifierFunction());
  87. int request_id = request_handler->last_sent_request_id();
  88. EXPECT_THAT(request_handler->GetPendingRequestIdsForTesting(),
  89. testing::UnorderedElementsAre(request_id));
  90. const char kArguments[] = "['foo',1,{'prop1':'bar'}]";
  91. request_handler->CompleteRequest(request_id, ListValueFromString(kArguments),
  92. std::string());
  93. EXPECT_TRUE(did_run_js());
  94. EXPECT_EQ(ReplaceSingleQuotes(kArguments),
  95. GetStringPropertyFromObject(context->Global(), context, "result"));
  96. EXPECT_TRUE(request_handler->GetPendingRequestIdsForTesting().empty());
  97. request_handler->StartRequest(
  98. context, kMethod, std::make_unique<base::ListValue>(),
  99. binding::AsyncResponseType::kNone, v8::Local<v8::Function>(),
  100. v8::Local<v8::Function>(), binding::ResultModifierFunction());
  101. request_id = request_handler->last_sent_request_id();
  102. EXPECT_NE(-1, request_id);
  103. request_handler->CompleteRequest(request_id, base::Value::List(),
  104. std::string());
  105. }
  106. // Tests that trying to run non-existent or invalided requests is a no-op.
  107. TEST_F(APIRequestHandlerTest, InvalidRequestsTest) {
  108. v8::HandleScope handle_scope(isolate());
  109. v8::Local<v8::Context> context = MainContext();
  110. std::unique_ptr<APIRequestHandler> request_handler = CreateRequestHandler();
  111. v8::Local<v8::Function> function = FunctionFromString(context, kEchoArgs);
  112. ASSERT_FALSE(function.IsEmpty());
  113. request_handler->StartRequest(
  114. context, kMethod, std::make_unique<base::ListValue>(),
  115. binding::AsyncResponseType::kCallback, function,
  116. v8::Local<v8::Function>(), binding::ResultModifierFunction());
  117. int request_id = request_handler->last_sent_request_id();
  118. EXPECT_THAT(request_handler->GetPendingRequestIdsForTesting(),
  119. testing::UnorderedElementsAre(request_id));
  120. // Try running with a non-existent request id.
  121. int fake_request_id = 42;
  122. request_handler->CompleteRequest(
  123. fake_request_id, ListValueFromString("['foo']"), std::string());
  124. EXPECT_FALSE(did_run_js());
  125. // Try running with a request from an invalidated context.
  126. request_handler->InvalidateContext(context);
  127. request_handler->CompleteRequest(request_id, ListValueFromString("['foo']"),
  128. std::string());
  129. EXPECT_FALSE(did_run_js());
  130. }
  131. TEST_F(APIRequestHandlerTest, MultipleRequestsAndContexts) {
  132. v8::HandleScope handle_scope(isolate());
  133. v8::Local<v8::Context> context_a = MainContext();
  134. v8::Local<v8::Context> context_b = AddContext();
  135. std::unique_ptr<APIRequestHandler> request_handler = CreateRequestHandler();
  136. // By having both different arguments and different behaviors in the
  137. // callbacks, we can easily verify that the right function is called in the
  138. // right context.
  139. v8::Local<v8::Function> function_a = FunctionFromString(
  140. context_a, "(function(res) { this.result = res + 'alpha'; })");
  141. v8::Local<v8::Function> function_b = FunctionFromString(
  142. context_b, "(function(res) { this.result = res + 'beta'; })");
  143. request_handler->StartRequest(
  144. context_a, kMethod, std::make_unique<base::ListValue>(),
  145. binding::AsyncResponseType::kCallback, function_a,
  146. v8::Local<v8::Function>(), binding::ResultModifierFunction());
  147. int request_a = request_handler->last_sent_request_id();
  148. request_handler->StartRequest(
  149. context_b, kMethod, std::make_unique<base::ListValue>(),
  150. binding::AsyncResponseType::kCallback, function_b,
  151. v8::Local<v8::Function>(), binding::ResultModifierFunction());
  152. int request_b = request_handler->last_sent_request_id();
  153. EXPECT_THAT(request_handler->GetPendingRequestIdsForTesting(),
  154. testing::UnorderedElementsAre(request_a, request_b));
  155. request_handler->CompleteRequest(
  156. request_a, ListValueFromString("['response_a:']"), std::string());
  157. EXPECT_TRUE(did_run_js());
  158. EXPECT_THAT(request_handler->GetPendingRequestIdsForTesting(),
  159. testing::UnorderedElementsAre(request_b));
  160. EXPECT_EQ(
  161. ReplaceSingleQuotes("'response_a:alpha'"),
  162. GetStringPropertyFromObject(context_a->Global(), context_a, "result"));
  163. request_handler->CompleteRequest(
  164. request_b, ListValueFromString("['response_b:']"), std::string());
  165. EXPECT_TRUE(request_handler->GetPendingRequestIdsForTesting().empty());
  166. EXPECT_EQ(
  167. ReplaceSingleQuotes("'response_b:beta'"),
  168. GetStringPropertyFromObject(context_b->Global(), context_b, "result"));
  169. }
  170. TEST_F(APIRequestHandlerTest, CustomCallbackArguments) {
  171. v8::HandleScope handle_scope(isolate());
  172. v8::Local<v8::Context> context = MainContext();
  173. std::unique_ptr<APIRequestHandler> request_handler = CreateRequestHandler();
  174. v8::Local<v8::Function> custom_callback =
  175. FunctionFromString(context, kEchoArgs);
  176. v8::Local<v8::Function> callback = FunctionFromString(
  177. context, "(function(arg) {this.callbackCalled = arg})");
  178. ASSERT_FALSE(callback.IsEmpty());
  179. ASSERT_FALSE(custom_callback.IsEmpty());
  180. request_handler->StartRequest(
  181. context, "method", std::make_unique<base::ListValue>(),
  182. binding::AsyncResponseType::kCallback, callback, custom_callback,
  183. binding::ResultModifierFunction());
  184. int request_id = request_handler->last_sent_request_id();
  185. EXPECT_THAT(request_handler->GetPendingRequestIdsForTesting(),
  186. testing::UnorderedElementsAre(request_id));
  187. request_handler->CompleteRequest(
  188. request_id, ListValueFromString("['response', 'arguments']"),
  189. std::string());
  190. EXPECT_TRUE(did_run_js());
  191. v8::Local<v8::Array> result;
  192. ASSERT_TRUE(
  193. GetPropertyFromObjectAs(context->Global(), context, "result", &result));
  194. ArgumentList args;
  195. ASSERT_TRUE(gin::Converter<ArgumentList>::FromV8(isolate(), result, &args));
  196. ASSERT_EQ(3u, args.size());
  197. EXPECT_TRUE(args[0]->IsFunction());
  198. EXPECT_EQ(R"("response")", V8ToString(args[1], context));
  199. EXPECT_EQ(R"("arguments")", V8ToString(args[2], context));
  200. EXPECT_TRUE(request_handler->GetPendingRequestIdsForTesting().empty());
  201. // The function passed to the custom callback isn't actually the same callback
  202. // that was passed in when calling the API, but invoking it below should still
  203. // result in the original callback being run.
  204. EXPECT_TRUE(
  205. GetPropertyFromObject(context->Global(), context, "callbackCalled")
  206. ->IsUndefined());
  207. v8::Local<v8::Value> callback_args[] = {gin::StringToV8(isolate(), "foo")};
  208. RunFunctionOnGlobal(args[0].As<v8::Function>(), context, 1, callback_args);
  209. EXPECT_EQ(R"("foo")", GetStringPropertyFromObject(context->Global(), context,
  210. "callbackCalled"));
  211. }
  212. TEST_F(APIRequestHandlerTest, CustomCallbackWithErrorInExtensionCallback) {
  213. v8::HandleScope handle_scope(isolate());
  214. v8::Local<v8::Context> context = MainContext();
  215. auto add_console_error = [](absl::optional<std::string>* error_out,
  216. v8::Local<v8::Context> context,
  217. const std::string& error) { *error_out = error; };
  218. absl::optional<std::string> logged_error;
  219. ExceptionHandler exception_handler(
  220. base::BindRepeating(add_console_error, &logged_error));
  221. APIRequestHandler request_handler(
  222. base::DoNothing(),
  223. APILastError(APILastError::GetParent(), binding::AddConsoleError()),
  224. &exception_handler, interaction_provider());
  225. constexpr char kExtensionCallback[] =
  226. R"((function() {
  227. this.callbackCalled = true;
  228. throw new Error('hello');
  229. }))";
  230. v8::Local<v8::Function> callback_throwing_error =
  231. FunctionFromString(context, kExtensionCallback);
  232. constexpr char kCustomCallback[] =
  233. R"((function(callback) {
  234. this.customCallbackCalled = true;
  235. callback();
  236. }))";
  237. v8::Local<v8::Function> custom_callback =
  238. FunctionFromString(context, kCustomCallback);
  239. ASSERT_FALSE(callback_throwing_error.IsEmpty());
  240. ASSERT_FALSE(custom_callback.IsEmpty());
  241. request_handler.StartRequest(
  242. context, "method", std::make_unique<base::ListValue>(),
  243. binding::AsyncResponseType::kCallback, callback_throwing_error,
  244. custom_callback, binding::ResultModifierFunction());
  245. int request_id = request_handler.last_sent_request_id();
  246. EXPECT_THAT(request_handler.GetPendingRequestIdsForTesting(),
  247. testing::UnorderedElementsAre(request_id));
  248. v8::TryCatch try_catch(isolate());
  249. {
  250. TestJSRunner::AllowErrors allow_errors;
  251. request_handler.CompleteRequest(request_id, base::Value::List(),
  252. std::string());
  253. }
  254. EXPECT_TRUE(did_run_js());
  255. EXPECT_TRUE(request_handler.GetPendingRequestIdsForTesting().empty());
  256. EXPECT_EQ("true", GetStringPropertyFromObject(context->Global(), context,
  257. "customCallbackCalled"));
  258. EXPECT_EQ("true", GetStringPropertyFromObject(context->Global(), context,
  259. "callbackCalled"));
  260. // The `try_catch` should not have caught an error. This is important to not
  261. // disrupt our bindings code (or other running JS) when asynchronously
  262. // returning from an API call. Instead, the error should be caught and handled
  263. // by the exception handler.
  264. EXPECT_FALSE(try_catch.HasCaught());
  265. ASSERT_TRUE(logged_error);
  266. EXPECT_THAT(*logged_error,
  267. testing::StartsWith("Error handling response: Error: hello"));
  268. }
  269. TEST_F(APIRequestHandlerTest, CustomCallbackPromiseBased) {
  270. v8::HandleScope handle_scope(isolate());
  271. v8::Local<v8::Context> context = MainContext();
  272. std::unique_ptr<APIRequestHandler> request_handler = CreateRequestHandler();
  273. v8::Local<v8::Function> custom_callback =
  274. FunctionFromString(context, kEchoArgs);
  275. ASSERT_FALSE(custom_callback.IsEmpty());
  276. v8::Local<v8::Promise> promise = request_handler->StartRequest(
  277. context, "method", std::make_unique<base::ListValue>(),
  278. binding::AsyncResponseType::kPromise, v8::Local<v8::Function>(),
  279. custom_callback, binding::ResultModifierFunction());
  280. ASSERT_FALSE(promise.IsEmpty());
  281. int request_id = request_handler->last_sent_request_id();
  282. EXPECT_THAT(request_handler->GetPendingRequestIdsForTesting(),
  283. testing::UnorderedElementsAre(request_id));
  284. request_handler->CompleteRequest(
  285. request_id, ListValueFromString("['response', 'arguments']"),
  286. std::string());
  287. EXPECT_TRUE(did_run_js());
  288. v8::Local<v8::Array> result;
  289. ASSERT_TRUE(
  290. GetPropertyFromObjectAs(context->Global(), context, "result", &result));
  291. ArgumentList args;
  292. ASSERT_TRUE(gin::Converter<ArgumentList>::FromV8(isolate(), result, &args));
  293. ASSERT_EQ(3u, args.size());
  294. // Even though this is a promise based request the custom callbacks expect a
  295. // function argument to be passed to them, hence why we get a function here.
  296. // Invoking the callback however, should still result in the promise being
  297. // resolved.
  298. EXPECT_TRUE(args[0]->IsFunction());
  299. EXPECT_EQ(R"("response")", V8ToString(args[1], context));
  300. EXPECT_EQ(R"("arguments")", V8ToString(args[2], context));
  301. EXPECT_TRUE(request_handler->GetPendingRequestIdsForTesting().empty());
  302. EXPECT_EQ(v8::Promise::kPending, promise->State());
  303. v8::Local<v8::Value> callback_args[] = {gin::StringToV8(isolate(), "foo")};
  304. RunFunctionOnGlobal(args[0].As<v8::Function>(), context, 1, callback_args);
  305. EXPECT_EQ(v8::Promise::kFulfilled, promise->State());
  306. EXPECT_EQ(R"("foo")", V8ToString(promise->Result(), context));
  307. }
  308. // Test that having a custom callback without an extension-provided callback
  309. // doesn't crash.
  310. TEST_F(APIRequestHandlerTest, CustomCallbackArgumentsWithEmptyCallback) {
  311. v8::HandleScope handle_scope(isolate());
  312. v8::Local<v8::Context> context = MainContext();
  313. std::unique_ptr<APIRequestHandler> request_handler = CreateRequestHandler();
  314. v8::Local<v8::Function> custom_callback =
  315. FunctionFromString(context, kEchoArgs);
  316. ASSERT_FALSE(custom_callback.IsEmpty());
  317. v8::Local<v8::Function> empty_callback;
  318. request_handler->StartRequest(
  319. context, "method", std::make_unique<base::ListValue>(),
  320. binding::AsyncResponseType::kNone, empty_callback, custom_callback,
  321. binding::ResultModifierFunction());
  322. int request_id = request_handler->last_sent_request_id();
  323. EXPECT_THAT(request_handler->GetPendingRequestIdsForTesting(),
  324. testing::UnorderedElementsAre(request_id));
  325. request_handler->CompleteRequest(request_id, base::Value::List(),
  326. std::string());
  327. EXPECT_TRUE(did_run_js());
  328. v8::Local<v8::Array> result;
  329. ASSERT_TRUE(
  330. GetPropertyFromObjectAs(context->Global(), context, "result", &result));
  331. ArgumentList args;
  332. ASSERT_TRUE(gin::Converter<ArgumentList>::FromV8(isolate(), result, &args));
  333. ASSERT_EQ(1u, args.size());
  334. EXPECT_TRUE(args[0]->IsUndefined());
  335. EXPECT_TRUE(request_handler->GetPendingRequestIdsForTesting().empty());
  336. }
  337. TEST_F(APIRequestHandlerTest, ResultModifier) {
  338. v8::HandleScope handle_scope(isolate());
  339. v8::Local<v8::Context> context = MainContext();
  340. binding::ResultModifierFunction result_modifier =
  341. base::BindOnce([](const std::vector<v8::Local<v8::Value>>& result_args,
  342. v8::Local<v8::Context> context,
  343. binding::AsyncResponseType async_type) {
  344. EXPECT_EQ(1u, result_args.size());
  345. EXPECT_TRUE(result_args[0]->IsObject());
  346. v8::Local<v8::Object> result_obj = result_args[0].As<v8::Object>();
  347. v8::Local<v8::Value> prop_1;
  348. bool success =
  349. v8_helpers::GetProperty(context, result_obj, "prop1", &prop_1);
  350. DCHECK(success);
  351. v8::Local<v8::Value> prop_2;
  352. success =
  353. v8_helpers::GetProperty(context, result_obj, "prop2", &prop_2);
  354. DCHECK(success);
  355. std::vector<v8::Local<v8::Value>> new_args{prop_1, prop_2};
  356. return new_args;
  357. });
  358. std::unique_ptr<APIRequestHandler> request_handler = CreateRequestHandler();
  359. v8::Local<v8::Function> callback = FunctionFromString(
  360. context, "(function(arg1, arg2) {this.arg1 = arg1; this.arg2 = arg2});");
  361. ASSERT_FALSE(callback.IsEmpty());
  362. request_handler->StartRequest(
  363. context, "method", std::make_unique<base::ListValue>(),
  364. binding::AsyncResponseType::kCallback, callback,
  365. v8::Local<v8::Function>(), std::move(result_modifier));
  366. int request_id = request_handler->last_sent_request_id();
  367. EXPECT_THAT(request_handler->GetPendingRequestIdsForTesting(),
  368. testing::UnorderedElementsAre(request_id));
  369. request_handler->CompleteRequest(
  370. request_id, ListValueFromString("[{'prop1':'foo', 'prop2':'bar'}]"),
  371. std::string());
  372. EXPECT_TRUE(did_run_js());
  373. EXPECT_TRUE(request_handler->GetPendingRequestIdsForTesting().empty());
  374. EXPECT_EQ(R"("foo")",
  375. GetStringPropertyFromObject(context->Global(), context, "arg1"));
  376. EXPECT_EQ(R"("bar")",
  377. GetStringPropertyFromObject(context->Global(), context, "arg2"));
  378. }
  379. // Test user gestures being curried around for API requests.
  380. TEST_F(APIRequestHandlerTest, UserGestureTest) {
  381. v8::HandleScope handle_scope(isolate());
  382. v8::Local<v8::Context> context = MainContext();
  383. std::unique_ptr<APIRequestHandler> request_handler = CreateRequestHandler();
  384. // Set up a callback to be used with the request so we can check if a user
  385. // gesture was active.
  386. absl::optional<bool> ran_with_user_gesture;
  387. v8::Local<v8::FunctionTemplate> function_template =
  388. gin::CreateFunctionTemplate(
  389. isolate(),
  390. base::BindRepeating(&APIRequestHandlerTest::SaveUserActivationState,
  391. base::Unretained(this), context,
  392. &ran_with_user_gesture));
  393. v8::Local<v8::Function> v8_callback =
  394. function_template->GetFunction(context).ToLocalChecked();
  395. // Try first without a user gesture.
  396. request_handler->StartRequest(
  397. context, kMethod, std::make_unique<base::ListValue>(),
  398. binding::AsyncResponseType::kCallback, v8_callback,
  399. v8::Local<v8::Function>(), binding::ResultModifierFunction());
  400. int request_id = request_handler->last_sent_request_id();
  401. request_handler->CompleteRequest(request_id, ListValueFromString("[]"),
  402. std::string());
  403. ASSERT_TRUE(ran_with_user_gesture);
  404. EXPECT_FALSE(*ran_with_user_gesture);
  405. ran_with_user_gesture.reset();
  406. // Next try calling with a user gesture. Since a gesture will be active at the
  407. // time of the call, it should also be active during the callback.
  408. ScopedTestUserActivation test_user_activation;
  409. // TODO(devlin): This isn't quite right with UAv1/UAv2. V1 should properly
  410. // activate a new user gesture on the stack, and v2 should rely on the gesture
  411. // being persisted (or generated from the browser). We should clean this up.
  412. EXPECT_TRUE(interaction_provider()->HasActiveInteraction(context));
  413. request_handler->StartRequest(
  414. context, kMethod, std::make_unique<base::ListValue>(),
  415. binding::AsyncResponseType::kCallback, v8_callback,
  416. v8::Local<v8::Function>(), binding::ResultModifierFunction());
  417. request_id = request_handler->last_sent_request_id();
  418. request_handler->CompleteRequest(request_id, ListValueFromString("[]"),
  419. std::string());
  420. ASSERT_TRUE(ran_with_user_gesture);
  421. EXPECT_TRUE(*ran_with_user_gesture);
  422. // Sanity check: the callback doesn't change the state
  423. EXPECT_TRUE(interaction_provider()->HasActiveInteraction(context));
  424. }
  425. TEST_F(APIRequestHandlerTest, SettingLastError) {
  426. v8::HandleScope handle_scope(isolate());
  427. v8::Local<v8::Context> context = MainContext();
  428. absl::optional<std::string> logged_error;
  429. auto get_parent = [](v8::Local<v8::Context> context,
  430. v8::Local<v8::Object>* secondary_parent) {
  431. return context->Global();
  432. };
  433. auto log_error = [](absl::optional<std::string>* logged_error,
  434. v8::Local<v8::Context> context,
  435. const std::string& error) { *logged_error = error; };
  436. APIRequestHandler request_handler(
  437. base::DoNothing(),
  438. APILastError(base::BindRepeating(get_parent),
  439. base::BindRepeating(log_error, &logged_error)),
  440. exception_handler(), interaction_provider());
  441. const char kReportExposedLastError[] =
  442. "(function() {\n"
  443. " if (this.lastError)\n"
  444. " this.seenLastError = this.lastError.message;\n"
  445. "})";
  446. auto get_exposed_error = [context]() {
  447. return GetStringPropertyFromObject(context->Global(), context,
  448. "seenLastError");
  449. };
  450. {
  451. // Test a successful function call. No last error should be emitted to the
  452. // console or exposed to the callback.
  453. v8::Local<v8::Function> callback =
  454. FunctionFromString(context, kReportExposedLastError);
  455. request_handler.StartRequest(
  456. context, kMethod, std::make_unique<base::ListValue>(),
  457. binding::AsyncResponseType::kCallback, callback,
  458. v8::Local<v8::Function>(), binding::ResultModifierFunction());
  459. int request_id = request_handler.last_sent_request_id();
  460. request_handler.CompleteRequest(request_id, base::Value::List(),
  461. std::string());
  462. EXPECT_FALSE(logged_error);
  463. EXPECT_EQ("undefined", get_exposed_error());
  464. logged_error.reset();
  465. }
  466. {
  467. // Test a function call resulting in an error. Since the callback checks the
  468. // last error, no error should be logged to the console (but it should be
  469. // exposed to the callback).
  470. v8::Local<v8::Function> callback =
  471. FunctionFromString(context, kReportExposedLastError);
  472. request_handler.StartRequest(
  473. context, kMethod, std::make_unique<base::ListValue>(),
  474. binding::AsyncResponseType::kCallback, callback,
  475. v8::Local<v8::Function>(), binding::ResultModifierFunction());
  476. int request_id = request_handler.last_sent_request_id();
  477. request_handler.CompleteRequest(request_id, base::Value::List(),
  478. "some error");
  479. EXPECT_FALSE(logged_error);
  480. EXPECT_EQ("\"some error\"", get_exposed_error());
  481. logged_error.reset();
  482. }
  483. {
  484. // Test a function call resulting in an error that goes unchecked by the
  485. // callback. The error should be logged.
  486. v8::Local<v8::Function> callback =
  487. FunctionFromString(context, "(function() {})");
  488. request_handler.StartRequest(
  489. context, kMethod, std::make_unique<base::ListValue>(),
  490. binding::AsyncResponseType::kCallback, callback,
  491. v8::Local<v8::Function>(), binding::ResultModifierFunction());
  492. int request_id = request_handler.last_sent_request_id();
  493. request_handler.CompleteRequest(request_id, base::Value::List(),
  494. "some error");
  495. ASSERT_TRUE(logged_error);
  496. EXPECT_EQ("Unchecked runtime.lastError: some error", *logged_error);
  497. logged_error.reset();
  498. }
  499. {
  500. // Test a function call resulting in an error with only a custom callback,
  501. // and no author-script-provided callback. The error should be logged.
  502. v8::Local<v8::Function> custom_callback =
  503. FunctionFromString(context, "(function() {})");
  504. request_handler.StartRequest(
  505. context, kMethod, std::make_unique<base::ListValue>(),
  506. binding::AsyncResponseType::kNone, v8::Local<v8::Function>(),
  507. custom_callback, binding::ResultModifierFunction());
  508. int request_id = request_handler.last_sent_request_id();
  509. request_handler.CompleteRequest(request_id, base::Value::List(),
  510. "some error");
  511. ASSERT_TRUE(logged_error);
  512. EXPECT_EQ("Unchecked runtime.lastError: some error", *logged_error);
  513. logged_error.reset();
  514. }
  515. {
  516. // Test a function call resulting in an error that does not have an
  517. // associated callback callback. The error should be logged.
  518. request_handler.StartRequest(
  519. context, kMethod, std::make_unique<base::ListValue>(),
  520. binding::AsyncResponseType::kNone, v8::Local<v8::Function>(),
  521. v8::Local<v8::Function>(), binding::ResultModifierFunction());
  522. int request_id = request_handler.last_sent_request_id();
  523. request_handler.CompleteRequest(request_id, base::Value::List(),
  524. "some error");
  525. ASSERT_TRUE(logged_error);
  526. EXPECT_EQ("Unchecked runtime.lastError: some error", *logged_error);
  527. logged_error.reset();
  528. }
  529. {
  530. // Test a function call resulting in an error for a request handler that has
  531. // an associated result modifier. The result modifier should never be called
  532. // and since the callback checks last error no error should be logged to the
  533. // console.
  534. bool result_modifier_called = false;
  535. auto result_modifier =
  536. [&result_modifier_called](
  537. const std::vector<v8::Local<v8::Value>>& result_args,
  538. v8::Local<v8::Context> context,
  539. binding::AsyncResponseType async_type) {
  540. result_modifier_called = true;
  541. return result_args;
  542. };
  543. v8::Local<v8::Function> callback =
  544. FunctionFromString(context, kReportExposedLastError);
  545. request_handler.StartRequest(
  546. context, kMethod, std::make_unique<base::ListValue>(),
  547. binding::AsyncResponseType::kCallback, callback,
  548. v8::Local<v8::Function>(), base::BindLambdaForTesting(result_modifier));
  549. int request_id = request_handler.last_sent_request_id();
  550. request_handler.CompleteRequest(request_id, base::Value::List(),
  551. "some error");
  552. EXPECT_FALSE(logged_error);
  553. EXPECT_EQ("\"some error\"", get_exposed_error());
  554. EXPECT_FALSE(result_modifier_called);
  555. logged_error.reset();
  556. }
  557. }
  558. TEST_F(APIRequestHandlerTest, AddPendingRequestCallback) {
  559. v8::HandleScope handle_scope(isolate());
  560. v8::Local<v8::Context> context = MainContext();
  561. bool dispatched_request = false;
  562. auto handle_request = [](bool* dispatched_request,
  563. std::unique_ptr<APIRequestHandler::Request> request,
  564. v8::Local<v8::Context> context) {
  565. *dispatched_request = true;
  566. };
  567. APIRequestHandler request_handler(
  568. base::BindRepeating(handle_request, &dispatched_request),
  569. APILastError(APILastError::GetParent(), binding::AddConsoleError()),
  570. exception_handler(), interaction_provider());
  571. EXPECT_TRUE(request_handler.GetPendingRequestIdsForTesting().empty());
  572. v8::Local<v8::Function> function = FunctionFromString(context, kEchoArgs);
  573. ASSERT_FALSE(function.IsEmpty());
  574. auto details = request_handler.AddPendingRequest(
  575. context, binding::AsyncResponseType::kCallback, function,
  576. binding::ResultModifierFunction());
  577. int request_id = details.request_id;
  578. EXPECT_TRUE(details.promise.IsEmpty());
  579. EXPECT_THAT(request_handler.GetPendingRequestIdsForTesting(),
  580. testing::UnorderedElementsAre(request_id));
  581. // Even though we add a pending request, we shouldn't have dispatched anything
  582. // because AddPendingRequest() is intended for renderer-side implementations.
  583. EXPECT_FALSE(dispatched_request);
  584. const char kArguments[] = "['foo',1,{'prop1':'bar'}]";
  585. request_handler.CompleteRequest(request_id, ListValueFromString(kArguments),
  586. std::string());
  587. EXPECT_EQ(ReplaceSingleQuotes(kArguments),
  588. GetStringPropertyFromObject(context->Global(), context, "result"));
  589. EXPECT_TRUE(request_handler.GetPendingRequestIdsForTesting().empty());
  590. EXPECT_FALSE(dispatched_request);
  591. }
  592. TEST_F(APIRequestHandlerTest, AddPendingRequestPromise) {
  593. v8::HandleScope handle_scope(isolate());
  594. v8::Local<v8::Context> context = MainContext();
  595. bool dispatched_request = false;
  596. auto handle_request = [](bool* dispatched_request,
  597. std::unique_ptr<APIRequestHandler::Request> request,
  598. v8::Local<v8::Context> context) {
  599. *dispatched_request = true;
  600. };
  601. APIRequestHandler request_handler(
  602. base::BindRepeating(handle_request, &dispatched_request),
  603. APILastError(APILastError::GetParent(), binding::AddConsoleError()),
  604. exception_handler(), interaction_provider());
  605. EXPECT_TRUE(request_handler.GetPendingRequestIdsForTesting().empty());
  606. auto details = request_handler.AddPendingRequest(
  607. context, binding::AsyncResponseType::kPromise, v8::Local<v8::Function>(),
  608. binding::ResultModifierFunction());
  609. int request_id = details.request_id;
  610. v8::Local<v8::Promise> promise = details.promise;
  611. EXPECT_THAT(request_handler.GetPendingRequestIdsForTesting(),
  612. testing::UnorderedElementsAre(request_id));
  613. ASSERT_FALSE(promise.IsEmpty());
  614. EXPECT_EQ(v8::Promise::kPending, promise->State());
  615. // Even though we add a pending request, we shouldn't have dispatched anything
  616. // because AddPendingRequest() is intended for renderer-side implementations.
  617. EXPECT_FALSE(dispatched_request);
  618. request_handler.CompleteRequest(
  619. request_id, ListValueFromString("[{'foo': 'bar'}]"), std::string());
  620. ASSERT_EQ(v8::Promise::kFulfilled, promise->State());
  621. EXPECT_EQ(R"({"foo":"bar"})", V8ToString(promise->Result(), context));
  622. EXPECT_TRUE(request_handler.GetPendingRequestIdsForTesting().empty());
  623. EXPECT_FALSE(dispatched_request);
  624. }
  625. TEST_F(APIRequestHandlerTest, AddPendingRequestWithResultModifier) {
  626. v8::HandleScope handle_scope(isolate());
  627. v8::Local<v8::Context> context = MainContext();
  628. binding::ResultModifierFunction result_modifier =
  629. base::BindOnce([](const std::vector<v8::Local<v8::Value>>& result_args,
  630. v8::Local<v8::Context> context,
  631. binding::AsyncResponseType async_type) {
  632. DCHECK_EQ(1u, result_args.size());
  633. DCHECK(result_args[0]->IsObject());
  634. v8::Local<v8::Object> result_obj = result_args[0].As<v8::Object>();
  635. v8::Local<v8::Value> prop_1;
  636. bool success =
  637. v8_helpers::GetProperty(context, result_obj, "prop1", &prop_1);
  638. DCHECK(success);
  639. v8::Local<v8::Value> prop_2;
  640. success =
  641. v8_helpers::GetProperty(context, result_obj, "prop2", &prop_2);
  642. DCHECK(success);
  643. std::vector<v8::Local<v8::Value>> new_args{prop_1, prop_2};
  644. return new_args;
  645. });
  646. std::unique_ptr<APIRequestHandler> request_handler = CreateRequestHandler();
  647. v8::Local<v8::Function> function = FunctionFromString(context, kEchoArgs);
  648. ASSERT_FALSE(function.IsEmpty());
  649. auto details = request_handler->AddPendingRequest(
  650. context, binding::AsyncResponseType::kCallback, function,
  651. std::move(result_modifier));
  652. int request_id = details.request_id;
  653. EXPECT_TRUE(details.promise.IsEmpty());
  654. const char kArguments[] = "[{'prop1':'bar', 'prop2':'baz'}]";
  655. request_handler->CompleteRequest(request_id, ListValueFromString(kArguments),
  656. std::string());
  657. EXPECT_EQ(R"(["bar","baz"])",
  658. GetStringPropertyFromObject(context->Global(), context, "result"));
  659. }
  660. // Tests that throwing an exception in a callback is properly handled.
  661. TEST_F(APIRequestHandlerTest, ThrowExceptionInCallback) {
  662. v8::HandleScope handle_scope(isolate());
  663. v8::Local<v8::Context> context = MainContext();
  664. auto add_console_error = [](absl::optional<std::string>* error_out,
  665. v8::Local<v8::Context> context,
  666. const std::string& error) { *error_out = error; };
  667. absl::optional<std::string> logged_error;
  668. ExceptionHandler exception_handler(
  669. base::BindRepeating(add_console_error, &logged_error));
  670. APIRequestHandler request_handler(
  671. base::DoNothing(),
  672. APILastError(APILastError::GetParent(), binding::AddConsoleError()),
  673. &exception_handler, interaction_provider());
  674. v8::TryCatch outer_try_catch(isolate());
  675. v8::Local<v8::Function> callback_throwing_error =
  676. FunctionFromString(context, "(function() { throw new Error('hello'); })");
  677. auto details = request_handler.AddPendingRequest(
  678. context, binding::AsyncResponseType::kCallback, callback_throwing_error,
  679. binding::ResultModifierFunction());
  680. int request_id = details.request_id;
  681. EXPECT_TRUE(details.promise.IsEmpty());
  682. {
  683. TestJSRunner::AllowErrors allow_errors;
  684. request_handler.CompleteRequest(request_id, base::Value::List(),
  685. std::string());
  686. }
  687. // |outer_try_catch| should not have caught an error. This is important to not
  688. // disrupt our bindings code (or other running JS) when asynchronously
  689. // returning from an API call. Instead, the error should be caught and handled
  690. // by the exception handler.
  691. EXPECT_FALSE(outer_try_catch.HasCaught());
  692. ASSERT_TRUE(logged_error);
  693. EXPECT_THAT(*logged_error,
  694. testing::StartsWith("Error handling response: Error: hello"));
  695. }
  696. // Tests promise-based requests with the promise being fulfilled.
  697. TEST_F(APIRequestHandlerTest, PromiseBasedRequests_Fulfilled) {
  698. v8::HandleScope handle_scope(isolate());
  699. v8::Local<v8::Context> context = MainContext();
  700. std::unique_ptr<APIRequestHandler> request_handler = CreateRequestHandler();
  701. EXPECT_TRUE(request_handler->GetPendingRequestIdsForTesting().empty());
  702. v8::Local<v8::Promise> promise = request_handler->StartRequest(
  703. context, kMethod, std::make_unique<base::ListValue>(),
  704. binding::AsyncResponseType::kPromise, v8::Local<v8::Function>(),
  705. v8::Local<v8::Function>(), binding::ResultModifierFunction());
  706. ASSERT_FALSE(promise.IsEmpty());
  707. int request_id = request_handler->last_sent_request_id();
  708. EXPECT_NE(-1, request_id);
  709. EXPECT_THAT(request_handler->GetPendingRequestIdsForTesting(),
  710. testing::UnorderedElementsAre(request_id));
  711. EXPECT_EQ(v8::Promise::kPending, promise->State());
  712. request_handler->CompleteRequest(request_id, ListValueFromString("['foo']"),
  713. std::string());
  714. ASSERT_EQ(v8::Promise::kFulfilled, promise->State());
  715. EXPECT_EQ(R"("foo")", V8ToString(promise->Result(), context));
  716. EXPECT_TRUE(request_handler->GetPendingRequestIdsForTesting().empty());
  717. }
  718. // Tests promise-based requests with the promise being rejected.
  719. TEST_F(APIRequestHandlerTest, PromiseBasedRequests_Rejected) {
  720. v8::HandleScope handle_scope(isolate());
  721. v8::Local<v8::Context> context = MainContext();
  722. std::unique_ptr<APIRequestHandler> request_handler = CreateRequestHandler();
  723. EXPECT_TRUE(request_handler->GetPendingRequestIdsForTesting().empty());
  724. v8::Local<v8::Promise> promise = request_handler->StartRequest(
  725. context, kMethod, std::make_unique<base::ListValue>(),
  726. binding::AsyncResponseType::kPromise, v8::Local<v8::Function>(),
  727. v8::Local<v8::Function>(), binding::ResultModifierFunction());
  728. ASSERT_FALSE(promise.IsEmpty());
  729. int request_id = request_handler->last_sent_request_id();
  730. EXPECT_NE(-1, request_id);
  731. EXPECT_THAT(request_handler->GetPendingRequestIdsForTesting(),
  732. testing::UnorderedElementsAre(request_id));
  733. EXPECT_EQ(v8::Promise::kPending, promise->State());
  734. constexpr char kError[] = "Something went wrong!";
  735. request_handler->CompleteRequest(request_id, base::Value::List(), kError);
  736. ASSERT_EQ(v8::Promise::kRejected, promise->State());
  737. v8::Local<v8::Value> result = promise->Result();
  738. ASSERT_FALSE(result.IsEmpty());
  739. EXPECT_EQ(
  740. base::StrCat({"Error: ", kError}),
  741. gin::V8ToString(isolate(), result->ToString(context).ToLocalChecked()));
  742. EXPECT_TRUE(request_handler->GetPendingRequestIdsForTesting().empty());
  743. }
  744. } // namespace extensions