普通文本  |  136行  |  4.53 KB

// 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 "base/callback.h"
#include "base/stl_util.h"
#include "chrome/browser/download/download_item_model.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_tabstrip.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"
#endif

namespace {

// DefaultUIControllerDelegate{Android,} is used when a DownloadUIController is
// constructed without specifying an explicit Delegate.
#if defined(OS_ANDROID)

class DefaultUIControllerDelegateAndroid
    : public DownloadUIController::Delegate {
 public:
  DefaultUIControllerDelegateAndroid() {}
  virtual ~DefaultUIControllerDelegateAndroid() {}

 private:
  // DownloadUIController::Delegate
  virtual void OnNewDownloadReady(content::DownloadItem* item) OVERRIDE;
};

void DefaultUIControllerDelegateAndroid::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 DefaultUIControllerDelegate : public DownloadUIController::Delegate {
 public:
  // |profile| is required to outlive DefaultUIControllerDelegate.
  explicit DefaultUIControllerDelegate(Profile* profile)
      : profile_(profile) {}
  virtual ~DefaultUIControllerDelegate() {}

 private:
  // DownloadUIController::Delegate
  virtual void OnNewDownloadReady(content::DownloadItem* item) OVERRIDE;

  Profile* profile_;
};

void DefaultUIControllerDelegate::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->ShowDownload(item);
}

#endif  // !OS_ANDROID

} // namespace

DownloadUIController::Delegate::~Delegate() {
}

DownloadUIController::DownloadUIController(content::DownloadManager* manager,
                                           scoped_ptr<Delegate> delegate)
    : download_notifier_(manager, this),
      delegate_(delegate.Pass()) {
  if (!delegate_) {
#if defined(OS_ANDROID)
    delegate_.reset(new DefaultUIControllerDelegateAndroid());
#else
    // The delegate should not be invoked after the profile has gone away. This
    // should be the case since DownloadUIController is owned by
    // DownloadService, which in turn is a profile keyed service.
    delegate_.reset(new DefaultUIControllerDelegate(
        Profile::FromBrowserContext(manager->GetBrowserContext())));
#endif
  }
}

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.
  if (item->GetTargetFilePath().empty())
    return;

  DownloadItemModel(item).SetWasUINotified(true);
  delegate_->OnNewDownloadReady(item);
}