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