blob: 2c5a5823920e5562e3db2203537934116b65ef7d [file] [log] [blame] [edit]
// Copyright (c) 2012 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/plugins/plugin_installer.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/file_util.h"
#include "base/process.h"
#include "chrome/browser/download/download_service.h"
#include "chrome/browser/download/download_service_factory.h"
#include "chrome/browser/download/download_util.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/plugins/plugin_installer_observer.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/tab_contents/tab_contents.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/download_id.h"
#include "content/public/browser/download_item.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/download_save_info.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/resource_context.h"
#include "content/public/browser/resource_dispatcher_host.h"
#include "content/public/browser/web_contents.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
using content::BrowserContext;
using content::BrowserThread;
using content::DownloadItem;
using content::DownloadManager;
using content::ResourceDispatcherHost;
namespace {
void BeginDownload(
const GURL& url,
content::ResourceContext* resource_context,
int render_process_host_id,
int render_view_host_routing_id,
const ResourceDispatcherHost::DownloadStartedCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
ResourceDispatcherHost* rdh = ResourceDispatcherHost::Get();
scoped_ptr<net::URLRequest> request(
resource_context->GetRequestContext()->CreateRequest(url, NULL));
net::Error error = rdh->BeginDownload(
request.Pass(),
false, // is_content_initiated
resource_context,
render_process_host_id,
render_view_host_routing_id,
true, // prefer_cache
content::DownloadSaveInfo(),
callback);
if (error != net::OK) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(callback, content::DownloadId::Invalid(), error));
}
}
} // namespace
PluginInstaller::PluginInstaller(PluginMetadata* plugin)
: plugin_(plugin),
state_(INSTALLER_STATE_IDLE) {
DCHECK(plugin_);
}
PluginInstaller::~PluginInstaller() {
}
void PluginInstaller::OnDownloadUpdated(DownloadItem* download) {
DownloadItem::DownloadState state = download->GetState();
switch (state) {
case DownloadItem::IN_PROGRESS:
return;
case DownloadItem::COMPLETE: {
DCHECK_EQ(INSTALLER_STATE_DOWNLOADING, state_);
state_ = INSTALLER_STATE_IDLE;
FOR_EACH_OBSERVER(PluginInstallerObserver, observers_,
DownloadFinished());
break;
}
case DownloadItem::CANCELLED: {
DownloadCancelled();
break;
}
case DownloadItem::INTERRUPTED: {
content::DownloadInterruptReason reason = download->GetLastReason();
DownloadError(content::InterruptReasonDebugString(reason));
break;
}
case DownloadItem::MAX_DOWNLOAD_STATE: {
NOTREACHED();
return;
}
}
download->RemoveObserver(this);
}
void PluginInstaller::OnDownloadDestroyed(DownloadItem* download) {
DCHECK_EQ(INSTALLER_STATE_DOWNLOADING, state_);
state_ = INSTALLER_STATE_IDLE;
download->RemoveObserver(this);
}
void PluginInstaller::AddObserver(PluginInstallerObserver* observer) {
observers_.AddObserver(observer);
}
void PluginInstaller::RemoveObserver(PluginInstallerObserver* observer) {
observers_.RemoveObserver(observer);
if (observers_.size() == weak_observers_.size()) {
FOR_EACH_OBSERVER(WeakPluginInstallerObserver, weak_observers_,
OnlyWeakObserversLeft());
}
}
void PluginInstaller::AddWeakObserver(WeakPluginInstallerObserver* observer) {
weak_observers_.AddObserver(observer);
}
void PluginInstaller::RemoveWeakObserver(
WeakPluginInstallerObserver* observer) {
weak_observers_.RemoveObserver(observer);
}
void PluginInstaller::StartInstalling(TabContents* tab_contents) {
DCHECK_EQ(INSTALLER_STATE_IDLE, state_);
DCHECK(!plugin_->url_for_display());
state_ = INSTALLER_STATE_DOWNLOADING;
FOR_EACH_OBSERVER(PluginInstallerObserver, observers_, DownloadStarted());
content::WebContents* web_contents = tab_contents->web_contents();
DownloadManager* download_manager =
BrowserContext::GetDownloadManager(tab_contents->profile());
download_util::RecordDownloadSource(
download_util::INITIATED_BY_PLUGIN_INSTALLER);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&BeginDownload,
plugin_->plugin_url(),
tab_contents->profile()->GetResourceContext(),
web_contents->GetRenderProcessHost()->GetID(),
web_contents->GetRenderViewHost()->GetRoutingID(),
base::Bind(&PluginInstaller::DownloadStarted,
base::Unretained(this),
make_scoped_refptr(download_manager))));
}
void PluginInstaller::DownloadStarted(
scoped_refptr<content::DownloadManager> dlm,
content::DownloadId download_id,
net::Error error) {
if (error != net::OK) {
std::string msg =
base::StringPrintf("Error %d: %s", error, net::ErrorToString(error));
DownloadError(msg);
return;
}
DownloadItem* download_item = dlm->GetDownload(download_id.local());
// TODO(benjhayden): DCHECK(item && item->IsInProgress()) after figuring out
// why DownloadStarted may get net:OK but an invalid id.
if (!download_item) {
DownloadError("Download not found");
return;
}
download_item->SetOpenWhenComplete(true);
download_item->AddObserver(this);
}
void PluginInstaller::OpenDownloadURL(content::WebContents* web_contents) {
DCHECK_EQ(INSTALLER_STATE_IDLE, state_);
DCHECK(plugin_->url_for_display());
web_contents->OpenURL(content::OpenURLParams(
plugin_->plugin_url(),
content::Referrer(web_contents->GetURL(),
WebKit::WebReferrerPolicyDefault),
NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_TYPED, false));
FOR_EACH_OBSERVER(PluginInstallerObserver, observers_, DownloadFinished());
}
void PluginInstaller::DownloadError(const std::string& msg) {
DCHECK_EQ(INSTALLER_STATE_DOWNLOADING, state_);
state_ = INSTALLER_STATE_IDLE;
FOR_EACH_OBSERVER(PluginInstallerObserver, observers_, DownloadError(msg));
}
void PluginInstaller::DownloadCancelled() {
DCHECK_EQ(INSTALLER_STATE_DOWNLOADING, state_);
state_ = INSTALLER_STATE_IDLE;
FOR_EACH_OBSERVER(PluginInstallerObserver, observers_, DownloadCancelled());
}