Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(687)

Side by Side Diff: extensions/renderer/api_request_handler_unittest.cc

Issue 2947463002: [Extensions Bindings] Add a bindings/ subdirectory under renderer (Closed)
Patch Set: . Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « extensions/renderer/api_request_handler.cc ('k') | extensions/renderer/api_signature.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
5 #include "extensions/renderer/api_request_handler.h"
6 #include "base/bind.h"
7 #include "base/memory/ptr_util.h"
8 #include "base/optional.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/values.h"
11 #include "extensions/renderer/api_binding_test.h"
12 #include "extensions/renderer/api_binding_test_util.h"
13 #include "gin/converter.h"
14 #include "gin/function_template.h"
15 #include "gin/public/context_holder.h"
16 #include "gin/public/isolate_holder.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "third_party/WebKit/public/web/WebScopedUserGesture.h"
19 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
20
21 namespace extensions {
22
23 namespace {
24
25 const char kEchoArgs[] =
26 "(function() { this.result = Array.from(arguments); })";
27
28 const char kMethod[] = "method";
29
30 // TODO(devlin): We should probably hoist this up to e.g. api_binding_types.h.
31 using ArgumentList = std::vector<v8::Local<v8::Value>>;
32
33 // TODO(devlin): Should we move some parts of api_binding_unittest.cc to here?
34 void DoNothingWithRequest(std::unique_ptr<APIRequestHandler::Request> request,
35 v8::Local<v8::Context> context) {}
36
37 } // namespace
38
39 class APIRequestHandlerTest : public APIBindingTest {
40 public:
41 // Runs the given |function|.
42 void RunJS(v8::Local<v8::Function> function,
43 v8::Local<v8::Context> context,
44 int argc,
45 v8::Local<v8::Value> argv[]) {
46 RunFunctionOnGlobal(function, context, argc, argv);
47 did_run_js_ = true;
48 }
49
50 protected:
51 APIRequestHandlerTest() {}
52 ~APIRequestHandlerTest() override {}
53
54 bool did_run_js() const { return did_run_js_; }
55
56 private:
57 bool did_run_js_ = false;
58
59 DISALLOW_COPY_AND_ASSIGN(APIRequestHandlerTest);
60 };
61
62 // Tests adding a request to the request handler, and then triggering the
63 // response.
64 TEST_F(APIRequestHandlerTest, AddRequestAndCompleteRequestTest) {
65 v8::HandleScope handle_scope(isolate());
66 v8::Local<v8::Context> context = MainContext();
67
68 APIRequestHandler request_handler(
69 base::Bind(&DoNothingWithRequest),
70 base::Bind(&APIRequestHandlerTest::RunJS, base::Unretained(this)),
71 APILastError(APILastError::GetParent(), APILastError::AddConsoleError()));
72
73 EXPECT_TRUE(request_handler.GetPendingRequestIdsForTesting().empty());
74
75 v8::Local<v8::Function> function = FunctionFromString(context, kEchoArgs);
76 ASSERT_FALSE(function.IsEmpty());
77
78 int request_id = request_handler.StartRequest(
79 context, kMethod, base::MakeUnique<base::ListValue>(), function,
80 v8::Local<v8::Function>(), binding::RequestThread::UI);
81 EXPECT_THAT(request_handler.GetPendingRequestIdsForTesting(),
82 testing::UnorderedElementsAre(request_id));
83
84 const char kArguments[] = "['foo',1,{'prop1':'bar'}]";
85 std::unique_ptr<base::ListValue> response_arguments =
86 ListValueFromString(kArguments);
87 ASSERT_TRUE(response_arguments);
88 request_handler.CompleteRequest(request_id, *response_arguments,
89 std::string());
90
91 EXPECT_TRUE(did_run_js());
92 EXPECT_EQ(ReplaceSingleQuotes(kArguments),
93 GetStringPropertyFromObject(context->Global(), context, "result"));
94
95 EXPECT_TRUE(request_handler.GetPendingRequestIdsForTesting().empty());
96
97 request_id = request_handler.StartRequest(
98 context, kMethod, base::MakeUnique<base::ListValue>(),
99 v8::Local<v8::Function>(), v8::Local<v8::Function>(),
100 binding::RequestThread::UI);
101 EXPECT_NE(-1, request_id);
102 request_handler.CompleteRequest(request_id, base::ListValue(), std::string());
103 }
104
105 // Tests that trying to run non-existent or invalided requests is a no-op.
106 TEST_F(APIRequestHandlerTest, InvalidRequestsTest) {
107 v8::HandleScope handle_scope(isolate());
108 v8::Local<v8::Context> context = MainContext();
109
110 APIRequestHandler request_handler(
111 base::Bind(&DoNothingWithRequest),
112 base::Bind(&APIRequestHandlerTest::RunJS, base::Unretained(this)),
113 APILastError(APILastError::GetParent(), APILastError::AddConsoleError()));
114
115 v8::Local<v8::Function> function = FunctionFromString(context, kEchoArgs);
116 ASSERT_FALSE(function.IsEmpty());
117
118 int request_id = request_handler.StartRequest(
119 context, kMethod, base::MakeUnique<base::ListValue>(), function,
120 v8::Local<v8::Function>(), binding::RequestThread::UI);
121 EXPECT_THAT(request_handler.GetPendingRequestIdsForTesting(),
122 testing::UnorderedElementsAre(request_id));
123
124 std::unique_ptr<base::ListValue> response_arguments =
125 ListValueFromString("['foo']");
126 ASSERT_TRUE(response_arguments);
127
128 // Try running with a non-existent request id.
129 int fake_request_id = 42;
130 request_handler.CompleteRequest(fake_request_id, *response_arguments,
131 std::string());
132 EXPECT_FALSE(did_run_js());
133
134 // Try running with a request from an invalidated context.
135 request_handler.InvalidateContext(context);
136 request_handler.CompleteRequest(request_id, *response_arguments,
137 std::string());
138 EXPECT_FALSE(did_run_js());
139 }
140
141 TEST_F(APIRequestHandlerTest, MultipleRequestsAndContexts) {
142 v8::HandleScope handle_scope(isolate());
143 v8::Local<v8::Context> context_a = MainContext();
144 v8::Local<v8::Context> context_b = AddContext();
145
146 APIRequestHandler request_handler(
147 base::Bind(&DoNothingWithRequest),
148 base::Bind(&APIRequestHandlerTest::RunJS, base::Unretained(this)),
149 APILastError(APILastError::GetParent(), APILastError::AddConsoleError()));
150
151 // By having both different arguments and different behaviors in the
152 // callbacks, we can easily verify that the right function is called in the
153 // right context.
154 v8::Local<v8::Function> function_a = FunctionFromString(
155 context_a, "(function(res) { this.result = res + 'alpha'; })");
156 v8::Local<v8::Function> function_b = FunctionFromString(
157 context_b, "(function(res) { this.result = res + 'beta'; })");
158
159 int request_a = request_handler.StartRequest(
160 context_a, kMethod, base::MakeUnique<base::ListValue>(), function_a,
161 v8::Local<v8::Function>(), binding::RequestThread::UI);
162 int request_b = request_handler.StartRequest(
163 context_b, kMethod, base::MakeUnique<base::ListValue>(), function_b,
164 v8::Local<v8::Function>(), binding::RequestThread::UI);
165
166 EXPECT_THAT(request_handler.GetPendingRequestIdsForTesting(),
167 testing::UnorderedElementsAre(request_a, request_b));
168
169 std::unique_ptr<base::ListValue> response_a =
170 ListValueFromString("['response_a:']");
171 ASSERT_TRUE(response_a);
172
173 request_handler.CompleteRequest(request_a, *response_a, std::string());
174 EXPECT_TRUE(did_run_js());
175 EXPECT_THAT(request_handler.GetPendingRequestIdsForTesting(),
176 testing::UnorderedElementsAre(request_b));
177
178 EXPECT_EQ(
179 ReplaceSingleQuotes("'response_a:alpha'"),
180 GetStringPropertyFromObject(context_a->Global(), context_a, "result"));
181
182 std::unique_ptr<base::ListValue> response_b =
183 ListValueFromString("['response_b:']");
184 ASSERT_TRUE(response_b);
185
186 request_handler.CompleteRequest(request_b, *response_b, std::string());
187 EXPECT_TRUE(request_handler.GetPendingRequestIdsForTesting().empty());
188
189 EXPECT_EQ(
190 ReplaceSingleQuotes("'response_b:beta'"),
191 GetStringPropertyFromObject(context_b->Global(), context_b, "result"));
192 }
193
194 TEST_F(APIRequestHandlerTest, CustomCallbackArguments) {
195 v8::HandleScope handle_scope(isolate());
196 v8::Local<v8::Context> context = MainContext();
197
198 APIRequestHandler request_handler(
199 base::Bind(&DoNothingWithRequest),
200 base::Bind(&APIRequestHandlerTest::RunJS, base::Unretained(this)),
201 APILastError(APILastError::GetParent(), APILastError::AddConsoleError()));
202
203 v8::Local<v8::Function> custom_callback =
204 FunctionFromString(context, kEchoArgs);
205 v8::Local<v8::Function> callback =
206 FunctionFromString(context, "(function() {})");
207 ASSERT_FALSE(callback.IsEmpty());
208 ASSERT_FALSE(custom_callback.IsEmpty());
209
210 int request_id = request_handler.StartRequest(
211 context, "method", base::MakeUnique<base::ListValue>(), callback,
212 custom_callback, binding::RequestThread::UI);
213 EXPECT_THAT(request_handler.GetPendingRequestIdsForTesting(),
214 testing::UnorderedElementsAre(request_id));
215
216 std::unique_ptr<base::ListValue> response_arguments =
217 ListValueFromString("['response', 'arguments']");
218 ASSERT_TRUE(response_arguments);
219 request_handler.CompleteRequest(request_id, *response_arguments,
220 std::string());
221
222 EXPECT_TRUE(did_run_js());
223 v8::Local<v8::Value> result =
224 GetPropertyFromObject(context->Global(), context, "result");
225 ASSERT_FALSE(result.IsEmpty());
226 ASSERT_TRUE(result->IsArray());
227 ArgumentList args;
228 ASSERT_TRUE(gin::Converter<ArgumentList>::FromV8(isolate(), result, &args));
229 ASSERT_EQ(5u, args.size());
230 EXPECT_EQ("\"method\"", V8ToString(args[0], context));
231 EXPECT_EQ(base::StringPrintf("{\"id\":%d}", request_id),
232 V8ToString(args[1], context));
233 EXPECT_EQ(callback, args[2]);
234 EXPECT_EQ("\"response\"", V8ToString(args[3], context));
235 EXPECT_EQ("\"arguments\"", V8ToString(args[4], context));
236
237 EXPECT_TRUE(request_handler.GetPendingRequestIdsForTesting().empty());
238 }
239
240 // Test that having a custom callback without an extension-provided callback
241 // doesn't crash.
242 TEST_F(APIRequestHandlerTest, CustomCallbackArgumentsWithEmptyCallback) {
243 v8::HandleScope handle_scope(isolate());
244 v8::Local<v8::Context> context = MainContext();
245
246 APIRequestHandler request_handler(
247 base::Bind(&DoNothingWithRequest),
248 base::Bind(&APIRequestHandlerTest::RunJS, base::Unretained(this)),
249 APILastError(APILastError::GetParent(), APILastError::AddConsoleError()));
250
251 v8::Local<v8::Function> custom_callback =
252 FunctionFromString(context, kEchoArgs);
253 ASSERT_FALSE(custom_callback.IsEmpty());
254
255 v8::Local<v8::Function> empty_callback;
256 int request_id = request_handler.StartRequest(
257 context, "method", base::MakeUnique<base::ListValue>(), empty_callback,
258 custom_callback, binding::RequestThread::UI);
259 EXPECT_THAT(request_handler.GetPendingRequestIdsForTesting(),
260 testing::UnorderedElementsAre(request_id));
261
262 request_handler.CompleteRequest(request_id, base::ListValue(), std::string());
263
264 EXPECT_TRUE(did_run_js());
265 v8::Local<v8::Value> result =
266 GetPropertyFromObject(context->Global(), context, "result");
267 ASSERT_FALSE(result.IsEmpty());
268 ASSERT_TRUE(result->IsArray());
269 ArgumentList args;
270 ASSERT_TRUE(gin::Converter<ArgumentList>::FromV8(isolate(), result, &args));
271 ASSERT_EQ(3u, args.size());
272 EXPECT_EQ("\"method\"", V8ToString(args[0], context));
273 EXPECT_EQ(base::StringPrintf("{\"id\":%d}", request_id),
274 V8ToString(args[1], context));
275 EXPECT_TRUE(args[2]->IsUndefined());
276
277 EXPECT_TRUE(request_handler.GetPendingRequestIdsForTesting().empty());
278 }
279
280 // Test user gestures being curried around for API requests.
281 TEST_F(APIRequestHandlerTest, UserGestureTest) {
282 v8::HandleScope handle_scope(isolate());
283 v8::Local<v8::Context> context = MainContext();
284
285 APIRequestHandler request_handler(
286 base::Bind(&DoNothingWithRequest),
287 base::Bind(&APIRequestHandlerTest::RunJS, base::Unretained(this)),
288 APILastError(APILastError::GetParent(), APILastError::AddConsoleError()));
289
290 auto callback = [](base::Optional<bool>* ran_with_user_gesture) {
291 *ran_with_user_gesture =
292 blink::WebUserGestureIndicator::IsProcessingUserGestureThreadSafe();
293 };
294
295 // Set up a callback to be used with the request so we can check if a user
296 // gesture was active.
297 base::Optional<bool> ran_with_user_gesture;
298 v8::Local<v8::FunctionTemplate> function_template =
299 gin::CreateFunctionTemplate(isolate(),
300 base::Bind(callback, &ran_with_user_gesture));
301 v8::Local<v8::Function> v8_callback =
302 function_template->GetFunction(context).ToLocalChecked();
303
304 // Try first without a user gesture.
305 int request_id = request_handler.StartRequest(
306 context, kMethod, base::MakeUnique<base::ListValue>(), v8_callback,
307 v8::Local<v8::Function>(), binding::RequestThread::UI);
308 request_handler.CompleteRequest(request_id, *ListValueFromString("[]"),
309 std::string());
310
311 ASSERT_TRUE(ran_with_user_gesture);
312 EXPECT_FALSE(*ran_with_user_gesture);
313 ran_with_user_gesture.reset();
314
315 // Next try calling with a user gesture. Since a gesture will be active at the
316 // time of the call, it should also be active during the callback.
317 {
318 blink::WebScopedUserGesture user_gesture(nullptr);
319 EXPECT_TRUE(
320 blink::WebUserGestureIndicator::IsProcessingUserGestureThreadSafe());
321 request_id = request_handler.StartRequest(
322 context, kMethod, base::MakeUnique<base::ListValue>(), v8_callback,
323 v8::Local<v8::Function>(), binding::RequestThread::UI);
324 }
325 EXPECT_FALSE(
326 blink::WebUserGestureIndicator::IsProcessingUserGestureThreadSafe());
327
328 request_handler.CompleteRequest(request_id, *ListValueFromString("[]"),
329 std::string());
330 ASSERT_TRUE(ran_with_user_gesture);
331 EXPECT_TRUE(*ran_with_user_gesture);
332 // Sanity check - after the callback ran, there shouldn't be an active
333 // gesture.
334 EXPECT_FALSE(
335 blink::WebUserGestureIndicator::IsProcessingUserGestureThreadSafe());
336 }
337
338 TEST_F(APIRequestHandlerTest, RequestThread) {
339 v8::HandleScope handle_scope(isolate());
340 v8::Local<v8::Context> context = MainContext();
341
342 base::Optional<binding::RequestThread> thread;
343 auto on_request = [](base::Optional<binding::RequestThread>* thread_out,
344 std::unique_ptr<APIRequestHandler::Request> request,
345 v8::Local<v8::Context> context) {
346 *thread_out = request->thread;
347 };
348
349 APIRequestHandler request_handler(
350 base::Bind(on_request, &thread),
351 base::Bind(&APIRequestHandlerTest::RunJS, base::Unretained(this)),
352 APILastError(APILastError::GetParent(), APILastError::AddConsoleError()));
353
354 request_handler.StartRequest(
355 context, kMethod, base::MakeUnique<base::ListValue>(),
356 v8::Local<v8::Function>(), v8::Local<v8::Function>(),
357 binding::RequestThread::UI);
358 ASSERT_TRUE(thread);
359 EXPECT_EQ(binding::RequestThread::UI, *thread);
360 thread.reset();
361
362 request_handler.StartRequest(
363 context, kMethod, base::MakeUnique<base::ListValue>(),
364 v8::Local<v8::Function>(), v8::Local<v8::Function>(),
365 binding::RequestThread::IO);
366 ASSERT_TRUE(thread);
367 EXPECT_EQ(binding::RequestThread::IO, *thread);
368 thread.reset();
369 }
370
371 TEST_F(APIRequestHandlerTest, SettingLastError) {
372 v8::HandleScope handle_scope(isolate());
373 v8::Local<v8::Context> context = MainContext();
374
375 base::Optional<std::string> logged_error;
376 auto get_parent = [](v8::Local<v8::Context> context) {
377 return context->Global();
378 };
379
380 auto log_error = [](base::Optional<std::string>* logged_error,
381 v8::Local<v8::Context> context,
382 const std::string& error) { *logged_error = error; };
383
384 APIRequestHandler request_handler(
385 base::Bind(&DoNothingWithRequest),
386 base::Bind(&APIRequestHandlerTest::RunJS, base::Unretained(this)),
387 APILastError(base::Bind(get_parent),
388 base::Bind(log_error, &logged_error)));
389
390 const char kReportExposedLastError[] =
391 "(function() {\n"
392 " if (this.lastError)\n"
393 " this.seenLastError = this.lastError.message;\n"
394 "})";
395 auto get_exposed_error = [context]() {
396 return GetStringPropertyFromObject(context->Global(), context,
397 "seenLastError");
398 };
399
400 {
401 // Test a successful function call. No last error should be emitted to the
402 // console or exposed to the callback.
403 v8::Local<v8::Function> callback =
404 FunctionFromString(context, kReportExposedLastError);
405 int request_id = request_handler.StartRequest(
406 context, kMethod, base::MakeUnique<base::ListValue>(), callback,
407 v8::Local<v8::Function>(), binding::RequestThread::UI);
408 request_handler.CompleteRequest(request_id, base::ListValue(),
409 std::string());
410 EXPECT_FALSE(logged_error);
411 EXPECT_EQ("undefined", get_exposed_error());
412 logged_error.reset();
413 }
414
415 {
416 // Test a function call resulting in an error. Since the callback checks the
417 // last error, no error should be logged to the console (but it should be
418 // exposed to the callback).
419 v8::Local<v8::Function> callback =
420 FunctionFromString(context, kReportExposedLastError);
421 int request_id = request_handler.StartRequest(
422 context, kMethod, base::MakeUnique<base::ListValue>(), callback,
423 v8::Local<v8::Function>(), binding::RequestThread::UI);
424 request_handler.CompleteRequest(request_id, base::ListValue(),
425 "some error");
426 EXPECT_FALSE(logged_error);
427 EXPECT_EQ("\"some error\"", get_exposed_error());
428 logged_error.reset();
429 }
430
431 {
432 // Test a function call resulting in an error that goes unchecked by the
433 // callback. The error should be logged.
434 v8::Local<v8::Function> callback =
435 FunctionFromString(context, "(function() {})");
436 int request_id = request_handler.StartRequest(
437 context, kMethod, base::MakeUnique<base::ListValue>(), callback,
438 v8::Local<v8::Function>(), binding::RequestThread::UI);
439 request_handler.CompleteRequest(request_id, base::ListValue(),
440 "some error");
441 ASSERT_TRUE(logged_error);
442 EXPECT_EQ("Unchecked runtime.lastError: some error", *logged_error);
443 logged_error.reset();
444 }
445 }
446
447 TEST_F(APIRequestHandlerTest, AddPendingRequest) {
448 v8::HandleScope handle_scope(isolate());
449 v8::Local<v8::Context> context = MainContext();
450
451 bool dispatched_request = false;
452 auto handle_request = [](bool* dispatched_request,
453 std::unique_ptr<APIRequestHandler::Request> request,
454 v8::Local<v8::Context> context) {
455 *dispatched_request = true;
456 };
457
458 APIRequestHandler request_handler(
459 base::Bind(handle_request, &dispatched_request),
460 base::Bind(&APIRequestHandlerTest::RunJS, base::Unretained(this)),
461 APILastError(APILastError::GetParent(), APILastError::AddConsoleError()));
462
463 EXPECT_TRUE(request_handler.GetPendingRequestIdsForTesting().empty());
464 v8::Local<v8::Function> function = FunctionFromString(context, kEchoArgs);
465 ASSERT_FALSE(function.IsEmpty());
466
467 int request_id = request_handler.AddPendingRequest(context, function);
468 EXPECT_THAT(request_handler.GetPendingRequestIdsForTesting(),
469 testing::UnorderedElementsAre(request_id));
470 // Even though we add a pending request, we shouldn't have dispatched anything
471 // because AddPendingRequest() is intended for renderer-side implementations.
472 EXPECT_FALSE(dispatched_request);
473
474 const char kArguments[] = "['foo',1,{'prop1':'bar'}]";
475 std::unique_ptr<base::ListValue> response_arguments =
476 ListValueFromString(kArguments);
477 ASSERT_TRUE(response_arguments);
478 request_handler.CompleteRequest(request_id, *response_arguments,
479 std::string());
480
481 EXPECT_EQ(ReplaceSingleQuotes(kArguments),
482 GetStringPropertyFromObject(context->Global(), context, "result"));
483
484 EXPECT_TRUE(request_handler.GetPendingRequestIdsForTesting().empty());
485 EXPECT_FALSE(dispatched_request);
486 }
487
488 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/renderer/api_request_handler.cc ('k') | extensions/renderer/api_signature.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698