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