page_capture_api.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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
22using content::BrowserThread;
23using content::ChildProcessSecurityPolicy;
24using content::WebContents;
25using extensions::PageCaptureSaveAsMHTMLFunction;
26using webkit_blob::ShareableFileReference;
27
28namespace SaveAsMHTML = extensions::api::page_capture::SaveAsMHTML;
29
30namespace {
31
32const char kFileTooBigError[] = "The MHTML file generated is too big.";
33const char kMHTMLGenerationFailedError[] = "Failed to generate MHTML.";
34const char kTemporaryFileError[] = "Failed to create a temporary file.";
35const char kTabClosedError[] = "Cannot find the tab for thie request.";
36
37}  // namespace
38
39static PageCaptureSaveAsMHTMLFunction::TestDelegate* test_delegate_ = NULL;
40
41PageCaptureSaveAsMHTMLFunction::PageCaptureSaveAsMHTMLFunction() {
42}
43
44PageCaptureSaveAsMHTMLFunction::~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
53void PageCaptureSaveAsMHTMLFunction::SetTestDelegate(TestDelegate* delegate) {
54  test_delegate_ = delegate;
55}
56
57bool 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
69bool 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
91void 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
100void 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
138void 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
153void 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
163void 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
188WebContents* 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