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/auto_reset.h"
6 #include "base/files/scoped_temp_dir.h"
7 #include "base/json/json_reader.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/values.h"
10 #include "chrome/browser/download/download_item_model.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/webui/downloads_dom_handler.h"
14 #include "chrome/common/pref_names.h"
15 #include "chrome/test/base/in_process_browser_test.h"
16 #include "content/public/browser/web_contents.h"
17 #include "content/public/test/mock_download_item.h"
18 #include "content/public/test/mock_download_manager.h"
19 #include "content/public/test/test_utils.h"
23 // Reads |right_json| into a ListValue |left_list|; returns true if all
24 // key-value pairs in in all dictionaries in |right_list| are also in the
25 // corresponding dictionary in |left_list|. Ignores keys in dictionaries in
26 // |left_list| that are not in the corresponding dictionary in |right_list|.
27 bool ListMatches(base::ListValue
* left_list
, const std::string
& right_json
) {
28 scoped_ptr
<base::Value
> right_value
= base::JSONReader::Read(right_json
);
29 base::ListValue
* right_list
= NULL
;
30 CHECK(right_value
->GetAsList(&right_list
));
31 for (size_t i
= 0; i
< left_list
->GetSize(); ++i
) {
32 base::DictionaryValue
* left_dict
= NULL
;
33 base::DictionaryValue
* right_dict
= NULL
;
34 CHECK(left_list
->GetDictionary(i
, &left_dict
));
35 CHECK(right_list
->GetDictionary(i
, &right_dict
));
36 for (base::DictionaryValue::Iterator
iter(*right_dict
);
37 !iter
.IsAtEnd(); iter
.Advance()) {
38 base::Value
* left_value
= NULL
;
39 if (left_dict
->HasKey(iter
.key()) &&
40 left_dict
->Get(iter
.key(), &left_value
) &&
41 !iter
.value().Equals(left_value
)) {
42 LOG(WARNING
) << "key \"" << iter
.key() << "\" doesn't match ("
43 << iter
.value() << " vs. " << *left_value
<< ")";
51 // A |DownloadsDOMHandler| that doesn't use a real WebUI object, but is real in
52 // all other respects.
53 class MockDownloadsDOMHandler
: public DownloadsDOMHandler
{
55 explicit MockDownloadsDOMHandler(content::DownloadManager
* download_manager
)
56 : DownloadsDOMHandler(download_manager
),
58 waiting_updated_(false) {
60 ~MockDownloadsDOMHandler() override
{}
62 base::ListValue
* downloads_list() { return downloads_list_
.get(); }
63 base::DictionaryValue
* download_updated() { return download_updated_
.get(); }
65 void WaitForDownloadsList() {
68 base::AutoReset
<bool> reset_waiting(&waiting_list_
, true);
69 content::RunMessageLoop();
72 void WaitForDownloadUpdated() {
73 if (download_updated_
)
75 base::AutoReset
<bool> reset_waiting(&waiting_updated_
, true);
76 content::RunMessageLoop();
79 void ForceSendCurrentDownloads() {
80 ScheduleSendCurrentDownloads();
83 void reset_downloads_list() { downloads_list_
.reset(); }
84 void reset_download_updated() { download_updated_
.reset(); }
86 using DownloadsDOMHandler::FinalizeRemovals
;
89 content::WebContents
* GetWebUIWebContents() override
{ return NULL
; }
91 void CallUpdateAll(const base::ListValue
& list
) override
{
92 downloads_list_
.reset(list
.DeepCopy());
94 content::BrowserThread::PostTask(content::BrowserThread::UI
,
96 base::MessageLoop::QuitClosure());
100 void CallUpdateItem(const base::DictionaryValue
& item
) override
{
101 download_updated_
.reset(item
.DeepCopy());
102 if (waiting_updated_
) {
103 content::BrowserThread::PostTask(content::BrowserThread::UI
,
105 base::MessageLoop::QuitClosure());
110 scoped_ptr
<base::ListValue
> downloads_list_
;
111 scoped_ptr
<base::DictionaryValue
> download_updated_
;
113 bool waiting_updated_
;
115 DISALLOW_COPY_AND_ASSIGN(MockDownloadsDOMHandler
);
120 class DownloadsDOMHandlerTest
: public InProcessBrowserTest
{
122 DownloadsDOMHandlerTest() {}
124 ~DownloadsDOMHandlerTest() override
{}
126 void SetUpOnMainThread() override
{
127 mock_handler_
.reset(new MockDownloadsDOMHandler(download_manager()));
128 CHECK(downloads_directory_
.CreateUniqueTempDir());
129 browser()->profile()->GetPrefs()->SetFilePath(
130 prefs::kDownloadDefaultDirectory
,
131 downloads_directory_
.path());
132 CHECK(test_server()->Start());
133 mock_handler_
->HandleGetDownloads(nullptr);
136 content::DownloadManager
* download_manager() {
137 return content::BrowserContext::GetDownloadManager(browser()->profile());
140 void DownloadAnItem() {
141 GURL url
= test_server()->GetURL("files/downloads/image.jpg");
142 std::vector
<GURL
> url_chain
;
143 url_chain
.push_back(url
);
144 base::Time
current(base::Time::Now());
145 download_manager()->CreateDownloadItem(
147 base::FilePath(FILE_PATH_LITERAL("/path/to/file")),
148 base::FilePath(FILE_PATH_LITERAL("/path/to/file")),
151 "application/octet-stream",
152 "application/octet-stream",
159 content::DownloadItem::COMPLETE
,
160 content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS
,
161 content::DOWNLOAD_INTERRUPT_REASON_NONE
,
164 mock_handler_
->ForceSendCurrentDownloads();
165 mock_handler_
->WaitForDownloadsList();
166 ASSERT_EQ(1, static_cast<int>(mock_handler_
->downloads_list()->GetSize()));
167 EXPECT_TRUE(ListMatches(
168 mock_handler_
->downloads_list(),
169 "[{\"file_externally_removed\": false,"
170 " \"file_name\": \"file\","
173 " \"since_string\": \"Today\","
174 " \"state\": \"COMPLETE\","
175 " \"total\": 128}]"));
179 scoped_ptr
<MockDownloadsDOMHandler
> mock_handler_
;
182 base::ScopedTempDir downloads_directory_
;
184 DISALLOW_COPY_AND_ASSIGN(DownloadsDOMHandlerTest
);
187 // Tests removing all items, both when prohibited and when allowed.
188 IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest
, RemoveAll
) {
191 mock_handler_
->reset_downloads_list();
192 browser()->profile()->GetPrefs()->SetBoolean(
193 prefs::kAllowDeletingBrowserHistory
, false);
194 mock_handler_
->HandleClearAll(NULL
);
195 // Attempting to clear all shouldn't do anything when deletion is disabled.
196 mock_handler_
->ForceSendCurrentDownloads();
197 mock_handler_
->WaitForDownloadsList();
198 ASSERT_EQ(1, static_cast<int>(mock_handler_
->downloads_list()->GetSize()));
200 mock_handler_
->reset_downloads_list();
201 browser()->profile()->GetPrefs()->SetBoolean(
202 prefs::kAllowDeletingBrowserHistory
, true);
203 mock_handler_
->HandleClearAll(NULL
);
204 mock_handler_
->WaitForDownloadsList();
205 EXPECT_EQ(0, static_cast<int>(mock_handler_
->downloads_list()->GetSize()));
208 // Tests removing one item, both when prohibited and when allowed.
209 IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest
, RemoveOneItem
) {
211 base::ListValue item
;
212 item
.AppendString("1");
214 mock_handler_
->reset_downloads_list();
215 browser()->profile()->GetPrefs()->SetBoolean(
216 prefs::kAllowDeletingBrowserHistory
, false);
217 mock_handler_
->HandleRemove(&item
);
218 // Removing an item only sends the new download list if anything was actually
219 // removed, so force it.
220 mock_handler_
->ForceSendCurrentDownloads();
221 mock_handler_
->WaitForDownloadsList();
222 ASSERT_EQ(1, static_cast<int>(mock_handler_
->downloads_list()->GetSize()));
224 mock_handler_
->reset_downloads_list();
225 browser()->profile()->GetPrefs()->SetBoolean(
226 prefs::kAllowDeletingBrowserHistory
, true);
227 mock_handler_
->HandleRemove(&item
);
228 mock_handler_
->WaitForDownloadsList();
229 EXPECT_EQ(0, static_cast<int>(mock_handler_
->downloads_list()->GetSize()));
232 IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest
, ClearAllSkipsInProgress
) {
233 testing::NiceMock
<content::MockDownloadManager
> manager
;
234 EXPECT_CALL(manager
, GetBrowserContext()).WillRepeatedly(
235 testing::Return(browser()->profile()));
236 mock_handler_
.reset(new MockDownloadsDOMHandler(&manager
));
237 mock_handler_
->HandleGetDownloads(nullptr);
239 content::MockDownloadItem item
;
240 EXPECT_CALL(item
, GetState()).WillRepeatedly(
241 testing::Return(content::DownloadItem::IN_PROGRESS
));
242 EXPECT_CALL(item
, UpdateObservers()).Times(0);
244 std::vector
<content::DownloadItem
*> items
;
245 items
.push_back(&item
);
246 EXPECT_CALL(manager
, GetAllDownloads(testing::_
)).WillOnce(
247 testing::SetArgPointee
<0>(items
));
249 mock_handler_
->HandleClearAll(NULL
);
250 EXPECT_TRUE(DownloadItemModel(&item
).ShouldShowInShelf());
252 mock_handler_
.reset();
255 // Tests that DownloadsDOMHandler detects new downloads and relays them to the
257 // crbug.com/159390: This test fails when daylight savings time ends.
258 IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest
, DownloadsRelayed
) {
261 mock_handler_
->WaitForDownloadUpdated();
262 const base::DictionaryValue
* update
= mock_handler_
->download_updated();
266 ASSERT_TRUE(update
->GetBoolean("file_externally_removed", &removed
));
267 EXPECT_TRUE(removed
);
270 ASSERT_TRUE(update
->GetString("id", &id
));
273 mock_handler_
->reset_downloads_list();
274 browser()->profile()->GetPrefs()->SetBoolean(
275 prefs::kAllowDeletingBrowserHistory
, true);
276 mock_handler_
->HandleClearAll(NULL
);
277 mock_handler_
->WaitForDownloadsList();
278 EXPECT_EQ(0, static_cast<int>(mock_handler_
->downloads_list()->GetSize()));
281 // Tests that DownloadsDOMHandler actually calls DownloadItem::Remove() when
282 // it's closed (and removals can no longer be undone).
283 IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest
, RemoveCalledOnPageClose
) {
284 testing::NiceMock
<content::MockDownloadManager
> manager
;
285 EXPECT_CALL(manager
, GetBrowserContext()).WillRepeatedly(
286 testing::Return(browser()->profile()));
287 mock_handler_
.reset(new MockDownloadsDOMHandler(&manager
));
288 mock_handler_
->HandleGetDownloads(nullptr);
290 content::MockDownloadItem item
;
291 EXPECT_CALL(item
, GetId()).WillRepeatedly(testing::Return(1));
292 EXPECT_CALL(item
, GetState()).WillRepeatedly(
293 testing::Return(content::DownloadItem::COMPLETE
));
295 DownloadItemModel
model(&item
);
296 EXPECT_TRUE(model
.ShouldShowInShelf());
298 EXPECT_CALL(manager
, GetDownload(1)).WillRepeatedly(testing::Return(&item
));
300 base::ListValue remove
;
301 remove
.AppendString("1");
302 EXPECT_CALL(item
, UpdateObservers()).Times(1);
303 mock_handler_
->HandleRemove(&remove
);
304 EXPECT_FALSE(model
.ShouldShowInShelf());
306 EXPECT_CALL(item
, Remove()).Times(1);
307 // Call |mock_handler_->FinalizeRemovals()| instead of |mock_handler_.reset()|
308 // because the vtable is affected during destruction and the fake manager
309 // rigging doesn't work.
310 mock_handler_
->FinalizeRemovals();
311 mock_handler_
.reset();
314 // TODO(benjhayden): Test the extension downloads filter for both
315 // mock_handler_.downloads_list() and mock_handler_.download_updated().
317 // TODO(benjhayden): Test incognito, both downloads_list() and that on-record
318 // calls can't access off-record items.
320 // TODO(benjhayden): Test that bad download ids incoming from the javascript are
321 // dropped on the floor.
323 // TODO(benjhayden): Test that IsTemporary() downloads are not shown.
325 // TODO(benjhayden): Test that RemoveObserver is called on all download items,
326 // including items that crossed IsTemporary() and back.