Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / download / drag_download_file.cc
blob13950a1e2aaefbe0905ae1018456fd975c3d8930
1 // Copyright (c) 2012 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.
5 #include "content/browser/download/drag_download_file.h"
7 #include "base/bind.h"
8 #include "base/files/file.h"
9 #include "base/message_loop/message_loop.h"
10 #include "content/browser/download/download_stats.h"
11 #include "content/browser/web_contents/web_contents_impl.h"
12 #include "content/public/browser/browser_context.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/download_item.h"
15 #include "content/public/browser/download_save_info.h"
16 #include "content/public/browser/download_url_parameters.h"
18 namespace content {
20 namespace {
22 typedef base::Callback<void(bool)> OnCompleted;
24 } // namespace
26 // On windows, DragDownloadFile runs on a thread other than the UI thread.
27 // DownloadItem and DownloadManager may not be accessed on any thread other than
28 // the UI thread. DragDownloadFile may run on either the "drag" thread or the UI
29 // thread depending on the platform, but DragDownloadFileUI strictly always runs
30 // on the UI thread. On platforms where DragDownloadFile runs on the UI thread,
31 // none of the PostTasks are necessary, but it simplifies the code to do them
32 // anyway.
33 class DragDownloadFile::DragDownloadFileUI : public DownloadItem::Observer {
34 public:
35 DragDownloadFileUI(const GURL& url,
36 const Referrer& referrer,
37 const std::string& referrer_encoding,
38 WebContents* web_contents,
39 base::MessageLoop* on_completed_loop,
40 const OnCompleted& on_completed)
41 : on_completed_loop_(on_completed_loop),
42 on_completed_(on_completed),
43 url_(url),
44 referrer_(referrer),
45 referrer_encoding_(referrer_encoding),
46 web_contents_(web_contents),
47 download_item_(NULL),
48 weak_ptr_factory_(this) {
49 DCHECK(on_completed_loop_);
50 DCHECK(!on_completed_.is_null());
51 DCHECK(web_contents_);
52 // May be called on any thread.
53 // Do not call weak_ptr_factory_.GetWeakPtr() outside the UI thread.
56 void InitiateDownload(base::File file,
57 const base::FilePath& file_path) {
58 DCHECK_CURRENTLY_ON(BrowserThread::UI);
59 DownloadManager* download_manager =
60 BrowserContext::GetDownloadManager(web_contents_->GetBrowserContext());
62 RecordDownloadSource(INITIATED_BY_DRAG_N_DROP);
63 scoped_ptr<content::DownloadUrlParameters> params(
64 DownloadUrlParameters::FromWebContents(web_contents_, url_));
65 params->set_referrer(referrer_);
66 params->set_referrer_encoding(referrer_encoding_);
67 params->set_callback(base::Bind(&DragDownloadFileUI::OnDownloadStarted,
68 weak_ptr_factory_.GetWeakPtr()));
69 params->set_file_path(file_path);
70 params->set_file(file.Pass()); // Nulls file.
71 download_manager->DownloadUrl(params.Pass());
74 void Cancel() {
75 DCHECK_CURRENTLY_ON(BrowserThread::UI);
76 if (download_item_)
77 download_item_->Cancel(true);
80 void Delete() {
81 DCHECK_CURRENTLY_ON(BrowserThread::UI);
82 delete this;
85 private:
86 ~DragDownloadFileUI() override {
87 DCHECK_CURRENTLY_ON(BrowserThread::UI);
88 if (download_item_)
89 download_item_->RemoveObserver(this);
92 void OnDownloadStarted(DownloadItem* item,
93 DownloadInterruptReason interrupt_reason) {
94 DCHECK_CURRENTLY_ON(BrowserThread::UI);
95 if (!item) {
96 DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
97 on_completed_loop_->PostTask(FROM_HERE, base::Bind(on_completed_, false));
98 return;
100 DCHECK_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
101 download_item_ = item;
102 download_item_->AddObserver(this);
105 // DownloadItem::Observer:
106 void OnDownloadUpdated(DownloadItem* item) override {
107 DCHECK_CURRENTLY_ON(BrowserThread::UI);
108 DCHECK_EQ(download_item_, item);
109 DownloadItem::DownloadState state = download_item_->GetState();
110 if (state == DownloadItem::COMPLETE ||
111 state == DownloadItem::CANCELLED ||
112 state == DownloadItem::INTERRUPTED) {
113 if (!on_completed_.is_null()) {
114 on_completed_loop_->PostTask(FROM_HERE, base::Bind(
115 on_completed_, state == DownloadItem::COMPLETE));
116 on_completed_.Reset();
118 download_item_->RemoveObserver(this);
119 download_item_ = NULL;
121 // Ignore other states.
124 void OnDownloadDestroyed(DownloadItem* item) override {
125 DCHECK_CURRENTLY_ON(BrowserThread::UI);
126 DCHECK_EQ(download_item_, item);
127 if (!on_completed_.is_null()) {
128 const bool is_complete =
129 download_item_->GetState() == DownloadItem::COMPLETE;
130 on_completed_loop_->PostTask(FROM_HERE, base::Bind(
131 on_completed_, is_complete));
132 on_completed_.Reset();
134 download_item_->RemoveObserver(this);
135 download_item_ = NULL;
138 base::MessageLoop* on_completed_loop_;
139 OnCompleted on_completed_;
140 GURL url_;
141 Referrer referrer_;
142 std::string referrer_encoding_;
143 WebContents* web_contents_;
144 DownloadItem* download_item_;
146 // Only used in the callback from DownloadManager::DownloadUrl().
147 base::WeakPtrFactory<DragDownloadFileUI> weak_ptr_factory_;
149 DISALLOW_COPY_AND_ASSIGN(DragDownloadFileUI);
152 DragDownloadFile::DragDownloadFile(const base::FilePath& file_path,
153 base::File file,
154 const GURL& url,
155 const Referrer& referrer,
156 const std::string& referrer_encoding,
157 WebContents* web_contents)
158 : file_path_(file_path),
159 file_(file.Pass()),
160 drag_message_loop_(base::MessageLoop::current()),
161 state_(INITIALIZED),
162 drag_ui_(NULL),
163 weak_ptr_factory_(this) {
164 drag_ui_ = new DragDownloadFileUI(
165 url,
166 referrer,
167 referrer_encoding,
168 web_contents,
169 drag_message_loop_,
170 base::Bind(&DragDownloadFile::DownloadCompleted,
171 weak_ptr_factory_.GetWeakPtr()));
172 DCHECK(!file_path_.empty());
175 DragDownloadFile::~DragDownloadFile() {
176 CheckThread();
178 // This is the only place that drag_ui_ can be deleted from. Post a message to
179 // the UI thread so that it calls RemoveObserver on the right thread, and so
180 // that this task will run after the InitiateDownload task runs on the UI
181 // thread.
182 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
183 &DragDownloadFileUI::Delete, base::Unretained(drag_ui_)));
184 drag_ui_ = NULL;
187 void DragDownloadFile::Start(ui::DownloadFileObserver* observer) {
188 CheckThread();
190 if (state_ != INITIALIZED)
191 return;
192 state_ = STARTED;
194 DCHECK(!observer_.get());
195 observer_ = observer;
196 DCHECK(observer_.get());
198 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
199 &DragDownloadFileUI::InitiateDownload, base::Unretained(drag_ui_),
200 base::Passed(&file_), file_path_));
203 bool DragDownloadFile::Wait() {
204 CheckThread();
205 if (state_ == STARTED)
206 nested_loop_.Run();
207 return state_ == SUCCESS;
210 void DragDownloadFile::Stop() {
211 CheckThread();
212 if (drag_ui_) {
213 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
214 &DragDownloadFileUI::Cancel, base::Unretained(drag_ui_)));
218 void DragDownloadFile::DownloadCompleted(bool is_successful) {
219 CheckThread();
221 state_ = is_successful ? SUCCESS : FAILURE;
223 if (is_successful)
224 observer_->OnDownloadCompleted(file_path_);
225 else
226 observer_->OnDownloadAborted();
228 // Release the observer since we do not need it any more.
229 observer_ = NULL;
231 if (nested_loop_.running())
232 nested_loop_.Quit();
235 void DragDownloadFile::CheckThread() {
236 #if defined(OS_WIN)
237 DCHECK(drag_message_loop_ == base::MessageLoop::current());
238 #else
239 DCHECK_CURRENTLY_ON(BrowserThread::UI);
240 #endif
243 } // namespace content