Chromium Code Reviews| Index: chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc |
| diff --git a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc |
| index 9806dd7b78c3109ecbe98cdaf53ebae273759849..49a8e9464c480074d8b166dbe06a1e5bcef4b9de 100644 |
| --- a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc |
| +++ b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc |
| @@ -5,6 +5,7 @@ |
| #include "chrome/browser/chromeos/printing/cups_print_job_manager_impl.h" |
| #include <cups/cups.h> |
| +#include <algorithm> |
| #include <set> |
| #include <string> |
| #include <utility> |
| @@ -33,35 +34,41 @@ namespace { |
| // The rate in milliseconds at which we will poll CUPS for print job updates. |
| const int kPollRate = 1000; |
| +// How long we'll wait to connect to a printer before declaring an error. |
| +const int kConnectingTimeout = 20; |
| + |
| // Threshold for giving up on communicating with CUPS. |
| const int kRetryMax = 6; |
| +using State = chromeos::CupsPrintJob::State; |
| +using ErrorCode = chromeos::CupsPrintJob::ErrorCode; |
| + |
| +using PrinterReason = printing::PrinterStatus::PrinterReason; |
| + |
| // Returns the equivalient CupsPrintJob#State from a CupsJob#JobState. |
| chromeos::CupsPrintJob::State ConvertState(printing::CupsJob::JobState state) { |
| - using cpj = chromeos::CupsPrintJob::State; |
| - |
| switch (state) { |
| case printing::CupsJob::PENDING: |
| - return cpj::STATE_WAITING; |
| + return State::STATE_WAITING; |
| case printing::CupsJob::HELD: |
| - return cpj::STATE_SUSPENDED; |
| + return State::STATE_SUSPENDED; |
| case printing::CupsJob::PROCESSING: |
| - return cpj::STATE_STARTED; |
| + return State::STATE_STARTED; |
| case printing::CupsJob::CANCELED: |
| - return cpj::STATE_CANCELLED; |
| + return State::STATE_CANCELLED; |
| case printing::CupsJob::COMPLETED: |
| - return cpj::STATE_DOCUMENT_DONE; |
| + return State::STATE_DOCUMENT_DONE; |
| case printing::CupsJob::STOPPED: |
| - return cpj::STATE_SUSPENDED; |
| + return State::STATE_SUSPENDED; |
| case printing::CupsJob::ABORTED: |
| - return cpj::STATE_ERROR; |
| + return State::STATE_ERROR; |
| case printing::CupsJob::UNKNOWN: |
| break; |
| } |
| NOTREACHED(); |
| - return cpj::STATE_NONE; |
| + return State::STATE_NONE; |
| } |
| chromeos::QueryResult QueryCups(::printing::CupsConnection* connection, |
| @@ -71,6 +78,108 @@ chromeos::QueryResult QueryCups(::printing::CupsConnection* connection, |
| return result; |
| } |
| +// Returns true if |printer_status|.reasons contains |reason|. |
| +bool ContainsReason(const printing::PrinterStatus printer_status, |
| + PrinterReason::Reason reason) { |
| + return std::find_if( |
| + printer_status.reasons.begin(), printer_status.reasons.end(), |
| + [reason](const PrinterReason& r) { return r.reason == reason; }) != |
| + printer_status.reasons.end(); |
| +} |
| + |
| +// Returns true if the reason is paper related. |
| +bool IsPaperReason(const PrinterReason& reason) { |
| + static std::set<PrinterReason::Reason> paper_reasons = { |
| + PrinterReason::MEDIA_JAM, PrinterReason::MEDIA_EMPTY, |
| + PrinterReason::MEDIA_NEEDED, PrinterReason::MEDIA_LOW}; |
| + |
| + return paper_reasons.find(reason.reason) != paper_reasons.end(); |
| +} |
| + |
| +// Returns true if the reason is ink related. |
| +bool IsInkReason(const PrinterReason& reason) { |
| + return reason.reason == PrinterReason::TONER_EMPTY || |
| + reason.reason == PrinterReason::TONER_LOW; |
| +} |
| + |
| +// Extracts an ErrorCode from PrinterStatus#reasons. Returns NO_ERROR if there |
| +// are no reasons which indicate an error. |
| +chromeos::CupsPrintJob::ErrorCode ErrorCodeFromReasons( |
| + const printing::PrinterStatus& printer_status) { |
| + const auto begin = printer_status.reasons.begin(); |
| + const auto end = printer_status.reasons.end(); |
| + |
| + if (std::find_if(begin, end, &IsPaperReason) != end) { |
| + return chromeos::CupsPrintJob::ErrorCode::PAPER_JAM; |
| + } else if (std::find_if(begin, end, &IsInkReason) != end) { |
| + return chromeos::CupsPrintJob::ErrorCode::OUT_OF_INK; |
| + } |
| + |
| + return chromeos::CupsPrintJob::ErrorCode::NO_ERROR; |
| +} |
| + |
| +// Update job in the processing state. Returns true if the number of pages |
| +// changed. |
| +bool UpdateForProcessing(const printing::PrinterStatus& printer_status, |
| + const printing::CupsJob& job, |
| + chromeos::CupsPrintJob* print_job) { |
| + if (ContainsReason(printer_status, PrinterReason::CONNECTING_TO_DEVICE)) { |
| + // Check to see if we should time out. |
| + base::TimeDelta time_waiting = |
| + base::Time::Now() - base::Time::FromTimeT(job.processing_started); |
| + if (time_waiting > base::TimeDelta::FromSeconds(kConnectingTimeout)) { |
| + print_job->set_state(chromeos::CupsPrintJob::State::STATE_ERROR); |
| + print_job->set_error_code( |
| + chromeos::CupsPrintJob::ErrorCode::PRINTER_UNREACHABLE); |
| + LOG(WARNING) << "Connecting to printer timed out"; |
| + } |
| + |
| + 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
|
| + } |
| + |
| + bool pages_updated = false; |
| + if (job.current_pages <= 0) { |
| + print_job->set_printed_page_number(0); |
| + print_job->set_state(State::STATE_STARTED); |
| + } else { |
| + pages_updated = job.current_pages != print_job->printed_page_number(); |
| + print_job->set_printed_page_number(job.current_pages); |
| + print_job->set_state(State::STATE_PAGE_DONE); |
| + } |
| + |
| + return pages_updated; |
| +} |
| + |
| +// Returns true if the state of |print_job| changed. Updates |print_job| with |
| +// the data in |printer_status| and |job|. |
| +bool UpdatePrintJob(const printing::PrinterStatus& printer_status, |
| + const printing::CupsJob& job, |
| + chromeos::CupsPrintJob* print_job) { |
| + DCHECK_EQ(job.id, print_job->job_id()); |
| + |
| + State old_state = print_job->state(); |
| + |
| + bool pages_updated = false; |
| + switch (job.state) { |
| + case printing::CupsJob::PROCESSING: |
| + pages_updated = UpdateForProcessing(printer_status, job, print_job); |
| + break; |
| + case printing::CupsJob::COMPLETED: |
| + DCHECK_GE(job.current_pages, print_job->total_page_number()); |
| + print_job->set_state(State::STATE_DOCUMENT_DONE); |
| + break; |
| + case printing::CupsJob::ABORTED: |
| + case printing::CupsJob::CANCELED: |
| + print_job->set_error_code(ErrorCodeFromReasons(printer_status)); |
| + // fall through |
| + default: |
| + print_job->set_state(ConvertState(job.state)); |
| + break; |
| + } |
| + |
| + return print_job->state() != old_state || pages_updated; |
| +} |
| + |
| } // namespace |
| namespace chromeos { |
| @@ -227,8 +336,10 @@ void CupsPrintJobManagerImpl::UpdateJobs(const QueryResult& result) { |
| CupsPrintJob* print_job = entry->second.get(); |
| - // Update a job we're tracking. |
| - JobStateUpdated(print_job, ConvertState(job.state)); |
| + if (UpdatePrintJob(queue.printer_status, job, print_job)) { |
| + // The state of the job changed, notify observers. |
| + NotifyJobStateUpdate(print_job); |
| + } |
| // Cleanup completed jobs. |
| if (print_job->IsJobFinished()) { |
| @@ -254,46 +365,41 @@ void CupsPrintJobManagerImpl::UpdateJobs(const QueryResult& result) { |
| void CupsPrintJobManagerImpl::PurgeJobs() { |
| for (const auto& entry : jobs_) { |
| // Declare all lost jobs errors. |
| - JobStateUpdated(entry.second.get(), CupsPrintJob::State::STATE_ERROR); |
| + CupsPrintJob* job = entry.second.get(); |
| + job->set_state(CupsPrintJob::State::STATE_ERROR); |
| + NotifyJobStateUpdate(job); |
| } |
| jobs_.clear(); |
| } |
| -void CupsPrintJobManagerImpl::JobStateUpdated(CupsPrintJob* job, |
| - CupsPrintJob::State new_state) { |
| - if (job->state() == new_state) |
| - return; |
| - |
| - // We don't track state transitions because some of them might be missed due |
| - // to how we query jobs. |
| - job->set_state(new_state); |
| - switch (new_state) { |
| - case CupsPrintJob::State::STATE_NONE: |
| +void CupsPrintJobManagerImpl::NotifyJobStateUpdate(CupsPrintJob* job) { |
| + switch (job->state()) { |
| + case State::STATE_NONE: |
| // State does not require notification. |
| break; |
| - case CupsPrintJob::State::STATE_WAITING: |
| + case State::STATE_WAITING: |
| NotifyJobUpdated(job); |
| break; |
| - case CupsPrintJob::State::STATE_STARTED: |
| + case State::STATE_STARTED: |
| NotifyJobStarted(job); |
| break; |
| - case CupsPrintJob::State::STATE_RESUMED: |
| + case State::STATE_PAGE_DONE: |
| + NotifyJobUpdated(job); |
| + break; |
| + case State::STATE_RESUMED: |
| NotifyJobResumed(job); |
| break; |
| - case CupsPrintJob::State::STATE_SUSPENDED: |
| + case State::STATE_SUSPENDED: |
| NotifyJobSuspended(job); |
| break; |
| - case CupsPrintJob::State::STATE_CANCELLED: |
| + case State::STATE_CANCELLED: |
| NotifyJobCanceled(job); |
| break; |
| - case CupsPrintJob::State::STATE_ERROR: |
| + case State::STATE_ERROR: |
| NotifyJobError(job); |
| break; |
| - case CupsPrintJob::State::STATE_PAGE_DONE: |
| - NOTREACHED() << "CUPS does not surface this state so it's not expected"; |
| - break; |
| - case CupsPrintJob::State::STATE_DOCUMENT_DONE: |
| + case State::STATE_DOCUMENT_DONE: |
| NotifyJobDone(job); |
| break; |
| } |