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"
8 #include "base/files/file.h"
9 #include "base/location.h"
10 #include "base/single_thread_task_runner.h"
11 #include "content/browser/download/download_stats.h"
12 #include "content/browser/web_contents/web_contents_impl.h"
13 #include "content/public/browser/browser_context.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/download_item.h"
16 #include "content/public/browser/download_save_info.h"
17 #include "content/public/browser/download_url_parameters.h"
23 typedef base::Callback
<void(bool)> OnCompleted
;
27 // On windows, DragDownloadFile runs on a thread other than the UI thread.
28 // DownloadItem and DownloadManager may not be accessed on any thread other than
29 // the UI thread. DragDownloadFile may run on either the "drag" thread or the UI
30 // thread depending on the platform, but DragDownloadFileUI strictly always runs
31 // on the UI thread. On platforms where DragDownloadFile runs on the UI thread,
32 // none of the PostTasks are necessary, but it simplifies the code to do them
34 class DragDownloadFile::DragDownloadFileUI
: public DownloadItem::Observer
{
36 DragDownloadFileUI(const GURL
& url
,
37 const Referrer
& referrer
,
38 const std::string
& referrer_encoding
,
39 WebContents
* web_contents
,
40 base::MessageLoop
* on_completed_loop
,
41 const OnCompleted
& on_completed
)
42 : on_completed_loop_(on_completed_loop
),
43 on_completed_(on_completed
),
46 referrer_encoding_(referrer_encoding
),
47 web_contents_(web_contents
),
49 weak_ptr_factory_(this) {
50 DCHECK(on_completed_loop_
);
51 DCHECK(!on_completed_
.is_null());
52 DCHECK(web_contents_
);
53 // May be called on any thread.
54 // Do not call weak_ptr_factory_.GetWeakPtr() outside the UI thread.
57 void InitiateDownload(base::File file
,
58 const base::FilePath
& file_path
) {
59 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
60 DownloadManager
* download_manager
=
61 BrowserContext::GetDownloadManager(web_contents_
->GetBrowserContext());
63 RecordDownloadSource(INITIATED_BY_DRAG_N_DROP
);
64 scoped_ptr
<content::DownloadUrlParameters
> params(
65 DownloadUrlParameters::FromWebContents(web_contents_
, url_
));
66 params
->set_referrer(referrer_
);
67 params
->set_referrer_encoding(referrer_encoding_
);
68 params
->set_callback(base::Bind(&DragDownloadFileUI::OnDownloadStarted
,
69 weak_ptr_factory_
.GetWeakPtr()));
70 params
->set_file_path(file_path
);
71 params
->set_file(file
.Pass()); // Nulls file.
72 download_manager
->DownloadUrl(params
.Pass());
76 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
78 download_item_
->Cancel(true);
82 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
87 ~DragDownloadFileUI() override
{
88 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
90 download_item_
->RemoveObserver(this);
93 void OnDownloadStarted(DownloadItem
* item
,
94 DownloadInterruptReason interrupt_reason
) {
95 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
97 DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE
, interrupt_reason
);
98 on_completed_loop_
->task_runner()->PostTask(
99 FROM_HERE
, base::Bind(on_completed_
, false));
102 DCHECK_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
, interrupt_reason
);
103 download_item_
= item
;
104 download_item_
->AddObserver(this);
107 // DownloadItem::Observer:
108 void OnDownloadUpdated(DownloadItem
* item
) override
{
109 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
110 DCHECK_EQ(download_item_
, item
);
111 DownloadItem::DownloadState state
= download_item_
->GetState();
112 if (state
== DownloadItem::COMPLETE
||
113 state
== DownloadItem::CANCELLED
||
114 state
== DownloadItem::INTERRUPTED
) {
115 if (!on_completed_
.is_null()) {
116 on_completed_loop_
->task_runner()->PostTask(
118 base::Bind(on_completed_
, state
== DownloadItem::COMPLETE
));
119 on_completed_
.Reset();
121 download_item_
->RemoveObserver(this);
122 download_item_
= NULL
;
124 // Ignore other states.
127 void OnDownloadDestroyed(DownloadItem
* item
) override
{
128 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
129 DCHECK_EQ(download_item_
, item
);
130 if (!on_completed_
.is_null()) {
131 const bool is_complete
=
132 download_item_
->GetState() == DownloadItem::COMPLETE
;
133 on_completed_loop_
->task_runner()->PostTask(
134 FROM_HERE
, base::Bind(on_completed_
, is_complete
));
135 on_completed_
.Reset();
137 download_item_
->RemoveObserver(this);
138 download_item_
= NULL
;
141 base::MessageLoop
* on_completed_loop_
;
142 OnCompleted on_completed_
;
145 std::string referrer_encoding_
;
146 WebContents
* web_contents_
;
147 DownloadItem
* download_item_
;
149 // Only used in the callback from DownloadManager::DownloadUrl().
150 base::WeakPtrFactory
<DragDownloadFileUI
> weak_ptr_factory_
;
152 DISALLOW_COPY_AND_ASSIGN(DragDownloadFileUI
);
155 DragDownloadFile::DragDownloadFile(const base::FilePath
& file_path
,
158 const Referrer
& referrer
,
159 const std::string
& referrer_encoding
,
160 WebContents
* web_contents
)
161 : file_path_(file_path
),
163 drag_message_loop_(base::MessageLoop::current()),
166 weak_ptr_factory_(this) {
167 drag_ui_
= new DragDownloadFileUI(
173 base::Bind(&DragDownloadFile::DownloadCompleted
,
174 weak_ptr_factory_
.GetWeakPtr()));
175 DCHECK(!file_path_
.empty());
178 DragDownloadFile::~DragDownloadFile() {
181 // This is the only place that drag_ui_ can be deleted from. Post a message to
182 // the UI thread so that it calls RemoveObserver on the right thread, and so
183 // that this task will run after the InitiateDownload task runs on the UI
185 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, base::Bind(
186 &DragDownloadFileUI::Delete
, base::Unretained(drag_ui_
)));
190 void DragDownloadFile::Start(ui::DownloadFileObserver
* observer
) {
193 if (state_
!= INITIALIZED
)
197 DCHECK(!observer_
.get());
198 observer_
= observer
;
199 DCHECK(observer_
.get());
201 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, base::Bind(
202 &DragDownloadFileUI::InitiateDownload
, base::Unretained(drag_ui_
),
203 base::Passed(&file_
), file_path_
));
206 bool DragDownloadFile::Wait() {
208 if (state_
== STARTED
)
210 return state_
== SUCCESS
;
213 void DragDownloadFile::Stop() {
216 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, base::Bind(
217 &DragDownloadFileUI::Cancel
, base::Unretained(drag_ui_
)));
221 void DragDownloadFile::DownloadCompleted(bool is_successful
) {
224 state_
= is_successful
? SUCCESS
: FAILURE
;
227 observer_
->OnDownloadCompleted(file_path_
);
229 observer_
->OnDownloadAborted();
231 // Release the observer since we do not need it any more.
234 if (nested_loop_
.running())
238 void DragDownloadFile::CheckThread() {
240 DCHECK(drag_message_loop_
== base::MessageLoop::current());
242 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
246 } // namespace content