| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/auto_reset.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/json/json_reader.h" |
| #include "base/prefs/pref_service.h" |
| #include "base/values.h" |
| #include "chrome/browser/history/download_row.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/webui/downloads_dom_handler.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "content/public/browser/web_contents.h" |
| |
| namespace { |
| |
| // Reads |right_json| into a ListValue |left_list|; returns true if all |
| // key-value pairs in in all dictionaries in |right_list| are also in the |
| // corresponding dictionary in |left_list|. Ignores keys in dictionaries in |
| // |left_list| that are not in the corresponding dictionary in |right_list|. |
| bool ListMatches(base::ListValue* left_list, const std::string& right_json) { |
| scoped_ptr<base::Value> right_value(base::JSONReader::Read(right_json)); |
| base::ListValue* right_list = NULL; |
| CHECK(right_value->GetAsList(&right_list)); |
| for (size_t i = 0; i < left_list->GetSize(); ++i) { |
| base::DictionaryValue* left_dict = NULL; |
| base::DictionaryValue* right_dict = NULL; |
| CHECK(left_list->GetDictionary(i, &left_dict)); |
| CHECK(right_list->GetDictionary(i, &right_dict)); |
| for (base::DictionaryValue::Iterator iter(*right_dict); |
| !iter.IsAtEnd(); iter.Advance()) { |
| base::Value* left_value = NULL; |
| if (left_dict->HasKey(iter.key()) && |
| left_dict->Get(iter.key(), &left_value) && |
| !iter.value().Equals(left_value)) { |
| LOG(WARNING) << iter.key(); |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| // A |DownloadsDOMHandler| that doesn't use a real WebUI object, but is real in |
| // all other respects. |
| class MockDownloadsDOMHandler : public DownloadsDOMHandler { |
| public: |
| explicit MockDownloadsDOMHandler(content::DownloadManager* dlm) |
| : DownloadsDOMHandler(dlm), |
| waiting_list_(false), |
| waiting_updated_(false) { |
| } |
| virtual ~MockDownloadsDOMHandler() {} |
| |
| base::ListValue* downloads_list() { return downloads_list_.get(); } |
| base::ListValue* download_updated() { return download_updated_.get(); } |
| |
| void WaitForDownloadsList() { |
| if (downloads_list_.get()) |
| return; |
| base::AutoReset<bool> reset_waiting(&waiting_list_, true); |
| content::RunMessageLoop(); |
| } |
| |
| void WaitForDownloadUpdated() { |
| if (download_updated_.get()) |
| return; |
| base::AutoReset<bool> reset_waiting(&waiting_updated_, true); |
| content::RunMessageLoop(); |
| } |
| |
| void ForceSendCurrentDownloads() { |
| ScheduleSendCurrentDownloads(); |
| } |
| |
| void reset_downloads_list() { downloads_list_.reset(); } |
| void reset_download_updated() { download_updated_.reset(); } |
| |
| protected: |
| virtual content::WebContents* GetWebUIWebContents() OVERRIDE { |
| return NULL; |
| } |
| |
| virtual void CallDownloadsList(const base::ListValue& downloads) OVERRIDE { |
| downloads_list_.reset(downloads.DeepCopy()); |
| if (waiting_list_) { |
| content::BrowserThread::PostTask(content::BrowserThread::UI, |
| FROM_HERE, |
| base::MessageLoop::QuitClosure()); |
| } |
| } |
| |
| virtual void CallDownloadUpdated(const base::ListValue& download) OVERRIDE { |
| download_updated_.reset(download.DeepCopy()); |
| if (waiting_updated_) { |
| content::BrowserThread::PostTask(content::BrowserThread::UI, |
| FROM_HERE, |
| base::MessageLoop::QuitClosure()); |
| } |
| } |
| |
| private: |
| scoped_ptr<base::ListValue> downloads_list_; |
| scoped_ptr<base::ListValue> download_updated_; |
| bool waiting_list_; |
| bool waiting_updated_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MockDownloadsDOMHandler); |
| }; |
| |
| } // namespace |
| |
| class DownloadsDOMHandlerTest : public InProcessBrowserTest { |
| public: |
| DownloadsDOMHandlerTest() {} |
| |
| virtual ~DownloadsDOMHandlerTest() {} |
| |
| virtual void SetUpOnMainThread() OVERRIDE { |
| mock_handler_.reset(new MockDownloadsDOMHandler(download_manager())); |
| CHECK(downloads_directory_.CreateUniqueTempDir()); |
| browser()->profile()->GetPrefs()->SetFilePath( |
| prefs::kDownloadDefaultDirectory, |
| downloads_directory_.path()); |
| CHECK(test_server()->Start()); |
| } |
| |
| content::DownloadManager* download_manager() { |
| return content::BrowserContext::GetDownloadManager(browser()->profile()); |
| } |
| |
| void DownloadAnItem() { |
| GURL url = test_server()->GetURL("files/downloads/image.jpg"); |
| std::vector<GURL> url_chain; |
| url_chain.push_back(url); |
| base::Time current(base::Time::Now()); |
| download_manager()->CreateDownloadItem( |
| 1, // id |
| base::FilePath(FILE_PATH_LITERAL("/path/to/file")), |
| base::FilePath(FILE_PATH_LITERAL("/path/to/file")), |
| url_chain, |
| GURL(std::string()), |
| current, |
| current, |
| std::string(), |
| std::string(), |
| 128, |
| 128, |
| content::DownloadItem::COMPLETE, |
| content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, |
| content::DOWNLOAD_INTERRUPT_REASON_NONE, |
| false); |
| |
| mock_handler_->WaitForDownloadsList(); |
| ASSERT_EQ(1, static_cast<int>(mock_handler_->downloads_list()->GetSize())); |
| EXPECT_TRUE(ListMatches( |
| mock_handler_->downloads_list(), |
| "[{\"file_externally_removed\": false," |
| " \"file_name\": \"file\"," |
| " \"id\": 1," |
| " \"otr\": false," |
| " \"since_string\": \"Today\"," |
| " \"state\": \"COMPLETE\"," |
| " \"total\": 128}]")); |
| } |
| |
| protected: |
| scoped_ptr<MockDownloadsDOMHandler> mock_handler_; |
| |
| private: |
| base::ScopedTempDir downloads_directory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DownloadsDOMHandlerTest); |
| }; |
| |
| // Tests removing all items, both when prohibited and when allowed. |
| IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest, RemoveAll) { |
| DownloadAnItem(); |
| |
| mock_handler_->reset_downloads_list(); |
| browser()->profile()->GetPrefs()->SetBoolean( |
| prefs::kAllowDeletingBrowserHistory, false); |
| mock_handler_->HandleClearAll(NULL); |
| mock_handler_->WaitForDownloadsList(); |
| ASSERT_EQ(1, static_cast<int>(mock_handler_->downloads_list()->GetSize())); |
| |
| mock_handler_->reset_downloads_list(); |
| browser()->profile()->GetPrefs()->SetBoolean( |
| prefs::kAllowDeletingBrowserHistory, true); |
| mock_handler_->HandleClearAll(NULL); |
| mock_handler_->WaitForDownloadsList(); |
| EXPECT_EQ(0, static_cast<int>(mock_handler_->downloads_list()->GetSize())); |
| } |
| |
| // Tests removing one item, both when prohibited and when allowed. |
| IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest, RemoveOneItem) { |
| DownloadAnItem(); |
| base::ListValue item; |
| item.AppendInteger(1); |
| |
| mock_handler_->reset_downloads_list(); |
| browser()->profile()->GetPrefs()->SetBoolean( |
| prefs::kAllowDeletingBrowserHistory, false); |
| mock_handler_->HandleRemove(&item); |
| // Removing an item only sends the new download list if anything was actually |
| // removed, so force it. |
| mock_handler_->ForceSendCurrentDownloads(); |
| mock_handler_->WaitForDownloadsList(); |
| ASSERT_EQ(1, static_cast<int>(mock_handler_->downloads_list()->GetSize())); |
| |
| mock_handler_->reset_downloads_list(); |
| browser()->profile()->GetPrefs()->SetBoolean( |
| prefs::kAllowDeletingBrowserHistory, true); |
| mock_handler_->HandleRemove(&item); |
| mock_handler_->WaitForDownloadsList(); |
| EXPECT_EQ(0, static_cast<int>(mock_handler_->downloads_list()->GetSize())); |
| } |
| |
| // Tests that DownloadsDOMHandler detects new downloads and relays them to the |
| // renderer. |
| // crbug.com/159390: This test fails when daylight savings time ends. |
| IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest, DownloadsRelayed) { |
| DownloadAnItem(); |
| |
| mock_handler_->WaitForDownloadUpdated(); |
| ASSERT_EQ(1, static_cast<int>(mock_handler_->download_updated()->GetSize())); |
| EXPECT_TRUE(ListMatches( |
| mock_handler_->download_updated(), |
| "[{\"file_externally_removed\": true," |
| " \"id\": 1}]")); |
| |
| mock_handler_->reset_downloads_list(); |
| browser()->profile()->GetPrefs()->SetBoolean( |
| prefs::kAllowDeletingBrowserHistory, true); |
| mock_handler_->HandleClearAll(NULL); |
| mock_handler_->WaitForDownloadsList(); |
| EXPECT_EQ(0, static_cast<int>(mock_handler_->downloads_list()->GetSize())); |
| } |
| |
| |
| // TODO(benjhayden): Test the extension downloads filter for both |
| // mock_handler_.downloads_list() and mock_handler_.download_updated(). |
| |
| // TODO(benjhayden): Test incognito, both downloads_list() and that on-record |
| // calls can't access off-record items. |
| |
| // TODO(benjhayden): Test that bad download ids incoming from the javascript are |
| // dropped on the floor. |
| |
| // TODO(benjhayden): Test that IsTemporary() downloads are not shown. |
| |
| // TODO(benjhayden): Test that RemoveObserver is called on all download items, |
| // including items that crossed IsTemporary() and back. |