OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 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 <vector> |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "base/message_loop.h" |
| 9 #include "base/stl_util.h" |
| 10 #include "base/task.h" |
| 11 #include "chrome/browser/download/download_test_observer.h" |
| 12 #include "chrome/test/ui_test_utils.h" |
| 13 #include "content/browser/browser_thread.h" |
| 14 |
| 15 // Fake user click on "Accept". |
| 16 void AcceptDangerousDownload(scoped_refptr<DownloadManager> download_manager, |
| 17 int32 download_id) { |
| 18 DownloadItem* download = download_manager->GetDownloadItem(download_id); |
| 19 download->DangerousDownloadValidated(); |
| 20 } |
| 21 |
| 22 // Fake user click on "Deny". |
| 23 void DenyDangerousDownload(scoped_refptr<DownloadManager> download_manager, |
| 24 int32 download_id) { |
| 25 DownloadItem* download = download_manager->GetDownloadItem(download_id); |
| 26 ASSERT_TRUE(download->IsPartialDownload()); |
| 27 download->Cancel(true); |
| 28 download->Delete(DownloadItem::DELETE_DUE_TO_USER_DISCARD); |
| 29 } |
| 30 |
| 31 DownloadTestObserver::DownloadTestObserver( |
| 32 DownloadManager* download_manager, |
| 33 size_t wait_count, |
| 34 DownloadItem::DownloadState download_finished_state, |
| 35 bool finish_on_select_file, |
| 36 DangerousDownloadAction dangerous_download_action) |
| 37 : download_manager_(download_manager), |
| 38 wait_count_(wait_count), |
| 39 finished_downloads_at_construction_(0), |
| 40 waiting_(false), |
| 41 download_finished_state_(download_finished_state), |
| 42 finish_on_select_file_(finish_on_select_file), |
| 43 select_file_dialog_seen_(false), |
| 44 dangerous_download_action_(dangerous_download_action) { |
| 45 download_manager_->AddObserver(this); // Will call initial ModelChanged(). |
| 46 finished_downloads_at_construction_ = finished_downloads_.size(); |
| 47 EXPECT_NE(DownloadItem::REMOVING, download_finished_state) |
| 48 << "Waiting for REMOVING is not supported. Try COMPLETE."; |
| 49 } |
| 50 |
| 51 DownloadTestObserver::~DownloadTestObserver() { |
| 52 std::set<DownloadItem*>::iterator it = downloads_observed_.begin(); |
| 53 for (; it != downloads_observed_.end(); ++it) |
| 54 (*it)->RemoveObserver(this); |
| 55 |
| 56 download_manager_->RemoveObserver(this); |
| 57 } |
| 58 |
| 59 void DownloadTestObserver::WaitForFinished() { |
| 60 if (!IsFinished()) { |
| 61 waiting_ = true; |
| 62 ui_test_utils::RunMessageLoop(); |
| 63 waiting_ = false; |
| 64 } |
| 65 } |
| 66 |
| 67 bool DownloadTestObserver::IsFinished() { |
| 68 if (finished_downloads_.size() - finished_downloads_at_construction_ |
| 69 >= wait_count_) |
| 70 return true; |
| 71 return (finish_on_select_file_ && select_file_dialog_seen_); |
| 72 } |
| 73 |
| 74 void DownloadTestObserver::OnDownloadUpdated(DownloadItem* download) { |
| 75 // The REMOVING state indicates that the download is being destroyed. |
| 76 // Stop observing. Do not do anything with it, as it is about to be gone. |
| 77 if (download->state() == DownloadItem::REMOVING) { |
| 78 std::set<DownloadItem*>::iterator it = downloads_observed_.find(download); |
| 79 ASSERT_TRUE(it != downloads_observed_.end()); |
| 80 downloads_observed_.erase(it); |
| 81 download->RemoveObserver(this); |
| 82 return; |
| 83 } |
| 84 |
| 85 // Real UI code gets the user's response after returning from the observer. |
| 86 if (download->safety_state() == DownloadItem::DANGEROUS && |
| 87 !ContainsKey(dangerous_downloads_seen_, download->id())) { |
| 88 dangerous_downloads_seen_.insert(download->id()); |
| 89 |
| 90 // Calling DangerousDownloadValidated() at this point will |
| 91 // cause the download to be completed twice. Do what the real UI |
| 92 // code does: make the call as a delayed task. |
| 93 switch (dangerous_download_action_) { |
| 94 case ON_DANGEROUS_DOWNLOAD_ACCEPT: |
| 95 // Fake user click on "Accept". Delay the actual click, as the |
| 96 // real UI would. |
| 97 BrowserThread::PostTask( |
| 98 BrowserThread::UI, FROM_HERE, |
| 99 NewRunnableFunction( |
| 100 &AcceptDangerousDownload, |
| 101 download_manager_, |
| 102 download->id())); |
| 103 break; |
| 104 |
| 105 case ON_DANGEROUS_DOWNLOAD_DENY: |
| 106 // Fake a user click on "Deny". Delay the actual click, as the |
| 107 // real UI would. |
| 108 BrowserThread::PostTask( |
| 109 BrowserThread::UI, FROM_HERE, |
| 110 NewRunnableFunction( |
| 111 &DenyDangerousDownload, |
| 112 download_manager_, |
| 113 download->id())); |
| 114 break; |
| 115 |
| 116 case ON_DANGEROUS_DOWNLOAD_FAIL: |
| 117 ADD_FAILURE() << "Unexpected dangerous download item."; |
| 118 break; |
| 119 |
| 120 default: |
| 121 NOTREACHED(); |
| 122 } |
| 123 } |
| 124 |
| 125 if (download->state() == download_finished_state_) { |
| 126 DownloadInFinalState(download); |
| 127 } |
| 128 } |
| 129 |
| 130 void DownloadTestObserver::ModelChanged() { |
| 131 // Regenerate DownloadItem observers. If there are any download items |
| 132 // in our final state, note them in |finished_downloads_| |
| 133 // (done by |OnDownloadUpdated()|). |
| 134 std::vector<DownloadItem*> downloads; |
| 135 download_manager_->GetAllDownloads(FilePath(), &downloads); |
| 136 |
| 137 std::vector<DownloadItem*>::iterator it = downloads.begin(); |
| 138 for (; it != downloads.end(); ++it) { |
| 139 OnDownloadUpdated(*it); // Safe to call multiple times; checks state. |
| 140 |
| 141 std::set<DownloadItem*>::const_iterator |
| 142 finished_it(finished_downloads_.find(*it)); |
| 143 std::set<DownloadItem*>::iterator |
| 144 observed_it(downloads_observed_.find(*it)); |
| 145 |
| 146 // If it isn't finished and we're aren't observing it, start. |
| 147 if (finished_it == finished_downloads_.end() && |
| 148 observed_it == downloads_observed_.end()) { |
| 149 (*it)->AddObserver(this); |
| 150 downloads_observed_.insert(*it); |
| 151 continue; |
| 152 } |
| 153 |
| 154 // If it is finished and we are observing it, stop. |
| 155 if (finished_it != finished_downloads_.end() && |
| 156 observed_it != downloads_observed_.end()) { |
| 157 (*it)->RemoveObserver(this); |
| 158 downloads_observed_.erase(observed_it); |
| 159 continue; |
| 160 } |
| 161 } |
| 162 } |
| 163 |
| 164 void DownloadTestObserver::SelectFileDialogDisplayed(int32 /* id */) { |
| 165 select_file_dialog_seen_ = true; |
| 166 SignalIfFinished(); |
| 167 } |
| 168 |
| 169 size_t DownloadTestObserver::NumDangerousDownloadsSeen() const { |
| 170 return dangerous_downloads_seen_.size(); |
| 171 } |
| 172 |
| 173 void DownloadTestObserver::DownloadInFinalState(DownloadItem* download) { |
| 174 if (finished_downloads_.find(download) != finished_downloads_.end()) { |
| 175 // We've already seen terminal state on this download. |
| 176 return; |
| 177 } |
| 178 |
| 179 // Record the transition. |
| 180 finished_downloads_.insert(download); |
| 181 |
| 182 SignalIfFinished(); |
| 183 } |
| 184 |
| 185 void DownloadTestObserver::SignalIfFinished() { |
| 186 if (waiting_ && IsFinished()) |
| 187 MessageLoopForUI::current()->Quit(); |
| 188 } |
| 189 |
| 190 void DownloadTestFlushObserver::WaitForFlush() { |
| 191 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 192 download_manager_->AddObserver(this); |
| 193 ui_test_utils::RunMessageLoop(); |
| 194 } |
| 195 |
| 196 void DownloadTestFlushObserver::ModelChanged() { |
| 197 // Model has changed, so there may be more DownloadItems to observe. |
| 198 CheckDownloadsInProgress(true); |
| 199 } |
| 200 |
| 201 void DownloadTestFlushObserver::OnDownloadUpdated(DownloadItem* download) { |
| 202 // No change in DownloadItem set on manager. |
| 203 CheckDownloadsInProgress(false); |
| 204 } |
| 205 |
| 206 DownloadTestFlushObserver::~DownloadTestFlushObserver() { |
| 207 download_manager_->RemoveObserver(this); |
| 208 for (std::set<DownloadItem*>::iterator it = downloads_observed_.begin(); |
| 209 it != downloads_observed_.end(); ++it) { |
| 210 (*it)->RemoveObserver(this); |
| 211 } |
| 212 } |
| 213 |
| 214 // If we're waiting for that flush point, check the number |
| 215 // of downloads in the IN_PROGRESS state and take appropriate |
| 216 // action. If requested, also observes all downloads while iterating. |
| 217 void DownloadTestFlushObserver::CheckDownloadsInProgress( |
| 218 bool observe_downloads) { |
| 219 if (waiting_for_zero_inprogress_) { |
| 220 int count = 0; |
| 221 |
| 222 std::vector<DownloadItem*> downloads; |
| 223 download_manager_->SearchDownloads(string16(), &downloads); |
| 224 std::vector<DownloadItem*>::iterator it = downloads.begin(); |
| 225 for (; it != downloads.end(); ++it) { |
| 226 if ((*it)->state() == DownloadItem::IN_PROGRESS) |
| 227 count++; |
| 228 if (observe_downloads) { |
| 229 if (downloads_observed_.find(*it) == downloads_observed_.end()) { |
| 230 (*it)->AddObserver(this); |
| 231 } |
| 232 // Download items are forever, and we don't want to make |
| 233 // assumptions about future state transitions, so once we |
| 234 // start observing them, we don't stop until destruction. |
| 235 } |
| 236 } |
| 237 |
| 238 if (count == 0) { |
| 239 waiting_for_zero_inprogress_ = false; |
| 240 // Stop observing DownloadItems. We maintain the observation |
| 241 // of DownloadManager so that we don't have to independently track |
| 242 // whether we are observing it for conditional destruction. |
| 243 for (std::set<DownloadItem*>::iterator it = downloads_observed_.begin(); |
| 244 it != downloads_observed_.end(); ++it) { |
| 245 (*it)->RemoveObserver(this); |
| 246 } |
| 247 downloads_observed_.clear(); |
| 248 |
| 249 // Trigger next step. We need to go past the IO thread twice, as |
| 250 // there's a self-task posting in the IO thread cancel path. |
| 251 BrowserThread::PostTask( |
| 252 BrowserThread::FILE, FROM_HERE, |
| 253 NewRunnableMethod(this, |
| 254 &DownloadTestFlushObserver::PingFileThread, 2)); |
| 255 } |
| 256 } |
| 257 } |
| 258 |
| 259 void DownloadTestFlushObserver::PingFileThread(int cycle) { |
| 260 BrowserThread::PostTask( |
| 261 BrowserThread::IO, FROM_HERE, |
| 262 NewRunnableMethod(this, &DownloadTestFlushObserver::PingIOThread, |
| 263 cycle)); |
| 264 } |
| 265 |
| 266 void DownloadTestFlushObserver::PingIOThread(int cycle) { |
| 267 if (--cycle) { |
| 268 BrowserThread::PostTask( |
| 269 BrowserThread::UI, FROM_HERE, |
| 270 NewRunnableMethod(this, &DownloadTestFlushObserver::PingFileThread, |
| 271 cycle)); |
| 272 } else { |
| 273 BrowserThread::PostTask( |
| 274 BrowserThread::UI, FROM_HERE, new MessageLoop::QuitTask()); |
| 275 } |
| 276 } |
OLD | NEW |