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/message_loop/message_loop.h"
9 #include "content/browser/download/download_stats.h"
10 #include "content/browser/web_contents/web_contents_impl.h"
11 #include "content/public/browser/browser_context.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "content/public/browser/download_item.h"
14 #include "content/public/browser/download_save_info.h"
15 #include "content/public/browser/download_url_parameters.h"
16 #include "net/base/file_stream.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(scoped_ptr
<net::FileStream
> file_stream
,
57 const base::FilePath
& file_path
) {
58 DCHECK(BrowserThread::CurrentlyOn(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_stream(file_stream
.Pass()); // Nulls file_stream.
71 download_manager
->DownloadUrl(params
.Pass());
75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
77 download_item_
->Cancel(true);
81 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
86 virtual ~DragDownloadFileUI() {
87 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
89 download_item_
->RemoveObserver(this);
92 void OnDownloadStarted(DownloadItem
* item
, net::Error error
) {
93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
95 DCHECK_NE(net::OK
, error
);
96 on_completed_loop_
->PostTask(FROM_HERE
, base::Bind(on_completed_
, false));
99 DCHECK_EQ(net::OK
, error
);
100 download_item_
= item
;
101 download_item_
->AddObserver(this);
104 // DownloadItem::Observer:
105 virtual void OnDownloadUpdated(DownloadItem
* item
) OVERRIDE
{
106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
107 DCHECK_EQ(download_item_
, item
);
108 DownloadItem::DownloadState state
= download_item_
->GetState();
109 if (state
== DownloadItem::COMPLETE
||
110 state
== DownloadItem::CANCELLED
||
111 state
== DownloadItem::INTERRUPTED
) {
112 if (!on_completed_
.is_null()) {
113 on_completed_loop_
->PostTask(FROM_HERE
, base::Bind(
114 on_completed_
, state
== DownloadItem::COMPLETE
));
115 on_completed_
.Reset();
117 download_item_
->RemoveObserver(this);
118 download_item_
= NULL
;
120 // Ignore other states.
123 virtual void OnDownloadDestroyed(DownloadItem
* item
) OVERRIDE
{
124 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
125 DCHECK_EQ(download_item_
, item
);
126 if (!on_completed_
.is_null()) {
127 const bool is_complete
=
128 download_item_
->GetState() == DownloadItem::COMPLETE
;
129 on_completed_loop_
->PostTask(FROM_HERE
, base::Bind(
130 on_completed_
, is_complete
));
131 on_completed_
.Reset();
133 download_item_
->RemoveObserver(this);
134 download_item_
= NULL
;
137 base::MessageLoop
* on_completed_loop_
;
138 OnCompleted on_completed_
;
141 std::string referrer_encoding_
;
142 WebContents
* web_contents_
;
143 DownloadItem
* download_item_
;
145 // Only used in the callback from DownloadManager::DownloadUrl().
146 base::WeakPtrFactory
<DragDownloadFileUI
> weak_ptr_factory_
;
148 DISALLOW_COPY_AND_ASSIGN(DragDownloadFileUI
);
151 DragDownloadFile::DragDownloadFile(const base::FilePath
& file_path
,
152 scoped_ptr
<net::FileStream
> file_stream
,
154 const Referrer
& referrer
,
155 const std::string
& referrer_encoding
,
156 WebContents
* web_contents
)
157 : file_path_(file_path
),
158 file_stream_(file_stream
.Pass()),
159 drag_message_loop_(base::MessageLoop::current()),
162 weak_ptr_factory_(this) {
163 drag_ui_
= new DragDownloadFileUI(
169 base::Bind(&DragDownloadFile::DownloadCompleted
,
170 weak_ptr_factory_
.GetWeakPtr()));
171 DCHECK(!file_path_
.empty());
174 DragDownloadFile::~DragDownloadFile() {
177 // This is the only place that drag_ui_ can be deleted from. Post a message to
178 // the UI thread so that it calls RemoveObserver on the right thread, and so
179 // that this task will run after the InitiateDownload task runs on the UI
181 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, base::Bind(
182 &DragDownloadFileUI::Delete
, base::Unretained(drag_ui_
)));
186 void DragDownloadFile::Start(ui::DownloadFileObserver
* observer
) {
189 if (state_
!= INITIALIZED
)
193 DCHECK(!observer_
.get());
194 observer_
= observer
;
195 DCHECK(observer_
.get());
197 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, base::Bind(
198 &DragDownloadFileUI::InitiateDownload
, base::Unretained(drag_ui_
),
199 base::Passed(&file_stream_
), file_path_
));
202 bool DragDownloadFile::Wait() {
204 if (state_
== STARTED
)
206 return state_
== SUCCESS
;
209 void DragDownloadFile::Stop() {
212 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, base::Bind(
213 &DragDownloadFileUI::Cancel
, base::Unretained(drag_ui_
)));
217 void DragDownloadFile::DownloadCompleted(bool is_successful
) {
220 state_
= is_successful
? SUCCESS
: FAILURE
;
223 observer_
->OnDownloadCompleted(file_path_
);
225 observer_
->OnDownloadAborted();
227 // Release the observer since we do not need it any more.
230 if (nested_loop_
.running())
234 void DragDownloadFile::CheckThread() {
236 DCHECK(drag_message_loop_
== base::MessageLoop::current());
238 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
242 } // namespace content