• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
PageCaptureSaveAsMHTMLFunction()42 PageCaptureSaveAsMHTMLFunction::PageCaptureSaveAsMHTMLFunction() {
43 }
44 
~PageCaptureSaveAsMHTMLFunction()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 
SetTestDelegate(TestDelegate * delegate)54 void PageCaptureSaveAsMHTMLFunction::SetTestDelegate(TestDelegate* delegate) {
55   test_delegate_ = delegate;
56 }
57 
RunAsync()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 
OnMessageReceived(const IPC::Message & message)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 
CreateTemporaryFile()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 
TemporaryFileCreated(bool success)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 
MHTMLGenerated(int64 mhtml_file_size)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 
ReturnFailure(const std::string & error)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 
ReturnSuccess(int64 file_size)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 
GetWebContents()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