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
DragDownloadFile(const FilePath & file_name_or_path,linked_ptr<net::FileStream> file_stream,const GURL & url,const GURL & referrer,const std::string & referrer_encoding,TabContents * tab_contents)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
~DragDownloadFile()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
Start(ui::DownloadFileObserver * observer)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
Stop()93 void DragDownloadFile::Stop() {
94 }
95
InitiateDownload()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
DownloadCompleted(bool is_successful)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
ModelChanged()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
OnDownloadUpdated(DownloadItem * download)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
AssertCurrentlyOnDragThread()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
AssertCurrentlyOnUIThread()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)
StartNestedMessageLoop()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
QuitNestedMessageLoop()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