• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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