Add service-based usage to start printing
Incorporate usage of StartPrinting() from within the Print Backend service utility for Windows, macOS, Linux, and ChromeOS. Testing coverage currently covers only Windows, macOS, and Linux. Adjust PrintUpdateSettings() to cache the print target type from the job settings. This is needed by the later call to StartPrinting(), since the deferred actions of PrintingContext::UpdatePrinterSettings() are not done in the service until StartPrinting() time. Include registration with the PrintBackendServiceManager so that the Print Backend process does not get terminated if there is a short amount of idleness between printing operations for this job. Only include unregister logic for failure case, since the printing success case logic comes later in https://crrev.com/c/3199044. Add ability for TestPrintingContext to respond with access-denied error for NewDocument() to enable testing of StartPrinting() fallback retry logic. Bug: 809738 Change-Id: I3713e59de44dba3f45eeb0921bd7adbb6eab2514 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3180059 Commit-Queue: Alan Screen <awscreen@chromium.org> Reviewed-by: Lei Zhang <thestig@chromium.org> Cr-Commit-Position: refs/heads/main@{#946875}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
fef4bada46
commit
1c9980191f
@ -37,6 +37,8 @@ class PrintBackendServiceManager {
|
||||
|
||||
// Register as a client of PrintBackendServiceManager. This acts as a signal
|
||||
// of impending activity enabling possible optimizations within the manager.
|
||||
// Returns an ID which the caller is to use with `UnregisterClient()` once it
|
||||
// is completed its printing activity.
|
||||
uint32_t RegisterClient();
|
||||
|
||||
// Notify the manager that this client is no longer needing print backend
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/auto_reset.h"
|
||||
#include "base/bind.h"
|
||||
@ -72,6 +73,7 @@
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
|
||||
#include "third_party/blink/public/common/features.h"
|
||||
#include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
|
||||
#include "ui/gfx/geometry/size.h"
|
||||
|
||||
namespace printing {
|
||||
|
||||
@ -101,11 +103,16 @@ namespace {
|
||||
// TODO(crbug.com/822505) ChromeOS uses different testing setup that isn't
|
||||
// hooked up to make use of `TestPrintingContext` yet.
|
||||
#if !defined(OS_CHROMEOS)
|
||||
constexpr int kPrinterCapabilitiesMaxCopies = 99;
|
||||
constexpr int kPrintSettingsCopies = 42;
|
||||
constexpr int kTestPrintingDpi = 72;
|
||||
constexpr int kTestPrinterCapabilitiesMaxCopies = 99;
|
||||
constexpr gfx::Size kTestPrinterCapabilitiesDpi(kTestPrintingDpi,
|
||||
kTestPrintingDpi);
|
||||
constexpr int kTestPrintSettingsCopies = 42;
|
||||
|
||||
const PrinterBasicInfoOptions kPrintInfoOptions{{"opt1", "123"},
|
||||
{"opt2", "456"}};
|
||||
const std::vector<gfx::Size> kTestPrinterCapabilitiesDefaultDpis{
|
||||
kTestPrinterCapabilitiesDpi};
|
||||
const PrinterBasicInfoOptions kTestDummyPrintInfoOptions{{"opt1", "123"},
|
||||
{"opt2", "456"}};
|
||||
#endif // !defined(OS_CHROMEOS)
|
||||
|
||||
constexpr int kDefaultDocumentCookie = 1234;
|
||||
@ -381,6 +388,18 @@ class TestPrintViewManager : public PrintViewManager {
|
||||
TestPrintViewManager& operator=(const TestPrintViewManager&) = delete;
|
||||
~TestPrintViewManager() override = default;
|
||||
|
||||
bool StartPrinting(content::WebContents* contents) {
|
||||
auto* print_view_manager = TestPrintViewManager::FromWebContents(contents);
|
||||
if (!print_view_manager)
|
||||
return false;
|
||||
|
||||
content::RenderFrameHost* rfh_to_use = GetFrameToPrint(contents);
|
||||
if (!rfh_to_use)
|
||||
return false;
|
||||
|
||||
return print_view_manager->PrintNow(rfh_to_use);
|
||||
}
|
||||
|
||||
PrintSettings* snooped_settings() { return snooped_settings_.get(); }
|
||||
|
||||
private:
|
||||
@ -1619,10 +1638,12 @@ class PrintBackendPrintBrowserTestBase : public PrintBrowserTest {
|
||||
/*display_name=*/"test printer",
|
||||
/*printer_description=*/"A printer for testing.",
|
||||
/*printer_status=*/0,
|
||||
/*is_default=*/true, kPrintInfoOptions);
|
||||
/*is_default=*/true, kTestDummyPrintInfoOptions);
|
||||
|
||||
auto default_caps = std::make_unique<PrinterSemanticCapsAndDefaults>();
|
||||
default_caps->copies_max = kPrinterCapabilitiesMaxCopies;
|
||||
default_caps->copies_max = kTestPrinterCapabilitiesMaxCopies;
|
||||
default_caps->dpis = kTestPrinterCapabilitiesDefaultDpis;
|
||||
default_caps->default_dpi = kTestPrinterCapabilitiesDpi;
|
||||
test_backend_->AddValidPrinter(
|
||||
printer_name, std::move(default_caps),
|
||||
std::make_unique<PrinterBasicInfo>(kPrinterInfo));
|
||||
@ -1633,6 +1654,46 @@ class PrintBackendPrintBrowserTestBase : public PrintBrowserTest {
|
||||
printer_name);
|
||||
}
|
||||
|
||||
void SetUpPrintViewManager(content::WebContents* web_contents) {
|
||||
web_contents->SetUserData(
|
||||
PrintViewManager::UserDataKey(),
|
||||
std::make_unique<TestPrintViewManager>(web_contents));
|
||||
}
|
||||
|
||||
void PrintAfterPreviewIsReadyAndLoaded() {
|
||||
// First invoke the Print Preview dialog with `StartPrint()`.
|
||||
PrintPreviewObserver print_preview_observer(/*wait_for_loaded=*/true);
|
||||
StartPrint(browser()->tab_strip_model()->GetActiveWebContents(),
|
||||
/*print_renderer=*/mojo::NullAssociatedRemote(),
|
||||
/*print_preview_disabled=*/false,
|
||||
/*has_selection=*/false);
|
||||
print_preview_observer.WaitUntilPreviewIsReady();
|
||||
|
||||
content::WebContents* preview_dialog =
|
||||
print_preview_observer.GetPrintPreviewDialog();
|
||||
ASSERT_TRUE(preview_dialog);
|
||||
|
||||
// Print Preview is completely ready, can now initiate printing.
|
||||
// This script locates and clicks the Print button.
|
||||
const char kScript[] = R"(
|
||||
const button = document.getElementsByTagName('print-preview-app')[0]
|
||||
.$['sidebar']
|
||||
.shadowRoot.querySelector('print-preview-button-strip')
|
||||
.shadowRoot.querySelector('.action-button');
|
||||
button.click();)";
|
||||
ASSERT_TRUE(content::ExecuteScript(preview_dialog, kScript));
|
||||
WaitUntilCallbackReceived();
|
||||
}
|
||||
|
||||
void PrimeForAccessDeniedErrorsInNewDocument() {
|
||||
test_printing_context_factory_.SetAccessDeniedErrorOnNewDocument(
|
||||
/*cause_errors=*/true);
|
||||
}
|
||||
|
||||
mojom::ResultCode start_printing_result() const {
|
||||
return start_printing_result_;
|
||||
}
|
||||
|
||||
private:
|
||||
class PrintBackendPrintingContextFactoryForTest
|
||||
: public PrintingContextFactoryForTest {
|
||||
@ -1644,11 +1705,15 @@ class PrintBackendPrintBrowserTestBase : public PrintBrowserTest {
|
||||
std::make_unique<TestPrintingContext>(delegate, skip_system_calls);
|
||||
|
||||
auto settings = std::make_unique<PrintSettings>();
|
||||
settings->set_copies(kPrintSettingsCopies);
|
||||
settings->set_copies(kTestPrintSettingsCopies);
|
||||
settings->set_dpi(kTestPrintingDpi);
|
||||
settings->set_device_name(
|
||||
base::ASCIIToUTF16(base::StringPiece(printer_name_)));
|
||||
context->SetDeviceSettings(printer_name_, std::move(settings));
|
||||
|
||||
if (access_denied_errors_for_new_document_)
|
||||
context->SetNewDocumentBlockedByPermissions();
|
||||
|
||||
return std::move(context);
|
||||
}
|
||||
|
||||
@ -1656,8 +1721,13 @@ class PrintBackendPrintBrowserTestBase : public PrintBrowserTest {
|
||||
printer_name_ = printer_name;
|
||||
}
|
||||
|
||||
void SetAccessDeniedErrorOnNewDocument(bool cause_errors) {
|
||||
access_denied_errors_for_new_document_ = cause_errors;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string printer_name_;
|
||||
bool access_denied_errors_for_new_document_ = false;
|
||||
};
|
||||
|
||||
std::unique_ptr<PrintJobWorker> CreatePrintJobWorker(int render_process_id,
|
||||
@ -1679,7 +1749,8 @@ class PrintBackendPrintBrowserTestBase : public PrintBrowserTest {
|
||||
}
|
||||
|
||||
void ResetForNoAccessDeniedErrors() {
|
||||
// TODO(crbug.com/809738) Fill in testing reset for access denied errors.
|
||||
test_printing_context_factory_.SetAccessDeniedErrorOnNewDocument(
|
||||
/*cause_errors=*/false);
|
||||
}
|
||||
|
||||
base::test::ScopedFeatureList feature_list_;
|
||||
@ -1693,6 +1764,15 @@ class PrintBackendPrintBrowserTestBase : public PrintBrowserTest {
|
||||
mojom::ResultCode start_printing_result_ = mojom::ResultCode::kFailed;
|
||||
};
|
||||
|
||||
class PrintBackendPrintBrowserTestService
|
||||
: public PrintBackendPrintBrowserTestBase {
|
||||
public:
|
||||
PrintBackendPrintBrowserTestService() = default;
|
||||
~PrintBackendPrintBrowserTestService() override = default;
|
||||
|
||||
bool UseService() override { return true; }
|
||||
};
|
||||
|
||||
class PrintBackendPrintBrowserTest : public PrintBackendPrintBrowserTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
@ -1722,7 +1802,7 @@ IN_PROC_BROWSER_TEST_P(PrintBackendPrintBrowserTest, UpdatePrintSettings) {
|
||||
|
||||
ASSERT_TRUE(print_view_manager.snooped_settings());
|
||||
EXPECT_EQ(print_view_manager.snooped_settings()->copies(),
|
||||
kPrintSettingsCopies);
|
||||
kTestPrintSettingsCopies);
|
||||
#if defined(OS_LINUX) && defined(USE_CUPS)
|
||||
// Collect just the keys to compare the info options vs. advanced settings.
|
||||
std::vector<std::string> advanced_setting_keys;
|
||||
@ -1732,13 +1812,55 @@ IN_PROC_BROWSER_TEST_P(PrintBackendPrintBrowserTest, UpdatePrintSettings) {
|
||||
for (const auto& advanced_setting : advanced_settings) {
|
||||
advanced_setting_keys.push_back(advanced_setting.first);
|
||||
}
|
||||
for (const auto& option : kPrintInfoOptions) {
|
||||
for (const auto& option : kTestDummyPrintInfoOptions) {
|
||||
print_info_options_keys.push_back(option.first);
|
||||
}
|
||||
EXPECT_THAT(advanced_setting_keys,
|
||||
testing::UnorderedElementsAreArray(print_info_options_keys));
|
||||
#endif // defined(OS_LINUX) && defined(USE_CUPS)
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(PrintBackendPrintBrowserTestService, StartPrinting) {
|
||||
AddPrinter("printer1");
|
||||
SetPrinterNameForSubsequentContexts("printer1");
|
||||
|
||||
ASSERT_TRUE(embedded_test_server()->Started());
|
||||
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
|
||||
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
|
||||
|
||||
content::WebContents* web_contents =
|
||||
browser()->tab_strip_model()->GetActiveWebContents();
|
||||
ASSERT_TRUE(web_contents);
|
||||
SetUpPrintViewManager(web_contents);
|
||||
|
||||
PrintAfterPreviewIsReadyAndLoaded();
|
||||
|
||||
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(PrintBackendPrintBrowserTestService,
|
||||
StartPrintingAccessDenied) {
|
||||
AddPrinter("printer1");
|
||||
SetPrinterNameForSubsequentContexts("printer1");
|
||||
PrimeForAccessDeniedErrorsInNewDocument();
|
||||
|
||||
ASSERT_TRUE(embedded_test_server()->Started());
|
||||
GURL url(embedded_test_server()->GetURL("/printing/test3.html"));
|
||||
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
|
||||
|
||||
content::WebContents* web_contents =
|
||||
browser()->tab_strip_model()->GetActiveWebContents();
|
||||
ASSERT_TRUE(web_contents);
|
||||
SetUpPrintViewManager(web_contents);
|
||||
|
||||
// The test will retry to print after getting an access-denied error when
|
||||
// trying to start printing, resulting in 2 calls.
|
||||
SetNumExpectedMessages(/*num=*/2);
|
||||
|
||||
PrintAfterPreviewIsReadyAndLoaded();
|
||||
|
||||
EXPECT_EQ(start_printing_result(), mojom::ResultCode::kSuccess);
|
||||
}
|
||||
#endif // !defined(OS_CHROMEOS)
|
||||
|
||||
} // namespace printing
|
||||
|
@ -333,30 +333,46 @@ void PrintJobWorker::UseDefaultSettings(SettingsCallback callback) {
|
||||
GetSettingsDone(std::move(callback), result);
|
||||
}
|
||||
|
||||
void PrintJobWorker::StartPrinting(PrintedDocument* new_document) {
|
||||
bool PrintJobWorker::StartPrintingSanityCheck(
|
||||
const PrintedDocument* new_document) const {
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
|
||||
if (page_number_ != PageNumber::npos()) {
|
||||
NOTREACHED();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!document_) {
|
||||
NOTREACHED();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (document_.get() != new_document) {
|
||||
NOTREACHED();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::u16string PrintJobWorker::GetDocumentName(
|
||||
const PrintedDocument* new_document) const {
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
|
||||
std::u16string document_name = SimplifyDocumentTitle(document_->name());
|
||||
if (document_name.empty()) {
|
||||
document_name = SimplifyDocumentTitle(
|
||||
l10n_util::GetStringUTF16(IDS_DEFAULT_PRINT_DOCUMENT_TITLE));
|
||||
}
|
||||
mojom::ResultCode result = printing_context_->NewDocument(document_name);
|
||||
return document_name;
|
||||
}
|
||||
|
||||
void PrintJobWorker::StartPrinting(PrintedDocument* new_document) {
|
||||
if (!StartPrintingSanityCheck(new_document))
|
||||
return;
|
||||
|
||||
mojom::ResultCode result =
|
||||
printing_context_->NewDocument(GetDocumentName(new_document));
|
||||
if (result != mojom::ResultCode::kSuccess) {
|
||||
OnFailure();
|
||||
return;
|
||||
|
@ -75,7 +75,7 @@ class PrintJobWorker {
|
||||
|
||||
// Starts the printing loop. Every pages are printed as soon as the data is
|
||||
// available. Makes sure the new_document is the right one.
|
||||
void StartPrinting(PrintedDocument* new_document);
|
||||
virtual void StartPrinting(PrintedDocument* new_document);
|
||||
|
||||
// Updates the printed document.
|
||||
void OnDocumentChanged(PrintedDocument* new_document);
|
||||
@ -109,6 +109,12 @@ class PrintJobWorker {
|
||||
content::WebContents* GetWebContents();
|
||||
|
||||
protected:
|
||||
// Sanity check that it is okay to proceed with starting a print job.
|
||||
bool StartPrintingSanityCheck(const PrintedDocument* new_document) const;
|
||||
|
||||
// Get the document name to be used when initiating printing.
|
||||
std::u16string GetDocumentName(const PrintedDocument* new_document) const;
|
||||
|
||||
// Reports settings back to |callback|.
|
||||
void GetSettingsDone(SettingsCallback callback, mojom::ResultCode result);
|
||||
|
||||
@ -116,8 +122,13 @@ class PrintJobWorker {
|
||||
virtual void UpdatePrintSettings(base::Value new_settings,
|
||||
SettingsCallback callback);
|
||||
|
||||
// Retrieves the context for testing only.
|
||||
// Discards the current document, the current page and cancels the printing
|
||||
// context.
|
||||
virtual void OnFailure();
|
||||
|
||||
PrintingContext* printing_context() { return printing_context_.get(); }
|
||||
PrintedDocument* document() { return document_.get(); }
|
||||
base::SequencedTaskRunner* task_runner() { return task_runner_.get(); }
|
||||
|
||||
private:
|
||||
// The shared NotificationService service can only be accessed from the UI
|
||||
@ -145,10 +156,6 @@ class PrintJobWorker {
|
||||
// Closes the job since spooling is done.
|
||||
void OnDocumentDone();
|
||||
|
||||
// Discards the current document, the current page and cancels the printing
|
||||
// context.
|
||||
void OnFailure();
|
||||
|
||||
// Asks the user for print settings. Must be called on the UI thread.
|
||||
// Required on Mac and Linux. Windows can display UI from non-main threads,
|
||||
// but sticks with this for consistency.
|
||||
|
@ -4,23 +4,99 @@
|
||||
|
||||
#include "chrome/browser/printing/print_job_worker_oop.h"
|
||||
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/values.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/browser/printing/print_backend_service_manager.h"
|
||||
#include "chrome/services/printing/public/mojom/print_backend_service.mojom.h"
|
||||
#include "components/device_event_log/device_event_log.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "printing/printed_document.h"
|
||||
#include "printing/printing_features.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace printing {
|
||||
|
||||
namespace {
|
||||
|
||||
mojom::PrintTargetType DeterminePrintTargetType(
|
||||
const base::Value& job_settings) {
|
||||
#if defined(OS_MAC)
|
||||
if (job_settings.FindKey(kSettingOpenPDFInPreview))
|
||||
return mojom::PrintTargetType::kExternalPreview;
|
||||
#endif
|
||||
if (job_settings.FindBoolKey(kSettingShowSystemDialog).value_or(false))
|
||||
return mojom::PrintTargetType::kSystemDialog;
|
||||
return mojom::PrintTargetType::kDirectToDevice;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PrintJobWorkerOop::PrintJobWorkerOop(int render_process_id, int render_frame_id)
|
||||
: PrintJobWorker(render_process_id, render_frame_id) {}
|
||||
|
||||
PrintJobWorkerOop::~PrintJobWorkerOop() = default;
|
||||
PrintJobWorkerOop::~PrintJobWorkerOop() {
|
||||
DCHECK(!service_manager_client_id_.has_value());
|
||||
}
|
||||
|
||||
void PrintJobWorkerOop::StartPrinting(PrintedDocument* new_document) {
|
||||
if (!StartPrintingSanityCheck(new_document))
|
||||
return;
|
||||
|
||||
// Do browser-side context setup.
|
||||
std::u16string document_name = GetDocumentName(new_document);
|
||||
mojom::ResultCode result = printing_context()->NewDocument(document_name);
|
||||
if (result != mojom::ResultCode::kSuccess) {
|
||||
OnFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string device_name =
|
||||
base::UTF16ToUTF8(document()->settings().device_name());
|
||||
VLOG(1) << "Start printing document " << document()->cookie() << " to "
|
||||
<< device_name;
|
||||
|
||||
// `PrintBackendServiceManager` interactions must happen on the UI thread.
|
||||
content::GetUIThreadTaskRunner({})->PostTask(
|
||||
FROM_HERE, base::BindOnce(&PrintJobWorkerOop::SendStartPrinting,
|
||||
ui_weak_factory_.GetWeakPtr(), device_name,
|
||||
document_name));
|
||||
}
|
||||
|
||||
void PrintJobWorkerOop::OnDidStartPrinting(mojom::ResultCode result) {
|
||||
// TODO(crbug.com/809738) Temporary placeholder for testing preparation.
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
if (result != mojom::ResultCode::kSuccess) {
|
||||
PRINTER_LOG(ERROR) << "Error initiating printing via service for document "
|
||||
<< document()->cookie() << ": " << result;
|
||||
if (result == mojom::ResultCode::kAccessDenied) {
|
||||
// Register that this printer requires elevated privileges.
|
||||
PrintBackendServiceManager& service_mgr =
|
||||
PrintBackendServiceManager::GetInstance();
|
||||
service_mgr.SetPrinterDriverRequiresElevatedPrivilege(device_name_);
|
||||
|
||||
// Failure from access-denied means we no longer need this client.
|
||||
UnregisterServiceManagerClient();
|
||||
|
||||
// Retry the operation which should now happen at a higher privilege
|
||||
// level.
|
||||
SendStartPrinting(device_name_, document_name_);
|
||||
return;
|
||||
}
|
||||
task_runner()->PostTask(FROM_HERE,
|
||||
base::BindOnce(&PrintJobWorkerOop::OnFailure,
|
||||
worker_weak_factory_.GetWeakPtr()));
|
||||
return;
|
||||
}
|
||||
VLOG(1) << "Printing initiated with service for document "
|
||||
<< document()->cookie();
|
||||
// TODO(crbug.com/809738) Still need more support for printing pipeline in
|
||||
// the service.
|
||||
task_runner()->PostTask(FROM_HERE,
|
||||
base::BindOnce(&PrintJobWorkerOop::OnFailure,
|
||||
worker_weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
void PrintJobWorkerOop::UpdatePrintSettings(base::Value new_settings,
|
||||
@ -31,18 +107,36 @@ void PrintJobWorkerOop::UpdatePrintSettings(base::Value new_settings,
|
||||
// isn't safe after TakeDict() destroys the internal dictionary for it.
|
||||
std::string device_name = *new_settings.FindStringKey(kSettingDeviceName);
|
||||
|
||||
// Save the print target type from the settings, since this will be needed
|
||||
// later when printing is started.
|
||||
print_target_type_ = DeterminePrintTargetType(new_settings);
|
||||
|
||||
VLOG(1) << "Updating print settings via service for " << device_name;
|
||||
PrintBackendServiceManager& service_mgr =
|
||||
PrintBackendServiceManager::GetInstance();
|
||||
|
||||
// Safe to use base::Unretained(this) since the callback owns `this`, and
|
||||
// `service_mgr` is a global instance which never exits and simply wraps
|
||||
// `callback` so that it is still called should the service terminate
|
||||
// unexpectedly.
|
||||
service_mgr.UpdatePrintSettings(
|
||||
device_name, std::move(new_settings).TakeDict(),
|
||||
base::BindOnce(&PrintJobWorkerOop::OnDidUpdatePrintSettings,
|
||||
base::Unretained(this), device_name, std::move(callback)));
|
||||
ui_weak_factory_.GetWeakPtr(), device_name,
|
||||
std::move(callback)));
|
||||
}
|
||||
|
||||
void PrintJobWorkerOop::OnFailure() {
|
||||
content::GetUIThreadTaskRunner({})->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(&PrintJobWorkerOop::UnregisterServiceManagerClient,
|
||||
ui_weak_factory_.GetWeakPtr()));
|
||||
PrintJobWorker::OnFailure();
|
||||
}
|
||||
|
||||
void PrintJobWorkerOop::UnregisterServiceManagerClient() {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
if (service_manager_client_id_.has_value()) {
|
||||
PrintBackendServiceManager::GetInstance().UnregisterClient(
|
||||
service_manager_client_id_.value());
|
||||
service_manager_client_id_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void PrintJobWorkerOop::OnDidUpdatePrintSettings(
|
||||
@ -68,4 +162,34 @@ void PrintJobWorkerOop::OnDidUpdatePrintSettings(
|
||||
GetSettingsDone(std::move(callback), result);
|
||||
}
|
||||
|
||||
void PrintJobWorkerOop::SendStartPrinting(const std::string& device_name,
|
||||
const std::u16string& document_name) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
DCHECK(features::kEnableOopPrintDriversJobPrint.Get());
|
||||
|
||||
// The device name is needed repeatedly for each call to the service, cache
|
||||
// that for this print job.
|
||||
device_name_ = device_name;
|
||||
|
||||
// Save the document name in case it is needed for retrying a job after
|
||||
// failure.
|
||||
document_name_ = document_name;
|
||||
|
||||
const int32_t document_cookie = document()->cookie();
|
||||
VLOG(1) << "Starting printing via service for to `" << device_name_
|
||||
<< "` for document " << document_cookie;
|
||||
|
||||
PrintBackendServiceManager& service_mgr =
|
||||
PrintBackendServiceManager::GetInstance();
|
||||
|
||||
// Register this worker as a printing client.
|
||||
service_manager_client_id_ = service_mgr.RegisterClient();
|
||||
|
||||
service_mgr.StartPrinting(
|
||||
device_name_, document_cookie, document_name_, print_target_type_,
|
||||
document()->settings(),
|
||||
base::BindOnce(&PrintJobWorkerOop::OnDidStartPrinting,
|
||||
ui_weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
} // namespace printing
|
||||
|
@ -5,10 +5,15 @@
|
||||
#ifndef CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_OOP_H_
|
||||
#define CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_OOP_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/values.h"
|
||||
#include "chrome/browser/printing/print_job_worker.h"
|
||||
#include "chrome/services/printing/public/mojom/print_backend_service.mojom-forward.h"
|
||||
#include "chrome/services/printing/public/mojom/print_backend_service.mojom.h"
|
||||
#include "printing/buildflags/buildflags.h"
|
||||
#include "printing/mojom/print.mojom.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
#if !BUILDFLAG(ENABLE_OOP_PRINTING)
|
||||
#error "OOP printing must be enabled"
|
||||
@ -16,6 +21,13 @@
|
||||
|
||||
namespace printing {
|
||||
|
||||
class PrintedDocument;
|
||||
|
||||
// Worker thread code. It manages the PrintingContext and offloads all system
|
||||
// driver interactions to the Print Backend service. This is the object that
|
||||
// generates most NOTIFY_PRINT_JOB_EVENT notifications, but they are generated
|
||||
// through a NotificationTask task to be executed from the right thread, the UI
|
||||
// thread. PrintJob always outlives its worker instance.
|
||||
class PrintJobWorkerOop : public PrintJobWorker {
|
||||
public:
|
||||
PrintJobWorkerOop(int render_process_id, int render_frame_id);
|
||||
@ -23,6 +35,9 @@ class PrintJobWorkerOop : public PrintJobWorker {
|
||||
PrintJobWorkerOop& operator=(const PrintJobWorkerOop&) = delete;
|
||||
~PrintJobWorkerOop() override;
|
||||
|
||||
// `PrintJobWorker` overrides.
|
||||
void StartPrinting(PrintedDocument* new_document) override;
|
||||
|
||||
protected:
|
||||
// Local callback wrapper for Print Backend Service mojom call. Virtual to
|
||||
// support testing.
|
||||
@ -31,12 +46,43 @@ class PrintJobWorkerOop : public PrintJobWorker {
|
||||
// `PrintJobWorker` overrides.
|
||||
void UpdatePrintSettings(base::Value new_settings,
|
||||
SettingsCallback callback) override;
|
||||
void OnFailure() override;
|
||||
|
||||
private:
|
||||
// Support to unregister this worker as a printing client. Applicable any
|
||||
// time a print job finishes, is canceled, or needs to be restarted.
|
||||
void UnregisterServiceManagerClient();
|
||||
|
||||
// Local callback wrapper for Print Backend Service mojom call.
|
||||
void OnDidUpdatePrintSettings(const std::string& device_name,
|
||||
SettingsCallback callback,
|
||||
mojom::PrintSettingsResultPtr print_settings);
|
||||
|
||||
// Mojo support to send messages from UI thread.
|
||||
void SendStartPrinting(const std::string& device_name,
|
||||
const std::u16string& document_name);
|
||||
|
||||
// Client ID with the print backend service manager for this print job.
|
||||
// Used only from UI thread.
|
||||
absl::optional<uint32_t> service_manager_client_id_;
|
||||
|
||||
// The device name used when printing via a service. Used only from the UI
|
||||
// thread.
|
||||
std::string device_name_;
|
||||
|
||||
// The processed name of the document being printed. Used only from the UI
|
||||
// thread.
|
||||
std::u16string document_name_;
|
||||
|
||||
// The type of target to print to. Used only from the UI thread.
|
||||
mojom::PrintTargetType print_target_type_ =
|
||||
mojom::PrintTargetType::kDirectToDevice;
|
||||
|
||||
// Weak pointers have flags that get bound to the thread where they are
|
||||
// checked, so it is necessary to use different factories when getting a
|
||||
// weak pointer to send to the worker task runner vs. to the UI thread.
|
||||
base::WeakPtrFactory<PrintJobWorkerOop> worker_weak_factory_{this};
|
||||
base::WeakPtrFactory<PrintJobWorkerOop> ui_weak_factory_{this};
|
||||
};
|
||||
|
||||
} // namespace printing
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "base/notreached.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "build/build_config.h"
|
||||
#include "printing/buildflags/buildflags.h"
|
||||
#include "printing/mojom/print.mojom.h"
|
||||
#include "printing/print_settings.h"
|
||||
#include "printing/printing_context.h"
|
||||
@ -99,6 +100,9 @@ mojom::ResultCode TestPrintingContext::UpdatePrinterSettings(
|
||||
|
||||
mojom::ResultCode TestPrintingContext::NewDocument(
|
||||
const std::u16string& document_name) {
|
||||
if (!skip_system_calls() && new_document_blocked_by_permissions_)
|
||||
return mojom::ResultCode::kAccessDenied;
|
||||
|
||||
// No-op.
|
||||
return mojom::ResultCode::kSuccess;
|
||||
}
|
||||
|
@ -43,6 +43,11 @@ class TestPrintingContext : public PrintingContext {
|
||||
void SetDeviceSettings(const std::string& device_name,
|
||||
std::unique_ptr<PrintSettings> settings);
|
||||
|
||||
// Enables tests to fail with an access-denied error.
|
||||
void SetNewDocumentBlockedByPermissions() {
|
||||
new_document_blocked_by_permissions_ = true;
|
||||
}
|
||||
|
||||
// PrintingContext overrides:
|
||||
void AskUserForSettings(int max_pages,
|
||||
bool has_selection,
|
||||
@ -66,6 +71,7 @@ class TestPrintingContext : public PrintingContext {
|
||||
|
||||
private:
|
||||
base::flat_map<std::string, std::unique_ptr<PrintSettings>> device_settings_;
|
||||
bool new_document_blocked_by_permissions_ = false;
|
||||
};
|
||||
|
||||
} // namespace printing
|
||||
|
Reference in New Issue
Block a user