Home | History | Annotate | Download | only in page_capture
      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 "chrome/browser/extensions/api/page_capture/page_capture_api.h"
      6 
      7 #include <limits>
      8 
      9 #include "base/bind.h"
     10 #include "base/file_util.h"
     11 #include "chrome/browser/browser_process.h"
     12 #include "chrome/browser/extensions/extension_tab_util.h"
     13 #include "chrome/common/extensions/extension_messages.h"
     14 #include "content/public/browser/child_process_security_policy.h"
     15 #include "content/public/browser/notification_details.h"
     16 #include "content/public/browser/notification_source.h"
     17 #include "content/public/browser/notification_types.h"
     18 #include "content/public/browser/render_process_host.h"
     19 #include "content/public/browser/render_view_host.h"
     20 #include "content/public/browser/web_contents.h"
     21 
     22 using content::BrowserThread;
     23 using content::ChildProcessSecurityPolicy;
     24 using content::WebContents;
     25 using extensions::PageCaptureSaveAsMHTMLFunction;
     26 using webkit_blob::ShareableFileReference;
     27 
     28 namespace SaveAsMHTML = extensions::api::page_capture::SaveAsMHTML;
     29 
     30 namespace {
     31 
     32 const char kFileTooBigError[] = "The MHTML file generated is too big.";
     33 const char kMHTMLGenerationFailedError[] = "Failed to generate MHTML.";
     34 const char kTemporaryFileError[] = "Failed to create a temporary file.";
     35 const char kTabClosedError[] = "Cannot find the tab for thie request.";
     36 
     37 }  // namespace
     38 
     39 static PageCaptureSaveAsMHTMLFunction::TestDelegate* test_delegate_ = NULL;
     40 
     41 PageCaptureSaveAsMHTMLFunction::PageCaptureSaveAsMHTMLFunction() {
     42 }
     43 
     44 PageCaptureSaveAsMHTMLFunction::~PageCaptureSaveAsMHTMLFunction() {
     45   if (mhtml_file_.get()) {
     46     webkit_blob::ShareableFileReference* to_release = mhtml_file_.get();
     47     to_release->AddRef();
     48     mhtml_file_ = NULL;
     49     BrowserThread::ReleaseSoon(BrowserThread::IO, FROM_HERE, to_release);
     50   }
     51 }
     52 
     53 void PageCaptureSaveAsMHTMLFunction::SetTestDelegate(TestDelegate* delegate) {
     54   test_delegate_ = delegate;
     55 }
     56 
     57 bool PageCaptureSaveAsMHTMLFunction::RunImpl() {
     58   params_ = SaveAsMHTML::Params::Create(*args_);
     59   EXTENSION_FUNCTION_VALIDATE(params_.get());
     60 
     61   AddRef();  // Balanced in ReturnFailure/ReturnSuccess()
     62 
     63   BrowserThread::PostTask(
     64       BrowserThread::FILE, FROM_HERE,
     65       base::Bind(&PageCaptureSaveAsMHTMLFunction::CreateTemporaryFile, this));
     66   return true;
     67 }
     68 
     69 bool PageCaptureSaveAsMHTMLFunction::OnMessageReceivedFromRenderView(
     70     const IPC::Message& message) {
     71   if (message.type() != ExtensionHostMsg_ResponseAck::ID)
     72     return false;
     73 
     74   int message_request_id;
     75   PickleIterator iter(message);
     76   if (!message.ReadInt(&iter, &message_request_id)) {
     77     NOTREACHED() << "malformed extension message";
     78     return true;
     79   }
     80 
     81   if (message_request_id != request_id())
     82     return false;
     83 
     84   // The extension process has processed the response and has created a
     85   // reference to the blob, it is safe for us to go away.
     86   Release();  // Balanced in Run()
     87 
     88   return true;
     89 }
     90 
     91 void PageCaptureSaveAsMHTMLFunction::CreateTemporaryFile() {
     92   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     93   bool success = file_util::CreateTemporaryFile(&mhtml_path_);
     94   BrowserThread::PostTask(
     95       BrowserThread::IO, FROM_HERE,
     96       base::Bind(&PageCaptureSaveAsMHTMLFunction::TemporaryFileCreated, this,
     97                  success));
     98 }
     99 
    100 void PageCaptureSaveAsMHTMLFunction::TemporaryFileCreated(bool success) {
    101   if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
    102     if (success) {
    103       // Setup a ShareableFileReference so the temporary file gets deleted
    104       // once it is no longer used.
    105       mhtml_file_ = ShareableFileReference::GetOrCreate(
    106           mhtml_path_,
    107           ShareableFileReference::DELETE_ON_FINAL_RELEASE,
    108           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)
    109               .get());
    110     }
    111     BrowserThread::PostTask(
    112         BrowserThread::UI, FROM_HERE,
    113         base::Bind(&PageCaptureSaveAsMHTMLFunction::TemporaryFileCreated, this,
    114                    success));
    115     return;
    116   }
    117 
    118   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    119   if (!success) {
    120     ReturnFailure(kTemporaryFileError);
    121     return;
    122   }
    123 
    124   if (test_delegate_)
    125     test_delegate_->OnTemporaryFileCreated(mhtml_path_);
    126 
    127   WebContents* web_contents = GetWebContents();
    128   if (!web_contents) {
    129     ReturnFailure(kTabClosedError);
    130     return;
    131   }
    132 
    133   web_contents->GenerateMHTML(
    134       mhtml_path_,
    135       base::Bind(&PageCaptureSaveAsMHTMLFunction::MHTMLGenerated, this));
    136 }
    137 
    138 void PageCaptureSaveAsMHTMLFunction::MHTMLGenerated(
    139     const base::FilePath& file_path,
    140     int64 mhtml_file_size) {
    141   DCHECK(mhtml_path_ == file_path);
    142   if (mhtml_file_size <= 0) {
    143     ReturnFailure(kMHTMLGenerationFailedError);
    144     return;
    145   }
    146 
    147   if (mhtml_file_size > std::numeric_limits<int>::max()) {
    148     ReturnFailure(kFileTooBigError);
    149     return;
    150   }
    151 
    152   ReturnSuccess(mhtml_file_size);
    153 }
    154 
    155 void PageCaptureSaveAsMHTMLFunction::ReturnFailure(const std::string& error) {
    156   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    157 
    158   error_ = error;
    159 
    160   SendResponse(false);
    161 
    162   Release();  // Balanced in Run()
    163 }
    164 
    165 void PageCaptureSaveAsMHTMLFunction::ReturnSuccess(int64 file_size) {
    166   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    167 
    168   WebContents* web_contents = GetWebContents();
    169   if (!web_contents || !render_view_host()) {
    170     ReturnFailure(kTabClosedError);
    171     return;
    172   }
    173 
    174   int child_id = render_view_host()->GetProcess()->GetID();
    175   ChildProcessSecurityPolicy::GetInstance()->GrantReadFile(
    176       child_id, mhtml_path_);
    177 
    178   base::DictionaryValue* dict = new base::DictionaryValue();
    179   SetResult(dict);
    180   dict->SetString("mhtmlFilePath", mhtml_path_.value());
    181   dict->SetInteger("mhtmlFileLength", file_size);
    182 
    183   SendResponse(true);
    184 
    185   // Note that we'll wait for a response ack message received in
    186   // OnMessageReceivedFromRenderView before we call Release() (to prevent the
    187   // blob file from being deleted).
    188 }
    189 
    190 WebContents* PageCaptureSaveAsMHTMLFunction::GetWebContents() {
    191   Browser* browser = NULL;
    192   content::WebContents* web_contents = NULL;
    193 
    194   if (!ExtensionTabUtil::GetTabById(params_->details.tab_id, profile(),
    195                                     include_incognito(), &browser, NULL,
    196                                     &web_contents, NULL)) {
    197     return NULL;
    198   }
    199   return web_contents;
    200 }
    201