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 19 namespace { 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|. 25 bool 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. 50 class 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 117 class 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 current, 148 current, 149 std::string(), 150 std::string(), 151 128, 152 128, 153 content::DownloadItem::COMPLETE, 154 content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, 155 content::DOWNLOAD_INTERRUPT_REASON_NONE, 156 false); 157 158 mock_handler_->WaitForDownloadsList(); 159 ASSERT_EQ(1, static_cast<int>(mock_handler_->downloads_list()->GetSize())); 160 EXPECT_TRUE(ListMatches( 161 mock_handler_->downloads_list(), 162 "[{\"file_externally_removed\": false," 163 " \"file_name\": \"file\"," 164 " \"id\": 1," 165 " \"otr\": false," 166 " \"since_string\": \"Today\"," 167 " \"state\": \"COMPLETE\"," 168 " \"total\": 128}]")); 169 } 170 171 protected: 172 scoped_ptr<MockDownloadsDOMHandler> mock_handler_; 173 174 private: 175 base::ScopedTempDir downloads_directory_; 176 177 DISALLOW_COPY_AND_ASSIGN(DownloadsDOMHandlerTest); 178 }; 179 180 // Tests removing all items, both when prohibited and when allowed. 181 IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest, RemoveAll) { 182 DownloadAnItem(); 183 184 mock_handler_->reset_downloads_list(); 185 browser()->profile()->GetPrefs()->SetBoolean( 186 prefs::kAllowDeletingBrowserHistory, false); 187 mock_handler_->HandleClearAll(NULL); 188 mock_handler_->WaitForDownloadsList(); 189 ASSERT_EQ(1, static_cast<int>(mock_handler_->downloads_list()->GetSize())); 190 191 mock_handler_->reset_downloads_list(); 192 browser()->profile()->GetPrefs()->SetBoolean( 193 prefs::kAllowDeletingBrowserHistory, true); 194 mock_handler_->HandleClearAll(NULL); 195 mock_handler_->WaitForDownloadsList(); 196 EXPECT_EQ(0, static_cast<int>(mock_handler_->downloads_list()->GetSize())); 197 } 198 199 // Tests removing one item, both when prohibited and when allowed. 200 IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest, RemoveOneItem) { 201 DownloadAnItem(); 202 base::ListValue item; 203 item.AppendInteger(1); 204 205 mock_handler_->reset_downloads_list(); 206 browser()->profile()->GetPrefs()->SetBoolean( 207 prefs::kAllowDeletingBrowserHistory, false); 208 mock_handler_->HandleRemove(&item); 209 // Removing an item only sends the new download list if anything was actually 210 // removed, so force it. 211 mock_handler_->ForceSendCurrentDownloads(); 212 mock_handler_->WaitForDownloadsList(); 213 ASSERT_EQ(1, static_cast<int>(mock_handler_->downloads_list()->GetSize())); 214 215 mock_handler_->reset_downloads_list(); 216 browser()->profile()->GetPrefs()->SetBoolean( 217 prefs::kAllowDeletingBrowserHistory, true); 218 mock_handler_->HandleRemove(&item); 219 mock_handler_->WaitForDownloadsList(); 220 EXPECT_EQ(0, static_cast<int>(mock_handler_->downloads_list()->GetSize())); 221 } 222 223 // Tests that DownloadsDOMHandler detects new downloads and relays them to the 224 // renderer. 225 // crbug.com/159390: This test fails when daylight savings time ends. 226 IN_PROC_BROWSER_TEST_F(DownloadsDOMHandlerTest, DownloadsRelayed) { 227 DownloadAnItem(); 228 229 mock_handler_->WaitForDownloadUpdated(); 230 ASSERT_EQ(1, static_cast<int>(mock_handler_->download_updated()->GetSize())); 231 EXPECT_TRUE(ListMatches( 232 mock_handler_->download_updated(), 233 "[{\"file_externally_removed\": true," 234 " \"id\": 1}]")); 235 236 mock_handler_->reset_downloads_list(); 237 browser()->profile()->GetPrefs()->SetBoolean( 238 prefs::kAllowDeletingBrowserHistory, true); 239 mock_handler_->HandleClearAll(NULL); 240 mock_handler_->WaitForDownloadsList(); 241 EXPECT_EQ(0, static_cast<int>(mock_handler_->downloads_list()->GetSize())); 242 } 243 244 245 // TODO(benjhayden): Test the extension downloads filter for both 246 // mock_handler_.downloads_list() and mock_handler_.download_updated(). 247 248 // TODO(benjhayden): Test incognito, both downloads_list() and that on-record 249 // calls can't access off-record items. 250 251 // TODO(benjhayden): Test that bad download ids incoming from the javascript are 252 // dropped on the floor. 253 254 // TODO(benjhayden): Test that IsTemporary() downloads are not shown. 255 256 // TODO(benjhayden): Test that RemoveObserver is called on all download items, 257 // including items that crossed IsTemporary() and back. 258