Commit 65304d5c authored by Alan Screen's avatar Alan Screen Committed by Commit Bot

Introduce print backend service

This is to support moving print driver interactions to the operating
system outside of the browser process.  This improves stability so that
crashes related to misbehavior with third party drivers can be contained
in a utility process instead of taking down the browser.

Start off the interface with the query to get the current default
printer.  This is a common initial first query when starting the
printing sequence.

Bug: 809738
Change-Id: I819ecf71c889494f2aa05cf150359e68f799553c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2422820Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Reviewed-by: default avatarDaniel Hosseinian <dhoss@chromium.org>
Commit-Queue: Alan Screen <awscreen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#822209}
parent 0fc32d22
...@@ -7,6 +7,10 @@ ...@@ -7,6 +7,10 @@
</message> </message>
</if> </if>
<message name="IDS_UTILITY_PROCESS_PRINT_BACKEND_SERVICE_NAME" desc="The name of the utility process used for backend interactions with printer drivers.">
Print Backend Service
</message>
<message name="IDS_PRINT_INVALID_PRINTER_SETTINGS" desc="Message to display when selected printer is not reachable or its settings are invalid."> <message name="IDS_PRINT_INVALID_PRINTER_SETTINGS" desc="Message to display when selected printer is not reachable or its settings are invalid.">
The selected printer is not available or not installed correctly. Check your printer or try selecting another printer. The selected printer is not available or not installed correctly. Check your printer or try selecting another printer.
</message> </message>
......
3ea9a2aba49c7709003d7298f2aa3e5edc5422ce
\ No newline at end of file
...@@ -5245,6 +5245,13 @@ static_library("browser") { ...@@ -5245,6 +5245,13 @@ static_library("browser") {
if (is_linux || is_chromeos) { if (is_linux || is_chromeos) {
sources += [ "printing/printer_manager_dialog_linux.cc" ] sources += [ "printing/printer_manager_dialog_linux.cc" ]
} }
if (is_win || is_mac || is_linux || is_chromeos) {
sources += [
"printing/print_backend_service.cc",
"printing/print_backend_service.h",
]
deps += [ "//chrome/services/printing/public/mojom" ]
}
if (is_win || enable_print_preview) { if (is_win || enable_print_preview) {
deps += [ "//chrome/services/printing/public/mojom" ] deps += [ "//chrome/services/printing/public/mojom" ]
} }
......
include_rules = [ include_rules = [
"+pdf/pdf.h", # Test only. "+pdf/pdf.h", # Test only.
] ]
specific_include_rules = {
"print_backend_browsertest.cc": [
"+chrome/services/printing/print_backend_service_impl.h",
]
}
// Copyright 2020 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 <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/check_op.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "chrome/services/printing/print_backend_service_impl.h"
#include "chrome/services/printing/public/mojom/print_backend_service.mojom.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/test/browser_test.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "printing/backend/test_print_backend.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace printing {
namespace {
constexpr char kDefaultPrinterName[] = "default-test-printer";
} // namespace
// PrintBackendServiceTestImpl uses a TestPrintBackend to enable testing of the
// PrintBackendService without relying upon the presence of real printer
// drivers.
class PrintBackendServiceTestImpl : public PrintBackendServiceImpl {
public:
explicit PrintBackendServiceTestImpl(
mojo::PendingReceiver<mojom::PrintBackendService> receiver)
: PrintBackendServiceImpl(std::move(receiver)) {}
PrintBackendServiceTestImpl(const PrintBackendServiceTestImpl&) = delete;
PrintBackendServiceTestImpl& operator=(const PrintBackendServiceTestImpl&) =
delete;
~PrintBackendServiceTestImpl() override = default;
// Overrides which need special handling for using `test_print_backend_`.
void Init(const std::string& locale) override {
test_print_backend_ = base::MakeRefCounted<TestPrintBackend>();
print_backend_ = test_print_backend_;
}
private:
friend class PrintBackendBrowserTest;
scoped_refptr<TestPrintBackend> test_print_backend_;
};
class PrintBackendBrowserTest : public InProcessBrowserTest {
public:
PrintBackendBrowserTest() = default;
~PrintBackendBrowserTest() override = default;
void PreRunTestOnMainThread() override {
InProcessBrowserTest::PreRunTestOnMainThread();
// Launch the service, and bind the testing interface to it.
mojo::PendingReceiver<mojom::PrintBackendService> receiver =
mojo::PendingRemote<mojom::PrintBackendService>()
.InitWithNewPipeAndPassReceiver();
print_backend_service_ =
std::make_unique<PrintBackendServiceTestImpl>(std::move(receiver));
}
// Initialize and load the backend service with some test print drivers.
void DoInitAndSetupTestData() {
print_backend_service_->Init(/*locale=*/"");
print_backend_service_->test_print_backend_->SetDefaultPrinterName(
kDefaultPrinterName);
}
// Public callbacks used by tests.
void OnDidGetDefaultPrinterName(
base::Optional<std::string>* capture_printer_name,
const base::Optional<std::string>& printer_name) {
*capture_printer_name = printer_name;
CheckForQuit();
}
// The following are helper functions for having a wait loop in the test and
// exit when expected messages are received.
void WaitUntilCallbackReceived() {
if (received_message_) {
// Callback happened before even needing to wait.
return;
}
base::RunLoop run_loop;
quit_callback_ = run_loop.QuitClosure();
run_loop.Run();
}
void CheckForQuit() {
received_message_ = true;
if (quit_callback_)
std::move(quit_callback_).Run();
}
// Get the print backend service being tested.
mojom::PrintBackendService* GetPrintBackendService() const {
return print_backend_service_.get();
}
private:
bool received_message_ = false;
base::OnceClosure quit_callback_;
std::unique_ptr<PrintBackendServiceTestImpl> print_backend_service_;
};
// A print backend service requires initialization prior to being used for a
// query/command. Verify that a query fails if one tries to use a new service
// without having performed initialization.
IN_PROC_BROWSER_TEST_F(PrintBackendBrowserTest, FailWithoutInit) {
base::Optional<std::string> default_printer_name;
// Safe to use base::Unretained(this) since waiting locally on the callback
// forces a shorter lifetime than `this`.
GetPrintBackendService()->GetDefaultPrinterName(
base::BindOnce(&PrintBackendBrowserTest::OnDidGetDefaultPrinterName,
base::Unretained(this), &default_printer_name));
WaitUntilCallbackReceived();
EXPECT_FALSE(default_printer_name.has_value());
}
IN_PROC_BROWSER_TEST_F(PrintBackendBrowserTest, GetDefaultPrinterName) {
base::Optional<std::string> default_printer_name;
DoInitAndSetupTestData();
// Safe to use base::Unretained(this) since waiting locally on the callback
// forces a shorter lifetime than `this`.
GetPrintBackendService()->GetDefaultPrinterName(
base::BindOnce(&PrintBackendBrowserTest::OnDidGetDefaultPrinterName,
base::Unretained(this), &default_printer_name));
WaitUntilCallbackReceived();
ASSERT_TRUE(default_printer_name.has_value());
EXPECT_EQ(default_printer_name.value(), kDefaultPrinterName);
}
} // namespace printing
// Copyright 2020 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 "chrome/browser/printing/print_backend_service.h"
#include <string>
#include "base/containers/flat_map.h"
#include "base/no_destructor.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/services/printing/public/mojom/print_backend_service.mojom.h"
#include "content/public/browser/service_process_host.h"
#include "mojo/public/cpp/bindings/remote.h"
namespace {
// Amount of idle time to wait before resetting the connection to the service.
constexpr base::TimeDelta kResetOnIdleTimeout =
base::TimeDelta::FromSeconds(20);
} // namespace
const mojo::Remote<printing::mojom::PrintBackendService>&
GetPrintBackendService(const std::string& locale,
const std::string& printer_name) {
static base::NoDestructor<base::flat_map<
std::string, mojo::Remote<printing::mojom::PrintBackendService>>>
remotes;
std::string remote_id;
#if defined(OS_WIN)
// Windows drivers are not thread safe. Use a process per driver to prevent
// bad interactions when interfacing to multiple drivers in parallel.
// https://crbug.com/957242
remote_id = printer_name;
#endif
auto iter = remotes->find(remote_id);
if (iter == remotes->end()) {
// First time for this `remote_id`.
auto result = remotes->emplace(
printer_name, mojo::Remote<printing::mojom::PrintBackendService>());
iter = result.first;
}
mojo::Remote<printing::mojom::PrintBackendService>& service = iter->second;
if (!service) {
content::ServiceProcessHost::Launch(
service.BindNewPipeAndPassReceiver(),
content::ServiceProcessHost::Options()
.WithDisplayName(IDS_UTILITY_PROCESS_PRINT_BACKEND_SERVICE_NAME)
.Pass());
// Ensure that if the interface is ever disconnected (e.g. the service
// process crashes) or goes idle for a short period of time -- meaning
// there are no in-flight messages and no other interfaces bound through
// this one -- then we will reset `remote`, causing the service process to
// be terminated if it isn't already.
service.reset_on_disconnect();
// TODO(crbug.com/809738) Interactions with the service should be expected
// as long as any Print Preview dialogs are open (and there could be more
// than one preview open at a time). Keeping the service present as long
// as those are open would help provide a more responsive experience for
// the user. For now, to ensure that this process doesn't stick around
// forever we make it go away after a short delay of idleness, but that
// should be adjusted to happen only after all UI references have been
// removed.
service.reset_on_idle_timeout(kResetOnIdleTimeout);
// Initialize the new service for the desired locale.
service->Init(locale);
}
return service;
}
// Copyright 2020 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.
#ifndef CHROME_BROWSER_PRINTING_PRINT_BACKEND_SERVICE_H_
#define CHROME_BROWSER_PRINTING_PRINT_BACKEND_SERVICE_H_
#include <string>
#include "chrome/services/printing/public/mojom/print_backend_service.mojom.h"
#include "mojo/public/cpp/bindings/remote.h"
// Acquires a remote handle to the Print Backend Service instance, launching a
// process to host the service if necessary.
const mojo::Remote<printing::mojom::PrintBackendService>&
GetPrintBackendService(const std::string& locale,
const std::string& printer_name);
#endif // CHROME_BROWSER_PRINTING_PRINT_BACKEND_SERVICE_H_
...@@ -53,6 +53,13 @@ source_set("lib") { ...@@ -53,6 +53,13 @@ source_set("lib") {
deps += [ "//skia" ] deps += [ "//skia" ]
} }
if (is_win || is_mac || is_linux || is_chromeos) {
sources += [
"print_backend_service_impl.cc",
"print_backend_service_impl.h",
]
}
} }
if (is_chromeos) { if (is_chromeos) {
......
// Copyright 2020 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 "chrome/services/printing/print_backend_service_impl.h"
#include <string>
#include <utility>
#include "base/logging.h"
#include "base/notreached.h"
#include "base/optional.h"
#include "chrome/services/printing/public/mojom/print_backend_service.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "printing/backend/print_backend.h"
namespace printing {
PrintBackendServiceImpl::PrintBackendServiceImpl(
mojo::PendingReceiver<mojom::PrintBackendService> receiver)
: receiver_(this, std::move(receiver)) {}
PrintBackendServiceImpl::~PrintBackendServiceImpl() = default;
void PrintBackendServiceImpl::Init(const std::string& locale) {
print_backend_ = PrintBackend::CreateInstance(locale);
}
void PrintBackendServiceImpl::GetDefaultPrinterName(
mojom::PrintBackendService::GetDefaultPrinterNameCallback callback) {
if (!print_backend_) {
DLOG(ERROR)
<< "Print backend instance has not been initialized for locale.";
std::move(callback).Run(base::nullopt);
return;
}
std::move(callback).Run(print_backend_->GetDefaultPrinterName());
}
} // namespace printing
// Copyright 2020 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.
#ifndef CHROME_SERVICES_PRINTING_PRINT_BACKEND_SERVICE_IMPL_H_
#define CHROME_SERVICES_PRINTING_PRINT_BACKEND_SERVICE_IMPL_H_
#include <string>
#include "base/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "chrome/services/printing/public/mojom/print_backend_service.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "printing/backend/print_backend.h"
namespace printing {
class PrintBackendServiceImpl : public mojom::PrintBackendService {
public:
explicit PrintBackendServiceImpl(
mojo::PendingReceiver<mojom::PrintBackendService> receiver);
PrintBackendServiceImpl(const PrintBackendServiceImpl&) = delete;
PrintBackendServiceImpl& operator=(const PrintBackendServiceImpl&) = delete;
~PrintBackendServiceImpl() override;
private:
friend class PrintBackendServiceTestImpl;
using GetDefaultPrinterCallback =
base::OnceCallback<void(const base::Optional<std::string>& printer_name)>;
// mojom::PrintBackendService implementation:
void Init(const std::string& locale) override;
void GetDefaultPrinterName(
mojom::PrintBackendService::GetDefaultPrinterNameCallback callback)
override;
scoped_refptr<PrintBackend> print_backend_;
mojo::Receiver<mojom::PrintBackendService> receiver_;
};
} // namespace printing
#endif // CHROME_SERVICES_PRINTING_PRINT_BACKEND_SERVICE_IMPL_H_
...@@ -34,6 +34,10 @@ mojom("mojom") { ...@@ -34,6 +34,10 @@ mojom("mojom") {
sources += [ "pdf_to_emf_converter.mojom" ] sources += [ "pdf_to_emf_converter.mojom" ]
} }
if (is_win || is_mac || is_linux || is_chromeos) {
sources += [ "print_backend_service.mojom" ]
}
cpp_typemaps = [ cpp_typemaps = [
{ {
types = [ types = [
......
// Copyright 2020 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.
module printing.mojom;
// The main interface to Chrome's Print Backend Service, which performs
// printer queries and commands to operating system printer drivers in an
// isolated process.
interface PrintBackendService {
// Establish the locale to be used for calls with this service and the
// interface to the underlying data source.
Init(string locale);
// Gets the default printer name from the data source.
// No value for `printer_name` is provided if there is a failure.
GetDefaultPrinterName()
=> (string? printer_name);
};
...@@ -3031,6 +3031,14 @@ if (!is_android) { ...@@ -3031,6 +3031,14 @@ if (!is_android) {
"//third_party/widevine/cdm", "//third_party/widevine/cdm",
] ]
} }
if (enable_basic_printing &&
(is_win || is_mac || is_linux || is_chromeos)) {
sources += [ "../browser/printing/print_backend_browsertest.cc" ]
deps += [
"//chrome/services/printing:lib",
"//printing:test_support",
]
}
if (enable_print_preview) { if (enable_print_preview) {
sources += [ sources += [
"../browser/printing/pdf_nup_converter_client_browsertest.cc", "../browser/printing/pdf_nup_converter_client_browsertest.cc",
......
...@@ -12,6 +12,7 @@ include_rules = [ ...@@ -12,6 +12,7 @@ include_rules = [
# when Cloud print chrome/service is removed. # when Cloud print chrome/service is removed.
"+chrome/services/printing/pdf_to_emf_converter_factory.h", "+chrome/services/printing/pdf_to_emf_converter_factory.h",
"+chrome/services/printing/printing_service.h", "+chrome/services/printing/printing_service.h",
"+chrome/services/printing/print_backend_service_impl.h",
"+chrome/services/printing/public/mojom", "+chrome/services/printing/public/mojom",
"+chrome/services/qrcode_generator", "+chrome/services/qrcode_generator",
"+chrome/services/removable_storage_writer", "+chrome/services/removable_storage_writer",
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "content/public/common/content_features.h" #include "content/public/common/content_features.h"
#include "content/public/utility/utility_thread.h" #include "content/public/utility/utility_thread.h"
#include "extensions/buildflags/buildflags.h" #include "extensions/buildflags/buildflags.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/service_factory.h" #include "mojo/public/cpp/bindings/service_factory.h"
#include "printing/buildflags/buildflags.h" #include "printing/buildflags/buildflags.h"
...@@ -74,9 +75,15 @@ ...@@ -74,9 +75,15 @@
#endif #endif
#if BUILDFLAG(ENABLE_PRINTING) #if BUILDFLAG(ENABLE_PRINTING)
#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
defined(OS_CHROMEOS)
#include "chrome/services/printing/print_backend_service_impl.h"
#include "chrome/services/printing/public/mojom/print_backend_service.mojom.h"
#endif
#include "components/services/print_compositor/print_compositor_impl.h" // nogncheck #include "components/services/print_compositor/print_compositor_impl.h" // nogncheck
#include "components/services/print_compositor/public/mojom/print_compositor.mojom.h" // nogncheck #include "components/services/print_compositor/public/mojom/print_compositor.mojom.h" // nogncheck
#endif #endif // BUILDFLAG(ENABLE_PRINTING)
#include "components/services/paint_preview_compositor/paint_preview_compositor_collection_impl.h" #include "components/services/paint_preview_compositor/paint_preview_compositor_collection_impl.h"
#include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h" #include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h"
...@@ -216,13 +223,22 @@ auto RunPaintPreviewCompositor( ...@@ -216,13 +223,22 @@ auto RunPaintPreviewCompositor(
#endif // BUILDFLAG(ENABLE_PAINT_PREVIEW) #endif // BUILDFLAG(ENABLE_PAINT_PREVIEW)
#if BUILDFLAG(ENABLE_PRINTING) #if BUILDFLAG(ENABLE_PRINTING)
#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
defined(OS_CHROMEOS)
auto RunPrintBackendService(
mojo::PendingReceiver<printing::mojom::PrintBackendService> receiver) {
return std::make_unique<printing::PrintBackendServiceImpl>(
std::move(receiver));
}
#endif
auto RunPrintCompositor( auto RunPrintCompositor(
mojo::PendingReceiver<printing::mojom::PrintCompositor> receiver) { mojo::PendingReceiver<printing::mojom::PrintCompositor> receiver) {
return std::make_unique<printing::PrintCompositorImpl>( return std::make_unique<printing::PrintCompositorImpl>(
std::move(receiver), true /* initialize_environment */, std::move(receiver), true /* initialize_environment */,
content::UtilityThread::Get()->GetIOTaskRunner()); content::UtilityThread::Get()->GetIOTaskRunner());
} }
#endif #endif // BUILDFLAG(ENABLE_PRINTING)
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
auto RunImeService( auto RunImeService(
...@@ -303,6 +319,10 @@ void RegisterMainThreadServices(mojo::ServiceFactory& services) { ...@@ -303,6 +319,10 @@ void RegisterMainThreadServices(mojo::ServiceFactory& services) {
#endif #endif
#if BUILDFLAG(ENABLE_PRINTING) #if BUILDFLAG(ENABLE_PRINTING)
#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
defined(OS_CHROMEOS)
services.Add(RunPrintBackendService);
#endif
services.Add(RunPrintCompositor); services.Add(RunPrintCompositor);
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment