Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / components / navigation_interception / intercept_navigation_resource_throttle_unittest.cc
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.
4
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/url_request.h"
29 #include "net/url_request/url_request_test_util.h"
30 #include "testing/gmock/include/gmock/gmock.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32
33 using content::ResourceType;
34 using testing::_;
35 using testing::Eq;
36 using testing::Ne;
37 using testing::Property;
38 using testing::Return;
39
40 namespace navigation_interception {
41
42 namespace {
43
44 const char kTestUrl[] = "http://www.test.com/";
45 const char kUnsafeTestUrl[] = "about:crash";
46
47 // The MS C++ compiler complains about not being able to resolve which url()
48 // method (const or non-const) to use if we use the Property matcher to check
49 // the return value of the NavigationParams::url() method.
50 // It is possible to suppress the error by specifying the types directly but
51 // that results in very ugly syntax, which is why these custom matchers are
52 // used instead.
53 MATCHER(NavigationParamsUrlIsTest, "") {
54   return arg.url() == GURL(kTestUrl);
55 }
56
57 MATCHER(NavigationParamsUrlIsSafe, "") {
58   return arg.url() != GURL(kUnsafeTestUrl);
59 }
60
61 }  // namespace
62
63
64 // MockInterceptCallbackReceiver ----------------------------------------------
65
66 class MockInterceptCallbackReceiver {
67  public:
68   MOCK_METHOD2(ShouldIgnoreNavigation,
69                bool(content::WebContents* source,
70                     const NavigationParams& navigation_params));
71 };
72
73 // MockResourceController -----------------------------------------------------
74 class MockResourceController : public content::ResourceController {
75  public:
76   enum Status {
77     UNKNOWN,
78     RESUMED,
79     CANCELLED
80   };
81
82   MockResourceController()
83       : status_(UNKNOWN) {
84   }
85
86   Status status() const { return status_; }
87
88   // ResourceController:
89   void Cancel() override { NOTREACHED(); }
90   void CancelAndIgnore() override { status_ = CANCELLED; }
91   void CancelWithError(int error_code) override { NOTREACHED(); }
92   void Resume() override {
93     DCHECK(status_ == UNKNOWN);
94     status_ = RESUMED;
95   }
96
97  private:
98   Status status_;
99 };
100
101 // TestIOThreadState ----------------------------------------------------------
102
103 enum RedirectMode {
104   REDIRECT_MODE_NO_REDIRECT,
105   REDIRECT_MODE_302,
106 };
107
108 class TestIOThreadState {
109  public:
110   TestIOThreadState(const GURL& url,
111                     int render_process_id,
112                     int render_frame_id,
113                     const std::string& request_method,
114                     RedirectMode redirect_mode,
115                     MockInterceptCallbackReceiver* callback_receiver)
116       : resource_context_(&test_url_request_context_),
117         request_(resource_context_.GetRequestContext()->CreateRequest(
118                      url,
119                      net::DEFAULT_PRIORITY,
120                      NULL /* delegate */,
121                      NULL /* cookie_store */)) {
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           false);
133     }
134     throttle_.reset(new InterceptNavigationResourceThrottle(
135         request_.get(),
136         base::Bind(&MockInterceptCallbackReceiver::ShouldIgnoreNavigation,
137                    base::Unretained(callback_receiver))));
138     throttle_->set_controller_for_testing(&throttle_controller_);
139     request_->set_method(request_method);
140
141     if (redirect_mode == REDIRECT_MODE_302) {
142       net::HttpResponseInfo& response_info =
143           const_cast<net::HttpResponseInfo&>(request_->response_info());
144       response_info.headers = new net::HttpResponseHeaders(
145           "Status: 302 Found\0\0");
146     }
147   }
148
149   void ThrottleWillStartRequest(bool* defer) {
150     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
151     throttle_->WillStartRequest(defer);
152   }
153
154   void ThrottleWillRedirectRequest(const GURL& new_url, bool* defer) {
155     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
156     throttle_->WillRedirectRequest(new_url, defer);
157   }
158
159   bool request_resumed() const {
160     return throttle_controller_.status() ==
161         MockResourceController::RESUMED;
162   }
163
164   bool request_cancelled() const {
165     return throttle_controller_.status() ==
166         MockResourceController::CANCELLED;
167   }
168
169  private:
170   net::TestURLRequestContext test_url_request_context_;
171   content::MockResourceContext resource_context_;
172   scoped_ptr<net::URLRequest> request_;
173   scoped_ptr<InterceptNavigationResourceThrottle> throttle_;
174   MockResourceController throttle_controller_;
175 };
176
177 // InterceptNavigationResourceThrottleTest ------------------------------------
178
179 class InterceptNavigationResourceThrottleTest
180   : public content::RenderViewHostTestHarness {
181  public:
182   InterceptNavigationResourceThrottleTest()
183       : mock_callback_receiver_(new MockInterceptCallbackReceiver()),
184         io_thread_state_(NULL) {
185   }
186
187   void SetUp() override { RenderViewHostTestHarness::SetUp(); }
188
189   void TearDown() override {
190     if (web_contents())
191       web_contents()->SetDelegate(NULL);
192
193     content::BrowserThread::DeleteSoon(
194         content::BrowserThread::IO, FROM_HERE, io_thread_state_);
195
196     RenderViewHostTestHarness::TearDown();
197   }
198
199   void SetIOThreadState(TestIOThreadState* io_thread_state) {
200     io_thread_state_ = io_thread_state;
201   }
202
203   void RunThrottleWillStartRequestOnIOThread(
204       const GURL& url,
205       const std::string& request_method,
206       RedirectMode redirect_mode,
207       int render_process_id,
208       int render_frame_id,
209       bool* defer) {
210     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
211     TestIOThreadState* io_thread_state =
212         new TestIOThreadState(url, render_process_id, render_frame_id,
213                               request_method, redirect_mode,
214                               mock_callback_receiver_.get());
215
216     SetIOThreadState(io_thread_state);
217
218     if (redirect_mode == REDIRECT_MODE_NO_REDIRECT)
219       io_thread_state->ThrottleWillStartRequest(defer);
220     else
221       io_thread_state->ThrottleWillRedirectRequest(url, defer);
222   }
223
224  protected:
225   enum ShouldIgnoreNavigationCallbackAction {
226     IgnoreNavigation,
227     DontIgnoreNavigation
228   };
229
230   void SetUpWebContentsDelegateAndDrainRunLoop(
231       ShouldIgnoreNavigationCallbackAction callback_action,
232       bool* defer) {
233     ON_CALL(*mock_callback_receiver_, ShouldIgnoreNavigation(_, _))
234       .WillByDefault(Return(callback_action == IgnoreNavigation));
235     EXPECT_CALL(*mock_callback_receiver_,
236                 ShouldIgnoreNavigation(web_contents(),
237                                        NavigationParamsUrlIsTest()))
238       .Times(1);
239
240     content::BrowserThread::PostTask(
241         content::BrowserThread::IO,
242         FROM_HERE,
243         base::Bind(
244             &InterceptNavigationResourceThrottleTest::
245                 RunThrottleWillStartRequestOnIOThread,
246             base::Unretained(this),
247             GURL(kTestUrl),
248             "GET",
249             REDIRECT_MODE_NO_REDIRECT,
250             web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
251             web_contents()->GetMainFrame()->GetRoutingID(),
252             base::Unretained(defer)));
253
254     // Wait for the request to finish processing.
255     base::RunLoop().RunUntilIdle();
256   }
257
258   void WaitForPreviouslyScheduledIoThreadWork() {
259     base::WaitableEvent io_thread_work_done(true, false);
260     content::BrowserThread::PostTask(
261         content::BrowserThread::IO,
262         FROM_HERE,
263         base::Bind(
264           &base::WaitableEvent::Signal,
265           base::Unretained(&io_thread_work_done)));
266     io_thread_work_done.Wait();
267   }
268
269   scoped_ptr<MockInterceptCallbackReceiver> mock_callback_receiver_;
270   TestIOThreadState* io_thread_state_;
271 };
272
273 TEST_F(InterceptNavigationResourceThrottleTest,
274        RequestDeferredAndResumedIfNavigationNotIgnored) {
275   bool defer = false;
276   SetUpWebContentsDelegateAndDrainRunLoop(DontIgnoreNavigation, &defer);
277
278   EXPECT_TRUE(defer);
279   ASSERT_TRUE(io_thread_state_);
280   EXPECT_TRUE(io_thread_state_->request_resumed());
281 }
282
283 TEST_F(InterceptNavigationResourceThrottleTest,
284        RequestDeferredAndCancelledIfNavigationIgnored) {
285   bool defer = false;
286   SetUpWebContentsDelegateAndDrainRunLoop(IgnoreNavigation, &defer);
287
288   EXPECT_TRUE(defer);
289   ASSERT_TRUE(io_thread_state_);
290   EXPECT_TRUE(io_thread_state_->request_cancelled());
291 }
292
293 TEST_F(InterceptNavigationResourceThrottleTest,
294        NoCallbackMadeIfContentsDeletedWhileThrottleRunning) {
295   bool defer = false;
296
297   // The tested scenario is when the WebContents is deleted after the
298   // ResourceThrottle has finished processing on the IO thread but before the
299   // UI thread callback has been processed.  Since both threads in this test
300   // are serviced by one message loop, the post order is the execution order.
301   EXPECT_CALL(*mock_callback_receiver_,
302               ShouldIgnoreNavigation(_, _))
303       .Times(0);
304
305   content::BrowserThread::PostTask(
306       content::BrowserThread::IO,
307       FROM_HERE,
308       base::Bind(
309           &InterceptNavigationResourceThrottleTest::
310           RunThrottleWillStartRequestOnIOThread,
311           base::Unretained(this),
312           GURL(kTestUrl),
313           "GET",
314           REDIRECT_MODE_NO_REDIRECT,
315           web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
316           web_contents()->GetMainFrame()->GetRoutingID(),
317           base::Unretained(&defer)));
318
319   content::BrowserThread::PostTask(
320       content::BrowserThread::UI,
321       FROM_HERE,
322       base::Bind(
323           &RenderViewHostTestHarness::DeleteContents,
324           base::Unretained(this)));
325
326   // The WebContents will now be deleted and only after that will the UI-thread
327   // callback posted by the ResourceThrottle be executed.
328   base::RunLoop().RunUntilIdle();
329
330   EXPECT_TRUE(defer);
331   ASSERT_TRUE(io_thread_state_);
332   EXPECT_TRUE(io_thread_state_->request_resumed());
333 }
334
335 TEST_F(InterceptNavigationResourceThrottleTest,
336        RequestNotDeferredForRequestNotAssociatedWithARenderView) {
337   bool defer = false;
338
339   content::BrowserThread::PostTask(
340       content::BrowserThread::IO,
341       FROM_HERE,
342       base::Bind(
343           &InterceptNavigationResourceThrottleTest::
344               RunThrottleWillStartRequestOnIOThread,
345           base::Unretained(this),
346           GURL(kTestUrl),
347           "GET",
348           REDIRECT_MODE_NO_REDIRECT,
349           MSG_ROUTING_NONE,
350           MSG_ROUTING_NONE,
351           base::Unretained(&defer)));
352
353   // Wait for the request to finish processing.
354   base::RunLoop().RunUntilIdle();
355
356   EXPECT_FALSE(defer);
357 }
358
359 TEST_F(InterceptNavigationResourceThrottleTest,
360        CallbackCalledWithFilteredUrl) {
361   bool defer = false;
362
363   ON_CALL(*mock_callback_receiver_,
364           ShouldIgnoreNavigation(_, NavigationParamsUrlIsSafe()))
365       .WillByDefault(Return(false));
366   EXPECT_CALL(*mock_callback_receiver_,
367               ShouldIgnoreNavigation(_, NavigationParamsUrlIsSafe()))
368       .Times(1);
369
370   content::BrowserThread::PostTask(
371       content::BrowserThread::IO,
372       FROM_HERE,
373       base::Bind(
374           &InterceptNavigationResourceThrottleTest::
375               RunThrottleWillStartRequestOnIOThread,
376           base::Unretained(this),
377           GURL(kUnsafeTestUrl),
378           "GET",
379           REDIRECT_MODE_NO_REDIRECT,
380           web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
381           web_contents()->GetMainFrame()->GetRoutingID(),
382           base::Unretained(&defer)));
383
384   // Wait for the request to finish processing.
385   base::RunLoop().RunUntilIdle();
386 }
387
388 TEST_F(InterceptNavigationResourceThrottleTest,
389        CallbackIsPostFalseForGet) {
390   bool defer = false;
391
392   EXPECT_CALL(*mock_callback_receiver_,
393               ShouldIgnoreNavigation(_, AllOf(
394                   NavigationParamsUrlIsSafe(),
395                   Property(&NavigationParams::is_post, Eq(false)))))
396       .WillOnce(Return(false));
397
398   content::BrowserThread::PostTask(
399       content::BrowserThread::IO,
400       FROM_HERE,
401       base::Bind(
402           &InterceptNavigationResourceThrottleTest::
403               RunThrottleWillStartRequestOnIOThread,
404           base::Unretained(this),
405           GURL(kTestUrl),
406           "GET",
407           REDIRECT_MODE_NO_REDIRECT,
408           web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
409           web_contents()->GetMainFrame()->GetRoutingID(),
410           base::Unretained(&defer)));
411
412   // Wait for the request to finish processing.
413   base::RunLoop().RunUntilIdle();
414 }
415
416 TEST_F(InterceptNavigationResourceThrottleTest,
417        CallbackIsPostTrueForPost) {
418   bool defer = false;
419
420   EXPECT_CALL(*mock_callback_receiver_,
421               ShouldIgnoreNavigation(_, AllOf(
422                   NavigationParamsUrlIsSafe(),
423                   Property(&NavigationParams::is_post, Eq(true)))))
424       .WillOnce(Return(false));
425
426   content::BrowserThread::PostTask(
427       content::BrowserThread::IO,
428       FROM_HERE,
429       base::Bind(
430           &InterceptNavigationResourceThrottleTest::
431               RunThrottleWillStartRequestOnIOThread,
432           base::Unretained(this),
433           GURL(kTestUrl),
434           "POST",
435           REDIRECT_MODE_NO_REDIRECT,
436           web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
437           web_contents()->GetMainFrame()->GetRoutingID(),
438           base::Unretained(&defer)));
439
440   // Wait for the request to finish processing.
441   base::RunLoop().RunUntilIdle();
442 }
443
444 TEST_F(InterceptNavigationResourceThrottleTest,
445        CallbackIsPostFalseForPostConvertedToGetBy302) {
446   bool defer = false;
447
448   EXPECT_CALL(*mock_callback_receiver_,
449               ShouldIgnoreNavigation(_, AllOf(
450                   NavigationParamsUrlIsSafe(),
451                   Property(&NavigationParams::is_post, Eq(false)))))
452       .WillOnce(Return(false));
453
454   content::BrowserThread::PostTask(
455       content::BrowserThread::IO,
456       FROM_HERE,
457       base::Bind(
458           &InterceptNavigationResourceThrottleTest::
459               RunThrottleWillStartRequestOnIOThread,
460           base::Unretained(this),
461           GURL(kTestUrl),
462           "POST",
463           REDIRECT_MODE_302,
464           web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
465           web_contents()->GetMainFrame()->GetRoutingID(),
466           base::Unretained(&defer)));
467
468   // Wait for the request to finish processing.
469   base::RunLoop().RunUntilIdle();
470 }
471
472 }  // namespace navigation_interception