Upstreaming TransitionPageHelper.
[chromium-blink-merge.git] / components / navigation_interception / intercept_navigation_resource_throttle_unittest.cc
blob52ba76a98046d2b2556f5695c0eac5162b70c885
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 NULL /* cookie_store */)) {
123 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
124 if (render_process_id != MSG_ROUTING_NONE &&
125 render_frame_id != MSG_ROUTING_NONE) {
126 content::ResourceRequestInfo::AllocateForTesting(
127 request_.get(),
128 content::RESOURCE_TYPE_MAIN_FRAME,
129 &resource_context_,
130 render_process_id,
131 MSG_ROUTING_NONE,
132 render_frame_id,
133 true, // is_main_frame
134 false, // parent_is_main_frame
135 true, // allow_download
136 false); // is_async
138 throttle_.reset(new InterceptNavigationResourceThrottle(
139 request_.get(),
140 base::Bind(&MockInterceptCallbackReceiver::ShouldIgnoreNavigation,
141 base::Unretained(callback_receiver))));
142 throttle_->set_controller_for_testing(&throttle_controller_);
143 request_->set_method(request_method);
145 if (redirect_mode == REDIRECT_MODE_302) {
146 net::HttpResponseInfo& response_info =
147 const_cast<net::HttpResponseInfo&>(request_->response_info());
148 response_info.headers = new net::HttpResponseHeaders(
149 "Status: 302 Found\0\0");
153 void ThrottleWillStartRequest(bool* defer) {
154 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
155 throttle_->WillStartRequest(defer);
158 void ThrottleWillRedirectRequest(const net::RedirectInfo& redirect_info,
159 bool* defer) {
160 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
161 throttle_->WillRedirectRequest(redirect_info, defer);
164 bool request_resumed() const {
165 return throttle_controller_.status() ==
166 MockResourceController::RESUMED;
169 bool request_cancelled() const {
170 return throttle_controller_.status() ==
171 MockResourceController::CANCELLED;
174 private:
175 net::TestURLRequestContext test_url_request_context_;
176 content::MockResourceContext resource_context_;
177 scoped_ptr<net::URLRequest> request_;
178 scoped_ptr<InterceptNavigationResourceThrottle> throttle_;
179 MockResourceController throttle_controller_;
182 // InterceptNavigationResourceThrottleTest ------------------------------------
184 class InterceptNavigationResourceThrottleTest
185 : public content::RenderViewHostTestHarness {
186 public:
187 InterceptNavigationResourceThrottleTest()
188 : mock_callback_receiver_(new MockInterceptCallbackReceiver()),
189 io_thread_state_(NULL) {
192 void SetUp() override { RenderViewHostTestHarness::SetUp(); }
194 void TearDown() override {
195 if (web_contents())
196 web_contents()->SetDelegate(NULL);
198 content::BrowserThread::DeleteSoon(
199 content::BrowserThread::IO, FROM_HERE, io_thread_state_);
201 RenderViewHostTestHarness::TearDown();
204 void SetIOThreadState(TestIOThreadState* io_thread_state) {
205 io_thread_state_ = io_thread_state;
208 void RunThrottleWillStartRequestOnIOThread(
209 const GURL& url,
210 const std::string& request_method,
211 RedirectMode redirect_mode,
212 int render_process_id,
213 int render_frame_id,
214 bool* defer) {
215 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
216 TestIOThreadState* io_thread_state =
217 new TestIOThreadState(url, render_process_id, render_frame_id,
218 request_method, redirect_mode,
219 mock_callback_receiver_.get());
221 SetIOThreadState(io_thread_state);
223 if (redirect_mode == REDIRECT_MODE_NO_REDIRECT) {
224 io_thread_state->ThrottleWillStartRequest(defer);
225 } else {
226 // 302 redirects convert POSTs to gets.
227 net::RedirectInfo redirect_info;
228 redirect_info.new_url = url;
229 redirect_info.new_method = "GET";
230 io_thread_state->ThrottleWillRedirectRequest(redirect_info, defer);
234 protected:
235 enum ShouldIgnoreNavigationCallbackAction {
236 IgnoreNavigation,
237 DontIgnoreNavigation
240 void SetUpWebContentsDelegateAndDrainRunLoop(
241 ShouldIgnoreNavigationCallbackAction callback_action,
242 bool* defer) {
243 ON_CALL(*mock_callback_receiver_, ShouldIgnoreNavigation(_, _))
244 .WillByDefault(Return(callback_action == IgnoreNavigation));
245 EXPECT_CALL(*mock_callback_receiver_,
246 ShouldIgnoreNavigation(web_contents(),
247 NavigationParamsUrlIsTest()))
248 .Times(1);
250 content::BrowserThread::PostTask(
251 content::BrowserThread::IO,
252 FROM_HERE,
253 base::Bind(
254 &InterceptNavigationResourceThrottleTest::
255 RunThrottleWillStartRequestOnIOThread,
256 base::Unretained(this),
257 GURL(kTestUrl),
258 "GET",
259 REDIRECT_MODE_NO_REDIRECT,
260 web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
261 web_contents()->GetMainFrame()->GetRoutingID(),
262 base::Unretained(defer)));
264 // Wait for the request to finish processing.
265 base::RunLoop().RunUntilIdle();
268 void WaitForPreviouslyScheduledIoThreadWork() {
269 base::WaitableEvent io_thread_work_done(true, false);
270 content::BrowserThread::PostTask(
271 content::BrowserThread::IO,
272 FROM_HERE,
273 base::Bind(
274 &base::WaitableEvent::Signal,
275 base::Unretained(&io_thread_work_done)));
276 io_thread_work_done.Wait();
279 scoped_ptr<MockInterceptCallbackReceiver> mock_callback_receiver_;
280 TestIOThreadState* io_thread_state_;
283 TEST_F(InterceptNavigationResourceThrottleTest,
284 RequestDeferredAndResumedIfNavigationNotIgnored) {
285 bool defer = false;
286 SetUpWebContentsDelegateAndDrainRunLoop(DontIgnoreNavigation, &defer);
288 EXPECT_TRUE(defer);
289 ASSERT_TRUE(io_thread_state_);
290 EXPECT_TRUE(io_thread_state_->request_resumed());
293 TEST_F(InterceptNavigationResourceThrottleTest,
294 RequestDeferredAndCancelledIfNavigationIgnored) {
295 bool defer = false;
296 SetUpWebContentsDelegateAndDrainRunLoop(IgnoreNavigation, &defer);
298 EXPECT_TRUE(defer);
299 ASSERT_TRUE(io_thread_state_);
300 EXPECT_TRUE(io_thread_state_->request_cancelled());
303 TEST_F(InterceptNavigationResourceThrottleTest,
304 NoCallbackMadeIfContentsDeletedWhileThrottleRunning) {
305 bool defer = false;
307 // The tested scenario is when the WebContents is deleted after the
308 // ResourceThrottle has finished processing on the IO thread but before the
309 // UI thread callback has been processed. Since both threads in this test
310 // are serviced by one message loop, the post order is the execution order.
311 EXPECT_CALL(*mock_callback_receiver_,
312 ShouldIgnoreNavigation(_, _))
313 .Times(0);
315 content::BrowserThread::PostTask(
316 content::BrowserThread::IO,
317 FROM_HERE,
318 base::Bind(
319 &InterceptNavigationResourceThrottleTest::
320 RunThrottleWillStartRequestOnIOThread,
321 base::Unretained(this),
322 GURL(kTestUrl),
323 "GET",
324 REDIRECT_MODE_NO_REDIRECT,
325 web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
326 web_contents()->GetMainFrame()->GetRoutingID(),
327 base::Unretained(&defer)));
329 content::BrowserThread::PostTask(
330 content::BrowserThread::UI,
331 FROM_HERE,
332 base::Bind(
333 &RenderViewHostTestHarness::DeleteContents,
334 base::Unretained(this)));
336 // The WebContents will now be deleted and only after that will the UI-thread
337 // callback posted by the ResourceThrottle be executed.
338 base::RunLoop().RunUntilIdle();
340 EXPECT_TRUE(defer);
341 ASSERT_TRUE(io_thread_state_);
342 EXPECT_TRUE(io_thread_state_->request_resumed());
345 TEST_F(InterceptNavigationResourceThrottleTest,
346 RequestNotDeferredForRequestNotAssociatedWithARenderView) {
347 bool defer = false;
349 content::BrowserThread::PostTask(
350 content::BrowserThread::IO,
351 FROM_HERE,
352 base::Bind(
353 &InterceptNavigationResourceThrottleTest::
354 RunThrottleWillStartRequestOnIOThread,
355 base::Unretained(this),
356 GURL(kTestUrl),
357 "GET",
358 REDIRECT_MODE_NO_REDIRECT,
359 MSG_ROUTING_NONE,
360 MSG_ROUTING_NONE,
361 base::Unretained(&defer)));
363 // Wait for the request to finish processing.
364 base::RunLoop().RunUntilIdle();
366 EXPECT_FALSE(defer);
369 TEST_F(InterceptNavigationResourceThrottleTest,
370 CallbackCalledWithFilteredUrl) {
371 bool defer = false;
373 ON_CALL(*mock_callback_receiver_,
374 ShouldIgnoreNavigation(_, NavigationParamsUrlIsSafe()))
375 .WillByDefault(Return(false));
376 EXPECT_CALL(*mock_callback_receiver_,
377 ShouldIgnoreNavigation(_, NavigationParamsUrlIsSafe()))
378 .Times(1);
380 content::BrowserThread::PostTask(
381 content::BrowserThread::IO,
382 FROM_HERE,
383 base::Bind(
384 &InterceptNavigationResourceThrottleTest::
385 RunThrottleWillStartRequestOnIOThread,
386 base::Unretained(this),
387 GURL(kUnsafeTestUrl),
388 "GET",
389 REDIRECT_MODE_NO_REDIRECT,
390 web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
391 web_contents()->GetMainFrame()->GetRoutingID(),
392 base::Unretained(&defer)));
394 // Wait for the request to finish processing.
395 base::RunLoop().RunUntilIdle();
398 TEST_F(InterceptNavigationResourceThrottleTest,
399 CallbackIsPostFalseForGet) {
400 bool defer = false;
402 EXPECT_CALL(*mock_callback_receiver_,
403 ShouldIgnoreNavigation(_, AllOf(
404 NavigationParamsUrlIsSafe(),
405 Property(&NavigationParams::is_post, Eq(false)))))
406 .WillOnce(Return(false));
408 content::BrowserThread::PostTask(
409 content::BrowserThread::IO,
410 FROM_HERE,
411 base::Bind(
412 &InterceptNavigationResourceThrottleTest::
413 RunThrottleWillStartRequestOnIOThread,
414 base::Unretained(this),
415 GURL(kTestUrl),
416 "GET",
417 REDIRECT_MODE_NO_REDIRECT,
418 web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
419 web_contents()->GetMainFrame()->GetRoutingID(),
420 base::Unretained(&defer)));
422 // Wait for the request to finish processing.
423 base::RunLoop().RunUntilIdle();
426 TEST_F(InterceptNavigationResourceThrottleTest,
427 CallbackIsPostTrueForPost) {
428 bool defer = false;
430 EXPECT_CALL(*mock_callback_receiver_,
431 ShouldIgnoreNavigation(_, AllOf(
432 NavigationParamsUrlIsSafe(),
433 Property(&NavigationParams::is_post, Eq(true)))))
434 .WillOnce(Return(false));
436 content::BrowserThread::PostTask(
437 content::BrowserThread::IO,
438 FROM_HERE,
439 base::Bind(
440 &InterceptNavigationResourceThrottleTest::
441 RunThrottleWillStartRequestOnIOThread,
442 base::Unretained(this),
443 GURL(kTestUrl),
444 "POST",
445 REDIRECT_MODE_NO_REDIRECT,
446 web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
447 web_contents()->GetMainFrame()->GetRoutingID(),
448 base::Unretained(&defer)));
450 // Wait for the request to finish processing.
451 base::RunLoop().RunUntilIdle();
454 TEST_F(InterceptNavigationResourceThrottleTest,
455 CallbackIsPostFalseForPostConvertedToGetBy302) {
456 bool defer = false;
458 EXPECT_CALL(*mock_callback_receiver_,
459 ShouldIgnoreNavigation(_, AllOf(
460 NavigationParamsUrlIsSafe(),
461 Property(&NavigationParams::is_post, Eq(false)))))
462 .WillOnce(Return(false));
464 content::BrowserThread::PostTask(
465 content::BrowserThread::IO,
466 FROM_HERE,
467 base::Bind(
468 &InterceptNavigationResourceThrottleTest::
469 RunThrottleWillStartRequestOnIOThread,
470 base::Unretained(this),
471 GURL(kTestUrl),
472 "POST",
473 REDIRECT_MODE_302,
474 web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
475 web_contents()->GetMainFrame()->GetRoutingID(),
476 base::Unretained(&defer)));
478 // Wait for the request to finish processing.
479 base::RunLoop().RunUntilIdle();
482 } // namespace navigation_interception