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