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/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:
     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(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 
     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,
     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:
    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 
    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 
    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 
    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 
    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 
    203 bool DragDownloadFile::Wait() {
    204   CheckThread();
    205   if (state_ == STARTED)
    206     nested_loop_.Run();
    207   return state_ == SUCCESS;
    208 }
    209 
    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 
    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 
    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