• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "content/browser/download/drag_download_file.h"
6 
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"
17 
18 namespace content {
19 
20 namespace {
21 
22 typedef base::Callback<void(bool)> OnCompleted;
23 
24 }  // namespace
25 
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:
DragDownloadFileUI(const GURL & url,const Referrer & referrer,const std::string & referrer_encoding,WebContents * web_contents,base::MessageLoop * on_completed_loop,const OnCompleted & on_completed)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.
54   }
55 
InitiateDownload(base::File file,const base::FilePath & file_path)56   void InitiateDownload(base::File file,
57                         const base::FilePath& file_path) {
58     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
59     DownloadManager* download_manager =
60         BrowserContext::GetDownloadManager(web_contents_->GetBrowserContext());
61 
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());
72   }
73 
Cancel()74   void Cancel() {
75     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
76     if (download_item_)
77       download_item_->Cancel(true);
78   }
79 
Delete()80   void Delete() {
81     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
82     delete this;
83   }
84 
85  private:
~DragDownloadFileUI()86   virtual ~DragDownloadFileUI() {
87     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
88     if (download_item_)
89       download_item_->RemoveObserver(this);
90   }
91 
OnDownloadStarted(DownloadItem * item,DownloadInterruptReason interrupt_reason)92   void OnDownloadStarted(DownloadItem* item,
93                          DownloadInterruptReason interrupt_reason) {
94     DCHECK(BrowserThread::CurrentlyOn(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;
99     }
100     DCHECK_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
101     download_item_ = item;
102     download_item_->AddObserver(this);
103   }
104 
105   // DownloadItem::Observer:
OnDownloadUpdated(DownloadItem * item)106   virtual void OnDownloadUpdated(DownloadItem* item) OVERRIDE {
107     DCHECK(BrowserThread::CurrentlyOn(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();
117       }
118       download_item_->RemoveObserver(this);
119       download_item_ = NULL;
120     }
121     // Ignore other states.
122   }
123 
OnDownloadDestroyed(DownloadItem * item)124   virtual void OnDownloadDestroyed(DownloadItem* item) OVERRIDE {
125     DCHECK(BrowserThread::CurrentlyOn(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();
133     }
134     download_item_->RemoveObserver(this);
135     download_item_ = NULL;
136   }
137 
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_;
145 
146   // Only used in the callback from DownloadManager::DownloadUrl().
147   base::WeakPtrFactory<DragDownloadFileUI> weak_ptr_factory_;
148 
149   DISALLOW_COPY_AND_ASSIGN(DragDownloadFileUI);
150 };
151 
DragDownloadFile(const base::FilePath & file_path,base::File file,const GURL & url,const Referrer & referrer,const std::string & referrer_encoding,WebContents * web_contents)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());
173 }
174 
~DragDownloadFile()175 DragDownloadFile::~DragDownloadFile() {
176   CheckThread();
177 
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;
185 }
186 
Start(ui::DownloadFileObserver * observer)187 void DragDownloadFile::Start(ui::DownloadFileObserver* observer) {
188   CheckThread();
189 
190   if (state_ != INITIALIZED)
191     return;
192   state_ = STARTED;
193 
194   DCHECK(!observer_.get());
195   observer_ = observer;
196   DCHECK(observer_.get());
197 
198   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
199       &DragDownloadFileUI::InitiateDownload, base::Unretained(drag_ui_),
200       base::Passed(&file_), file_path_));
201 }
202 
Wait()203 bool DragDownloadFile::Wait() {
204   CheckThread();
205   if (state_ == STARTED)
206     nested_loop_.Run();
207   return state_ == SUCCESS;
208 }
209 
Stop()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_)));
215   }
216 }
217 
DownloadCompleted(bool is_successful)218 void DragDownloadFile::DownloadCompleted(bool is_successful) {
219   CheckThread();
220 
221   state_ = is_successful ? SUCCESS : FAILURE;
222 
223   if (is_successful)
224     observer_->OnDownloadCompleted(file_path_);
225   else
226     observer_->OnDownloadAborted();
227 
228   // Release the observer since we do not need it any more.
229   observer_ = NULL;
230 
231   if (nested_loop_.running())
232     nested_loop_.Quit();
233 }
234 
CheckThread()235 void DragDownloadFile::CheckThread() {
236 #if defined(OS_WIN)
237   DCHECK(drag_message_loop_ == base::MessageLoop::current());
238 #else
239   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
240 #endif
241 }
242 
243 }  // namespace content
244