1 // Copyright 2014 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 "chrome/browser/safe_browsing/incident_reporting/last_download_finder.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/files/file_util.h"
13 #include "base/run_loop.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/history/chrome_history_client.h"
17 #include "chrome/browser/history/chrome_history_client_factory.h"
18 #include "chrome/browser/history/history_service_factory.h"
19 #include "chrome/browser/history/web_history_service_factory.h"
20 #include "chrome/browser/prefs/browser_prefs.h"
21 #include "chrome/browser/profiles/profile_manager.h"
22 #include "chrome/common/chrome_constants.h"
23 #include "chrome/common/pref_names.h"
24 #include "chrome/common/safe_browsing/csd.pb.h"
25 #include "chrome/test/base/testing_browser_process.h"
26 #include "chrome/test/base/testing_pref_service_syncable.h"
27 #include "chrome/test/base/testing_profile.h"
28 #include "chrome/test/base/testing_profile_manager.h"
29 #include "components/history/content/browser/content_visit_delegate.h"
30 #include "components/history/content/browser/download_constants_utils.h"
31 #include "components/history/content/browser/history_database_helper.h"
32 #include "components/history/core/browser/download_constants.h"
33 #include "components/history/core/browser/download_row.h"
34 #include "components/history/core/browser/history_constants.h"
35 #include "components/history/core/browser/history_database_params.h"
36 #include "components/history/core/browser/history_service.h"
37 #include "content/public/test/test_browser_thread_bundle.h"
38 #include "content/public/test/test_utils.h"
39 #include "testing/gtest/include/gtest/gtest.h"
43 // A BrowserContextKeyedServiceFactory::TestingFactoryFunction that creates a
44 // HistoryService for a TestingProfile.
45 KeyedService
* BuildHistoryService(content::BrowserContext
* context
) {
46 TestingProfile
* profile
= static_cast<TestingProfile
*>(context
);
48 // Delete the file before creating the service.
49 base::FilePath
history_path(
50 profile
->GetPath().Append(history::kHistoryFilename
));
51 if (!base::DeleteFile(history_path
, false) ||
52 base::PathExists(history_path
)) {
53 ADD_FAILURE() << "failed to delete history db file "
54 << history_path
.value();
58 history::HistoryService
* history_service
= new history::HistoryService(
59 ChromeHistoryClientFactory::GetForProfile(profile
),
60 scoped_ptr
<history::VisitDelegate
>());
61 if (history_service
->Init(
62 profile
->GetPrefs()->GetString(prefs::kAcceptLanguages
),
63 history::HistoryDatabaseParamsForPath(profile
->GetPath()))) {
64 return history_service
;
67 ADD_FAILURE() << "failed to initialize history service";
68 delete history_service
;
74 namespace safe_browsing
{
76 class LastDownloadFinderTest
: public testing::Test
{
78 void NeverCalled(scoped_ptr
<ClientIncidentReport_DownloadDetails
> download
) {
82 // Creates a new profile that participates in safe browsing and adds a
83 // download to its history.
84 void CreateProfileWithDownload() {
85 TestingProfile
* profile
= CreateProfile(SAFE_BROWSING_OPT_IN
);
86 history::HistoryService
* history_service
=
87 HistoryServiceFactory::GetForProfile(
88 profile
, ServiceAccessType::EXPLICIT_ACCESS
);
89 history_service
->CreateDownload(
90 CreateTestDownloadRow(),
91 base::Bind(&LastDownloadFinderTest::OnDownloadCreated
,
92 base::Unretained(this)));
95 // LastDownloadFinder::LastDownloadCallback implementation that
96 // passes the found download to |result| and then runs a closure.
98 scoped_ptr
<ClientIncidentReport_DownloadDetails
>* result
,
99 const base::Closure
& quit_closure
,
100 scoped_ptr
<ClientIncidentReport_DownloadDetails
> download
) {
101 *result
= download
.Pass();
106 // A type for specifying whether or not a profile created by CreateProfile
107 // participates in safe browsing.
108 enum SafeBrowsingDisposition
{
109 SAFE_BROWSING_OPT_OUT
,
110 SAFE_BROWSING_OPT_IN
,
113 LastDownloadFinderTest() : profile_number_() {}
115 void SetUp() override
{
116 testing::Test::SetUp();
117 profile_manager_
.reset(
118 new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
119 ASSERT_TRUE(profile_manager_
->SetUp());
122 void TearDown() override
{
123 // Shut down the history service on all profiles.
124 std::vector
<Profile
*> profiles(
125 profile_manager_
->profile_manager()->GetLoadedProfiles());
126 for (size_t i
= 0; i
< profiles
.size(); ++i
) {
127 profiles
[0]->AsTestingProfile()->DestroyHistoryService();
129 profile_manager_
.reset();
130 TestingBrowserProcess::DeleteInstance();
131 testing::Test::TearDown();
134 TestingProfile
* CreateProfile(SafeBrowsingDisposition safe_browsing_opt_in
) {
135 std::string
profile_name("profile");
136 profile_name
.append(base::IntToString(++profile_number_
));
138 // Set up keyed service factories.
139 TestingProfile::TestingFactories factories
;
140 // Build up a custom history service.
141 factories
.push_back(std::make_pair(HistoryServiceFactory::GetInstance(),
142 &BuildHistoryService
));
143 // Suppress WebHistoryService since it makes network requests.
144 factories
.push_back(std::make_pair(
145 WebHistoryServiceFactory::GetInstance(),
146 static_cast<BrowserContextKeyedServiceFactory::TestingFactoryFunction
>(
149 // Create prefs for the profile with safe browsing enabled or not.
150 scoped_ptr
<TestingPrefServiceSyncable
> prefs(
151 new TestingPrefServiceSyncable
);
152 chrome::RegisterUserProfilePrefs(prefs
->registry());
153 prefs
->SetBoolean(prefs::kSafeBrowsingEnabled
,
154 safe_browsing_opt_in
== SAFE_BROWSING_OPT_IN
);
156 TestingProfile
* profile
= profile_manager_
->CreateTestingProfile(
159 base::UTF8ToUTF16(profile_name
), // user_name
161 std::string(), // supervised_user_id
167 LastDownloadFinder::DownloadDetailsGetter
GetDownloadDetailsGetter() {
168 return base::Bind(&LastDownloadFinderTest::GetDownloadDetails
,
169 base::Unretained(this));
172 void AddDownload(Profile
* profile
, const history::DownloadRow
& download
) {
173 base::RunLoop run_loop
;
175 history::HistoryService
* history_service
=
176 HistoryServiceFactory::GetForProfile(
177 profile
, ServiceAccessType::EXPLICIT_ACCESS
);
178 history_service
->CreateDownload(
180 base::Bind(&LastDownloadFinderTest::ContinueOnDownloadCreated
,
181 base::Unretained(this),
182 run_loop
.QuitClosure()));
186 // Wait for the history backend thread to process any outstanding tasks.
187 // This is needed because HistoryService::QueryDownloads uses PostTaskAndReply
188 // to do work on the backend thread and then invoke the caller's callback on
189 // the originating thread. The PostTaskAndReplyRelay holds a reference to the
190 // backend until its RunReplyAndSelfDestruct is called on the originating
191 // thread. This reference MUST be released (on the originating thread,
192 // remember) _before_ calling DestroyHistoryService in TearDown(). See the
193 // giant comment in HistoryService::Cleanup explaining where the backend's
195 void FlushHistoryBackend(Profile
* profile
) {
196 base::RunLoop run_loop
;
197 HistoryServiceFactory::GetForProfile(profile
,
198 ServiceAccessType::EXPLICIT_ACCESS
)
199 ->FlushForTest(run_loop
.QuitClosure());
201 // Then make sure anything bounced back to the main thread has been handled.
202 base::RunLoop().RunUntilIdle();
205 // Runs the last download finder on all loaded profiles, returning the found
206 // download or an empty pointer if none was found.
207 scoped_ptr
<ClientIncidentReport_DownloadDetails
> RunLastDownloadFinder() {
208 base::RunLoop run_loop
;
210 scoped_ptr
<ClientIncidentReport_DownloadDetails
> last_download
;
212 scoped_ptr
<LastDownloadFinder
> finder(LastDownloadFinder::Create(
213 GetDownloadDetailsGetter(),
214 base::Bind(&LastDownloadFinderTest::OnLastDownload
,
215 base::Unretained(this),
217 run_loop
.QuitClosure())));
222 return last_download
.Pass();
225 history::DownloadRow
CreateTestDownloadRow() {
226 base::Time
now(base::Time::Now());
227 return history::DownloadRow(
228 base::FilePath(FILE_PATH_LITERAL("spam.exe")),
229 base::FilePath(FILE_PATH_LITERAL("spam.exe")),
230 std::vector
<GURL
>(1, GURL("http://www.google.com")), // url_chain
232 "application/octet-stream", // mime_type
233 "application/octet-stream", // original_mime_type
234 now
- base::TimeDelta::FromMinutes(10), // start
235 now
- base::TimeDelta::FromMinutes(9), // end
236 std::string(), // etag
237 std::string(), // last_modified
240 history::DownloadState::COMPLETE
, // download_state
241 history::DownloadDangerType::NOT_DANGEROUS
, // danger_type
242 history::ToHistoryDownloadInterruptReason(
243 content::DOWNLOAD_INTERRUPT_REASON_NONE
), // interrupt_reason,
245 false, // download_opened
246 std::string(), // ext_id
247 std::string()); // ext_name
250 void ExpectNoDownloadFound(
251 scoped_ptr
<ClientIncidentReport_DownloadDetails
> download
) {
252 EXPECT_FALSE(download
);
255 void ExpectFoundTestDownload(
256 scoped_ptr
<ClientIncidentReport_DownloadDetails
> download
) {
257 ASSERT_TRUE(download
);
260 content::TestBrowserThreadBundle browser_thread_bundle_
;
261 scoped_ptr
<TestingProfileManager
> profile_manager_
;
264 // A HistoryService::DownloadCreateCallback that asserts that the download was
265 // created and runs |closure|.
266 void ContinueOnDownloadCreated(const base::Closure
& closure
, bool created
) {
267 ASSERT_TRUE(created
);
271 // A HistoryService::DownloadCreateCallback that asserts that the download was
273 void OnDownloadCreated(bool created
) { ASSERT_TRUE(created
); }
275 void GetDownloadDetails(
276 content::BrowserContext
* context
,
277 const DownloadMetadataManager::GetDownloadDetailsCallback
& callback
) {
278 callback
.Run(scoped_ptr
<ClientIncidentReport_DownloadDetails
>());
284 // Tests that nothing happens if there are no profiles at all.
285 TEST_F(LastDownloadFinderTest
, NoProfiles
) {
286 ExpectNoDownloadFound(RunLastDownloadFinder());
289 // Tests that nothing happens other than the callback being invoked if there are
290 // no profiles participating in safe browsing.
291 TEST_F(LastDownloadFinderTest
, NoParticipatingProfiles
) {
292 // Create a profile with a history service that is opted-out
293 TestingProfile
* profile
= CreateProfile(SAFE_BROWSING_OPT_OUT
);
296 AddDownload(profile
, CreateTestDownloadRow());
298 ExpectNoDownloadFound(RunLastDownloadFinder());
301 // Tests that a download is found from a single profile.
302 TEST_F(LastDownloadFinderTest
, SimpleEndToEnd
) {
303 // Create a profile with a history service that is opted-in.
304 TestingProfile
* profile
= CreateProfile(SAFE_BROWSING_OPT_IN
);
307 AddDownload(profile
, CreateTestDownloadRow());
309 ExpectFoundTestDownload(RunLastDownloadFinder());
312 // Tests that there is no crash if the finder is deleted before results arrive.
313 TEST_F(LastDownloadFinderTest
, DeleteBeforeResults
) {
314 // Create a profile with a history service that is opted-in.
315 TestingProfile
* profile
= CreateProfile(SAFE_BROWSING_OPT_IN
);
318 AddDownload(profile
, CreateTestDownloadRow());
320 // Start a finder and kill it before the search completes.
321 LastDownloadFinder::Create(GetDownloadDetailsGetter(),
322 base::Bind(&LastDownloadFinderTest::NeverCalled
,
323 base::Unretained(this))).reset();
325 // Flush tasks on the history backend thread.
326 FlushHistoryBackend(profile
);
329 // Tests that a download in profile added after the search is begun is found.
330 TEST_F(LastDownloadFinderTest
, AddProfileAfterStarting
) {
331 // Create a profile with a history service that is opted-in.
332 CreateProfile(SAFE_BROWSING_OPT_IN
);
334 scoped_ptr
<ClientIncidentReport_DownloadDetails
> last_download
;
335 base::RunLoop run_loop
;
337 // Post a task that will create a second profile once the main loop is run.
338 base::MessageLoop::current()->PostTask(
340 base::Bind(&LastDownloadFinderTest::CreateProfileWithDownload
,
341 base::Unretained(this)));
343 // Create a finder that we expect will find a download in the second profile.
344 scoped_ptr
<LastDownloadFinder
> finder(LastDownloadFinder::Create(
345 GetDownloadDetailsGetter(),
346 base::Bind(&LastDownloadFinderTest::OnLastDownload
,
347 base::Unretained(this),
349 run_loop
.QuitClosure())));
353 ExpectFoundTestDownload(last_download
.Pass());
356 } // namespace safe_browsing