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/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/history/download_row.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 "chrome/test/base/ui_test_utils.h" 17#include "content/public/browser/web_contents.h" 18 19namespace { 20 21// Reads |right_json| into a ListValue |left_list|; returns true if all 22// key-value pairs in in all dictionaries in |right_list| are also in the 23// corresponding dictionary in |left_list|. Ignores keys in dictionaries in 24// |left_list| that are not in the corresponding dictionary in |right_list|. 25bool ListMatches(base::ListValue* left_list, const std::string& right_json) { 26 scoped_ptr<base::Value> right_value(base::JSONReader::Read(right_json)); 27 base::ListValue* right_list = NULL; 28 CHECK(right_value->GetAsList(&right_list)); 29 for (size_t i = 0; i < left_list->GetSize(); ++i) { 30 base::DictionaryValue* left_dict = NULL; 31 base::DictionaryValue* right_dict = NULL; 32 CHECK(left_list->GetDictionary(i, &left_dict)); 33 CHECK(right_list->GetDictionary(i, &right_dict)); 34 for (base::DictionaryValue::Iterator iter(*right_dict); 35 !iter.IsAtEnd(); iter.Advance()) { 36 base::Value* left_value = NULL; 37 if (left_dict->HasKey(iter.key()) && 38 left_dict->Get(iter.key(), &left_value) && 39 !iter.value().Equals(left_value)) { 40 LOG(WARNING) << iter.key(); 41 return false; 42 } 43 } 44 } 45 return true; 46} 47 48// A |DownloadsDOMHandler| that doesn't use a real WebUI object, but is real in 49// all other respects. 50class MockDownloadsDOMHandler : public DownloadsDOMHandler { 51 public: 52 explicit MockDownloadsDOMHandler(content::DownloadManager* dlm) 53 : DownloadsDOMHandler(dlm), 54 waiting_list_(false), 55 waiting_updated_(false) { 56 } 57 virtual ~MockDownloadsDOMHandler() {} 58 59 base::ListValue* downloads_list() { return downloads_list_.get(); } 60 base::ListValue* download_updated() { return download_updated_.get(); } 61 62 void WaitForDownloadsList() { 63 if (downloads_list_.get()) 64 return; 65 base::AutoReset<bool> reset_waiting(&waiting_list_, true); 66 content::RunMessageLoop(); 67 } 68 69 void WaitForDownloadUpdated() { 70 if (download_updated_.get()) 71 return; 72 base::AutoReset<bool> reset_waiting(&waiting_updated_, true); 73 content::RunMessageLoop(); 74 } 75 76 void ForceSendCurrentDownloads() { 77 ScheduleSendCurrentDownloads(); 78 } 79 80 void reset_downloads_list() { downloads_list_.reset(); } 81 void reset_download_updated() { download_updated_.reset(); } 82 83 protected: 84 virtual content::WebContents* GetWebUIWebContents() OVERRIDE { 85 return NULL; 86 } 87 88 virtual void CallDownloadsList(const base::ListValue& downloads) OVERRIDE { 89 downloads_list_.reset(downloads.DeepCopy()); 90 if (waiting_list_) { 91 content::BrowserThread::PostTask(content::BrowserThread::UI, 92 FROM_HERE, 93 base::MessageLoop::QuitClosure()); 94 } 95 } 96 97 virtual void CallDownloadUpdated(const base::ListValue& download) OVERRIDE { 98 download_updated_.reset(download.DeepCopy()); 99 if (waiting_updated_) { 100 content::BrowserThread::PostTask(content::BrowserThread::UI, 101 FROM_HERE, 102 base::MessageLoop::QuitClosure()); 103 } 104 } 105 106 private: 107 scoped_ptr<base::ListValue> downloads_list_; 108 scoped_ptr<base::ListValue> download_updated_; 109 bool waiting_list_; 110 bool waiting_updated_; 111 112 DISALLOW_COPY_AND_ASSIGN(MockDownloadsDOMHandler); 113}; 114 115} // namespace 116 117class DownloadsDOMHandlerTest : public InProcessBrowserTest { 118 public: 119 DownloadsDOMHandlerTest() {} 120 121 virtual ~DownloadsDOMHandlerTest() {} 122 123 virtual void SetUpOnMainThread() OVERRIDE { 124 mock_handler_.reset(new MockDownloadsDOMHandler(download_manager())); 125 CHECK(downloads_directory_.CreateUniqueTempDir()); 126 browser()->profile()->GetPrefs()->SetFilePath( 127 prefs::kDownloadDefaultDirectory, 128 downloads_directory_.path()); 129 CHECK(test_server()->Start()); 130 } 131 132 content::DownloadManager* download_manager() { 133 return content::BrowserContext::GetDownloadManager(browser()->profile()); 134 } 135 136 void DownloadAnItem() { 137 GURL url = test_server()->GetURL("files/downloads/image.jpg"); 138 std::vector<GURL> url_chain; 139 url_chain.push_back(url); 140 base::Time current(base::Time::Now()); 141 download_manager()->CreateDownloadItem( 142 1, // id 143 base::FilePath(FILE_PATH_LITERAL("/path/to/file")), 144 base::FilePath(FILE_PATH_LITERAL("/path/to/file")), 145 url_chain, 146 GURL(std::string()), 147 "application/octet-stream", 148 "application/octet-stream", 149 current, 150 current, 151 std::string(), 152 std::string(), 153 128, 154 128, 155 content::DownloadItem::COMPLETE, 156 content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, 157 content::DOWNLOAD_INTERRUPT_REASON_NONE, 158 false); 159 160 mock_handler_->WaitForDownloadsList(); 161 ASSERT_EQ(1, static_cast<int>(mock_handler_->downloads_list()->GetSize())); 162 EXPECT_TRUE(ListMatches( 163 mock_handler_->downloads_list(), 164 "[{\"file_externally_removed\": false," 165 " \"file_name\": \"file\"," 166 " \"id\": 1," 167 " \"otr\": false," 168 " \"since_string\": \"Today\"," 169 " \"state\": \"COMPLETE\"," 170 " \"total\": 128}]")); 171 } 172 173 protected: 174 scoped_ptr<MockDownloadsDOMHandler> mock_handler_; 175 176 private: 177 base::ScopedTempDir downloads_directory_; 178 179 DISALLOW_COPY_AND_ASSIGN(DownloadsDOMHandlerTest); 180}; 181 182// Tests removing all items, both when prohibited and when allowed. 183IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest, RemoveAll) { 184 DownloadAnItem(); 185 186 mock_handler_->reset_downloads_list(); 187 browser()->profile()->GetPrefs()->SetBoolean( 188 prefs::kAllowDeletingBrowserHistory, false); 189 mock_handler_->HandleClearAll(NULL); 190 mock_handler_->WaitForDownloadsList(); 191 ASSERT_EQ(1, static_cast<int>(mock_handler_->downloads_list()->GetSize())); 192 193 mock_handler_->reset_downloads_list(); 194 browser()->profile()->GetPrefs()->SetBoolean( 195 prefs::kAllowDeletingBrowserHistory, true); 196 mock_handler_->HandleClearAll(NULL); 197 mock_handler_->WaitForDownloadsList(); 198 EXPECT_EQ(0, static_cast<int>(mock_handler_->downloads_list()->GetSize())); 199} 200 201// Tests removing one item, both when prohibited and when allowed. 202IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest, RemoveOneItem) { 203 DownloadAnItem(); 204 base::ListValue item; 205 item.AppendInteger(1); 206 207 mock_handler_->reset_downloads_list(); 208 browser()->profile()->GetPrefs()->SetBoolean( 209 prefs::kAllowDeletingBrowserHistory, false); 210 mock_handler_->HandleRemove(&item); 211 // Removing an item only sends the new download list if anything was actually 212 // removed, so force it. 213 mock_handler_->ForceSendCurrentDownloads(); 214 mock_handler_->WaitForDownloadsList(); 215 ASSERT_EQ(1, static_cast<int>(mock_handler_->downloads_list()->GetSize())); 216 217 mock_handler_->reset_downloads_list(); 218 browser()->profile()->GetPrefs()->SetBoolean( 219 prefs::kAllowDeletingBrowserHistory, true); 220 mock_handler_->HandleRemove(&item); 221 mock_handler_->WaitForDownloadsList(); 222 EXPECT_EQ(0, static_cast<int>(mock_handler_->downloads_list()->GetSize())); 223} 224 225// Tests that DownloadsDOMHandler detects new downloads and relays them to the 226// renderer. 227// crbug.com/159390: This test fails when daylight savings time ends. 228IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest, DownloadsRelayed) { 229 DownloadAnItem(); 230 231 mock_handler_->WaitForDownloadUpdated(); 232 ASSERT_EQ(1, static_cast<int>(mock_handler_->download_updated()->GetSize())); 233 EXPECT_TRUE(ListMatches( 234 mock_handler_->download_updated(), 235 "[{\"file_externally_removed\": true," 236 " \"id\": 1}]")); 237 238 mock_handler_->reset_downloads_list(); 239 browser()->profile()->GetPrefs()->SetBoolean( 240 prefs::kAllowDeletingBrowserHistory, true); 241 mock_handler_->HandleClearAll(NULL); 242 mock_handler_->WaitForDownloadsList(); 243 EXPECT_EQ(0, static_cast<int>(mock_handler_->downloads_list()->GetSize())); 244} 245 246 247// TODO(benjhayden): Test the extension downloads filter for both 248// mock_handler_.downloads_list() and mock_handler_.download_updated(). 249 250// TODO(benjhayden): Test incognito, both downloads_list() and that on-record 251// calls can't access off-record items. 252 253// TODO(benjhayden): Test that bad download ids incoming from the javascript are 254// dropped on the floor. 255 256// TODO(benjhayden): Test that IsTemporary() downloads are not shown. 257 258// TODO(benjhayden): Test that RemoveObserver is called on all download items, 259// including items that crossed IsTemporary() and back. 260