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

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: rebase 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(
85 printer_status.reasons.begin(), printer_status.reasons.end(),
86 [reason](const PrinterReason& r) { return r.reason == reason; }) !=
87 printer_status.reasons.end();
88 }
89
90 // Returns true if the reason is paper related.
91 bool IsPaperReason(const PrinterReason& reason) {
92 static std::set<PrinterReason::Reason> paper_reasons = {
93 PrinterReason::MEDIA_JAM, PrinterReason::MEDIA_EMPTY,
94 PrinterReason::MEDIA_NEEDED, PrinterReason::MEDIA_LOW};
95
96 return paper_reasons.find(reason.reason) != paper_reasons.end();
97 }
98
99 // Returns true if the reason is ink related.
100 bool IsInkReason(const PrinterReason& reason) {
101 return reason.reason == PrinterReason::TONER_EMPTY ||
102 reason.reason == PrinterReason::TONER_LOW;
103 }
104
105 // Extracts an ErrorCode from PrinterStatus#reasons. Returns NO_ERROR if there
106 // are no reasons which indicate an error.
107 chromeos::CupsPrintJob::ErrorCode ErrorCodeFromReasons(
108 const printing::PrinterStatus& printer_status) {
109 const auto begin = printer_status.reasons.begin();
110 const auto end = printer_status.reasons.end();
111
112 if (std::find_if(begin, end, &IsPaperReason) != end) {
113 return chromeos::CupsPrintJob::ErrorCode::PAPER_JAM;
114 } else if (std::find_if(begin, end, &IsInkReason) != end) {
115 return chromeos::CupsPrintJob::ErrorCode::OUT_OF_INK;
116 }
117
118 return chromeos::CupsPrintJob::ErrorCode::NO_ERROR;
119 }
120
121 // Update job in the processing state. Returns true if the number of pages
122 // changed.
123 bool UpdateForProcessing(const printing::PrinterStatus& printer_status,
124 const printing::CupsJob& job,
125 chromeos::CupsPrintJob* print_job) {
126 if (ContainsReason(printer_status, PrinterReason::CONNECTING_TO_DEVICE)) {
127 // Check to see if we should time out.
128 base::TimeDelta time_waiting =
129 base::Time::Now() - base::Time::FromTimeT(job.processing_started);
130 if (time_waiting > base::TimeDelta::FromSeconds(kConnectingTimeout)) {
131 print_job->set_state(chromeos::CupsPrintJob::State::STATE_ERROR);
132 print_job->set_error_code(
133 chromeos::CupsPrintJob::ErrorCode::PRINTER_UNREACHABLE);
134 LOG(WARNING) << "Connecting to printer timed out";
135 }
136
137 return false;
xdai1 2017/03/13 18:48:37 return in line 134 instead of here?
skau 2017/03/14 02:31:29 The intention is that we always return false if wh
138 }
139
140 bool pages_updated = false;
141 if (job.current_pages <= 0) {
142 print_job->set_printed_page_number(0);
143 print_job->set_state(State::STATE_STARTED);
144 } else {
145 pages_updated = job.current_pages != print_job->printed_page_number();
146 print_job->set_printed_page_number(job.current_pages);
147 print_job->set_state(State::STATE_PAGE_DONE);
148 }
149
150 return pages_updated;
151 }
152
153 // Returns true if the state of |print_job| changed. Updates |print_job| with
154 // the data in |printer_status| and |job|.
155 bool UpdatePrintJob(const printing::PrinterStatus& printer_status,
156 const printing::CupsJob& job,
157 chromeos::CupsPrintJob* print_job) {
158 DCHECK_EQ(job.id, print_job->job_id());
159
160 State old_state = print_job->state();
161
162 bool pages_updated = false;
163 switch (job.state) {
164 case printing::CupsJob::PROCESSING:
165 pages_updated = UpdateForProcessing(printer_status, job, print_job);
166 break;
167 case printing::CupsJob::COMPLETED:
168 DCHECK_GE(job.current_pages, print_job->total_page_number());
169 print_job->set_state(State::STATE_DOCUMENT_DONE);
170 break;
171 case printing::CupsJob::ABORTED:
172 case printing::CupsJob::CANCELED:
173 print_job->set_error_code(ErrorCodeFromReasons(printer_status));
174 // fall through
175 default:
176 print_job->set_state(ConvertState(job.state));
177 break;
178 }
179
180 return print_job->state() != old_state || pages_updated;
181 }
182
74 } // namespace 183 } // namespace
75 184
76 namespace chromeos { 185 namespace chromeos {
77 186
78 CupsPrintJobManagerImpl::CupsPrintJobManagerImpl(Profile* profile) 187 CupsPrintJobManagerImpl::CupsPrintJobManagerImpl(Profile* profile)
79 : CupsPrintJobManager(profile), 188 : CupsPrintJobManager(profile),
80 cups_connection_(GURL(), HTTP_ENCRYPT_NEVER, false), 189 cups_connection_(GURL(), HTTP_ENCRYPT_NEVER, false),
81 weak_ptr_factory_(this) { 190 weak_ptr_factory_(this) {
82 registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, 191 registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
83 content::NotificationService::AllSources()); 192 content::NotificationService::AllSources());
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
220 std::vector<std::string> active_jobs; 329 std::vector<std::string> active_jobs;
221 for (const auto& queue : queues) { 330 for (const auto& queue : queues) {
222 for (auto& job : queue.jobs) { 331 for (auto& job : queue.jobs) {
223 std::string key = CupsPrintJob::GetUniqueId(job.printer_id, job.id); 332 std::string key = CupsPrintJob::GetUniqueId(job.printer_id, job.id);
224 const auto& entry = jobs_.find(key); 333 const auto& entry = jobs_.find(key);
225 if (entry == jobs_.end()) 334 if (entry == jobs_.end())
226 continue; 335 continue;
227 336
228 CupsPrintJob* print_job = entry->second.get(); 337 CupsPrintJob* print_job = entry->second.get();
229 338
230 // Update a job we're tracking. 339 if (UpdatePrintJob(queue.printer_status, job, print_job)) {
231 JobStateUpdated(print_job, ConvertState(job.state)); 340 // The state of the job changed, notify observers.
341 NotifyJobStateUpdate(print_job);
342 }
232 343
233 // Cleanup completed jobs. 344 // Cleanup completed jobs.
234 if (print_job->IsJobFinished()) { 345 if (print_job->IsJobFinished()) {
235 jobs_.erase(entry); 346 jobs_.erase(entry);
236 } else { 347 } else {
237 active_jobs.push_back(key); 348 active_jobs.push_back(key);
238 } 349 }
239 } 350 }
240 } 351 }
241 352
242 // Keep polling until all jobs complete or error. 353 // Keep polling until all jobs complete or error.
243 if (!active_jobs.empty()) { 354 if (!active_jobs.empty()) {
244 // During normal operations, we poll at the default rate. 355 // During normal operations, we poll at the default rate.
245 ScheduleQuery(); 356 ScheduleQuery();
246 } else if (!jobs_.empty()) { 357 } else if (!jobs_.empty()) {
247 // We're tracking jobs that we didn't receive an update for. Something bad 358 // We're tracking jobs that we didn't receive an update for. Something bad
248 // has happened. 359 // has happened.
249 LOG(ERROR) << "Lost track of (" << jobs_.size() << ") jobs"; 360 LOG(ERROR) << "Lost track of (" << jobs_.size() << ") jobs";
250 PurgeJobs(); 361 PurgeJobs();
251 } 362 }
252 } 363 }
253 364
254 void CupsPrintJobManagerImpl::PurgeJobs() { 365 void CupsPrintJobManagerImpl::PurgeJobs() {
255 for (const auto& entry : jobs_) { 366 for (const auto& entry : jobs_) {
256 // Declare all lost jobs errors. 367 // Declare all lost jobs errors.
257 JobStateUpdated(entry.second.get(), CupsPrintJob::State::STATE_ERROR); 368 CupsPrintJob* job = entry.second.get();
369 job->set_state(CupsPrintJob::State::STATE_ERROR);
370 NotifyJobStateUpdate(job);
258 } 371 }
259 372
260 jobs_.clear(); 373 jobs_.clear();
261 } 374 }
262 375
263 void CupsPrintJobManagerImpl::JobStateUpdated(CupsPrintJob* job, 376 void CupsPrintJobManagerImpl::NotifyJobStateUpdate(CupsPrintJob* job) {
264 CupsPrintJob::State new_state) { 377 switch (job->state()) {
265 if (job->state() == new_state) 378 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. 379 // State does not require notification.
274 break; 380 break;
275 case CupsPrintJob::State::STATE_WAITING: 381 case State::STATE_WAITING:
276 NotifyJobUpdated(job); 382 NotifyJobUpdated(job);
277 break; 383 break;
278 case CupsPrintJob::State::STATE_STARTED: 384 case State::STATE_STARTED:
279 NotifyJobStarted(job); 385 NotifyJobStarted(job);
280 break; 386 break;
281 case CupsPrintJob::State::STATE_RESUMED: 387 case State::STATE_PAGE_DONE:
388 NotifyJobUpdated(job);
389 break;
390 case State::STATE_RESUMED:
282 NotifyJobResumed(job); 391 NotifyJobResumed(job);
283 break; 392 break;
284 case CupsPrintJob::State::STATE_SUSPENDED: 393 case State::STATE_SUSPENDED:
285 NotifyJobSuspended(job); 394 NotifyJobSuspended(job);
286 break; 395 break;
287 case CupsPrintJob::State::STATE_CANCELLED: 396 case State::STATE_CANCELLED:
288 NotifyJobCanceled(job); 397 NotifyJobCanceled(job);
289 break; 398 break;
290 case CupsPrintJob::State::STATE_ERROR: 399 case State::STATE_ERROR:
291 NotifyJobError(job); 400 NotifyJobError(job);
292 break; 401 break;
293 case CupsPrintJob::State::STATE_PAGE_DONE: 402 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); 403 NotifyJobDone(job);
298 break; 404 break;
299 } 405 }
300 } 406 }
301 407
302 // static 408 // static
303 CupsPrintJobManager* CupsPrintJobManager::CreateInstance(Profile* profile) { 409 CupsPrintJobManager* CupsPrintJobManager::CreateInstance(Profile* profile) {
304 return new CupsPrintJobManagerImpl(profile); 410 return new CupsPrintJobManagerImpl(profile);
305 } 411 }
306 412
307 } // namespace chromeos 413 } // 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