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/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"
22 typedef base::Callback
<void(bool)> OnCompleted
;
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
33 class DragDownloadFile::DragDownloadFileUI
: public DownloadItem::Observer
{
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
),
45 referrer_encoding_(referrer_encoding
),
46 web_contents_(web_contents
),
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());
75 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
77 download_item_
->Cancel(true);
81 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
86 ~DragDownloadFileUI() override
{
87 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
89 download_item_
->RemoveObserver(this);
92 void OnDownloadStarted(DownloadItem
* item
,
93 DownloadInterruptReason interrupt_reason
) {
94 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
96 DCHECK_NE(DOWNLOAD_INTERRUPT_REASON_NONE
, interrupt_reason
);
97 on_completed_loop_
->PostTask(FROM_HERE
, base::Bind(on_completed_
, false));
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_
;
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
,
155 const Referrer
& referrer
,
156 const std::string
& referrer_encoding
,
157 WebContents
* web_contents
)
158 : file_path_(file_path
),
160 drag_message_loop_(base::MessageLoop::current()),
163 weak_ptr_factory_(this) {
164 drag_ui_
= new DragDownloadFileUI(
170 base::Bind(&DragDownloadFile::DownloadCompleted
,
171 weak_ptr_factory_
.GetWeakPtr()));
172 DCHECK(!file_path_
.empty());
175 DragDownloadFile::~DragDownloadFile() {
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
182 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, base::Bind(
183 &DragDownloadFileUI::Delete
, base::Unretained(drag_ui_
)));
187 void DragDownloadFile::Start(ui::DownloadFileObserver
* observer
) {
190 if (state_
!= INITIALIZED
)
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() {
205 if (state_
== STARTED
)
207 return state_
== SUCCESS
;
210 void DragDownloadFile::Stop() {
213 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, base::Bind(
214 &DragDownloadFileUI::Cancel
, base::Unretained(drag_ui_
)));
218 void DragDownloadFile::DownloadCompleted(bool is_successful
) {
221 state_
= is_successful
? SUCCESS
: FAILURE
;
224 observer_
->OnDownloadCompleted(file_path_
);
226 observer_
->OnDownloadAborted();
228 // Release the observer since we do not need it any more.
231 if (nested_loop_
.running())
235 void DragDownloadFile::CheckThread() {
237 DCHECK(drag_message_loop_
== base::MessageLoop::current());
239 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
243 } // namespace content