download_manager_delegate_impl.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. // Copyright 2019 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. #include "weblayer/browser/download_manager_delegate_impl.h"
  5. #include "base/files/file_util.h"
  6. #include "base/task/thread_pool.h"
  7. #include "base/threading/sequenced_task_runner_handle.h"
  8. #include "build/build_config.h"
  9. #include "components/download/public/common/download_item.h"
  10. #include "components/prefs/pref_service.h"
  11. #include "content/public/browser/browser_task_traits.h"
  12. #include "content/public/browser/browser_thread.h"
  13. #include "content/public/browser/download_item_utils.h"
  14. #include "content/public/browser/download_manager.h"
  15. #include "net/base/filename_util.h"
  16. #include "third_party/abseil-cpp/absl/types/optional.h"
  17. #include "weblayer/browser/browser_context_impl.h"
  18. #include "weblayer/browser/browser_process.h"
  19. #include "weblayer/browser/download_manager_delegate_impl.h"
  20. #include "weblayer/browser/persistent_download.h"
  21. #include "weblayer/browser/profile_impl.h"
  22. #include "weblayer/browser/tab_impl.h"
  23. #include "weblayer/public/download_delegate.h"
  24. namespace weblayer {
  25. namespace {
  26. void GenerateFilename(
  27. const GURL& url,
  28. const std::string& content_disposition,
  29. const std::string& suggested_filename,
  30. const std::string& mime_type,
  31. const base::FilePath& suggested_directory,
  32. base::OnceCallback<void(const base::FilePath&)> callback) {
  33. base::FilePath generated_name =
  34. net::GenerateFileName(url, content_disposition, std::string(),
  35. suggested_filename, mime_type, "download");
  36. if (!base::PathExists(suggested_directory))
  37. base::CreateDirectory(suggested_directory);
  38. base::FilePath suggested_path(suggested_directory.Append(generated_name));
  39. content::GetUIThreadTaskRunner({})->PostTask(
  40. FROM_HERE, base::BindOnce(std::move(callback), suggested_path));
  41. }
  42. } // namespace
  43. const char kDownloadNextIDPref[] = "weblayer_download_next_id";
  44. DownloadManagerDelegateImpl::DownloadManagerDelegateImpl(
  45. content::DownloadManager* download_manager)
  46. : download_manager_(download_manager) {
  47. download_manager_->AddObserver(this);
  48. // WebLayer doesn't use a history DB as the in-progress database maintained by
  49. // the download component is enough. However the download code still depends
  50. // this notification. TODO(jam): update download code to handle this.
  51. download_manager_->PostInitialization(
  52. content::DownloadManager::DOWNLOAD_INITIALIZATION_DEPENDENCY_HISTORY_DB);
  53. }
  54. DownloadManagerDelegateImpl::~DownloadManagerDelegateImpl() {
  55. download_manager_->RemoveObserver(this);
  56. // Match the AddObserver calls added in OnDownloadCreated to avoid UaF.
  57. download::SimpleDownloadManager::DownloadVector downloads;
  58. download_manager_->GetAllDownloads(&downloads);
  59. for (auto* download : downloads)
  60. download->RemoveObserver(this);
  61. }
  62. void DownloadManagerDelegateImpl::GetNextId(
  63. content::DownloadIdCallback callback) {
  64. // Need to return a unique id, even across crashes, to avoid notification
  65. // intents with different data (e.g. notification GUID) getting dup'd. This is
  66. // also persisted in the on-disk download database to support resumption.
  67. auto* local_state = BrowserProcess::GetInstance()->GetLocalState();
  68. std::move(callback).Run(local_state->GetInteger(kDownloadNextIDPref));
  69. }
  70. bool DownloadManagerDelegateImpl::DetermineDownloadTarget(
  71. download::DownloadItem* item,
  72. content::DownloadTargetCallback* callback) {
  73. if (!item->GetForcedFilePath().empty()) {
  74. std::move(*callback).Run(
  75. item->GetForcedFilePath(),
  76. download::DownloadItem::TARGET_DISPOSITION_OVERWRITE,
  77. download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
  78. download::DownloadItem::MixedContentStatus::UNKNOWN,
  79. item->GetForcedFilePath(), base::FilePath(),
  80. std::string() /*mime_type*/, download::DOWNLOAD_INTERRUPT_REASON_NONE);
  81. return true;
  82. }
  83. auto filename_determined_callback = base::BindOnce(
  84. &DownloadManagerDelegateImpl::OnDownloadPathGenerated,
  85. weak_ptr_factory_.GetWeakPtr(), item->GetId(), std::move(*callback));
  86. auto* browser_context = content::DownloadItemUtils::GetBrowserContext(item);
  87. base::FilePath default_download_path;
  88. GetSaveDir(browser_context, nullptr, &default_download_path);
  89. base::ThreadPool::PostTask(
  90. FROM_HERE,
  91. {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
  92. base::TaskPriority::USER_VISIBLE},
  93. base::BindOnce(
  94. GenerateFilename, item->GetURL(), item->GetContentDisposition(),
  95. item->GetSuggestedFilename(), item->GetMimeType(),
  96. default_download_path, std::move(filename_determined_callback)));
  97. return true;
  98. }
  99. bool DownloadManagerDelegateImpl::InterceptDownloadIfApplicable(
  100. const GURL& url,
  101. const std::string& user_agent,
  102. const std::string& content_disposition,
  103. const std::string& mime_type,
  104. const std::string& request_origin,
  105. int64_t content_length,
  106. bool is_transient,
  107. content::WebContents* web_contents) {
  108. // Don't intercept transient downloads (such as Background Fetches).
  109. if (is_transient)
  110. return false;
  111. // If there's no DownloadDelegate, the download is simply dropped.
  112. auto* delegate = GetDelegate(web_contents);
  113. if (!delegate)
  114. return true;
  115. return delegate->InterceptDownload(url, user_agent, content_disposition,
  116. mime_type, content_length);
  117. }
  118. void DownloadManagerDelegateImpl::GetSaveDir(
  119. content::BrowserContext* browser_context,
  120. base::FilePath* website_save_dir,
  121. base::FilePath* download_save_dir) {
  122. auto* browser_context_impl =
  123. static_cast<BrowserContextImpl*>(browser_context);
  124. auto* profile = browser_context_impl->profile_impl();
  125. if (!profile->download_directory().empty())
  126. *download_save_dir = profile->download_directory();
  127. }
  128. void DownloadManagerDelegateImpl::CheckDownloadAllowed(
  129. const content::WebContents::Getter& web_contents_getter,
  130. const GURL& url,
  131. const std::string& request_method,
  132. absl::optional<url::Origin> request_initiator,
  133. bool from_download_cross_origin_redirect,
  134. bool content_initiated,
  135. content::CheckDownloadAllowedCallback check_download_allowed_cb) {
  136. auto* web_contents = web_contents_getter.Run();
  137. // If there's no DownloadDelegate, the download is simply dropped.
  138. auto* delegate = GetDelegate(web_contents);
  139. auto* tab = TabImpl::FromWebContents(web_contents);
  140. if (!delegate || !tab) {
  141. std::move(check_download_allowed_cb).Run(false);
  142. return;
  143. }
  144. delegate->AllowDownload(tab, url, request_method, request_initiator,
  145. std::move(check_download_allowed_cb));
  146. }
  147. void DownloadManagerDelegateImpl::OnDownloadCreated(
  148. content::DownloadManager* manager,
  149. download::DownloadItem* item) {
  150. auto* local_state = BrowserProcess::GetInstance()->GetLocalState();
  151. int next_id = local_state->GetInteger(kDownloadNextIDPref);
  152. if (item->GetId() >= static_cast<uint32_t>(next_id)) {
  153. next_id = item->GetId();
  154. // Reset the counter when it gets close to max value of unsigned 32 bit
  155. // integer since that's what the download system persists.
  156. if (++next_id == (std::numeric_limits<uint32_t>::max() / 2) - 1)
  157. next_id = 0;
  158. local_state->SetInteger(kDownloadNextIDPref, next_id);
  159. }
  160. if (item->IsTransient())
  161. return;
  162. // As per the documentation in DownloadItem, transient items should not
  163. // be shown in the UI. (Note that they may be surface by other means,
  164. // such as through the BackgroundFetch system.)
  165. item->AddObserver(this);
  166. // Create a PersistentDownload which will be owned by |item|.
  167. PersistentDownload::Create(item);
  168. if (item->GetLastReason() == download::DOWNLOAD_INTERRUPT_REASON_CRASH &&
  169. item->CanResume() &&
  170. // Don't automatically resume downloads which were previously paused.
  171. !item->IsPaused()) {
  172. PersistentDownload::Get(item)->Resume();
  173. }
  174. auto* delegate = GetDelegate(item);
  175. if (delegate)
  176. delegate->DownloadStarted(PersistentDownload::Get(item));
  177. }
  178. void DownloadManagerDelegateImpl::OnDownloadDropped(
  179. content::DownloadManager* manager) {
  180. if (download_dropped_callback_)
  181. download_dropped_callback_.Run();
  182. }
  183. void DownloadManagerDelegateImpl::OnManagerInitialized() {
  184. auto* browser_context_impl =
  185. static_cast<BrowserContextImpl*>(download_manager_->GetBrowserContext());
  186. auto* profile = browser_context_impl->profile_impl();
  187. profile->DownloadsInitialized();
  188. }
  189. void DownloadManagerDelegateImpl::OnDownloadUpdated(
  190. download::DownloadItem* item) {
  191. // If this is the first navigation in a tab it should be closed. Wait until
  192. // the target path is determined or the download is canceled to check.
  193. if (!item->GetTargetFilePath().empty() ||
  194. item->GetState() == download::DownloadItem::CANCELLED) {
  195. content::WebContents* web_contents =
  196. content::DownloadItemUtils::GetWebContents(item);
  197. if (web_contents && web_contents->GetController().IsInitialNavigation())
  198. web_contents->Close();
  199. }
  200. auto* delegate = GetDelegate(item);
  201. if (item->GetState() == download::DownloadItem::COMPLETE ||
  202. item->GetState() == download::DownloadItem::CANCELLED ||
  203. item->GetState() == download::DownloadItem::INTERRUPTED) {
  204. // Stop observing now to ensure we only send one complete/fail notification.
  205. item->RemoveObserver(this);
  206. if (item->GetState() == download::DownloadItem::COMPLETE)
  207. delegate->DownloadCompleted(PersistentDownload::Get(item));
  208. else
  209. delegate->DownloadFailed(PersistentDownload::Get(item));
  210. // Needs to happen asynchronously to avoid nested observer calls.
  211. base::SequencedTaskRunnerHandle::Get()->PostTask(
  212. FROM_HERE,
  213. base::BindOnce(&DownloadManagerDelegateImpl::RemoveItem,
  214. weak_ptr_factory_.GetWeakPtr(), item->GetGuid()));
  215. return;
  216. }
  217. if (delegate)
  218. delegate->DownloadProgressChanged(PersistentDownload::Get(item));
  219. }
  220. void DownloadManagerDelegateImpl::OnDownloadPathGenerated(
  221. uint32_t download_id,
  222. content::DownloadTargetCallback callback,
  223. const base::FilePath& suggested_path) {
  224. std::move(callback).Run(
  225. suggested_path, download::DownloadItem::TARGET_DISPOSITION_OVERWRITE,
  226. download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
  227. download::DownloadItem::MixedContentStatus::UNKNOWN,
  228. suggested_path.AddExtension(FILE_PATH_LITERAL(".crdownload")),
  229. base::FilePath(), std::string() /*mime_type*/,
  230. download::DOWNLOAD_INTERRUPT_REASON_NONE);
  231. }
  232. void DownloadManagerDelegateImpl::RemoveItem(const std::string& guid) {
  233. auto* item = download_manager_->GetDownloadByGuid(guid);
  234. if (item)
  235. item->Remove();
  236. }
  237. DownloadDelegate* DownloadManagerDelegateImpl::GetDelegate(
  238. content::WebContents* web_contents) {
  239. if (!web_contents)
  240. return nullptr;
  241. return GetDelegate(web_contents->GetBrowserContext());
  242. }
  243. DownloadDelegate* DownloadManagerDelegateImpl::GetDelegate(
  244. content::BrowserContext* browser_context) {
  245. auto* profile = ProfileImpl::FromBrowserContext(browser_context);
  246. if (!profile)
  247. return nullptr;
  248. return profile->download_delegate();
  249. }
  250. DownloadDelegate* DownloadManagerDelegateImpl::GetDelegate(
  251. download::DownloadItem* item) {
  252. auto* browser_context = content::DownloadItemUtils::GetBrowserContext(item);
  253. return GetDelegate(browser_context);
  254. }
  255. } // namespace weblayer