Home | History | Annotate | Download | only in download
      1 // Copyright (c) 2012 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 "content/browser/download/mhtml_generation_manager.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/platform_file.h"
      9 #include "content/browser/renderer_host/render_process_host_impl.h"
     10 #include "content/browser/renderer_host/render_view_host_impl.h"
     11 #include "content/public/browser/browser_thread.h"
     12 #include "content/public/browser/notification_service.h"
     13 #include "content/public/browser/web_contents.h"
     14 #include "content/common/view_messages.h"
     15 #include "content/public/browser/notification_types.h"
     16 
     17 namespace content {
     18 
     19 MHTMLGenerationManager::Job::Job()
     20     : browser_file(base::kInvalidPlatformFileValue),
     21       renderer_file(IPC::InvalidPlatformFileForTransit()),
     22       process_id(-1),
     23       routing_id(-1) {
     24 }
     25 
     26 MHTMLGenerationManager::Job::~Job() {
     27 }
     28 
     29 MHTMLGenerationManager* MHTMLGenerationManager::GetInstance() {
     30   return Singleton<MHTMLGenerationManager>::get();
     31 }
     32 
     33 MHTMLGenerationManager::MHTMLGenerationManager() {
     34 }
     35 
     36 MHTMLGenerationManager::~MHTMLGenerationManager() {
     37 }
     38 
     39 void MHTMLGenerationManager::GenerateMHTML(
     40     WebContents* web_contents,
     41     const base::FilePath& file,
     42     const GenerateMHTMLCallback& callback) {
     43   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     44   static int id_counter = 0;
     45 
     46   int job_id = id_counter++;
     47   Job job;
     48   job.file_path = file;
     49   job.process_id = web_contents->GetRenderProcessHost()->GetID();
     50   job.routing_id = web_contents->GetRenderViewHost()->GetRoutingID();
     51   job.callback = callback;
     52   id_to_job_[job_id] = job;
     53   if (!registrar_.IsRegistered(
     54           this,
     55           NOTIFICATION_RENDERER_PROCESS_TERMINATED,
     56           Source<RenderProcessHost>(web_contents->GetRenderProcessHost()))) {
     57     registrar_.Add(
     58         this,
     59         NOTIFICATION_RENDERER_PROCESS_TERMINATED,
     60         Source<RenderProcessHost>(web_contents->GetRenderProcessHost()));
     61   }
     62 
     63   base::ProcessHandle renderer_process =
     64       web_contents->GetRenderProcessHost()->GetHandle();
     65   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
     66       base::Bind(&MHTMLGenerationManager::CreateFile, base::Unretained(this),
     67                  job_id, file, renderer_process));
     68 }
     69 
     70 void MHTMLGenerationManager::MHTMLGenerated(int job_id, int64 mhtml_data_size) {
     71   JobFinished(job_id, mhtml_data_size);
     72 }
     73 
     74 void MHTMLGenerationManager::CreateFile(
     75     int job_id, const base::FilePath& file_path,
     76     base::ProcessHandle renderer_process) {
     77   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     78   base::PlatformFile browser_file = base::CreatePlatformFile(file_path,
     79       base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE,
     80       NULL, NULL);
     81   if (browser_file == base::kInvalidPlatformFileValue) {
     82     LOG(ERROR) << "Failed to create file to save MHTML at: " <<
     83         file_path.value();
     84   }
     85 
     86   IPC::PlatformFileForTransit renderer_file =
     87       IPC::GetFileHandleForProcess(browser_file, renderer_process, false);
     88 
     89   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
     90       base::Bind(&MHTMLGenerationManager::FileCreated, base::Unretained(this),
     91                  job_id, browser_file, renderer_file));
     92 }
     93 
     94 void MHTMLGenerationManager::FileCreated(int job_id,
     95     base::PlatformFile browser_file,
     96     IPC::PlatformFileForTransit renderer_file) {
     97   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     98   if (browser_file == base::kInvalidPlatformFileValue) {
     99     LOG(ERROR) << "Failed to create file";
    100     JobFinished(job_id, -1);
    101     return;
    102   }
    103 
    104   IDToJobMap::iterator iter = id_to_job_.find(job_id);
    105   if (iter == id_to_job_.end()) {
    106     NOTREACHED();
    107     return;
    108   }
    109 
    110   Job& job = iter->second;
    111   job.browser_file = browser_file;
    112   job.renderer_file = renderer_file;
    113 
    114   RenderViewHostImpl* rvh = RenderViewHostImpl::FromID(
    115       job.process_id, job.routing_id);
    116   if (!rvh) {
    117     // The contents went away.
    118     JobFinished(job_id, -1);
    119     return;
    120   }
    121 
    122   rvh->Send(new ViewMsg_SavePageAsMHTML(rvh->GetRoutingID(), job_id,
    123                                         renderer_file));
    124 }
    125 
    126 void MHTMLGenerationManager::JobFinished(int job_id, int64 file_size) {
    127   IDToJobMap::iterator iter = id_to_job_.find(job_id);
    128   if (iter == id_to_job_.end()) {
    129     NOTREACHED();
    130     return;
    131   }
    132 
    133   Job& job = iter->second;
    134   job.callback.Run(job.file_path, file_size);
    135 
    136   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
    137       base::Bind(&MHTMLGenerationManager::CloseFile, base::Unretained(this),
    138                  job.browser_file));
    139 
    140   id_to_job_.erase(job_id);
    141 }
    142 
    143 void MHTMLGenerationManager::CloseFile(base::PlatformFile file) {
    144   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    145   base::ClosePlatformFile(file);
    146 }
    147 
    148 void MHTMLGenerationManager::Observe(int type,
    149                                      const NotificationSource& source,
    150                                      const NotificationDetails& details) {
    151   DCHECK(type == NOTIFICATION_RENDERER_PROCESS_TERMINATED);
    152   RenderProcessHost* host = Source<RenderProcessHost>(source).ptr();
    153   registrar_.Remove(
    154       this,
    155       NOTIFICATION_RENDERER_PROCESS_TERMINATED,
    156       source);
    157   std::set<int> job_to_delete;
    158   for (IDToJobMap::iterator it = id_to_job_.begin(); it != id_to_job_.end();
    159        ++it) {
    160     if (it->second.process_id == host->GetID())
    161       job_to_delete.insert(it->first);
    162   }
    163   for (std::set<int>::iterator it = job_to_delete.begin();
    164        it != job_to_delete.end();
    165        ++it) {
    166     JobFinished(*it, -1);
    167   }
    168 }
    169 
    170 }  // namespace content
    171