1 // Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4
5 #include <algorithm>
6 #include <cmath>
7 #include <memory>
8 #include <sstream>
9 #include <string>
10 #include <vector>
11
12 #include "include/base/cef_callback.h"
13 #include "include/cef_cookie.h"
14 #include "include/cef_request_context_handler.h"
15 #include "include/wrapper/cef_closure_task.h"
16 #include "include/wrapper/cef_stream_resource_handler.h"
17 #include "tests/ceftests/test_handler.h"
18 #include "tests/ceftests/test_util.h"
19 #include "tests/gtest/include/gtest/gtest.h"
20 #include "tests/shared/browser/client_app_browser.h"
21 #include "tests/shared/renderer/client_app_renderer.h"
22
23 using client::ClientAppBrowser;
24 using client::ClientAppRenderer;
25
26 namespace {
27
28 enum NetNotifyTestType {
29 NNTT_NONE = 0,
30 NNTT_NORMAL,
31 NNTT_DELAYED_RENDERER,
32 NNTT_DELAYED_BROWSER,
33 };
34
35 const char kNetNotifyOrigin1[] = "http://tests-netnotify1/";
36 const char kNetNotifyOrigin2[] = "http://tests-netnotify2/";
37 const char kNetNotifyMsg[] = "RequestHandlerTest.NetNotify";
38 const char kNetNotifyTestCmdKey[] = "rh-net-notify-test";
39
40 // Browser side.
41 class NetNotifyTestHandler : public TestHandler {
42 public:
NetNotifyTestHandler(CompletionState * completion_state,NetNotifyTestType test_type,bool same_origin)43 NetNotifyTestHandler(CompletionState* completion_state,
44 NetNotifyTestType test_type,
45 bool same_origin)
46 : TestHandler(completion_state),
47 test_type_(test_type),
48 same_origin_(same_origin) {}
49
SetupTest()50 void SetupTest() override {
51 std::stringstream ss;
52 ss << kNetNotifyOrigin1 << "nav1.html?t=" << test_type_;
53 url1_ = ss.str();
54 ss.str("");
55 ss << (same_origin_ ? kNetNotifyOrigin1 : kNetNotifyOrigin2)
56 << "nav2.html?t=" << test_type_;
57 url2_ = ss.str();
58
59 const std::string& resource1 =
60 "<html>"
61 "<head><script>document.cookie='name1=value1';</script></head>"
62 "<body>Nav1</body>"
63 "</html>";
64 response_length1_ = static_cast<int64>(resource1.size());
65 AddResource(url1_, resource1, "text/html");
66
67 const std::string& resource2 =
68 "<html>"
69 "<head><script>document.cookie='name2=value2';</script></head>"
70 "<body>Nav2</body>"
71 "</html>";
72 response_length2_ = static_cast<int64>(resource2.size());
73 AddResource(url2_, resource2, "text/html");
74
75 // Create the request context that will use an in-memory cache.
76 CefRequestContextSettings settings;
77 CefRefPtr<CefRequestContext> request_context =
78 CefRequestContext::CreateContext(settings, nullptr);
79 cookie_manager_ = request_context->GetCookieManager(nullptr);
80
81 CefRefPtr<CefDictionaryValue> extra_info = CefDictionaryValue::Create();
82 extra_info->SetBool(kNetNotifyTestCmdKey, true);
83
84 // Create browser that loads the 1st URL.
85 CreateBrowser(url1_, request_context, extra_info);
86 }
87
RunTest()88 void RunTest() override {
89 // Navigate to the 2nd URL.
90 GetBrowser()->GetMainFrame()->LoadURL(url2_);
91
92 // Time out the test after a reasonable period of time.
93 SetTestTimeout();
94 }
95
OnBeforeResourceLoad(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefRequest> request,CefRefPtr<CefCallback> callback)96 cef_return_value_t OnBeforeResourceLoad(
97 CefRefPtr<CefBrowser> browser,
98 CefRefPtr<CefFrame> frame,
99 CefRefPtr<CefRequest> request,
100 CefRefPtr<CefCallback> callback) override {
101 EXPECT_TRUE(CefCurrentlyOn(TID_IO));
102
103 const std::string& url = request->GetURL();
104 if (IgnoreURL(url))
105 return RV_CONTINUE;
106
107 if (url.find(url1_) == 0)
108 got_before_resource_load1_.yes();
109 else if (url.find(url2_) == 0)
110 got_before_resource_load2_.yes();
111 else
112 EXPECT_TRUE(false); // Not reached
113
114 return RV_CONTINUE;
115 }
116
GetResourceHandler(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefRequest> request)117 CefRefPtr<CefResourceHandler> GetResourceHandler(
118 CefRefPtr<CefBrowser> browser,
119 CefRefPtr<CefFrame> frame,
120 CefRefPtr<CefRequest> request) override {
121 EXPECT_TRUE(CefCurrentlyOn(TID_IO));
122
123 const std::string& url = request->GetURL();
124 if (IgnoreURL(url))
125 return nullptr;
126
127 if (url.find(url1_) == 0)
128 got_get_resource_handler1_.yes();
129 else if (url.find(url2_) == 0)
130 got_get_resource_handler2_.yes();
131 else
132 EXPECT_TRUE(false); // Not reached
133
134 return TestHandler::GetResourceHandler(browser, frame, request);
135 }
136
OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefRequest> request,CefRefPtr<CefResponse> response,URLRequestStatus status,int64 received_content_length)137 void OnResourceLoadComplete(CefRefPtr<CefBrowser> browser,
138 CefRefPtr<CefFrame> frame,
139 CefRefPtr<CefRequest> request,
140 CefRefPtr<CefResponse> response,
141 URLRequestStatus status,
142 int64 received_content_length) override {
143 EXPECT_TRUE(CefCurrentlyOn(TID_IO));
144
145 const std::string& url = request->GetURL();
146 if (IgnoreURL(url))
147 return;
148
149 EXPECT_EQ(UR_SUCCESS, status);
150 if (url.find(url1_) == 0) {
151 got_resource_load_complete1_.yes();
152 EXPECT_EQ(response_length1_, received_content_length);
153 } else if (url.find(url2_) == 0) {
154 got_resource_load_complete2_.yes();
155 EXPECT_EQ(response_length2_, received_content_length);
156 } else {
157 EXPECT_TRUE(false); // Not reached
158 }
159 }
160
OnBeforeBrowse(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefRequest> request,bool user_gesture,bool is_redirect)161 bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
162 CefRefPtr<CefFrame> frame,
163 CefRefPtr<CefRequest> request,
164 bool user_gesture,
165 bool is_redirect) override {
166 std::string url = request->GetURL();
167
168 // Check if the load has already been delayed.
169 bool delay_loaded = (url.find("delayed=true") != std::string::npos);
170
171 if (url.find(url1_) == 0) {
172 got_before_browse1_.yes();
173 EXPECT_FALSE(delay_loaded);
174 } else if (url.find(url2_) == 0) {
175 got_before_browse2_.yes();
176 if (delay_loaded) {
177 got_before_browse2_delayed_.yes();
178 } else if (test_type_ == NNTT_DELAYED_RENDERER ||
179 test_type_ == NNTT_DELAYED_BROWSER) {
180 got_before_browse2_will_delay_.yes();
181
182 // Navigating cross-origin from the browser process will cause a new
183 // render process to be created. We therefore need some information in
184 // the request itself to tell us that the navigation has already been
185 // delayed.
186 // Navigating cross-origin from the renderer process will cause the
187 // process to be terminated with "bad IPC message" reason
188 // INVALID_INITIATOR_ORIGIN (213).
189 url += "&delayed=true";
190
191 if (test_type_ == NNTT_DELAYED_RENDERER) {
192 // Load the URL from the render process.
193 CefRefPtr<CefProcessMessage> message =
194 CefProcessMessage::Create(kNetNotifyMsg);
195 CefRefPtr<CefListValue> args = message->GetArgumentList();
196 args->SetInt(0, test_type_);
197 args->SetString(1, url);
198 frame->SendProcessMessage(PID_RENDERER, message);
199 } else {
200 // Load the URL from the browser process.
201 frame->LoadURL(url);
202 }
203
204 // Cancel the load.
205 return true;
206 }
207 } else {
208 EXPECT_TRUE(false); // Not reached
209 }
210
211 // Allow the load to continue.
212 return false;
213 }
214
OnLoadEnd(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,int httpStatusCode)215 void OnLoadEnd(CefRefPtr<CefBrowser> browser,
216 CefRefPtr<CefFrame> frame,
217 int httpStatusCode) override {
218 const std::string& url = frame->GetURL();
219 if (url.find(url1_) == 0) {
220 got_load_end1_.yes();
221 SetupCompleteIfDone();
222 } else if (url.find(url2_) == 0) {
223 got_load_end2_.yes();
224 FinishTestIfDone();
225 } else {
226 EXPECT_TRUE(false); // Not reached
227 }
228 }
229
OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefProcessId source_process,CefRefPtr<CefProcessMessage> message)230 bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
231 CefRefPtr<CefFrame> frame,
232 CefProcessId source_process,
233 CefRefPtr<CefProcessMessage> message) override {
234 if (message->GetName().ToString() == kNetNotifyMsg) {
235 CefRefPtr<CefListValue> args = message->GetArgumentList();
236 EXPECT_TRUE(args.get());
237
238 std::string url = args->GetString(0);
239 if (url.find(url1_) == 0) {
240 got_process_message1_.yes();
241 SetupCompleteIfDone();
242 } else if (url.find(url2_) == 0) {
243 got_process_message2_.yes();
244 FinishTestIfDone();
245 } else {
246 EXPECT_TRUE(false); // Not reached
247 }
248
249 return true;
250 }
251
252 // Message not handled.
253 return false;
254 }
255
OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,TerminationStatus status)256 void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
257 TerminationStatus status) override {
258 got_process_terminated_ct_++;
259
260 // Termination is expected for cross-origin requests initiated from the
261 // renderer process.
262 if (!(test_type_ == NNTT_DELAYED_RENDERER && !same_origin_)) {
263 TestHandler::OnRenderProcessTerminated(browser, status);
264 }
265
266 FinishTest();
267 }
268
269 protected:
SetupCompleteIfDone()270 void SetupCompleteIfDone() {
271 if (got_load_end1_ && got_process_message1_)
272 SetupComplete();
273 }
274
FinishTestIfDone()275 void FinishTestIfDone() {
276 if (got_load_end2_ && got_process_message2_)
277 FinishTest();
278 }
279
FinishTest()280 void FinishTest() {
281 // Verify that cookies were set correctly.
282 class TestVisitor : public CefCookieVisitor {
283 public:
284 explicit TestVisitor(NetNotifyTestHandler* handler) : handler_(handler) {}
285 ~TestVisitor() override {
286 // Destroy the test.
287 CefPostTask(TID_UI, base::BindOnce(&NetNotifyTestHandler::DestroyTest,
288 handler_));
289 }
290
291 bool Visit(const CefCookie& cookie,
292 int count,
293 int total,
294 bool& deleteCookie) override {
295 const std::string& name = CefString(&cookie.name);
296 const std::string& value = CefString(&cookie.value);
297 if (name == "name1" && value == "value1") {
298 handler_->got_cookie1_.yes();
299 deleteCookie = true;
300 } else if (name == "name2" && value == "value2") {
301 handler_->got_cookie2_.yes();
302 deleteCookie = true;
303 }
304 return true;
305 }
306
307 private:
308 NetNotifyTestHandler* handler_;
309 IMPLEMENT_REFCOUNTING(TestVisitor);
310 };
311
312 cookie_manager_->VisitAllCookies(new TestVisitor(this));
313 }
314
DestroyTest()315 void DestroyTest() override {
316 int browser_id = GetBrowser()->GetIdentifier();
317
318 // Verify test expectations.
319 EXPECT_TRUE(got_before_browse1_) << " browser " << browser_id;
320 EXPECT_TRUE(got_load_end1_) << " browser " << browser_id;
321 EXPECT_TRUE(got_before_resource_load1_) << " browser " << browser_id;
322 EXPECT_TRUE(got_get_resource_handler1_) << " browser " << browser_id;
323 EXPECT_TRUE(got_resource_load_complete1_) << " browser " << browser_id;
324 EXPECT_TRUE(got_cookie1_) << " browser " << browser_id;
325 EXPECT_TRUE(got_process_message1_) << " browser " << browser_id;
326 EXPECT_TRUE(got_before_browse2_) << " browser " << browser_id;
327
328 if (test_type_ == NNTT_DELAYED_RENDERER && !same_origin_) {
329 EXPECT_EQ(1, got_process_terminated_ct_) << " browser " << browser_id;
330 EXPECT_FALSE(got_load_end2_) << " browser " << browser_id;
331 EXPECT_FALSE(got_before_resource_load2_) << " browser " << browser_id;
332 EXPECT_FALSE(got_get_resource_handler2_) << " browser " << browser_id;
333 EXPECT_FALSE(got_resource_load_complete2_) << " browser " << browser_id;
334 EXPECT_FALSE(got_cookie2_) << " browser " << browser_id;
335 EXPECT_FALSE(got_process_message2_) << " browser " << browser_id;
336 } else {
337 EXPECT_EQ(0, got_process_terminated_ct_) << " browser " << browser_id;
338 EXPECT_TRUE(got_load_end2_) << " browser " << browser_id;
339 EXPECT_TRUE(got_before_resource_load2_) << " browser " << browser_id;
340 EXPECT_TRUE(got_get_resource_handler2_) << " browser " << browser_id;
341 EXPECT_TRUE(got_resource_load_complete2_) << " browser " << browser_id;
342 EXPECT_TRUE(got_cookie2_) << " browser " << browser_id;
343 EXPECT_TRUE(got_process_message2_) << " browser " << browser_id;
344 }
345
346 if (test_type_ == NNTT_DELAYED_RENDERER ||
347 test_type_ == NNTT_DELAYED_BROWSER) {
348 EXPECT_TRUE(got_before_browse2_will_delay_) << " browser " << browser_id;
349 if (test_type_ == NNTT_DELAYED_RENDERER && !same_origin_) {
350 EXPECT_FALSE(got_before_browse2_delayed_) << " browser " << browser_id;
351 } else {
352 EXPECT_TRUE(got_before_browse2_delayed_) << " browser " << browser_id;
353 }
354 } else {
355 EXPECT_FALSE(got_before_browse2_will_delay_) << " browser " << browser_id;
356 EXPECT_FALSE(got_before_browse2_delayed_) << " browser " << browser_id;
357 }
358
359 cookie_manager_ = nullptr;
360
361 TestHandler::DestroyTest();
362 }
363
364 NetNotifyTestType test_type_;
365 bool same_origin_;
366 std::string url1_;
367 std::string url2_;
368
369 CefRefPtr<CefCookieManager> cookie_manager_;
370
371 TrackCallback got_before_browse1_;
372 TrackCallback got_load_end1_;
373 TrackCallback got_before_resource_load1_;
374 TrackCallback got_get_resource_handler1_;
375 TrackCallback got_resource_load_complete1_;
376 TrackCallback got_cookie1_;
377 TrackCallback got_process_message1_;
378 TrackCallback got_before_browse2_;
379 TrackCallback got_load_end2_;
380 TrackCallback got_before_resource_load2_;
381 TrackCallback got_get_resource_handler2_;
382 TrackCallback got_resource_load_complete2_;
383 TrackCallback got_cookie2_;
384 TrackCallback got_process_message2_;
385 TrackCallback got_before_browse2_will_delay_;
386 TrackCallback got_before_browse2_delayed_;
387 int got_process_terminated_ct_ = 0;
388
389 int64 response_length1_;
390 int64 response_length2_;
391
392 IMPLEMENT_REFCOUNTING(NetNotifyTestHandler);
393 };
394
395 // Renderer side.
396 class NetNotifyRendererTest : public ClientAppRenderer::Delegate,
397 public CefLoadHandler {
398 public:
NetNotifyRendererTest()399 NetNotifyRendererTest() : run_test_(false) {}
400
OnBrowserCreated(CefRefPtr<ClientAppRenderer> app,CefRefPtr<CefBrowser> browser,CefRefPtr<CefDictionaryValue> extra_info)401 void OnBrowserCreated(CefRefPtr<ClientAppRenderer> app,
402 CefRefPtr<CefBrowser> browser,
403 CefRefPtr<CefDictionaryValue> extra_info) override {
404 run_test_ = extra_info && extra_info->HasKey(kNetNotifyTestCmdKey);
405 }
406
GetLoadHandler(CefRefPtr<ClientAppRenderer> app)407 CefRefPtr<CefLoadHandler> GetLoadHandler(
408 CefRefPtr<ClientAppRenderer> app) override {
409 if (run_test_)
410 return this;
411 return nullptr;
412 }
413
OnLoadEnd(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,int httpStatusCode)414 void OnLoadEnd(CefRefPtr<CefBrowser> browser,
415 CefRefPtr<CefFrame> frame,
416 int httpStatusCode) override {
417 if (!run_test_)
418 return;
419
420 const std::string& url = frame->GetURL();
421
422 // Continue in the browser process.
423 CefRefPtr<CefProcessMessage> message =
424 CefProcessMessage::Create(kNetNotifyMsg);
425 CefRefPtr<CefListValue> args = message->GetArgumentList();
426 args->SetString(0, url);
427 frame->SendProcessMessage(PID_BROWSER, message);
428 }
429
OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefProcessId source_process,CefRefPtr<CefProcessMessage> message)430 bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
431 CefRefPtr<CefBrowser> browser,
432 CefRefPtr<CefFrame> frame,
433 CefProcessId source_process,
434 CefRefPtr<CefProcessMessage> message) override {
435 if (message->GetName().ToString() == kNetNotifyMsg) {
436 CefRefPtr<CefListValue> args = message->GetArgumentList();
437 EXPECT_TRUE(args.get());
438
439 NetNotifyTestType test_type =
440 static_cast<NetNotifyTestType>(args->GetInt(0));
441 EXPECT_EQ(test_type, NNTT_DELAYED_RENDERER);
442
443 const std::string& url = args->GetString(1);
444
445 // Load the URL from the render process.
446 frame->LoadURL(url);
447 return true;
448 }
449
450 // Message not handled.
451 return false;
452 }
453
454 private:
455 bool run_test_;
456
457 IMPLEMENT_REFCOUNTING(NetNotifyRendererTest);
458 };
459
RunNetNotifyTest(NetNotifyTestType test_type,bool same_origin,size_t count=3U)460 void RunNetNotifyTest(NetNotifyTestType test_type,
461 bool same_origin,
462 size_t count = 3U) {
463 TestHandler::CompletionState completion_state(static_cast<int>(count));
464 TestHandler::Collection collection(&completion_state);
465
466 std::vector<CefRefPtr<NetNotifyTestHandler>> handlers;
467 for (size_t i = 0U; i < count; ++i) {
468 CefRefPtr<NetNotifyTestHandler> handler =
469 new NetNotifyTestHandler(&completion_state, test_type, same_origin);
470 collection.AddTestHandler(handler.get());
471 handlers.push_back(handler);
472 }
473
474 collection.ExecuteTests();
475
476 while (!handlers.empty()) {
477 auto handler = handlers.front();
478 handlers.erase(handlers.begin());
479 ReleaseAndWaitForDestructor(handler);
480 }
481 }
482
483 } // namespace
484
485 // Verify network notifications for multiple browsers existing simultaniously.
486 // URL loading is from the same origin and is not delayed.
TEST(RequestHandlerTest,NotificationsSameOriginDirect)487 TEST(RequestHandlerTest, NotificationsSameOriginDirect) {
488 RunNetNotifyTest(NNTT_NORMAL, true);
489 }
490
491 // Verify network notifications for multiple browsers existing simultaniously.
492 // URL loading is from the same origin and is continued asynchronously from the
493 // render process.
TEST(RequestHandlerTest,NotificationsSameOriginDelayedRenderer)494 TEST(RequestHandlerTest, NotificationsSameOriginDelayedRenderer) {
495 RunNetNotifyTest(NNTT_DELAYED_RENDERER, true);
496 }
497
498 // Verify network notifications for multiple browsers existing simultaniously.
499 // URL loading is from the same origin and is continued asynchronously from the
500 // browser process.
TEST(RequestHandlerTest,NotificationsSameOriginDelayedBrowser)501 TEST(RequestHandlerTest, NotificationsSameOriginDelayedBrowser) {
502 RunNetNotifyTest(NNTT_DELAYED_BROWSER, true);
503 }
504
505 // Verify network notifications for multiple browsers existing simultaniously.
506 // URL loading is from a different origin and is not delayed.
TEST(RequestHandlerTest,NotificationsCrossOriginDirect)507 TEST(RequestHandlerTest, NotificationsCrossOriginDirect) {
508 RunNetNotifyTest(NNTT_NORMAL, false);
509 }
510
511 // Verify network notifications for multiple browsers existing simultaniously.
512 // URL loading is from a different origin and is continued asynchronously from
513 // the render process.
TEST(RequestHandlerTest,NotificationsCrossOriginDelayedRenderer)514 TEST(RequestHandlerTest, NotificationsCrossOriginDelayedRenderer) {
515 RunNetNotifyTest(NNTT_DELAYED_RENDERER, false);
516 }
517
518 // Verify network notifications for multiple browsers existing simultaniously.
519 // URL loading is from a different origin and is continued asynchronously from
520 // the browser process.
TEST(RequestHandlerTest,NotificationsCrossOriginDelayedBrowser)521 TEST(RequestHandlerTest, NotificationsCrossOriginDelayedBrowser) {
522 RunNetNotifyTest(NNTT_DELAYED_BROWSER, false);
523 }
524
525 // Entry point for creating request handler renderer test objects.
526 // Called from client_app_delegates.cc.
CreateRequestHandlerRendererTests(ClientAppRenderer::DelegateSet & delegates)527 void CreateRequestHandlerRendererTests(
528 ClientAppRenderer::DelegateSet& delegates) {
529 delegates.insert(new NetNotifyRendererTest);
530 }
531