Home | History | Annotate | Download | only in 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/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"
     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:
     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 
     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());
     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_stream(file_stream.Pass());  // Nulls file_stream.
     71     download_manager->DownloadUrl(params.Pass());
     72   }
     73 
     74   void Cancel() {
     75     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     76     if (download_item_)
     77       download_item_->Cancel(true);
     78   }
     79 
     80   void Delete() {
     81     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     82     delete this;
     83   }
     84 
     85  private:
     86   virtual ~DragDownloadFileUI() {
     87     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     88     if (download_item_)
     89       download_item_->RemoveObserver(this);
     90   }
     91 
     92   void OnDownloadStarted(DownloadItem* item, net::Error error) {
     93     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     94     if (!item) {
     95       DCHECK_NE(net::OK, error);
     96       on_completed_loop_->PostTask(FROM_HERE, base::Bind(on_completed_, false));
     97       return;
     98     }
     99     DCHECK_EQ(net::OK, error);
    100     download_item_ = item;
    101     download_item_->AddObserver(this);
    102   }
    103 
    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();
    116       }
    117       download_item_->RemoveObserver(this);
    118       download_item_ = NULL;
    119     }
    120     // Ignore other states.
    121   }
    122 
    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();
    132     }
    133     download_item_->RemoveObserver(this);
    134     download_item_ = NULL;
    135   }
    136 
    137   base::MessageLoop* on_completed_loop_;
    138   OnCompleted on_completed_;
    139   GURL url_;
    140   Referrer referrer_;
    141   std::string referrer_encoding_;
    142   WebContents* web_contents_;
    143   DownloadItem* download_item_;
    144 
    145   // Only used in the callback from DownloadManager::DownloadUrl().
    146   base::WeakPtrFactory<DragDownloadFileUI> weak_ptr_factory_;
    147 
    148   DISALLOW_COPY_AND_ASSIGN(DragDownloadFileUI);
    149 };
    150 
    151 DragDownloadFile::DragDownloadFile(const base::FilePath& file_path,
    152                                    scoped_ptr<net::FileStream> file_stream,
    153                                    const GURL& url,
    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()),
    160       state_(INITIALIZED),
    161       drag_ui_(NULL),
    162       weak_ptr_factory_(this) {
    163   drag_ui_ = new DragDownloadFileUI(
    164       url,
    165       referrer,
    166       referrer_encoding,
    167       web_contents,
    168       drag_message_loop_,
    169       base::Bind(&DragDownloadFile::DownloadCompleted,
    170                  weak_ptr_factory_.GetWeakPtr()));
    171   DCHECK(!file_path_.empty());
    172 }
    173 
    174 DragDownloadFile::~DragDownloadFile() {
    175   CheckThread();
    176 
    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
    180   // thread.
    181   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
    182       &DragDownloadFileUI::Delete, base::Unretained(drag_ui_)));
    183   drag_ui_ = NULL;
    184 }
    185 
    186 void DragDownloadFile::Start(ui::DownloadFileObserver* observer) {
    187   CheckThread();
    188 
    189   if (state_ != INITIALIZED)
    190     return;
    191   state_ = STARTED;
    192 
    193   DCHECK(!observer_.get());
    194   observer_ = observer;
    195   DCHECK(observer_.get());
    196 
    197   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
    198       &DragDownloadFileUI::InitiateDownload, base::Unretained(drag_ui_),
    199       base::Passed(&file_stream_), file_path_));
    200 }
    201 
    202 bool DragDownloadFile::Wait() {
    203   CheckThread();
    204   if (state_ == STARTED)
    205     nested_loop_.Run();
    206   return state_ == SUCCESS;
    207 }
    208 
    209 void DragDownloadFile::Stop() {
    210   CheckThread();
    211   if (drag_ui_) {
    212     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(
    213         &DragDownloadFileUI::Cancel, base::Unretained(drag_ui_)));
    214   }
    215 }
    216 
    217 void DragDownloadFile::DownloadCompleted(bool is_successful) {
    218   CheckThread();
    219 
    220   state_ = is_successful ? SUCCESS : FAILURE;
    221 
    222   if (is_successful)
    223     observer_->OnDownloadCompleted(file_path_);
    224   else
    225     observer_->OnDownloadAborted();
    226 
    227   // Release the observer since we do not need it any more.
    228   observer_ = NULL;
    229 
    230   if (nested_loop_.running())
    231     nested_loop_.Quit();
    232 }
    233 
    234 void DragDownloadFile::CheckThread() {
    235 #if defined(OS_WIN)
    236   DCHECK(drag_message_loop_ == base::MessageLoop::current());
    237 #else
    238   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    239 #endif
    240 }
    241 
    242 }  // namespace content
    243