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