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 17DragDownloadFile::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 43DragDownloadFile::~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 61bool 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 93void DragDownloadFile::Stop() { 94} 95 96void 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 123void 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 154void 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 168void 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 183void 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 190void 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) 198void 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 208void 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