// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/printing/print_job_controller.h"
#include <memory>
#include <string>
#include "base/check_op.h"
#include "base/containers/flat_set.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/printing/print_job.h"
#include "chrome/browser/printing/printer_query.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/global_routing_id.h"
#include "printing/metafile_skia.h"
#include "printing/print_settings.h"
#include "printing/printed_document.h"
namespace printing {
namespace {
void OnPrintSettingsApplied(scoped_refptr<PrintJob> print_job,
std::unique_ptr<MetafileSkia> pdf,
std::unique_ptr<PrinterQuery> query,
uint32_t page_count,
PrintJob::Source source,
const std::string& source_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
CHECK_GT(page_count, 0U);
std::u16string title = query->settings().title();
print_job->Initialize(std::move(query), title, page_count);
print_job->SetSource(source, source_id);
print_job->document()->SetDocument(std::move(pdf));
print_job->StartPrinting();
}
} // namespace
class PendingJob;
// Keeps track of pending jobs and removes them from the storage once
// processed.
class PrintJobController::PendingJobStorage {
public:
PendingJobStorage() = default;
~PendingJobStorage() = default;
PendingJobStorage(const PendingJobStorage&) = delete;
PendingJobStorage& operator=(const PendingJobStorage&) = delete;
void StartWatchingPrintJob(scoped_refptr<PrintJob> print_job,
PrintJobCreatedCallback callback);
void DeletePendingJobPlease(PendingJob* pending_job);
private:
using PendingJobs =
base::flat_set<std::unique_ptr<PendingJob>, base::UniquePtrComparator>;
PendingJobs pending_jobs_;
};
// Observes the given `print_job` and invokes `callback` once the job signals
// OnDocDone() or OnFailed().
class PendingJob : public PrintJob::Observer {
public:
PendingJob(PrintJobController::PendingJobStorage* storage,
scoped_refptr<PrintJob> print_job,
PrintJobController::PrintJobCreatedCallback callback);
~PendingJob() override;
PendingJob(const PendingJob&) = delete;
PendingJob& operator=(const PendingJob&) = delete;
void OnDocDone(int job_id, PrintedDocument* document) override;
void OnFailed() override;
private:
// `storage_` owns `this`.
const raw_ref<PrintJobController::PendingJobStorage> storage_;
scoped_refptr<PrintJob> print_job_;
PrintJobController::PrintJobCreatedCallback callback_;
};
void PrintJobController::PendingJobStorage::StartWatchingPrintJob(
scoped_refptr<PrintJob> print_job,
PrintJobCreatedCallback callback) {
auto pending_job = std::make_unique<PendingJob>(this, std::move(print_job),
std::move(callback));
pending_jobs_.insert(std::move(pending_job));
}
void PrintJobController::PendingJobStorage::DeletePendingJobPlease(
PendingJob* pending_job) {
pending_jobs_.erase(pending_job);
}
PrintJobController::PrintJobController()
: pending_job_storage_(std::make_unique<PendingJobStorage>()) {}
PrintJobController::~PrintJobController() = default;
void PrintJobController::StartWatchingPrintJob(
scoped_refptr<PrintJob> print_job,
PrintJobCreatedCallback callback) {
pending_job_storage_->StartWatchingPrintJob(std::move(print_job),
std::move(callback));
}
PendingJob::PendingJob(PrintJobController::PendingJobStorage* storage,
scoped_refptr<PrintJob> print_job,
PrintJobController::PrintJobCreatedCallback callback)
: storage_(*storage),
print_job_(std::move(print_job)),
callback_(std::move(callback)) {
print_job_->AddObserver(*this);
}
PendingJob::~PendingJob() {
print_job_->RemoveObserver(*this);
}
void PendingJob::OnDocDone(int job_id, PrintedDocument* document) {
auto document_ref = raw_ref<PrintedDocument>::from_ptr(document);
std::move(callback_).Run(PrintJobCreatedInfo{job_id, document_ref});
storage_->DeletePendingJobPlease(this);
}
void PendingJob::OnFailed() {
std::move(callback_).Run(std::nullopt);
storage_->DeletePendingJobPlease(this);
}
void PrintJobController::CreatePrintJob(std::unique_ptr<MetafileSkia> pdf,
std::unique_ptr<PrintSettings> settings,
uint32_t page_count,
PrintJob::Source source,
const std::string& source_id,
PrintJobCreatedCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto print_job =
base::MakeRefCounted<PrintJob>(g_browser_process->print_job_manager());
StartWatchingPrintJob(print_job, std::move(callback));
auto query = PrinterQuery::Create(content::GlobalRenderFrameHostId());
auto* query_ptr = query.get();
query_ptr->SetSettingsFromPOD(
std::move(settings),
base::BindOnce(&OnPrintSettingsApplied, print_job, std::move(pdf),
std::move(query), page_count, source, source_id));
}
} // namespace printing