blob: c4bc378c1480907cb61800114c0b788e3d376077 [file] [log] [blame]
// 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 "content/browser/download/mhtml_generation_manager.h"
#include "base/bind.h"
#include "base/platform_file.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/web_contents.h"
#include "content/common/view_messages.h"
#include "content/public/browser/notification_types.h"
namespace content {
MHTMLGenerationManager::Job::Job()
: browser_file(base::kInvalidPlatformFileValue),
renderer_file(IPC::InvalidPlatformFileForTransit()),
process_id(-1),
routing_id(-1) {
}
MHTMLGenerationManager::Job::~Job() {
}
MHTMLGenerationManager* MHTMLGenerationManager::GetInstance() {
return Singleton<MHTMLGenerationManager>::get();
}
MHTMLGenerationManager::MHTMLGenerationManager() {
}
MHTMLGenerationManager::~MHTMLGenerationManager() {
}
void MHTMLGenerationManager::GenerateMHTML(
WebContents* web_contents,
const base::FilePath& file,
const GenerateMHTMLCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
static int id_counter = 0;
int job_id = id_counter++;
Job job;
job.file_path = file;
job.process_id = web_contents->GetRenderProcessHost()->GetID();
job.routing_id = web_contents->GetRenderViewHost()->GetRoutingID();
job.callback = callback;
id_to_job_[job_id] = job;
if (!registrar_.IsRegistered(
this,
NOTIFICATION_RENDERER_PROCESS_TERMINATED,
Source<RenderProcessHost>(web_contents->GetRenderProcessHost()))) {
registrar_.Add(
this,
NOTIFICATION_RENDERER_PROCESS_TERMINATED,
Source<RenderProcessHost>(web_contents->GetRenderProcessHost()));
}
base::ProcessHandle renderer_process =
web_contents->GetRenderProcessHost()->GetHandle();
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&MHTMLGenerationManager::CreateFile, base::Unretained(this),
job_id, file, renderer_process));
}
void MHTMLGenerationManager::MHTMLGenerated(int job_id, int64 mhtml_data_size) {
JobFinished(job_id, mhtml_data_size);
}
void MHTMLGenerationManager::CreateFile(
int job_id, const base::FilePath& file_path,
base::ProcessHandle renderer_process) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
base::PlatformFile browser_file = base::CreatePlatformFile(file_path,
base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE,
NULL, NULL);
if (browser_file == base::kInvalidPlatformFileValue) {
LOG(ERROR) << "Failed to create file to save MHTML at: " <<
file_path.value();
}
IPC::PlatformFileForTransit renderer_file =
IPC::GetFileHandleForProcess(browser_file, renderer_process, false);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&MHTMLGenerationManager::FileCreated, base::Unretained(this),
job_id, browser_file, renderer_file));
}
void MHTMLGenerationManager::FileCreated(int job_id,
base::PlatformFile browser_file,
IPC::PlatformFileForTransit renderer_file) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (browser_file == base::kInvalidPlatformFileValue) {
LOG(ERROR) << "Failed to create file";
JobFinished(job_id, -1);
return;
}
IDToJobMap::iterator iter = id_to_job_.find(job_id);
if (iter == id_to_job_.end()) {
NOTREACHED();
return;
}
Job& job = iter->second;
job.browser_file = browser_file;
job.renderer_file = renderer_file;
RenderViewHostImpl* rvh = RenderViewHostImpl::FromID(
job.process_id, job.routing_id);
if (!rvh) {
// The contents went away.
JobFinished(job_id, -1);
return;
}
rvh->Send(new ViewMsg_SavePageAsMHTML(rvh->GetRoutingID(), job_id,
renderer_file));
}
void MHTMLGenerationManager::JobFinished(int job_id, int64 file_size) {
IDToJobMap::iterator iter = id_to_job_.find(job_id);
if (iter == id_to_job_.end()) {
NOTREACHED();
return;
}
Job& job = iter->second;
job.callback.Run(job.file_path, file_size);
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&MHTMLGenerationManager::CloseFile, base::Unretained(this),
job.browser_file));
id_to_job_.erase(job_id);
}
void MHTMLGenerationManager::CloseFile(base::PlatformFile file) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
base::ClosePlatformFile(file);
}
void MHTMLGenerationManager::Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) {
DCHECK(type == NOTIFICATION_RENDERER_PROCESS_TERMINATED);
RenderProcessHost* host = Source<RenderProcessHost>(source).ptr();
registrar_.Remove(
this,
NOTIFICATION_RENDERER_PROCESS_TERMINATED,
source);
std::set<int> job_to_delete;
for (IDToJobMap::iterator it = id_to_job_.begin(); it != id_to_job_.end();
++it) {
if (it->second.process_id == host->GetID())
job_to_delete.insert(it->first);
}
for (std::set<int>::iterator it = job_to_delete.begin();
it != job_to_delete.end();
++it) {
JobFinished(*it, -1);
}
}
} // namespace content