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
PageCaptureSaveAsMHTMLFunction()41 PageCaptureSaveAsMHTMLFunction::PageCaptureSaveAsMHTMLFunction() {
42 }
43
~PageCaptureSaveAsMHTMLFunction()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
SetTestDelegate(TestDelegate * delegate)53 void PageCaptureSaveAsMHTMLFunction::SetTestDelegate(TestDelegate* delegate) {
54 test_delegate_ = delegate;
55 }
56
RunImpl()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
OnMessageReceivedFromRenderView(const IPC::Message & message)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
CreateTemporaryFile()91 void PageCaptureSaveAsMHTMLFunction::CreateTemporaryFile() {
92 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
93 bool success = base::CreateTemporaryFile(&mhtml_path_);
94 BrowserThread::PostTask(
95 BrowserThread::IO, FROM_HERE,
96 base::Bind(&PageCaptureSaveAsMHTMLFunction::TemporaryFileCreated, this,
97 success));
98 }
99
TemporaryFileCreated(bool success)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
MHTMLGenerated(int64 mhtml_file_size)138 void PageCaptureSaveAsMHTMLFunction::MHTMLGenerated(
139 int64 mhtml_file_size) {
140 if (mhtml_file_size <= 0) {
141 ReturnFailure(kMHTMLGenerationFailedError);
142 return;
143 }
144
145 if (mhtml_file_size > std::numeric_limits<int>::max()) {
146 ReturnFailure(kFileTooBigError);
147 return;
148 }
149
150 ReturnSuccess(mhtml_file_size);
151 }
152
ReturnFailure(const std::string & error)153 void PageCaptureSaveAsMHTMLFunction::ReturnFailure(const std::string& error) {
154 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
155
156 error_ = error;
157
158 SendResponse(false);
159
160 Release(); // Balanced in Run()
161 }
162
ReturnSuccess(int64 file_size)163 void PageCaptureSaveAsMHTMLFunction::ReturnSuccess(int64 file_size) {
164 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
165
166 WebContents* web_contents = GetWebContents();
167 if (!web_contents || !render_view_host()) {
168 ReturnFailure(kTabClosedError);
169 return;
170 }
171
172 int child_id = render_view_host()->GetProcess()->GetID();
173 ChildProcessSecurityPolicy::GetInstance()->GrantReadFile(
174 child_id, mhtml_path_);
175
176 base::DictionaryValue* dict = new base::DictionaryValue();
177 SetResult(dict);
178 dict->SetString("mhtmlFilePath", mhtml_path_.value());
179 dict->SetInteger("mhtmlFileLength", file_size);
180
181 SendResponse(true);
182
183 // Note that we'll wait for a response ack message received in
184 // OnMessageReceivedFromRenderView before we call Release() (to prevent the
185 // blob file from being deleted).
186 }
187
GetWebContents()188 WebContents* PageCaptureSaveAsMHTMLFunction::GetWebContents() {
189 Browser* browser = NULL;
190 content::WebContents* web_contents = NULL;
191
192 if (!ExtensionTabUtil::GetTabById(params_->details.tab_id,
193 GetProfile(),
194 include_incognito(),
195 &browser,
196 NULL,
197 &web_contents,
198 NULL)) {
199 return NULL;
200 }
201 return web_contents;
202 }
203