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