Roll src/third_party/WebKit f36d5e0:68b67cd (svn 193299:193303)
[chromium-blink-merge.git] / components / navigation_interception / intercept_navigation_resource_throttle_unittest.cc
blob3640e8f6f0e55acbe6adeb5f0a09d7a5af1a98eb
1 // Copyright (c) 2012 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.
5 #include "base/bind.h"
6 #include "base/bind_helpers.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "base/run_loop.h"
9 #include "base/synchronization/waitable_event.h"
10 #include "components/navigation_interception/intercept_navigation_resource_throttle.h"
11 #include "components/navigation_interception/navigation_params.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "content/public/browser/render_frame_host.h"
14 #include "content/public/browser/render_process_host.h"
15 #include "content/public/browser/resource_context.h"
16 #include "content/public/browser/resource_controller.h"
17 #include "content/public/browser/resource_dispatcher_host.h"
18 #include "content/public/browser/resource_dispatcher_host_delegate.h"
19 #include "content/public/browser/resource_request_info.h"
20 #include "content/public/browser/resource_throttle.h"
21 #include "content/public/browser/web_contents.h"
22 #include "content/public/browser/web_contents_delegate.h"
23 #include "content/public/test/mock_resource_context.h"
24 #include "content/public/test/test_renderer_host.h"
25 #include "net/base/request_priority.h"
26 #include "net/http/http_response_headers.h"
27 #include "net/http/http_response_info.h"
28 #include "net/url_request/redirect_info.h"
29 #include "net/url_request/url_request.h"
30 #include "net/url_request/url_request_test_util.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "testing/gtest/include/gtest/gtest.h"
34 using content::ResourceType;
35 using testing::_;
36 using testing::Eq;
37 using testing::Ne;
38 using testing::Property;
39 using testing::Return;
41 namespace navigation_interception {
43 namespace {
45 const char kTestUrl[] = "http://www.test.com/";
46 const char kUnsafeTestUrl[] = "about:crash";
48 // The MS C++ compiler complains about not being able to resolve which url()
49 // method (const or non-const) to use if we use the Property matcher to check
50 // the return value of the NavigationParams::url() method.
51 // It is possible to suppress the error by specifying the types directly but
52 // that results in very ugly syntax, which is why these custom matchers are
53 // used instead.
54 MATCHER(NavigationParamsUrlIsTest, "") {
55 return arg.url() == GURL(kTestUrl);
58 MATCHER(NavigationParamsUrlIsSafe, "") {
59 return arg.url() != GURL(kUnsafeTestUrl);
62 } // namespace
65 // MockInterceptCallbackReceiver ----------------------------------------------
67 class MockInterceptCallbackReceiver {
68 public:
69 MOCK_METHOD2(ShouldIgnoreNavigation,
70 bool(content::WebContents* source,
71 const NavigationParams& navigation_params));
74 // MockResourceController -----------------------------------------------------
75 class MockResourceController : public content::ResourceController {
76 public:
77 enum Status {
78 UNKNOWN,
79 RESUMED,
80 CANCELLED
83 MockResourceController()
84 : status_(UNKNOWN) {
87 Status status() const { return status_; }
89 // ResourceController:
90 void Cancel() override { NOTREACHED(); }
91 void CancelAndIgnore() override { status_ = CANCELLED; }
92 void CancelWithError(int error_code) override { NOTREACHED(); }
93 void Resume() override {
94 DCHECK(status_ == UNKNOWN);
95 status_ = RESUMED;
98 private:
99 Status status_;
102 // TestIOThreadState ----------------------------------------------------------
104 enum RedirectMode {
105 REDIRECT_MODE_NO_REDIRECT,
106 REDIRECT_MODE_302,
109 class TestIOThreadState {
110 public:
111 TestIOThreadState(const GURL& url,
112 int render_process_id,
113 int render_frame_id,
114 const std::string& request_method,
115 RedirectMode redirect_mode,
116 MockInterceptCallbackReceiver* callback_receiver)
117 : resource_context_(&test_url_request_context_),
118 request_(resource_context_.GetRequestContext()->CreateRequest(
119 url,
120 net::DEFAULT_PRIORITY,
121 NULL /* delegate */)) {
122 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
123 if (render_process_id != MSG_ROUTING_NONE &&
124 render_frame_id != MSG_ROUTING_NONE) {
125 content::ResourceRequestInfo::AllocateForTesting(
126 request_.get(),
127 content::RESOURCE_TYPE_MAIN_FRAME,
128 &resource_context_,
129 render_process_id,
130 MSG_ROUTING_NONE,
131 render_frame_id,
132 true, // is_main_frame
133 false, // parent_is_main_frame
134 true, // allow_download
135 false); // is_async
137 throttle_.reset(new InterceptNavigationResourceThrottle(
138 request_.get(),
139 base::Bind(&MockInterceptCallbackReceiver::ShouldIgnoreNavigation,
140 base::Unretained(callback_receiver))));
141 throttle_->set_controller_for_testing(&throttle_controller_);
142 request_->set_method(request_method);
144 if (redirect_mode == REDIRECT_MODE_302) {
145 net::HttpResponseInfo& response_info =
146 const_cast<net::HttpResponseInfo&>(request_->response_info());
147 response_info.headers = new net::HttpResponseHeaders(
148 "Status: 302 Found\0\0");
152 void ThrottleWillStartRequest(bool* defer) {
153 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
154 throttle_->WillStartRequest(defer);
157 void ThrottleWillRedirectRequest(const net::RedirectInfo& redirect_info,
158 bool* defer) {
159 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
160 throttle_->WillRedirectRequest(redirect_info, defer);
163 bool request_resumed() const {
164 return throttle_controller_.status() ==
165 MockResourceController::RESUMED;
168 bool request_cancelled() const {
169 return throttle_controller_.status() ==
170 MockResourceController::CANCELLED;
173 private:
174 net::TestURLRequestContext test_url_request_context_;
175 content::MockResourceContext resource_context_;
176 scoped_ptr<net::URLRequest> request_;
177 scoped_ptr<InterceptNavigationResourceThrottle> throttle_;
178 MockResourceController throttle_controller_;
181 // InterceptNavigationResourceThrottleTest ------------------------------------
183 class InterceptNavigationResourceThrottleTest
184 : public content::RenderViewHostTestHarness {
185 public:
186 InterceptNavigationResourceThrottleTest()
187 : mock_callback_receiver_(new MockInterceptCallbackReceiver()),
188 io_thread_state_(NULL) {
191 void SetUp() override { RenderViewHostTestHarness::SetUp(); }
193 void TearDown() override {
194 if (web_contents())
195 web_contents()->SetDelegate(NULL);
197 content::BrowserThread::DeleteSoon(
198 content::BrowserThread::IO, FROM_HERE, io_thread_state_);
200 RenderViewHostTestHarness::TearDown();
203 void SetIOThreadState(TestIOThreadState* io_thread_state) {
204 io_thread_state_ = io_thread_state;
207 void RunThrottleWillStartRequestOnIOThread(
208 const GURL& url,
209 const std::string& request_method,
210 RedirectMode redirect_mode,
211 int render_process_id,
212 int render_frame_id,
213 bool* defer) {
214 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
215 TestIOThreadState* io_thread_state =
216 new TestIOThreadState(url, render_process_id, render_frame_id,
217 request_method, redirect_mode,
218 mock_callback_receiver_.get());
220 SetIOThreadState(io_thread_state);
222 if (redirect_mode == REDIRECT_MODE_NO_REDIRECT) {
223 io_thread_state->ThrottleWillStartRequest(defer);
224 } else {
225 // 302 redirects convert POSTs to gets.
226 net::RedirectInfo redirect_info;
227 redirect_info.new_url = url;
228 redirect_info.new_method = "GET";
229 io_thread_state->ThrottleWillRedirectRequest(redirect_info, defer);
233 protected:
234 enum ShouldIgnoreNavigationCallbackAction {
235 IgnoreNavigation,
236 DontIgnoreNavigation
239 void SetUpWebContentsDelegateAndDrainRunLoop(
240 ShouldIgnoreNavigationCallbackAction callback_action,
241 bool* defer) {
242 ON_CALL(*mock_callback_receiver_, ShouldIgnoreNavigation(_, _))
243 .WillByDefault(Return(callback_action == IgnoreNavigation));
244 EXPECT_CALL(*mock_callback_receiver_,
245 ShouldIgnoreNavigation(web_contents(),
246 NavigationParamsUrlIsTest()))
247 .Times(1);
249 content::BrowserThread::PostTask(
250 content::BrowserThread::IO,
251 FROM_HERE,
252 base::Bind(
253 &InterceptNavigationResourceThrottleTest::
254 RunThrottleWillStartRequestOnIOThread,
255 base::Unretained(this),
256 GURL(kTestUrl),
257 "GET",
258 REDIRECT_MODE_NO_REDIRECT,
259 web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
260 web_contents()->GetMainFrame()->GetRoutingID(),
261 base::Unretained(defer)));
263 // Wait for the request to finish processing.
264 base::RunLoop().RunUntilIdle();
267 void WaitForPreviouslyScheduledIoThreadWork() {
268 base::WaitableEvent io_thread_work_done(true, false);
269 content::BrowserThread::PostTask(
270 content::BrowserThread::IO,
271 FROM_HERE,
272 base::Bind(
273 &base::WaitableEvent::Signal,
274 base::Unretained(&io_thread_work_done)));
275 io_thread_work_done.Wait();
278 scoped_ptr<MockInterceptCallbackReceiver> mock_callback_receiver_;
279 TestIOThreadState* io_thread_state_;
282 TEST_F(InterceptNavigationResourceThrottleTest,
283 RequestDeferredAndResumedIfNavigationNotIgnored) {
284 bool defer = false;
285 SetUpWebContentsDelegateAndDrainRunLoop(DontIgnoreNavigation, &defer);
287 EXPECT_TRUE(defer);
288 ASSERT_TRUE(io_thread_state_);
289 EXPECT_TRUE(io_thread_state_->request_resumed());
292 TEST_F(InterceptNavigationResourceThrottleTest,
293 RequestDeferredAndCancelledIfNavigationIgnored) {
294 bool defer = false;
295 SetUpWebContentsDelegateAndDrainRunLoop(IgnoreNavigation, &defer);
297 EXPECT_TRUE(defer);
298 ASSERT_TRUE(io_thread_state_);
299 EXPECT_TRUE(io_thread_state_->request_cancelled());
302 TEST_F(InterceptNavigationResourceThrottleTest,
303 NoCallbackMadeIfContentsDeletedWhileThrottleRunning) {
304 bool defer = false;
306 // The tested scenario is when the WebContents is deleted after the
307 // ResourceThrottle has finished processing on the IO thread but before the
308 // UI thread callback has been processed. Since both threads in this test
309 // are serviced by one message loop, the post order is the execution order.
310 EXPECT_CALL(*mock_callback_receiver_,
311 ShouldIgnoreNavigation(_, _))
312 .Times(0);
314 content::BrowserThread::PostTask(
315 content::BrowserThread::IO,
316 FROM_HERE,
317 base::Bind(
318 &InterceptNavigationResourceThrottleTest::
319 RunThrottleWillStartRequestOnIOThread,
320 base::Unretained(this),
321 GURL(kTestUrl),
322 "GET",
323 REDIRECT_MODE_NO_REDIRECT,
324 web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
325 web_contents()->GetMainFrame()->GetRoutingID(),
326 base::Unretained(&defer)));
328 content::BrowserThread::PostTask(
329 content::BrowserThread::UI,
330 FROM_HERE,
331 base::Bind(
332 &RenderViewHostTestHarness::DeleteContents,
333 base::Unretained(this)));
335 // The WebContents will now be deleted and only after that will the UI-thread
336 // callback posted by the ResourceThrottle be executed.
337 base::RunLoop().RunUntilIdle();
339 EXPECT_TRUE(defer);
340 ASSERT_TRUE(io_thread_state_);
341 EXPECT_TRUE(io_thread_state_->request_resumed());
344 TEST_F(InterceptNavigationResourceThrottleTest,
345 RequestNotDeferredForRequestNotAssociatedWithARenderView) {
346 bool defer = false;
348 content::BrowserThread::PostTask(
349 content::BrowserThread::IO,
350 FROM_HERE,
351 base::Bind(
352 &InterceptNavigationResourceThrottleTest::
353 RunThrottleWillStartRequestOnIOThread,
354 base::Unretained(this),
355 GURL(kTestUrl),
356 "GET",
357 REDIRECT_MODE_NO_REDIRECT,
358 MSG_ROUTING_NONE,
359 MSG_ROUTING_NONE,
360 base::Unretained(&defer)));
362 // Wait for the request to finish processing.
363 base::RunLoop().RunUntilIdle();
365 EXPECT_FALSE(defer);
368 TEST_F(InterceptNavigationResourceThrottleTest,
369 CallbackCalledWithFilteredUrl) {
370 bool defer = false;
372 ON_CALL(*mock_callback_receiver_,
373 ShouldIgnoreNavigation(_, NavigationParamsUrlIsSafe()))
374 .WillByDefault(Return(false));
375 EXPECT_CALL(*mock_callback_receiver_,
376 ShouldIgnoreNavigation(_, NavigationParamsUrlIsSafe()))
377 .Times(1);
379 content::BrowserThread::PostTask(
380 content::BrowserThread::IO,
381 FROM_HERE,
382 base::Bind(
383 &InterceptNavigationResourceThrottleTest::
384 RunThrottleWillStartRequestOnIOThread,
385 base::Unretained(this),
386 GURL(kUnsafeTestUrl),
387 "GET",
388 REDIRECT_MODE_NO_REDIRECT,
389 web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
390 web_contents()->GetMainFrame()->GetRoutingID(),
391 base::Unretained(&defer)));
393 // Wait for the request to finish processing.
394 base::RunLoop().RunUntilIdle();
397 TEST_F(InterceptNavigationResourceThrottleTest,
398 CallbackIsPostFalseForGet) {
399 bool defer = false;
401 EXPECT_CALL(*mock_callback_receiver_,
402 ShouldIgnoreNavigation(_, AllOf(
403 NavigationParamsUrlIsSafe(),
404 Property(&NavigationParams::is_post, Eq(false)))))
405 .WillOnce(Return(false));
407 content::BrowserThread::PostTask(
408 content::BrowserThread::IO,
409 FROM_HERE,
410 base::Bind(
411 &InterceptNavigationResourceThrottleTest::
412 RunThrottleWillStartRequestOnIOThread,
413 base::Unretained(this),
414 GURL(kTestUrl),
415 "GET",
416 REDIRECT_MODE_NO_REDIRECT,
417 web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
418 web_contents()->GetMainFrame()->GetRoutingID(),
419 base::Unretained(&defer)));
421 // Wait for the request to finish processing.
422 base::RunLoop().RunUntilIdle();
425 TEST_F(InterceptNavigationResourceThrottleTest,
426 CallbackIsPostTrueForPost) {
427 bool defer = false;
429 EXPECT_CALL(*mock_callback_receiver_,
430 ShouldIgnoreNavigation(_, AllOf(
431 NavigationParamsUrlIsSafe(),
432 Property(&NavigationParams::is_post, Eq(true)))))
433 .WillOnce(Return(false));
435 content::BrowserThread::PostTask(
436 content::BrowserThread::IO,
437 FROM_HERE,
438 base::Bind(
439 &InterceptNavigationResourceThrottleTest::
440 RunThrottleWillStartRequestOnIOThread,
441 base::Unretained(this),
442 GURL(kTestUrl),
443 "POST",
444 REDIRECT_MODE_NO_REDIRECT,
445 web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
446 web_contents()->GetMainFrame()->GetRoutingID(),
447 base::Unretained(&defer)));
449 // Wait for the request to finish processing.
450 base::RunLoop().RunUntilIdle();
453 TEST_F(InterceptNavigationResourceThrottleTest,
454 CallbackIsPostFalseForPostConvertedToGetBy302) {
455 bool defer = false;
457 EXPECT_CALL(*mock_callback_receiver_,
458 ShouldIgnoreNavigation(_, AllOf(
459 NavigationParamsUrlIsSafe(),
460 Property(&NavigationParams::is_post, Eq(false)))))
461 .WillOnce(Return(false));
463 content::BrowserThread::PostTask(
464 content::BrowserThread::IO,
465 FROM_HERE,
466 base::Bind(
467 &InterceptNavigationResourceThrottleTest::
468 RunThrottleWillStartRequestOnIOThread,
469 base::Unretained(this),
470 GURL(kTestUrl),
471 "POST",
472 REDIRECT_MODE_302,
473 web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
474 web_contents()->GetMainFrame()->GetRoutingID(),
475 base::Unretained(&defer)));
477 // Wait for the request to finish processing.
478 base::RunLoop().RunUntilIdle();
481 } // namespace navigation_interception