blob: 9ed3d43365ebb966710dcae22b1f68089fab897c [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/devtools/protocol/devtools_mhtml_helper.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/task/thread_pool.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/mhtml_generation_params.h"
#include "storage/browser/blob/shareable_file_reference.h"
namespace content {
namespace protocol {
namespace {
void ClearFileReferenceOnIOThread(
scoped_refptr<storage::ShareableFileReference>) {}
} // namespace
DevToolsMHTMLHelper::DevToolsMHTMLHelper(
const WebContents::Getter& web_contents_getter,
std::unique_ptr<PageHandler::CaptureSnapshotCallback> callback)
: web_contents_getter_(web_contents_getter),
callback_(std::move(callback)) {}
DevToolsMHTMLHelper::~DevToolsMHTMLHelper() {
if (mhtml_file_.get()) {
GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&ClearFileReferenceOnIOThread, std::move(mhtml_file_)));
}
}
// static
void DevToolsMHTMLHelper::Capture(
const WebContents::Getter& web_contents_getter,
std::unique_ptr<PageHandler::CaptureSnapshotCallback> callback) {
scoped_refptr<DevToolsMHTMLHelper> helper =
new DevToolsMHTMLHelper(web_contents_getter, std::move(callback));
base::ThreadPool::PostTask(
FROM_HERE,
{// Requires IO.
base::MayBlock(),
// TaskShutdownBehavior: use SKIP_ON_SHUTDOWN so that the helper's
// fields do not suddenly become invalid.
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&DevToolsMHTMLHelper::CreateTemporaryFile, helper));
}
void DevToolsMHTMLHelper::CreateTemporaryFile() {
if (!base::CreateTemporaryFile(&mhtml_snapshot_path_)) {
ReportFailure("Unable to create temporary file");
return;
}
GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&DevToolsMHTMLHelper::TemporaryFileCreatedOnIO, this));
}
void DevToolsMHTMLHelper::TemporaryFileCreatedOnIO() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// Setup a ShareableFileReference so the temporary file gets deleted
// once it is no longer used.
mhtml_file_ = storage::ShareableFileReference::GetOrCreate(
mhtml_snapshot_path_,
storage::ShareableFileReference::DELETE_ON_FINAL_RELEASE,
base::ThreadPool::CreateSequencedTaskRunner(
{// Requires IO.
base::MayBlock(),
// Because we are using DELETE_ON_FINAL_RELEASE here, the
// storage::ScopedFile inside ShareableFileReference requires
// a shutdown blocking task runner to ensure that its deletion
// task runs.
base::TaskShutdownBehavior::BLOCK_SHUTDOWN})
.get());
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&DevToolsMHTMLHelper::TemporaryFileCreatedOnUI, this));
}
void DevToolsMHTMLHelper::TemporaryFileCreatedOnUI() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
WebContents* web_contents = web_contents_getter_.Run();
if (!web_contents) {
ReportFailure("No web contents");
return;
}
web_contents->GenerateMHTML(
content::MHTMLGenerationParams(mhtml_snapshot_path_),
base::BindOnce(&DevToolsMHTMLHelper::MHTMLGeneratedOnUI, this));
}
void DevToolsMHTMLHelper::MHTMLGeneratedOnUI(int64_t mhtml_file_size) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (mhtml_file_size <= 0 ||
mhtml_file_size > std::numeric_limits<int>::max()) {
ReportFailure("Failed to generate MHTML");
return;
}
base::ThreadPool::PostTask(
FROM_HERE,
{// Requires IO.
base::MayBlock(),
// TaskShutdownBehavior: use SKIP_ON_SHUTDOWN so that the
// helper's fields do not suddenly become invalid.
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&DevToolsMHTMLHelper::ReadMHTML, this));
}
void DevToolsMHTMLHelper::ReadMHTML() {
std::string buffer;
if (!base::ReadFileToString(mhtml_snapshot_path_, &buffer)) {
LOG(ERROR) << "Failed to read " << mhtml_snapshot_path_;
ReportFailure("Failed to read MHTML file");
return;
}
std::unique_ptr<std::string> buffer_ptr =
std::make_unique<std::string>(buffer);
ReportSuccess(std::move(buffer_ptr));
}
void DevToolsMHTMLHelper::ReportFailure(const std::string& message) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&DevToolsMHTMLHelper::ReportFailure, this, message));
return;
}
if (message.empty())
callback_->sendFailure(Response::InternalError());
else
callback_->sendFailure(Response::ServerError(message));
}
void DevToolsMHTMLHelper::ReportSuccess(
std::unique_ptr<std::string> mhtml_snapshot) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&DevToolsMHTMLHelper::ReportSuccess, this,
std::move(mhtml_snapshot)));
return;
}
callback_->sendSuccess(*mhtml_snapshot);
}
} // namespace protocol
} // namespace content