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