1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/extensions/printing/printing_api_handler.h"
6
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/no_destructor.h"
13 #include "base/stl_util.h"
14 #include "base/task/post_task.h"
15 #include "base/threading/sequenced_task_runner_handle.h"
16 #include "chrome/browser/chromeos/extensions/printing/printing_api_utils.h"
17 #include "chrome/browser/chromeos/printing/cups_print_job.h"
18 #include "chrome/browser/chromeos/printing/cups_printers_manager.h"
19 #include "chrome/browser/chromeos/printing/cups_wrapper.h"
20 #include "chrome/browser/chromeos/printing/printer_configurer.h"
21 #include "chrome/browser/chromeos/printing/printer_error_codes.h"
22 #include "chrome/browser/chromeos/profiles/profile_helper.h"
23 #include "chrome/browser/printing/print_preview_sticky_settings.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "chrome/common/pref_names.h"
26 #include "chromeos/printing/printer_configuration.h"
27 #include "components/prefs/pref_registry_simple.h"
28 #include "components/prefs/pref_service.h"
29 #include "components/printing/common/cloud_print_cdd_conversion.h"
30 #include "content/public/browser/browser_context.h"
31 #include "extensions/browser/event_router.h"
32 #include "extensions/browser/extension_registry.h"
33 #include "printing/backend/cups_jobs.h"
34 #include "printing/backend/print_backend.h"
35
36 namespace extensions {
37
38 namespace {
39
40 constexpr char kInvalidPrinterIdError[] = "Invalid printer ID";
41 constexpr char kNoActivePrintJobWithIdError[] =
42 "No active print job with given ID";
43
44 } // namespace
45
46 // static
CreateForTesting(content::BrowserContext * browser_context,EventRouter * event_router,ExtensionRegistry * extension_registry,chromeos::CupsPrintJobManager * print_job_manager,chromeos::CupsPrintersManager * printers_manager,std::unique_ptr<PrintJobController> print_job_controller,std::unique_ptr<chromeos::PrinterConfigurer> printer_configurer,std::unique_ptr<chromeos::CupsWrapper> cups_wrapper)47 std::unique_ptr<PrintingAPIHandler> PrintingAPIHandler::CreateForTesting(
48 content::BrowserContext* browser_context,
49 EventRouter* event_router,
50 ExtensionRegistry* extension_registry,
51 chromeos::CupsPrintJobManager* print_job_manager,
52 chromeos::CupsPrintersManager* printers_manager,
53 std::unique_ptr<PrintJobController> print_job_controller,
54 std::unique_ptr<chromeos::PrinterConfigurer> printer_configurer,
55 std::unique_ptr<chromeos::CupsWrapper> cups_wrapper) {
56 return base::WrapUnique(new PrintingAPIHandler(
57 browser_context, event_router, extension_registry, print_job_manager,
58 printers_manager, std::move(print_job_controller),
59 std::move(printer_configurer), std::move(cups_wrapper)));
60 }
61
PrintingAPIHandler(content::BrowserContext * browser_context)62 PrintingAPIHandler::PrintingAPIHandler(content::BrowserContext* browser_context)
63 : PrintingAPIHandler(
64 browser_context,
65 EventRouter::Get(browser_context),
66 ExtensionRegistry::Get(browser_context),
67 chromeos::CupsPrintJobManagerFactory::GetForBrowserContext(
68 browser_context),
69 chromeos::CupsPrintersManagerFactory::GetForBrowserContext(
70 browser_context),
71 PrintJobController::Create(
72 chromeos::CupsPrintJobManagerFactory::GetForBrowserContext(
73 browser_context)),
74 chromeos::PrinterConfigurer::Create(
75 Profile::FromBrowserContext(browser_context)),
76 chromeos::CupsWrapper::Create()) {}
77
PrintingAPIHandler(content::BrowserContext * browser_context,EventRouter * event_router,ExtensionRegistry * extension_registry,chromeos::CupsPrintJobManager * print_job_manager,chromeos::CupsPrintersManager * printers_manager,std::unique_ptr<PrintJobController> print_job_controller,std::unique_ptr<chromeos::PrinterConfigurer> printer_configurer,std::unique_ptr<chromeos::CupsWrapper> cups_wrapper)78 PrintingAPIHandler::PrintingAPIHandler(
79 content::BrowserContext* browser_context,
80 EventRouter* event_router,
81 ExtensionRegistry* extension_registry,
82 chromeos::CupsPrintJobManager* print_job_manager,
83 chromeos::CupsPrintersManager* printers_manager,
84 std::unique_ptr<PrintJobController> print_job_controller,
85 std::unique_ptr<chromeos::PrinterConfigurer> printer_configurer,
86 std::unique_ptr<chromeos::CupsWrapper> cups_wrapper)
87 : browser_context_(browser_context),
88 event_router_(event_router),
89 extension_registry_(extension_registry),
90 print_job_manager_(print_job_manager),
91 printers_manager_(printers_manager),
92 print_job_controller_(std::move(print_job_controller)),
93 printer_capabilities_provider_(printers_manager,
94 std::move(printer_configurer)),
95 cups_wrapper_(std::move(cups_wrapper)),
96 print_job_manager_observer_(this) {
97 print_job_manager_observer_.Add(print_job_manager_);
98 }
99
100 PrintingAPIHandler::~PrintingAPIHandler() = default;
101
102 // static
103 BrowserContextKeyedAPIFactory<PrintingAPIHandler>*
GetFactoryInstance()104 PrintingAPIHandler::GetFactoryInstance() {
105 static base::NoDestructor<BrowserContextKeyedAPIFactory<PrintingAPIHandler>>
106 instance;
107 return instance.get();
108 }
109
110 // static
Get(content::BrowserContext * browser_context)111 PrintingAPIHandler* PrintingAPIHandler::Get(
112 content::BrowserContext* browser_context) {
113 return BrowserContextKeyedAPIFactory<PrintingAPIHandler>::Get(
114 browser_context);
115 }
116
117 // static
RegisterProfilePrefs(PrefRegistrySimple * registry)118 void PrintingAPIHandler::RegisterProfilePrefs(PrefRegistrySimple* registry) {
119 registry->RegisterListPref(prefs::kPrintingAPIExtensionsAllowlist);
120 }
121
SubmitJob(gfx::NativeWindow native_window,scoped_refptr<const extensions::Extension> extension,std::unique_ptr<api::printing::SubmitJob::Params> params,PrintJobSubmitter::SubmitJobCallback callback)122 void PrintingAPIHandler::SubmitJob(
123 gfx::NativeWindow native_window,
124 scoped_refptr<const extensions::Extension> extension,
125 std::unique_ptr<api::printing::SubmitJob::Params> params,
126 PrintJobSubmitter::SubmitJobCallback callback) {
127 auto print_job_submitter = std::make_unique<PrintJobSubmitter>(
128 native_window, browser_context_, printers_manager_,
129 &printer_capabilities_provider_, print_job_controller_.get(),
130 &pdf_flattener_, std::move(extension), std::move(params->request));
131 PrintJobSubmitter* print_job_submitter_ptr = print_job_submitter.get();
132 print_job_submitter_ptr->Start(base::BindOnce(
133 &PrintingAPIHandler::OnPrintJobSubmitted, weak_ptr_factory_.GetWeakPtr(),
134 std::move(print_job_submitter), std::move(callback)));
135 }
136
OnPrintJobSubmitted(std::unique_ptr<PrintJobSubmitter> print_job_submitter,PrintJobSubmitter::SubmitJobCallback callback,base::Optional<api::printing::SubmitJobStatus> status,std::unique_ptr<std::string> job_id,base::Optional<std::string> error)137 void PrintingAPIHandler::OnPrintJobSubmitted(
138 std::unique_ptr<PrintJobSubmitter> print_job_submitter,
139 PrintJobSubmitter::SubmitJobCallback callback,
140 base::Optional<api::printing::SubmitJobStatus> status,
141 std::unique_ptr<std::string> job_id,
142 base::Optional<std::string> error) {
143 base::SequencedTaskRunnerHandle::Get()->PostTask(
144 FROM_HERE,
145 base::BindOnce(std::move(callback), status, std::move(job_id), error));
146 }
147
CancelJob(const std::string & extension_id,const std::string & job_id)148 base::Optional<std::string> PrintingAPIHandler::CancelJob(
149 const std::string& extension_id,
150 const std::string& job_id) {
151 auto it = print_jobs_extension_ids_.find(job_id);
152 // If there was no print job with specified id sent by the extension return
153 // an error.
154 if (it == print_jobs_extension_ids_.end() || it->second != extension_id)
155 return kNoActivePrintJobWithIdError;
156
157 // If we can't cancel the print job (e.g. it's in terminated state) return an
158 // error.
159 if (!print_job_controller_->CancelPrintJob(job_id))
160 return kNoActivePrintJobWithIdError;
161
162 // Return no error otherwise.
163 return base::nullopt;
164 }
165
GetPrinters()166 std::vector<api::printing::Printer> PrintingAPIHandler::GetPrinters() {
167 PrefService* prefs =
168 Profile::FromBrowserContext(browser_context_)->GetPrefs();
169
170 base::Optional<DefaultPrinterRules> default_printer_rules =
171 GetDefaultPrinterRules(prefs->GetString(
172 prefs::kPrintPreviewDefaultDestinationSelectionRules));
173
174 auto* sticky_settings = printing::PrintPreviewStickySettings::GetInstance();
175 sticky_settings->RestoreFromPrefs(prefs);
176 base::flat_map<std::string, int> recently_used_ranks =
177 sticky_settings->GetPrinterRecentlyUsedRanks();
178
179 static constexpr chromeos::PrinterClass kPrinterClasses[] = {
180 chromeos::PrinterClass::kEnterprise,
181 chromeos::PrinterClass::kSaved,
182 chromeos::PrinterClass::kAutomatic,
183 };
184 std::vector<api::printing::Printer> all_printers;
185 for (auto printer_class : kPrinterClasses) {
186 const std::vector<chromeos::Printer>& printers =
187 printers_manager_->GetPrinters(printer_class);
188 for (const auto& printer : printers) {
189 all_printers.emplace_back(
190 PrinterToIdl(printer, default_printer_rules, recently_used_ranks));
191 }
192 }
193 return all_printers;
194 }
195
GetPrinterInfo(const std::string & printer_id,GetPrinterInfoCallback callback)196 void PrintingAPIHandler::GetPrinterInfo(const std::string& printer_id,
197 GetPrinterInfoCallback callback) {
198 if (!printers_manager_->GetPrinter(printer_id)) {
199 base::SequencedTaskRunnerHandle::Get()->PostTask(
200 FROM_HERE,
201 base::BindOnce(std::move(callback), /*capabilities=*/base::nullopt,
202 /*status=*/base::nullopt, kInvalidPrinterIdError));
203 return;
204 }
205 printer_capabilities_provider_.GetPrinterCapabilities(
206 printer_id, base::BindOnce(&PrintingAPIHandler::GetPrinterStatus,
207 weak_ptr_factory_.GetWeakPtr(), printer_id,
208 std::move(callback)));
209 }
210
GetPrinterStatus(const std::string & printer_id,GetPrinterInfoCallback callback,base::Optional<printing::PrinterSemanticCapsAndDefaults> capabilities)211 void PrintingAPIHandler::GetPrinterStatus(
212 const std::string& printer_id,
213 GetPrinterInfoCallback callback,
214 base::Optional<printing::PrinterSemanticCapsAndDefaults> capabilities) {
215 if (!capabilities.has_value()) {
216 base::SequencedTaskRunnerHandle::Get()->PostTask(
217 FROM_HERE,
218 base::BindOnce(std::move(callback), /*capabilities=*/base::nullopt,
219 api::printing::PRINTER_STATUS_UNREACHABLE,
220 /*error=*/base::nullopt));
221 return;
222 }
223 base::Value capabilities_value =
224 cloud_print::PrinterSemanticCapsAndDefaultsToCdd(capabilities.value());
225 cups_wrapper_->QueryCupsPrinterStatus(
226 printer_id,
227 base::BindOnce(&PrintingAPIHandler::OnPrinterStatusRetrieved,
228 weak_ptr_factory_.GetWeakPtr(), std::move(callback),
229 std::move(capabilities_value)));
230 }
231
OnPrinterStatusRetrieved(GetPrinterInfoCallback callback,base::Value capabilities,std::unique_ptr<::printing::PrinterStatus> printer_status)232 void PrintingAPIHandler::OnPrinterStatusRetrieved(
233 GetPrinterInfoCallback callback,
234 base::Value capabilities,
235 std::unique_ptr<::printing::PrinterStatus> printer_status) {
236 if (!printer_status) {
237 base::SequencedTaskRunnerHandle::Get()->PostTask(
238 FROM_HERE, base::BindOnce(std::move(callback), std::move(capabilities),
239 api::printing::PRINTER_STATUS_UNREACHABLE,
240 /*error=*/base::nullopt));
241 return;
242 }
243 base::SequencedTaskRunnerHandle::Get()->PostTask(
244 FROM_HERE,
245 base::BindOnce(
246 std::move(callback), std::move(capabilities),
247 PrinterStatusToIdl(chromeos::PrinterErrorCodeFromPrinterStatusReasons(
248 *printer_status)),
249 /*error=*/base::nullopt));
250 }
251
SetPrintJobControllerForTesting(std::unique_ptr<PrintJobController> print_job_controller)252 void PrintingAPIHandler::SetPrintJobControllerForTesting(
253 std::unique_ptr<PrintJobController> print_job_controller) {
254 print_job_controller_ = std::move(print_job_controller);
255 }
256
OnPrintJobCreated(base::WeakPtr<chromeos::CupsPrintJob> job)257 void PrintingAPIHandler::OnPrintJobCreated(
258 base::WeakPtr<chromeos::CupsPrintJob> job) {
259 DispatchJobStatusChangedEvent(api::printing::JOB_STATUS_PENDING, job);
260 if (!job || job->source() != printing::PrintJob::Source::EXTENSION)
261 return;
262 const std::string& extension_id = job->source_id();
263 const std::string& job_id = job->GetUniqueId();
264 print_jobs_extension_ids_[job_id] = extension_id;
265 print_job_controller_->OnPrintJobCreated(extension_id, job_id, job);
266 }
267
OnPrintJobStarted(base::WeakPtr<chromeos::CupsPrintJob> job)268 void PrintingAPIHandler::OnPrintJobStarted(
269 base::WeakPtr<chromeos::CupsPrintJob> job) {
270 DispatchJobStatusChangedEvent(api::printing::JOB_STATUS_IN_PROGRESS, job);
271 }
272
OnPrintJobDone(base::WeakPtr<chromeos::CupsPrintJob> job)273 void PrintingAPIHandler::OnPrintJobDone(
274 base::WeakPtr<chromeos::CupsPrintJob> job) {
275 DispatchJobStatusChangedEvent(api::printing::JOB_STATUS_PRINTED, job);
276 FinishJob(job);
277 }
278
OnPrintJobError(base::WeakPtr<chromeos::CupsPrintJob> job)279 void PrintingAPIHandler::OnPrintJobError(
280 base::WeakPtr<chromeos::CupsPrintJob> job) {
281 DispatchJobStatusChangedEvent(api::printing::JOB_STATUS_FAILED, job);
282 FinishJob(job);
283 }
284
OnPrintJobCancelled(base::WeakPtr<chromeos::CupsPrintJob> job)285 void PrintingAPIHandler::OnPrintJobCancelled(
286 base::WeakPtr<chromeos::CupsPrintJob> job) {
287 DispatchJobStatusChangedEvent(api::printing::JOB_STATUS_CANCELED, job);
288 FinishJob(job);
289 }
290
DispatchJobStatusChangedEvent(api::printing::JobStatus job_status,base::WeakPtr<chromeos::CupsPrintJob> job)291 void PrintingAPIHandler::DispatchJobStatusChangedEvent(
292 api::printing::JobStatus job_status,
293 base::WeakPtr<chromeos::CupsPrintJob> job) {
294 if (!job || job->source() != printing::PrintJob::Source::EXTENSION)
295 return;
296
297 auto event =
298 std::make_unique<Event>(events::PRINTING_ON_JOB_STATUS_CHANGED,
299 api::printing::OnJobStatusChanged::kEventName,
300 api::printing::OnJobStatusChanged::Create(
301 job->GetUniqueId(), job_status));
302
303 if (extension_registry_->enabled_extensions().Contains(job->source_id()))
304 event_router_->DispatchEventToExtension(job->source_id(), std::move(event));
305 }
306
FinishJob(base::WeakPtr<chromeos::CupsPrintJob> job)307 void PrintingAPIHandler::FinishJob(base::WeakPtr<chromeos::CupsPrintJob> job) {
308 if (!job || job->source() != printing::PrintJob::Source::EXTENSION)
309 return;
310 const std::string& job_id = job->GetUniqueId();
311 print_jobs_extension_ids_.erase(job_id);
312 print_job_controller_->OnPrintJobFinished(job_id);
313 }
314
315 template <>
316 KeyedService*
BuildServiceInstanceFor(content::BrowserContext * context) const317 BrowserContextKeyedAPIFactory<PrintingAPIHandler>::BuildServiceInstanceFor(
318 content::BrowserContext* context) const {
319 Profile* profile = Profile::FromBrowserContext(context);
320 // We do not want an instance of PrintingAPIHandler on the lock screen.
321 // This will lead to multiple printing notifications.
322 if (chromeos::ProfileHelper::IsLockScreenAppProfile(profile) ||
323 chromeos::ProfileHelper::IsSigninProfile(profile)) {
324 return nullptr;
325 }
326 return new PrintingAPIHandler(context);
327 }
328
329 } // namespace extensions
330