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.
5 #include "chrome/browser/extensions/api/page_capture/page_capture_api.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_frame_host.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/browser/web_contents.h"
21 #include "extensions/common/extension_messages.h"
23 using content::BrowserThread
;
24 using content::ChildProcessSecurityPolicy
;
25 using content::WebContents
;
26 using extensions::PageCaptureSaveAsMHTMLFunction
;
27 using storage::ShareableFileReference
;
29 namespace SaveAsMHTML
= extensions::api::page_capture::SaveAsMHTML
;
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.";
40 static PageCaptureSaveAsMHTMLFunction::TestDelegate
* test_delegate_
= NULL
;
42 PageCaptureSaveAsMHTMLFunction::PageCaptureSaveAsMHTMLFunction() {
45 PageCaptureSaveAsMHTMLFunction::~PageCaptureSaveAsMHTMLFunction() {
46 if (mhtml_file_
.get()) {
47 storage::ShareableFileReference
* to_release
= mhtml_file_
.get();
50 BrowserThread::ReleaseSoon(BrowserThread::IO
, FROM_HERE
, to_release
);
54 void PageCaptureSaveAsMHTMLFunction::SetTestDelegate(TestDelegate
* delegate
) {
55 test_delegate_
= delegate
;
58 bool PageCaptureSaveAsMHTMLFunction::RunAsync() {
59 params_
= SaveAsMHTML::Params::Create(*args_
);
60 EXTENSION_FUNCTION_VALIDATE(params_
.get());
62 AddRef(); // Balanced in ReturnFailure/ReturnSuccess()
64 BrowserThread::PostTask(
65 BrowserThread::FILE, FROM_HERE
,
66 base::Bind(&PageCaptureSaveAsMHTMLFunction::CreateTemporaryFile
, this));
70 bool PageCaptureSaveAsMHTMLFunction::OnMessageReceived(
71 const IPC::Message
& message
) {
72 if (message
.type() != ExtensionHostMsg_ResponseAck::ID
)
75 int message_request_id
;
76 base::PickleIterator
iter(message
);
77 if (!iter
.ReadInt(&message_request_id
)) {
78 NOTREACHED() << "malformed extension message";
82 if (message_request_id
!= request_id())
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()
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,
101 void PageCaptureSaveAsMHTMLFunction::TemporaryFileCreated(bool success
) {
102 if (BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
104 // Setup a ShareableFileReference so the temporary file gets deleted
105 // once it is no longer used.
106 mhtml_file_
= ShareableFileReference::GetOrCreate(
108 ShareableFileReference::DELETE_ON_FINAL_RELEASE
,
109 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)
112 BrowserThread::PostTask(
113 BrowserThread::UI
, FROM_HERE
,
114 base::Bind(&PageCaptureSaveAsMHTMLFunction::TemporaryFileCreated
, this,
119 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
121 ReturnFailure(kTemporaryFileError
);
126 test_delegate_
->OnTemporaryFileCreated(mhtml_path_
);
128 WebContents
* web_contents
= GetWebContents();
130 ReturnFailure(kTabClosedError
);
134 web_contents
->GenerateMHTML(
136 base::Bind(&PageCaptureSaveAsMHTMLFunction::MHTMLGenerated
, this));
139 void PageCaptureSaveAsMHTMLFunction::MHTMLGenerated(
140 int64 mhtml_file_size
) {
141 if (mhtml_file_size
<= 0) {
142 ReturnFailure(kMHTMLGenerationFailedError
);
146 if (mhtml_file_size
> std::numeric_limits
<int>::max()) {
147 ReturnFailure(kFileTooBigError
);
151 ReturnSuccess(mhtml_file_size
);
154 void PageCaptureSaveAsMHTMLFunction::ReturnFailure(const std::string
& error
) {
155 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
161 Release(); // Balanced in Run()
164 void PageCaptureSaveAsMHTMLFunction::ReturnSuccess(int64 file_size
) {
165 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
167 WebContents
* web_contents
= GetWebContents();
168 if (!web_contents
|| !render_frame_host()) {
169 ReturnFailure(kTabClosedError
);
173 int child_id
= render_frame_host()->GetProcess()->GetID();
174 ChildProcessSecurityPolicy::GetInstance()->GrantReadFile(
175 child_id
, mhtml_path_
);
177 base::DictionaryValue
* dict
= new base::DictionaryValue();
179 dict
->SetString("mhtmlFilePath", mhtml_path_
.value());
180 dict
->SetInteger("mhtmlFileLength", file_size
);
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
189 WebContents
* PageCaptureSaveAsMHTMLFunction::GetWebContents() {
190 Browser
* browser
= NULL
;
191 content::WebContents
* web_contents
= NULL
;
193 if (!ExtensionTabUtil::GetTabById(params_
->details
.tab_id
,