blob: f2159ce2b9f35d2f50705a52aaf27afd09860a16 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/offline_pages/core/offline_page_archiver.h"
#include <errno.h>
#include <utility>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task_runner_util.h"
#include "components/offline_pages/core/model/offline_page_model_taskified.h"
#include "components/offline_pages/core/model/offline_page_model_utils.h"
#include "components/offline_pages/core/offline_store_utils.h"
#include "components/offline_pages/core/system_download_manager.h"
namespace offline_pages {
namespace {
const char* kMoveFileFailureReason =
"OfflinePages.PublishArchive.MoveFileFailureReason";
const int kSourceMissing = 0;
const int kDestinationMissing = 1;
using offline_pages::SavePageResult;
// Helper function to do the move and register synchronously. Make sure this is
// called from a background thread.
PublishArchiveResult MoveAndRegisterArchive(
const offline_pages::OfflinePageItem& offline_page,
const base::FilePath& publish_directory,
offline_pages::SystemDownloadManager* download_manager) {
PublishArchiveResult archive_result;
// Calculate the new file name.
base::FilePath new_file_path =
offline_pages::model_utils::GenerateUniqueFilenameForOfflinePage(
offline_page.title, offline_page.url, publish_directory);
// Create the destination directory if it does not already exist.
if (!publish_directory.empty() && !base::DirectoryExists(publish_directory)) {
base::File::Error file_error;
if (!base::CreateDirectoryAndGetError(publish_directory, &file_error)) {
UMA_HISTOGRAM_ENUMERATION(
"OfflinePages.PublishArchive.CreateDirectoryError", -file_error,
-base::File::Error::FILE_ERROR_MAX);
}
}
// Move the file.
bool moved = base::Move(offline_page.file_path, new_file_path);
if (!moved) {
archive_result.move_result = SavePageResult::FILE_MOVE_FAILED;
DVPLOG(0) << "OfflinePage publishing file move failure " << __func__;
base::UmaHistogramSparse("OfflinePages.PublishArchive.MoveFileError",
errno);
if (!base::PathExists(offline_page.file_path)) {
DVLOG(0) << "Can't copy from non-existent path, from "
<< offline_page.file_path << " " << __func__;
base::UmaHistogramBoolean(kMoveFileFailureReason, kSourceMissing);
}
if (!base::PathExists(publish_directory)) {
DVLOG(0) << "Target directory does not exist, " << publish_directory
<< " " << __func__;
base::UmaHistogramBoolean(kMoveFileFailureReason, kDestinationMissing);
}
return archive_result;
}
// Tell the download manager about our file, get back an id.
if (!download_manager->IsDownloadManagerInstalled()) {
archive_result.move_result = SavePageResult::ADD_TO_DOWNLOAD_MANAGER_FAILED;
return archive_result;
}
// TODO(petewil): Handle empty page title.
std::string page_title = base::UTF16ToUTF8(offline_page.title);
// We use the title for a description, since the add to the download manager
// fails without a description, and we don't have anything better to use.
int64_t download_id = download_manager->AddCompletedDownload(
page_title, page_title,
offline_pages::store_utils::ToDatabaseFilePath(new_file_path),
offline_page.file_size, offline_page.url.spec(), std::string());
if (download_id == 0LL) {
archive_result.move_result = SavePageResult::ADD_TO_DOWNLOAD_MANAGER_FAILED;
return archive_result;
}
// Put results into the result object.
archive_result.move_result = SavePageResult::SUCCESS;
archive_result.new_file_path = new_file_path;
archive_result.download_id = download_id;
return archive_result;
}
} // namespace
OfflinePageArchiver::CreateArchiveParams::CreateArchiveParams(
const std::string& name_space)
: name_space(name_space) {}
void OfflinePageArchiver::PublishArchive(
const OfflinePageItem& offline_page,
const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
const base::FilePath& publish_directory,
SystemDownloadManager* download_manager,
PublishArchiveDoneCallback publish_done_callback) {
// Note: once the |publish_done_callback| is invoked it is very likely that
// this instance will be destroyed. So all parameters sent to it must not be
// bound to the lifetime on this.
base::PostTaskAndReplyWithResult(
background_task_runner.get(), FROM_HERE,
base::BindOnce(&MoveAndRegisterArchive, offline_page, publish_directory,
download_manager),
base::BindOnce(std::move(publish_done_callback), offline_page));
}
} // namespace offline_pages