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::SaveMHTML(WebContents* web_contents,
     40                                        const base::FilePath& file,
     41                                        const GenerateMHTMLCallback& callback) {
     42   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     43 
     44   int job_id = NewJob(web_contents, callback);
     45 
     46   base::ProcessHandle renderer_process =
     47       web_contents->GetRenderProcessHost()->GetHandle();
     48   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
     49       base::Bind(&MHTMLGenerationManager::CreateFile, base::Unretained(this),
     50                  job_id, file, renderer_process));
     51 }
     52 
     53 void MHTMLGenerationManager::StreamMHTML(
     54     WebContents* web_contents,
     55     const base::PlatformFile browser_file,
     56     const GenerateMHTMLCallback& callback) {
     57   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     58 
     59   int job_id = NewJob(web_contents, callback);
     60 
     61   base::ProcessHandle renderer_process =
     62       web_contents->GetRenderProcessHost()->GetHandle();
     63   IPC::PlatformFileForTransit renderer_file =
     64       IPC::GetFileHandleForProcess(browser_file, renderer_process, false);
     65 
     66   FileHandleAvailable(job_id, browser_file, renderer_file);
     67 }
     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(
     90       BrowserThread::UI,
     91       FROM_HERE,
     92       base::Bind(&MHTMLGenerationManager::FileHandleAvailable,
     93                  base::Unretained(this),
     94                  job_id,
     95                  browser_file,
     96                  renderer_file));
     97 }
     98 
     99 void MHTMLGenerationManager::FileHandleAvailable(int job_id,
    100     base::PlatformFile browser_file,
    101     IPC::PlatformFileForTransit renderer_file) {
    102   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    103   if (browser_file == base::kInvalidPlatformFileValue) {
    104     LOG(ERROR) << "Failed to create file";
    105     JobFinished(job_id, -1);
    106     return;
    107   }
    108 
    109   IDToJobMap::iterator iter = id_to_job_.find(job_id);
    110   if (iter == id_to_job_.end()) {
    111     NOTREACHED();
    112     return;
    113   }
    114 
    115   Job& job = iter->second;
    116   job.browser_file = browser_file;
    117   job.renderer_file = renderer_file;
    118 
    119   RenderViewHostImpl* rvh = RenderViewHostImpl::FromID(
    120       job.process_id, job.routing_id);
    121   if (!rvh) {
    122     // The contents went away.
    123     JobFinished(job_id, -1);
    124     return;
    125   }
    126 
    127   rvh->Send(new ViewMsg_SavePageAsMHTML(rvh->GetRoutingID(), job_id,
    128                                         renderer_file));
    129 }
    130 
    131 void MHTMLGenerationManager::JobFinished(int job_id, int64 file_size) {
    132   IDToJobMap::iterator iter = id_to_job_.find(job_id);
    133   if (iter == id_to_job_.end()) {
    134     NOTREACHED();
    135     return;
    136   }
    137 
    138   Job& job = iter->second;
    139   job.callback.Run(file_size);
    140 
    141   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
    142       base::Bind(&MHTMLGenerationManager::CloseFile, base::Unretained(this),
    143                  job.browser_file));
    144 
    145   id_to_job_.erase(job_id);
    146 }
    147 
    148 void MHTMLGenerationManager::CloseFile(base::PlatformFile file) {
    149   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    150   base::ClosePlatformFile(file);
    151 }
    152 
    153 int MHTMLGenerationManager::NewJob(WebContents* web_contents,
    154                                    const GenerateMHTMLCallback& callback) {
    155   static int id_counter = 0;
    156   int job_id = id_counter++;
    157   Job& job = id_to_job_[job_id];
    158   job.process_id = web_contents->GetRenderProcessHost()->GetID();
    159   job.routing_id = web_contents->GetRenderViewHost()->GetRoutingID();
    160   job.callback = callback;
    161   if (!registrar_.IsRegistered(
    162           this,
    163           NOTIFICATION_RENDERER_PROCESS_TERMINATED,
    164           Source<RenderProcessHost>(web_contents->GetRenderProcessHost()))) {
    165     registrar_.Add(
    166         this,
    167         NOTIFICATION_RENDERER_PROCESS_TERMINATED,
    168         Source<RenderProcessHost>(web_contents->GetRenderProcessHost()));
    169   }
    170   return job_id;
    171 }
    172 
    173 void MHTMLGenerationManager::Observe(int type,
    174                                      const NotificationSource& source,
    175                                      const NotificationDetails& details) {
    176   DCHECK(type == NOTIFICATION_RENDERER_PROCESS_TERMINATED);
    177   RenderProcessHost* host = Source<RenderProcessHost>(source).ptr();
    178   registrar_.Remove(
    179       this,
    180       NOTIFICATION_RENDERER_PROCESS_TERMINATED,
    181       source);
    182   std::set<int> job_to_delete;
    183   for (IDToJobMap::iterator it = id_to_job_.begin(); it != id_to_job_.end();
    184        ++it) {
    185     if (it->second.process_id == host->GetID())
    186       job_to_delete.insert(it->first);
    187   }
    188   for (std::set<int>::iterator it = job_to_delete.begin();
    189        it != job_to_delete.end();
    190        ++it) {
    191     JobFinished(*it, -1);
    192   }
    193 }
    194 
    195 }  // namespace content
    196