// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/download/download_ui_controller.h" #include #include "base/callback.h" #include "base/stl_util.h" #include "build/build_config.h" #include "chrome/browser/download/download_item_model.h" #include "chrome/browser/download/download_shelf.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "content/public/browser/download_item.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_delegate.h" #if defined(OS_ANDROID) #include "content/public/browser/android/download_controller_android.h" #else #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/host_desktop.h" #endif #if defined(OS_CHROMEOS) #include "chrome/browser/download/notification/download_notification_manager.h" #endif namespace { // DownloadShelfUIControllerDelegate{Android,} is used when a // DownloadUIController is // constructed without specifying an explicit Delegate. #if defined(OS_ANDROID) class AndroidUIControllerDelegate : public DownloadUIController::Delegate { public: AndroidUIControllerDelegate() {} ~AndroidUIControllerDelegate() override {} private: // DownloadUIController::Delegate void OnNewDownloadReady(content::DownloadItem* item) override; }; void AndroidUIControllerDelegate::OnNewDownloadReady( content::DownloadItem* item) { // The Android DownloadController is only interested in IN_PROGRESS downloads. // Ones which are INTERRUPTED etc. can't be handed over to the Android // DownloadManager. if (item->GetState() != content::DownloadItem::IN_PROGRESS) return; // GET downloads without authentication are delegated to the Android // DownloadManager. Chrome is responsible for the rest. See // InterceptDownloadResourceThrottle::ProcessDownloadRequest(). content::DownloadControllerAndroid::Get()->OnDownloadStarted(item); } #else // OS_ANDROID class DownloadShelfUIControllerDelegate : public DownloadUIController::Delegate { public: // |profile| is required to outlive DownloadShelfUIControllerDelegate. explicit DownloadShelfUIControllerDelegate(Profile* profile) : profile_(profile) {} ~DownloadShelfUIControllerDelegate() override {} private: // DownloadUIController::Delegate void OnNewDownloadReady(content::DownloadItem* item) override; Profile* profile_; }; void DownloadShelfUIControllerDelegate::OnNewDownloadReady( content::DownloadItem* item) { content::WebContents* web_contents = item->GetWebContents(); Browser* browser = web_contents ? chrome::FindBrowserWithWebContents(web_contents) : NULL; // As a last resort, use the last active browser for this profile. Not ideal, // but better than not showing the download at all. if (browser == NULL) { browser = chrome::FindLastActiveWithProfile(profile_, chrome::GetActiveDesktop()); } if (browser && browser->window() && DownloadItemModel(item).ShouldShowInShelf()) { // GetDownloadShelf creates the download shelf if it was not yet created. browser->window()->GetDownloadShelf()->AddDownload(item); } } #endif // !OS_ANDROID } // namespace DownloadUIController::Delegate::~Delegate() { } DownloadUIController::DownloadUIController(content::DownloadManager* manager, scoped_ptr delegate) : download_notifier_(manager, this), delegate_(std::move(delegate)) { #if defined(OS_ANDROID) if (!delegate_) delegate_.reset(new AndroidUIControllerDelegate()); #else #if defined(OS_CHROMEOS) if (!delegate_ && DownloadNotificationManager::IsEnabled()) { // The Profile is guaranteed to be valid since DownloadUIController is owned // by DownloadService, which in turn is a profile keyed service. delegate_.reset(new DownloadNotificationManager( Profile::FromBrowserContext(manager->GetBrowserContext()))); } #endif // defined(OS_CHROMEOS) if (!delegate_) { delegate_.reset(new DownloadShelfUIControllerDelegate( Profile::FromBrowserContext(manager->GetBrowserContext()))); } #endif // defined(OS_ANDROID) } DownloadUIController::~DownloadUIController() { } void DownloadUIController::OnDownloadCreated(content::DownloadManager* manager, content::DownloadItem* item) { // SavePackage downloads are created in a state where they can be shown in the // browser. Call OnDownloadUpdated() once to notify the UI immediately. OnDownloadUpdated(manager, item); } void DownloadUIController::OnDownloadUpdated(content::DownloadManager* manager, content::DownloadItem* item) { DownloadItemModel item_model(item); // Ignore if we've already notified the UI about |item| or if it isn't a new // download. if (item_model.WasUINotified() || !item_model.ShouldNotifyUI()) return; // Wait until the target path is determined or the download is canceled. if (item->GetTargetFilePath().empty() && item->GetState() != content::DownloadItem::CANCELLED) return; #if !defined(OS_ANDROID) content::WebContents* web_contents = item->GetWebContents(); if (web_contents) { Browser* browser = chrome::FindBrowserWithWebContents(web_contents); // If the download occurs in a new tab, and it's not a save page // download (started before initial navigation completed) close it. // Avoid calling CloseContents if the tab is not in this browser's tab strip // model; this can happen if the download was initiated by something // internal to Chrome, such as by the app list. if (browser && web_contents->GetController().IsInitialNavigation() && browser->tab_strip_model()->count() > 1 && browser->tab_strip_model()->GetIndexOfWebContents(web_contents) != TabStripModel::kNoTab && !item->IsSavePackageDownload()) { web_contents->Close(); } } #endif if (item->GetState() == content::DownloadItem::CANCELLED) return; DownloadItemModel(item).SetWasUINotified(true); delegate_->OnNewDownloadReady(item); }