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