Home | History | Annotate | Download | only in download
      1 // Copyright (c) 2011 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 "chrome/browser/download/drag_download_file.h"
      6 
      7 #include "base/file_util.h"
      8 #include "base/message_loop.h"
      9 #include "chrome/browser/download/download_file.h"
     10 #include "chrome/browser/download/download_item.h"
     11 #include "chrome/browser/download/download_util.h"
     12 #include "chrome/browser/profiles/profile.h"
     13 #include "content/browser/browser_thread.h"
     14 #include "content/browser/tab_contents/tab_contents.h"
     15 #include "net/base/file_stream.h"
     16 
     17 DragDownloadFile::DragDownloadFile(
     18     const FilePath& file_name_or_path,
     19     linked_ptr<net::FileStream> file_stream,
     20     const GURL& url,
     21     const GURL& referrer,
     22     const std::string& referrer_encoding,
     23     TabContents* tab_contents)
     24     : file_stream_(file_stream),
     25       url_(url),
     26       referrer_(referrer),
     27       referrer_encoding_(referrer_encoding),
     28       tab_contents_(tab_contents),
     29       drag_message_loop_(MessageLoop::current()),
     30       is_started_(false),
     31       is_successful_(false),
     32       download_manager_(NULL),
     33       download_item_observer_added_(false) {
     34 #if defined(OS_WIN)
     35   DCHECK(!file_name_or_path.empty() && !file_stream.get());
     36   file_name_ = file_name_or_path;
     37 #elif defined(OS_POSIX)
     38   DCHECK(!file_name_or_path.empty() && file_stream.get());
     39   file_path_ = file_name_or_path;
     40 #endif
     41 }
     42 
     43 DragDownloadFile::~DragDownloadFile() {
     44   AssertCurrentlyOnDragThread();
     45 
     46   // Since the target application can still hold and use the dragged file,
     47   // we do not know the time that it can be safely deleted. To solve this
     48   // problem, we schedule it to be removed after the system is restarted.
     49 #if defined(OS_WIN)
     50   if (!temp_dir_path_.empty()) {
     51     if (!file_path_.empty())
     52       file_util::DeleteAfterReboot(file_path_);
     53     file_util::DeleteAfterReboot(temp_dir_path_);
     54   }
     55 #endif
     56 
     57   if (download_manager_)
     58     download_manager_->RemoveObserver(this);
     59 }
     60 
     61 bool DragDownloadFile::Start(ui::DownloadFileObserver* observer) {
     62   AssertCurrentlyOnDragThread();
     63 
     64   if (is_started_)
     65     return true;
     66   is_started_ = true;
     67 
     68   DCHECK(!observer_.get());
     69   observer_ = observer;
     70 
     71   if (!file_stream_.get()) {
     72     // Create a temporary directory to save the temporary download file. We do
     73     // not want to use the default download directory since we do not want the
     74     // twisted file name shown in the download shelf if the file with the same
     75     // name already exists.
     76     if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome"),
     77                                            &temp_dir_path_))
     78       return false;
     79 
     80     file_path_ = temp_dir_path_.Append(file_name_);
     81   }
     82 
     83   InitiateDownload();
     84 
     85   // On Windows, we need to wait till the download file is completed.
     86 #if defined(OS_WIN)
     87   StartNestedMessageLoop();
     88 #endif
     89 
     90   return is_successful_;
     91 }
     92 
     93 void DragDownloadFile::Stop() {
     94 }
     95 
     96 void DragDownloadFile::InitiateDownload() {
     97 #if defined(OS_WIN)
     98   // DownloadManager could only be invoked from the UI thread.
     99   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    100     BrowserThread::PostTask(
    101         BrowserThread::UI, FROM_HERE,
    102         NewRunnableMethod(this,
    103                           &DragDownloadFile::InitiateDownload));
    104     return;
    105   }
    106 #endif
    107 
    108   download_manager_ = tab_contents_->profile()->GetDownloadManager();
    109   download_manager_->AddObserver(this);
    110 
    111   DownloadSaveInfo save_info;
    112   save_info.file_path = file_path_;
    113   save_info.file_stream = file_stream_;
    114   download_manager_->DownloadUrlToFile(url_,
    115                                        referrer_,
    116                                        referrer_encoding_,
    117                                        save_info,
    118                                        tab_contents_);
    119   download_util::RecordDownloadCount(
    120       download_util::INITIATED_BY_DRAG_N_DROP_COUNT);
    121 }
    122 
    123 void DragDownloadFile::DownloadCompleted(bool is_successful) {
    124 #if defined(OS_WIN)
    125   // If not in drag-and-drop thread, defer the running to it.
    126   if (drag_message_loop_ != MessageLoop::current()) {
    127     drag_message_loop_->PostTask(
    128         FROM_HERE,
    129         NewRunnableMethod(this,
    130                           &DragDownloadFile::DownloadCompleted,
    131                           is_successful));
    132     return;
    133   }
    134 #endif
    135 
    136   is_successful_ = is_successful;
    137 
    138   // Call the observer.
    139   DCHECK(observer_);
    140   if (is_successful)
    141     observer_->OnDownloadCompleted(file_path_);
    142   else
    143     observer_->OnDownloadAborted();
    144 
    145   // Release the observer since we do not need it any more.
    146   observer_ = NULL;
    147 
    148   // On Windows, we need to stop the waiting.
    149 #if defined(OS_WIN)
    150   QuitNestedMessageLoop();
    151 #endif
    152 }
    153 
    154 void DragDownloadFile::ModelChanged() {
    155   AssertCurrentlyOnUIThread();
    156 
    157   std::vector<DownloadItem*> downloads;
    158   download_manager_->GetTemporaryDownloads(file_path_.DirName(), &downloads);
    159   for (std::vector<DownloadItem*>::const_iterator i = downloads.begin();
    160        i != downloads.end(); ++i) {
    161     if (!download_item_observer_added_ && (*i)->original_url() == url_) {
    162       download_item_observer_added_ = true;
    163       (*i)->AddObserver(this);
    164     }
    165   }
    166 }
    167 
    168 void DragDownloadFile::OnDownloadUpdated(DownloadItem* download) {
    169   AssertCurrentlyOnUIThread();
    170   if (download->IsCancelled()) {
    171     download->RemoveObserver(this);
    172     download_manager_->RemoveObserver(this);
    173 
    174     DownloadCompleted(false);
    175   } else if (download->IsComplete()) {
    176     download->RemoveObserver(this);
    177     download_manager_->RemoveObserver(this);
    178     DownloadCompleted(true);
    179   }
    180   // Ignore other states.
    181 }
    182 
    183 void DragDownloadFile::AssertCurrentlyOnDragThread() {
    184   // Only do the check on Windows where two threads are involved.
    185 #if defined(OS_WIN)
    186   DCHECK(drag_message_loop_ == MessageLoop::current());
    187 #endif
    188 }
    189 
    190 void DragDownloadFile::AssertCurrentlyOnUIThread() {
    191   // Only do the check on Windows where two threads are involved.
    192 #if defined(OS_WIN)
    193   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    194 #endif
    195 }
    196 
    197 #if defined(OS_WIN)
    198 void DragDownloadFile::StartNestedMessageLoop() {
    199   AssertCurrentlyOnDragThread();
    200 
    201   bool old_state = MessageLoop::current()->NestableTasksAllowed();
    202   MessageLoop::current()->SetNestableTasksAllowed(true);
    203   is_running_nested_message_loop_ = true;
    204   MessageLoop::current()->Run();
    205   MessageLoop::current()->SetNestableTasksAllowed(old_state);
    206 }
    207 
    208 void DragDownloadFile::QuitNestedMessageLoop() {
    209   AssertCurrentlyOnDragThread();
    210 
    211   if (is_running_nested_message_loop_) {
    212     is_running_nested_message_loop_ = false;
    213     MessageLoop::current()->Quit();
    214   }
    215 }
    216 #endif
    217