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 "chrome/browser/download/download_request_limiter.h"
6
7#include "base/bind.h"
8#include "base/run_loop.h"
9#include "chrome/browser/content_settings/host_content_settings_map.h"
10#include "chrome/browser/download/download_request_infobar_delegate.h"
11#include "chrome/browser/infobars/infobar_service.h"
12#include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h"
13#include "chrome/test/base/chrome_render_view_host_test_harness.h"
14#include "chrome/test/base/testing_profile.h"
15#include "content/public/browser/navigation_controller.h"
16#include "content/public/browser/web_contents.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
19using content::WebContents;
20
21class DownloadRequestLimiterTest : public ChromeRenderViewHostTestHarness {
22 public:
23  enum TestingAction {
24    ACCEPT,
25    CANCEL,
26    WAIT
27  };
28
29  virtual void SetUp() {
30    ChromeRenderViewHostTestHarness::SetUp();
31    BlockedContentTabHelper::CreateForWebContents(web_contents());
32    InfoBarService::CreateForWebContents(web_contents());
33    testing_action_ = ACCEPT;
34    ask_allow_count_ = cancel_count_ = continue_count_ = 0;
35    download_request_limiter_ = new DownloadRequestLimiter();
36    fake_create_callback_ = base::Bind(
37        &DownloadRequestLimiterTest::FakeCreate, base::Unretained(this));
38    DownloadRequestInfoBarDelegate::SetCallbackForTesting(
39        &fake_create_callback_);
40    content_settings_ = new HostContentSettingsMap(profile_.GetPrefs(), false);
41    DownloadRequestLimiter::SetContentSettingsForTesting(
42        content_settings_.get());
43  }
44
45  void FakeCreate(
46      InfoBarService* infobar_service,
47      base::WeakPtr<DownloadRequestLimiter::TabDownloadState> host) {
48    ask_allow_count_++;
49    switch (testing_action_) {
50      case ACCEPT:
51        host->Accept();
52        break;
53      case CANCEL:
54        host->Cancel();
55        break;
56      case WAIT:
57        break;
58    }
59  }
60
61  virtual void TearDown() {
62    content_settings_->ShutdownOnUIThread();
63    content_settings_ = NULL;
64    UnsetDelegate();
65    ChromeRenderViewHostTestHarness::TearDown();
66  }
67
68  virtual void UnsetDelegate() {
69    DownloadRequestInfoBarDelegate::SetCallbackForTesting(NULL);
70  }
71
72  void CanDownload() {
73    CanDownloadFor(web_contents());
74  }
75
76  void CanDownloadFor(WebContents* web_contents) {
77    download_request_limiter_->CanDownloadImpl(
78        web_contents,
79        -1,  // request id
80        "GET",  // request method
81        base::Bind(&DownloadRequestLimiterTest::ContinueDownload,
82                   base::Unretained(this)));
83    base::RunLoop().RunUntilIdle();
84  }
85
86  void OnUserGesture() {
87    OnUserGestureFor(web_contents());
88  }
89
90  void OnUserGestureFor(WebContents* web_contents) {
91    DownloadRequestLimiter::TabDownloadState* state =
92        download_request_limiter_->GetDownloadState(web_contents, NULL, false);
93    if (state)
94      state->DidGetUserGesture();
95  }
96
97  void AboutToNavigateRenderView() {
98    DownloadRequestLimiter::TabDownloadState* state =
99        download_request_limiter_->GetDownloadState(
100            web_contents(), NULL, false);
101    state->AboutToNavigateRenderView(NULL);
102  }
103
104  void ExpectAndResetCounts(
105      int expect_continues,
106      int expect_cancels,
107      int expect_asks,
108      int line) {
109    EXPECT_EQ(expect_continues, continue_count_) << "line " << line;
110    EXPECT_EQ(expect_cancels, cancel_count_) << "line " << line;
111    EXPECT_EQ(expect_asks, ask_allow_count_) << "line " << line;
112    continue_count_ = cancel_count_ = ask_allow_count_ = 0;
113  }
114
115 protected:
116  void ContinueDownload(bool allow) {
117    if (allow) {
118      continue_count_++;
119    } else {
120      cancel_count_++;
121    }
122  }
123
124  scoped_refptr<DownloadRequestLimiter> download_request_limiter_;
125
126  // The action that FakeCreate() should take.
127  TestingAction testing_action_;
128
129  // Number of times ContinueDownload was invoked.
130  int continue_count_;
131
132  // Number of times CancelDownload was invoked.
133  int cancel_count_;
134
135  // Number of times ShouldAllowDownload was invoked.
136  int ask_allow_count_;
137
138  scoped_refptr<HostContentSettingsMap> content_settings_;
139
140 private:
141  DownloadRequestInfoBarDelegate::FakeCreateCallback fake_create_callback_;
142  TestingProfile profile_;
143};
144
145TEST_F(DownloadRequestLimiterTest,
146       DownloadRequestLimiter_Allow) {
147  // All tabs should initially start at ALLOW_ONE_DOWNLOAD.
148  ASSERT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
149            download_request_limiter_->GetDownloadStatus(web_contents()));
150
151  // Ask if the tab can do a download. This moves to PROMPT_BEFORE_DOWNLOAD.
152  CanDownload();
153  ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
154            download_request_limiter_->GetDownloadStatus(web_contents()));
155  // We should have been told we can download.
156  ExpectAndResetCounts(1, 0, 0, __LINE__);
157
158  // Ask again. This triggers asking the delegate for allow/disallow.
159  testing_action_ = ACCEPT;
160  CanDownload();
161  // This should ask us if the download is allowed.
162  // We should have been told we can download.
163  ExpectAndResetCounts(1, 0, 1, __LINE__);
164  ASSERT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
165            download_request_limiter_->GetDownloadStatus(web_contents()));
166
167  // Ask again and make sure continue is invoked.
168  CanDownload();
169  // The state is at allow_all, which means the delegate shouldn't be asked.
170  // We should have been told we can download.
171  ExpectAndResetCounts(1, 0, 0, __LINE__);
172  ASSERT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
173            download_request_limiter_->GetDownloadStatus(web_contents()));
174}
175
176TEST_F(DownloadRequestLimiterTest,
177       DownloadRequestLimiter_ResetOnNavigation) {
178  NavigateAndCommit(GURL("http://foo.com/bar"));
179
180  // Do two downloads, allowing the second so that we end up with allow all.
181  CanDownload();
182  ExpectAndResetCounts(1, 0, 0, __LINE__);
183  ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
184            download_request_limiter_->GetDownloadStatus(web_contents()));
185
186  testing_action_ = ACCEPT;
187  CanDownload();
188  ExpectAndResetCounts(1, 0, 1, __LINE__);
189  ASSERT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
190            download_request_limiter_->GetDownloadStatus(web_contents()));
191
192  // Navigate to a new URL with the same host, which shouldn't reset the allow
193  // all state.
194  NavigateAndCommit(GURL("http://foo.com/bar2"));
195  CanDownload();
196  ExpectAndResetCounts(1, 0, 0, __LINE__);
197  ASSERT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
198            download_request_limiter_->GetDownloadStatus(web_contents()));
199
200  // Do a user gesture, because we're at allow all, this shouldn't change the
201  // state.
202  OnUserGesture();
203  ASSERT_EQ(DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS,
204            download_request_limiter_->GetDownloadStatus(web_contents()));
205
206  // Navigate to a completely different host, which should reset the state.
207  NavigateAndCommit(GURL("http://fooey.com"));
208  ASSERT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
209            download_request_limiter_->GetDownloadStatus(web_contents()));
210
211  // Do two downloads, allowing the second so that we end up with allow all.
212  CanDownload();
213  ExpectAndResetCounts(1, 0, 0, __LINE__);
214  ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
215            download_request_limiter_->GetDownloadStatus(web_contents()));
216
217  testing_action_ = CANCEL;
218  CanDownload();
219  ExpectAndResetCounts(0, 1, 1, __LINE__);
220  ASSERT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
221            download_request_limiter_->GetDownloadStatus(web_contents()));
222
223  // Navigate to a new URL with the same host, which shouldn't reset the allow
224  // all state.
225  NavigateAndCommit(GURL("http://fooey.com/bar2"));
226  CanDownload();
227  ExpectAndResetCounts(0, 1, 0, __LINE__);
228  ASSERT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
229            download_request_limiter_->GetDownloadStatus(web_contents()));
230}
231
232TEST_F(DownloadRequestLimiterTest,
233       DownloadRequestLimiter_ResetOnUserGesture) {
234  NavigateAndCommit(GURL("http://foo.com/bar"));
235
236  // Do one download, which should change to prompt before download.
237  CanDownload();
238  ExpectAndResetCounts(1, 0, 0, __LINE__);
239  ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
240            download_request_limiter_->GetDownloadStatus(web_contents()));
241
242  // Do a user gesture, which should reset back to allow one.
243  OnUserGesture();
244  ASSERT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
245            download_request_limiter_->GetDownloadStatus(web_contents()));
246
247  // Ask twice, which triggers calling the delegate. Don't allow the download
248  // so that we end up with not allowed.
249  CanDownload();
250  ExpectAndResetCounts(1, 0, 0, __LINE__);
251  ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
252            download_request_limiter_->GetDownloadStatus(web_contents()));
253
254  testing_action_ = CANCEL;
255  CanDownload();
256  ASSERT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
257            download_request_limiter_->GetDownloadStatus(web_contents()));
258  ExpectAndResetCounts(0, 1, 1, __LINE__);
259
260  // A user gesture now should NOT change the state.
261  OnUserGesture();
262  ASSERT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
263            download_request_limiter_->GetDownloadStatus(web_contents()));
264  // And make sure we really can't download.
265  CanDownload();
266  ExpectAndResetCounts(0, 1, 0, __LINE__);
267  // And the state shouldn't have changed.
268  ASSERT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
269            download_request_limiter_->GetDownloadStatus(web_contents()));
270}
271
272TEST_F(DownloadRequestLimiterTest,
273       DownloadRequestLimiter_ResetOnReload) {
274  NavigateAndCommit(GURL("http://foo.com/bar"));
275  ASSERT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
276            download_request_limiter_->GetDownloadStatus(web_contents()));
277
278  // If the user refreshes the page without responding to the infobar, pretend
279  // like the refresh is the initial load: they get 1 free download (probably
280  // the same as the actual initial load), then an infobar.
281  testing_action_ = WAIT;
282
283  CanDownload();
284  ExpectAndResetCounts(1, 0, 0, __LINE__);
285  ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
286            download_request_limiter_->GetDownloadStatus(web_contents()));
287
288  CanDownload();
289  ExpectAndResetCounts(0, 0, 1, __LINE__);
290  ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
291            download_request_limiter_->GetDownloadStatus(web_contents()));
292
293  AboutToNavigateRenderView();
294  base::RunLoop().RunUntilIdle();
295  ExpectAndResetCounts(0, 1, 0, __LINE__);
296  ASSERT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
297            download_request_limiter_->GetDownloadStatus(web_contents()));
298
299  CanDownload();
300  ASSERT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
301            download_request_limiter_->GetDownloadStatus(web_contents()));
302  ExpectAndResetCounts(1, 0, 0, __LINE__);
303
304  testing_action_ = CANCEL;
305  CanDownload();
306  ASSERT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
307            download_request_limiter_->GetDownloadStatus(web_contents()));
308  ExpectAndResetCounts(0, 1, 1, __LINE__);
309
310  AboutToNavigateRenderView();
311  base::RunLoop().RunUntilIdle();
312  ASSERT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
313            download_request_limiter_->GetDownloadStatus(web_contents()));
314  CanDownload();
315  ExpectAndResetCounts(0, 1, 0, __LINE__);
316  ASSERT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
317            download_request_limiter_->GetDownloadStatus(web_contents()));
318}
319
320TEST_F(DownloadRequestLimiterTest,
321       DownloadRequestLimiter_RawWebContents) {
322  scoped_ptr<WebContents> web_contents(CreateTestWebContents());
323  // DownloadRequestLimiter won't try to make an infobar if it doesn't have an
324  // InfoBarService, and we want to test that it will Cancel() instead of
325  // prompting when it doesn't have a InfoBarService, so unset the delegate.
326  UnsetDelegate();
327  ExpectAndResetCounts(0, 0, 0, __LINE__);
328  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
329            download_request_limiter_->GetDownloadStatus(web_contents.get()));
330  // You get one freebie.
331  CanDownloadFor(web_contents.get());
332  ExpectAndResetCounts(1, 0, 0, __LINE__);
333  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
334            download_request_limiter_->GetDownloadStatus(web_contents.get()));
335  OnUserGestureFor(web_contents.get());
336  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
337            download_request_limiter_->GetDownloadStatus(web_contents.get()));
338  CanDownloadFor(web_contents.get());
339  ExpectAndResetCounts(1, 0, 0, __LINE__);
340  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
341            download_request_limiter_->GetDownloadStatus(web_contents.get()));
342  CanDownloadFor(web_contents.get());
343  ExpectAndResetCounts(0, 1, 0, __LINE__);
344  EXPECT_EQ(DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED,
345            download_request_limiter_->GetDownloadStatus(web_contents.get()));
346  OnUserGestureFor(web_contents.get());
347  EXPECT_EQ(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD,
348            download_request_limiter_->GetDownloadStatus(web_contents.get()));
349  CanDownloadFor(web_contents.get());
350  ExpectAndResetCounts(1, 0, 0, __LINE__);
351  EXPECT_EQ(DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD,
352            download_request_limiter_->GetDownloadStatus(web_contents.get()));
353}
354