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 "chrome/browser/download/drag_download_file.h" 6 7 #include "base/file_util.h" 8 #include "base/message_loop.h" 9 #include "chrome/browser/download/download_file.h" 10 #include "chrome/browser/download/download_item.h" 11 #include "chrome/browser/download/download_util.h" 12 #include "chrome/browser/profiles/profile.h" 13 #include "content/browser/browser_thread.h" 14 #include "content/browser/tab_contents/tab_contents.h" 15 #include "net/base/file_stream.h" 16 17 DragDownloadFile::DragDownloadFile( 18 const FilePath& file_name_or_path, 19 linked_ptr<net::FileStream> file_stream, 20 const GURL& url, 21 const GURL& referrer, 22 const std::string& referrer_encoding, 23 TabContents* tab_contents) 24 : file_stream_(file_stream), 25 url_(url), 26 referrer_(referrer), 27 referrer_encoding_(referrer_encoding), 28 tab_contents_(tab_contents), 29 drag_message_loop_(MessageLoop::current()), 30 is_started_(false), 31 is_successful_(false), 32 download_manager_(NULL), 33 download_item_observer_added_(false) { 34 #if defined(OS_WIN) 35 DCHECK(!file_name_or_path.empty() && !file_stream.get()); 36 file_name_ = file_name_or_path; 37 #elif defined(OS_POSIX) 38 DCHECK(!file_name_or_path.empty() && file_stream.get()); 39 file_path_ = file_name_or_path; 40 #endif 41 } 42 43 DragDownloadFile::~DragDownloadFile() { 44 AssertCurrentlyOnDragThread(); 45 46 // Since the target application can still hold and use the dragged file, 47 // we do not know the time that it can be safely deleted. To solve this 48 // problem, we schedule it to be removed after the system is restarted. 49 #if defined(OS_WIN) 50 if (!temp_dir_path_.empty()) { 51 if (!file_path_.empty()) 52 file_util::DeleteAfterReboot(file_path_); 53 file_util::DeleteAfterReboot(temp_dir_path_); 54 } 55 #endif 56 57 if (download_manager_) 58 download_manager_->RemoveObserver(this); 59 } 60 61 bool DragDownloadFile::Start(ui::DownloadFileObserver* observer) { 62 AssertCurrentlyOnDragThread(); 63 64 if (is_started_) 65 return true; 66 is_started_ = true; 67 68 DCHECK(!observer_.get()); 69 observer_ = observer; 70 71 if (!file_stream_.get()) { 72 // Create a temporary directory to save the temporary download file. We do 73 // not want to use the default download directory since we do not want the 74 // twisted file name shown in the download shelf if the file with the same 75 // name already exists. 76 if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome"), 77 &temp_dir_path_)) 78 return false; 79 80 file_path_ = temp_dir_path_.Append(file_name_); 81 } 82 83 InitiateDownload(); 84 85 // On Windows, we need to wait till the download file is completed. 86 #if defined(OS_WIN) 87 StartNestedMessageLoop(); 88 #endif 89 90 return is_successful_; 91 } 92 93 void DragDownloadFile::Stop() { 94 } 95 96 void DragDownloadFile::InitiateDownload() { 97 #if defined(OS_WIN) 98 // DownloadManager could only be invoked from the UI thread. 99 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 100 BrowserThread::PostTask( 101 BrowserThread::UI, FROM_HERE, 102 NewRunnableMethod(this, 103 &DragDownloadFile::InitiateDownload)); 104 return; 105 } 106 #endif 107 108 download_manager_ = tab_contents_->profile()->GetDownloadManager(); 109 download_manager_->AddObserver(this); 110 111 DownloadSaveInfo save_info; 112 save_info.file_path = file_path_; 113 save_info.file_stream = file_stream_; 114 download_manager_->DownloadUrlToFile(url_, 115 referrer_, 116 referrer_encoding_, 117 save_info, 118 tab_contents_); 119 download_util::RecordDownloadCount( 120 download_util::INITIATED_BY_DRAG_N_DROP_COUNT); 121 } 122 123 void DragDownloadFile::DownloadCompleted(bool is_successful) { 124 #if defined(OS_WIN) 125 // If not in drag-and-drop thread, defer the running to it. 126 if (drag_message_loop_ != MessageLoop::current()) { 127 drag_message_loop_->PostTask( 128 FROM_HERE, 129 NewRunnableMethod(this, 130 &DragDownloadFile::DownloadCompleted, 131 is_successful)); 132 return; 133 } 134 #endif 135 136 is_successful_ = is_successful; 137 138 // Call the observer. 139 DCHECK(observer_); 140 if (is_successful) 141 observer_->OnDownloadCompleted(file_path_); 142 else 143 observer_->OnDownloadAborted(); 144 145 // Release the observer since we do not need it any more. 146 observer_ = NULL; 147 148 // On Windows, we need to stop the waiting. 149 #if defined(OS_WIN) 150 QuitNestedMessageLoop(); 151 #endif 152 } 153 154 void DragDownloadFile::ModelChanged() { 155 AssertCurrentlyOnUIThread(); 156 157 std::vector<DownloadItem*> downloads; 158 download_manager_->GetTemporaryDownloads(file_path_.DirName(), &downloads); 159 for (std::vector<DownloadItem*>::const_iterator i = downloads.begin(); 160 i != downloads.end(); ++i) { 161 if (!download_item_observer_added_ && (*i)->original_url() == url_) { 162 download_item_observer_added_ = true; 163 (*i)->AddObserver(this); 164 } 165 } 166 } 167 168 void DragDownloadFile::OnDownloadUpdated(DownloadItem* download) { 169 AssertCurrentlyOnUIThread(); 170 if (download->IsCancelled()) { 171 download->RemoveObserver(this); 172 download_manager_->RemoveObserver(this); 173 174 DownloadCompleted(false); 175 } else if (download->IsComplete()) { 176 download->RemoveObserver(this); 177 download_manager_->RemoveObserver(this); 178 DownloadCompleted(true); 179 } 180 // Ignore other states. 181 } 182 183 void DragDownloadFile::AssertCurrentlyOnDragThread() { 184 // Only do the check on Windows where two threads are involved. 185 #if defined(OS_WIN) 186 DCHECK(drag_message_loop_ == MessageLoop::current()); 187 #endif 188 } 189 190 void DragDownloadFile::AssertCurrentlyOnUIThread() { 191 // Only do the check on Windows where two threads are involved. 192 #if defined(OS_WIN) 193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 194 #endif 195 } 196 197 #if defined(OS_WIN) 198 void DragDownloadFile::StartNestedMessageLoop() { 199 AssertCurrentlyOnDragThread(); 200 201 bool old_state = MessageLoop::current()->NestableTasksAllowed(); 202 MessageLoop::current()->SetNestableTasksAllowed(true); 203 is_running_nested_message_loop_ = true; 204 MessageLoop::current()->Run(); 205 MessageLoop::current()->SetNestableTasksAllowed(old_state); 206 } 207 208 void DragDownloadFile::QuitNestedMessageLoop() { 209 AssertCurrentlyOnDragThread(); 210 211 if (is_running_nested_message_loop_) { 212 is_running_nested_message_loop_ = false; 213 MessageLoop::current()->Quit(); 214 } 215 } 216 #endif 217