Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(354)

Side by Side Diff: chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc

Issue 2705333007: Refine notifications for print jobs in progress. (Closed)
Patch Set: clarify per justincarlson@ Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/chromeos/printing/cups_print_job_manager_impl.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/chromeos/printing/cups_print_job_manager_impl.h" 5 #include "chrome/browser/chromeos/printing/cups_print_job_manager_impl.h"
6 6
7 #include <cups/cups.h> 7 #include <cups/cups.h>
8 #include <algorithm>
8 #include <set> 9 #include <set>
9 #include <string> 10 #include <string>
10 #include <utility> 11 #include <utility>
11 #include <vector> 12 #include <vector>
12 13
13 #include "base/bind.h" 14 #include "base/bind.h"
14 #include "base/memory/ptr_util.h" 15 #include "base/memory/ptr_util.h"
15 #include "base/stl_util.h" 16 #include "base/stl_util.h"
16 #include "base/strings/utf_string_conversions.h" 17 #include "base/strings/utf_string_conversions.h"
17 #include "base/threading/sequenced_task_runner_handle.h" 18 #include "base/threading/sequenced_task_runner_handle.h"
18 #include "chrome/browser/chrome_notification_types.h" 19 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/chromeos/printing/cups_print_job.h" 20 #include "chrome/browser/chromeos/printing/cups_print_job.h"
20 #include "chrome/browser/chromeos/printing/printers_manager.h" 21 #include "chrome/browser/chromeos/printing/printers_manager.h"
21 #include "chrome/browser/chromeos/printing/printers_manager_factory.h" 22 #include "chrome/browser/chromeos/printing/printers_manager_factory.h"
22 #include "chrome/browser/printing/print_job.h" 23 #include "chrome/browser/printing/print_job.h"
23 #include "chrome/browser/profiles/profile.h" 24 #include "chrome/browser/profiles/profile.h"
24 #include "content/public/browser/browser_context.h" 25 #include "content/public/browser/browser_context.h"
25 #include "content/public/browser/browser_thread.h" 26 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/notification_registrar.h" 27 #include "content/public/browser/notification_registrar.h"
27 #include "content/public/browser/notification_service.h" 28 #include "content/public/browser/notification_service.h"
28 #include "printing/backend/cups_connection.h" 29 #include "printing/backend/cups_connection.h"
29 #include "printing/printed_document.h" 30 #include "printing/printed_document.h"
30 31
31 namespace { 32 namespace {
32 33
33 // The rate in milliseconds at which we will poll CUPS for print job updates. 34 // The rate in milliseconds at which we will poll CUPS for print job updates.
34 const int kPollRate = 1000; 35 const int kPollRate = 1000;
35 36
37 // How long we'll wait to connect to a printer before declaring an error.
38 const int kConnectingTimeout = 20;
39
36 // Threshold for giving up on communicating with CUPS. 40 // Threshold for giving up on communicating with CUPS.
37 const int kRetryMax = 6; 41 const int kRetryMax = 6;
38 42
43 using State = chromeos::CupsPrintJob::State;
44 using ErrorCode = chromeos::CupsPrintJob::ErrorCode;
45
46 using PrinterReason = printing::PrinterStatus::PrinterReason;
47
39 // Returns the equivalient CupsPrintJob#State from a CupsJob#JobState. 48 // Returns the equivalient CupsPrintJob#State from a CupsJob#JobState.
40 chromeos::CupsPrintJob::State ConvertState(printing::CupsJob::JobState state) { 49 chromeos::CupsPrintJob::State ConvertState(printing::CupsJob::JobState state) {
41 using cpj = chromeos::CupsPrintJob::State;
42
43 switch (state) { 50 switch (state) {
44 case printing::CupsJob::PENDING: 51 case printing::CupsJob::PENDING:
45 return cpj::STATE_WAITING; 52 return State::STATE_WAITING;
46 case printing::CupsJob::HELD: 53 case printing::CupsJob::HELD:
47 return cpj::STATE_SUSPENDED; 54 return State::STATE_SUSPENDED;
48 case printing::CupsJob::PROCESSING: 55 case printing::CupsJob::PROCESSING:
49 return cpj::STATE_STARTED; 56 return State::STATE_STARTED;
50 case printing::CupsJob::CANCELED: 57 case printing::CupsJob::CANCELED:
51 return cpj::STATE_CANCELLED; 58 return State::STATE_CANCELLED;
52 case printing::CupsJob::COMPLETED: 59 case printing::CupsJob::COMPLETED:
53 return cpj::STATE_DOCUMENT_DONE; 60 return State::STATE_DOCUMENT_DONE;
54 case printing::CupsJob::STOPPED: 61 case printing::CupsJob::STOPPED:
55 return cpj::STATE_SUSPENDED; 62 return State::STATE_SUSPENDED;
56 case printing::CupsJob::ABORTED: 63 case printing::CupsJob::ABORTED:
57 return cpj::STATE_ERROR; 64 return State::STATE_ERROR;
58 case printing::CupsJob::UNKNOWN: 65 case printing::CupsJob::UNKNOWN:
59 break; 66 break;
60 } 67 }
61 68
62 NOTREACHED(); 69 NOTREACHED();
63 70
64 return cpj::STATE_NONE; 71 return State::STATE_NONE;
65 } 72 }
66 73
67 chromeos::QueryResult QueryCups(::printing::CupsConnection* connection, 74 chromeos::QueryResult QueryCups(::printing::CupsConnection* connection,
68 const std::vector<std::string>& printer_ids) { 75 const std::vector<std::string>& printer_ids) {
69 chromeos::QueryResult result; 76 chromeos::QueryResult result;
70 result.success = connection->GetJobs(printer_ids, &result.queues); 77 result.success = connection->GetJobs(printer_ids, &result.queues);
71 return result; 78 return result;
72 } 79 }
73 80
81 // Returns true if |printer_status|.reasons contains |reason|.
82 bool ContainsReason(const printing::PrinterStatus printer_status,
83 PrinterReason::Reason reason) {
84 return std::find_if(printer_status.reasons.begin(),
85 printer_status.reasons.end(),
86 [&reason](const PrinterReason& r) {
87 return r.reason == reason;
88 }) != printer_status.reasons.end();
89 }
90
91 // Extracts an ErrorCode from PrinterStatus#reasons. Returns NO_ERROR if there
92 // are no reasons which indicate an error.
93 chromeos::CupsPrintJob::ErrorCode ErrorCodeFromReasons(
94 const printing::PrinterStatus& printer_status) {
95 for (const auto& reason : printer_status.reasons) {
96 switch (reason.reason) {
97 case PrinterReason::MEDIA_JAM:
98 case PrinterReason::MEDIA_EMPTY:
99 case PrinterReason::MEDIA_NEEDED:
100 case PrinterReason::MEDIA_LOW:
101 return chromeos::CupsPrintJob::ErrorCode::PAPER_JAM;
102 case PrinterReason::TONER_EMPTY:
103 case PrinterReason::TONER_LOW:
104 return chromeos::CupsPrintJob::ErrorCode::OUT_OF_INK;
105 default:
106 break;
107 }
108 }
109 return chromeos::CupsPrintJob::ErrorCode::NO_ERROR;
110 }
111
112 // Check if the job should timeout. Returns true if the job has timed out.
113 bool EnforceTimeout(const printing::CupsJob& job,
114 chromeos::CupsPrintJob* print_job) {
115 // Check to see if we should time out.
116 base::TimeDelta time_waiting =
117 base::Time::Now() - base::Time::FromTimeT(job.processing_started);
118 if (time_waiting > base::TimeDelta::FromSeconds(kConnectingTimeout)) {
119 print_job->set_state(chromeos::CupsPrintJob::State::STATE_ERROR);
120 print_job->set_error_code(
121 chromeos::CupsPrintJob::ErrorCode::PRINTER_UNREACHABLE);
122 LOG(WARNING) << "Connecting to printer timed out";
123 return true;
124 }
125
126 return false;
127 }
128
129 // Update the current printed page. Returns true of the page has been updated.
130 bool UpdateCurrentPage(const printing::CupsJob& job,
131 chromeos::CupsPrintJob* print_job) {
132 bool pages_updated = false;
133 if (job.current_pages <= 0) {
134 print_job->set_printed_page_number(0);
135 print_job->set_state(State::STATE_STARTED);
136 } else {
137 pages_updated = job.current_pages != print_job->printed_page_number();
138 print_job->set_printed_page_number(job.current_pages);
139 print_job->set_state(State::STATE_PAGE_DONE);
140 }
141
142 return pages_updated;
143 }
144
145 // Updates the state of a print job based on |printer_status| and |job|. Returns
146 // true if observers need to be notified of an update.
147 bool UpdatePrintJob(const printing::PrinterStatus& printer_status,
148 const printing::CupsJob& job,
149 chromeos::CupsPrintJob* print_job) {
150 DCHECK_EQ(job.id, print_job->job_id());
151
152 State old_state = print_job->state();
153
154 bool pages_updated = false;
155 switch (job.state) {
156 case printing::CupsJob::PROCESSING:
157 if (ContainsReason(printer_status, PrinterReason::CONNECTING_TO_DEVICE)) {
158 EnforceTimeout(job, print_job);
Carlson 2017/03/14 21:54:05 I suspect you meant to use the return value for En
skau 2017/03/15 01:29:09 I did. But I actually need it later since the cha
159 break;
160 }
161 pages_updated = UpdateCurrentPage(job, print_job);
162 break;
163 case printing::CupsJob::COMPLETED:
164 DCHECK_GE(job.current_pages, print_job->total_page_number());
165 print_job->set_state(State::STATE_DOCUMENT_DONE);
166 break;
167 case printing::CupsJob::ABORTED:
168 case printing::CupsJob::CANCELED:
169 print_job->set_error_code(ErrorCodeFromReasons(printer_status));
170 // fall through
171 default:
172 print_job->set_state(ConvertState(job.state));
173 break;
174 }
175
176 return print_job->state() != old_state || pages_updated;
177 }
178
74 } // namespace 179 } // namespace
75 180
76 namespace chromeos { 181 namespace chromeos {
77 182
78 CupsPrintJobManagerImpl::CupsPrintJobManagerImpl(Profile* profile) 183 CupsPrintJobManagerImpl::CupsPrintJobManagerImpl(Profile* profile)
79 : CupsPrintJobManager(profile), 184 : CupsPrintJobManager(profile),
80 cups_connection_(GURL(), HTTP_ENCRYPT_NEVER, false), 185 cups_connection_(GURL(), HTTP_ENCRYPT_NEVER, false),
81 weak_ptr_factory_(this) { 186 weak_ptr_factory_(this) {
82 registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, 187 registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
83 content::NotificationService::AllSources()); 188 content::NotificationService::AllSources());
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
220 std::vector<std::string> active_jobs; 325 std::vector<std::string> active_jobs;
221 for (const auto& queue : queues) { 326 for (const auto& queue : queues) {
222 for (auto& job : queue.jobs) { 327 for (auto& job : queue.jobs) {
223 std::string key = CupsPrintJob::GetUniqueId(job.printer_id, job.id); 328 std::string key = CupsPrintJob::GetUniqueId(job.printer_id, job.id);
224 const auto& entry = jobs_.find(key); 329 const auto& entry = jobs_.find(key);
225 if (entry == jobs_.end()) 330 if (entry == jobs_.end())
226 continue; 331 continue;
227 332
228 CupsPrintJob* print_job = entry->second.get(); 333 CupsPrintJob* print_job = entry->second.get();
229 334
230 // Update a job we're tracking. 335 if (UpdatePrintJob(queue.printer_status, job, print_job)) {
231 JobStateUpdated(print_job, ConvertState(job.state)); 336 // The state of the job changed, notify observers.
337 NotifyJobStateUpdate(print_job);
338 }
232 339
233 // Cleanup completed jobs. 340 // Cleanup completed jobs.
234 if (print_job->IsJobFinished()) { 341 if (print_job->IsJobFinished()) {
235 jobs_.erase(entry); 342 jobs_.erase(entry);
236 } else { 343 } else {
237 active_jobs.push_back(key); 344 active_jobs.push_back(key);
238 } 345 }
239 } 346 }
240 } 347 }
241 348
242 // Keep polling until all jobs complete or error. 349 // Keep polling until all jobs complete or error.
243 if (!active_jobs.empty()) { 350 if (!active_jobs.empty()) {
244 // During normal operations, we poll at the default rate. 351 // During normal operations, we poll at the default rate.
245 ScheduleQuery(); 352 ScheduleQuery();
246 } else if (!jobs_.empty()) { 353 } else if (!jobs_.empty()) {
247 // We're tracking jobs that we didn't receive an update for. Something bad 354 // We're tracking jobs that we didn't receive an update for. Something bad
248 // has happened. 355 // has happened.
249 LOG(ERROR) << "Lost track of (" << jobs_.size() << ") jobs"; 356 LOG(ERROR) << "Lost track of (" << jobs_.size() << ") jobs";
250 PurgeJobs(); 357 PurgeJobs();
251 } 358 }
252 } 359 }
253 360
254 void CupsPrintJobManagerImpl::PurgeJobs() { 361 void CupsPrintJobManagerImpl::PurgeJobs() {
255 for (const auto& entry : jobs_) { 362 for (const auto& entry : jobs_) {
256 // Declare all lost jobs errors. 363 // Declare all lost jobs errors.
257 JobStateUpdated(entry.second.get(), CupsPrintJob::State::STATE_ERROR); 364 CupsPrintJob* job = entry.second.get();
365 job->set_state(CupsPrintJob::State::STATE_ERROR);
366 NotifyJobStateUpdate(job);
258 } 367 }
259 368
260 jobs_.clear(); 369 jobs_.clear();
261 } 370 }
262 371
263 void CupsPrintJobManagerImpl::JobStateUpdated(CupsPrintJob* job, 372 void CupsPrintJobManagerImpl::NotifyJobStateUpdate(CupsPrintJob* job) {
264 CupsPrintJob::State new_state) { 373 switch (job->state()) {
265 if (job->state() == new_state) 374 case State::STATE_NONE:
266 return;
267
268 // We don't track state transitions because some of them might be missed due
269 // to how we query jobs.
270 job->set_state(new_state);
271 switch (new_state) {
272 case CupsPrintJob::State::STATE_NONE:
273 // State does not require notification. 375 // State does not require notification.
274 break; 376 break;
275 case CupsPrintJob::State::STATE_WAITING: 377 case State::STATE_WAITING:
276 NotifyJobUpdated(job); 378 NotifyJobUpdated(job);
277 break; 379 break;
278 case CupsPrintJob::State::STATE_STARTED: 380 case State::STATE_STARTED:
279 NotifyJobStarted(job); 381 NotifyJobStarted(job);
280 break; 382 break;
281 case CupsPrintJob::State::STATE_RESUMED: 383 case State::STATE_PAGE_DONE:
384 NotifyJobUpdated(job);
385 break;
386 case State::STATE_RESUMED:
282 NotifyJobResumed(job); 387 NotifyJobResumed(job);
283 break; 388 break;
284 case CupsPrintJob::State::STATE_SUSPENDED: 389 case State::STATE_SUSPENDED:
285 NotifyJobSuspended(job); 390 NotifyJobSuspended(job);
286 break; 391 break;
287 case CupsPrintJob::State::STATE_CANCELLED: 392 case State::STATE_CANCELLED:
288 NotifyJobCanceled(job); 393 NotifyJobCanceled(job);
289 break; 394 break;
290 case CupsPrintJob::State::STATE_ERROR: 395 case State::STATE_ERROR:
291 NotifyJobError(job); 396 NotifyJobError(job);
292 break; 397 break;
293 case CupsPrintJob::State::STATE_PAGE_DONE: 398 case State::STATE_DOCUMENT_DONE:
294 NOTREACHED() << "CUPS does not surface this state so it's not expected";
295 break;
296 case CupsPrintJob::State::STATE_DOCUMENT_DONE:
297 NotifyJobDone(job); 399 NotifyJobDone(job);
298 break; 400 break;
299 } 401 }
300 } 402 }
301 403
302 // static 404 // static
303 CupsPrintJobManager* CupsPrintJobManager::CreateInstance(Profile* profile) { 405 CupsPrintJobManager* CupsPrintJobManager::CreateInstance(Profile* profile) {
304 return new CupsPrintJobManagerImpl(profile); 406 return new CupsPrintJobManagerImpl(profile);
305 } 407 }
306 408
307 } // namespace chromeos 409 } // namespace chromeos
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/printing/cups_print_job_manager_impl.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698