1 // Copyright 2013 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/download_ui_controller.h" 6 7 #include "base/stl_util.h" 8 #include "chrome/browser/download/download_item_model.h" 9 #include "chrome/browser/ui/browser_finder.h" 10 #include "chrome/browser/ui/browser_tabstrip.h" 11 #include "content/public/browser/download_item.h" 12 #include "content/public/browser/web_contents.h" 13 #include "content/public/browser/web_contents_delegate.h" 14 15 #if defined(OS_ANDROID) 16 #include "content/public/browser/android/download_controller_android.h" 17 #else 18 #include "chrome/browser/profiles/profile.h" 19 #endif 20 21 namespace { 22 23 // DefaultUIControllerDelegate{Android,} is used when a DownloadUIController is 24 // constructed without specifying an explicit Delegate. 25 #if defined(OS_ANDROID) 26 27 class DefaultUIControllerDelegateAndroid 28 : public DownloadUIController::Delegate { 29 public: 30 DefaultUIControllerDelegateAndroid() {} 31 virtual ~DefaultUIControllerDelegateAndroid() {} 32 33 private: 34 // DownloadUIController::Delegate 35 virtual void NotifyDownloadStarting(content::DownloadItem* item) OVERRIDE; 36 }; 37 38 void DefaultUIControllerDelegateAndroid::NotifyDownloadStarting( 39 content::DownloadItem* item) { 40 // GET downloads without authentication are delegated to the Android 41 // DownloadManager. Chrome is responsible for the rest. See 42 // InterceptDownloadResourceThrottle::ProcessDownloadRequest(). 43 content::DownloadControllerAndroid::Get()->OnDownloadStarted(item); 44 } 45 46 #else // OS_ANDROID 47 48 class DefaultUIControllerDelegate : public DownloadUIController::Delegate { 49 public: 50 // |profile| is required to outlive DefaultUIControllerDelegate. 51 explicit DefaultUIControllerDelegate(Profile* profile) 52 : profile_(profile) {} 53 virtual ~DefaultUIControllerDelegate() {} 54 55 private: 56 // DownloadUIController::Delegate 57 virtual void NotifyDownloadStarting(content::DownloadItem* item) OVERRIDE; 58 59 Profile* profile_; 60 }; 61 62 void DefaultUIControllerDelegate::NotifyDownloadStarting( 63 content::DownloadItem* item) { 64 content::WebContents* web_contents = item->GetWebContents(); 65 Browser* browser = 66 web_contents ? chrome::FindBrowserWithWebContents(web_contents) : NULL; 67 68 // As a last resort, use the last active browser for this profile. Not ideal, 69 // but better than not showing the download at all. 70 if (browser == NULL) { 71 browser = chrome::FindLastActiveWithProfile(profile_, 72 chrome::GetActiveDesktop()); 73 } 74 75 if (browser) 76 browser->ShowDownload(item); 77 } 78 79 #endif // !OS_ANDROID 80 81 } // namespace 82 83 DownloadUIController::Delegate::~Delegate() { 84 } 85 86 DownloadUIController::DownloadUIController(content::DownloadManager* manager, 87 scoped_ptr<Delegate> delegate) 88 : download_notifier_(manager, this), 89 delegate_(delegate.Pass()) { 90 if (!delegate_) { 91 #if defined(OS_ANDROID) 92 delegate_.reset(new DefaultUIControllerDelegateAndroid()); 93 #else 94 // The delegate should not be invoked after the profile has gone away. This 95 // should be the case since DownloadUIController is owned by 96 // DownloadService, which in turn is a profile keyed service. 97 delegate_.reset(new DefaultUIControllerDelegate( 98 Profile::FromBrowserContext(manager->GetBrowserContext()))); 99 #endif 100 } 101 } 102 103 DownloadUIController::~DownloadUIController() { 104 } 105 106 void DownloadUIController::OnDownloadCreated(content::DownloadManager* manager, 107 content::DownloadItem* item) { 108 // If this isn't a new download, there's nothing to do. 109 if (item->GetState() != content::DownloadItem::IN_PROGRESS) 110 return; 111 112 DownloadItemModel(item).SetShouldNotifyUI(true); 113 // SavePackage downloads are created in a state where they can be shown in the 114 // browser. Call OnDownloadUpdated() once to notify the UI immediately. 115 OnDownloadUpdated(manager, item); 116 } 117 118 void DownloadUIController::OnDownloadUpdated(content::DownloadManager* manager, 119 content::DownloadItem* item) { 120 // Ignore if we've already notified the UI about |item| or if it isn't a new 121 // download. 122 if (!DownloadItemModel(item).ShouldNotifyUI()) 123 return; 124 125 // Wait until the target path is determined. 126 if (item->GetTargetFilePath().empty()) 127 return; 128 129 // Can't be complete. That would imply that we didn't receive an 130 // OnDownloadUpdated() after the target was determined. 131 DCHECK_NE(content::DownloadItem::COMPLETE, item->GetState()); 132 133 DownloadItemModel(item).SetShouldNotifyUI(false); 134 delegate_->NotifyDownloadStarting(item); 135 } 136