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