• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • 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 
DragDownloadFile(const FilePath & file_name_or_path,linked_ptr<net::FileStream> file_stream,const GURL & url,const GURL & referrer,const std::string & referrer_encoding,TabContents * tab_contents)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 
~DragDownloadFile()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 
Start(ui::DownloadFileObserver * observer)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 
Stop()93 void DragDownloadFile::Stop() {
94 }
95 
InitiateDownload()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 
DownloadCompleted(bool is_successful)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 
ModelChanged()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 
OnDownloadUpdated(DownloadItem * download)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 
AssertCurrentlyOnDragThread()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 
AssertCurrentlyOnUIThread()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)
StartNestedMessageLoop()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 
QuitNestedMessageLoop()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