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..a4778346beb5d2da28ee1242a57f019b7313ab76 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,106 @@ 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(); |
+} |
+ |
+// 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) { |
+ for (const auto& reason : printer_status.reasons) { |
+ switch (reason.reason) { |
+ case PrinterReason::MEDIA_JAM: |
+ case PrinterReason::MEDIA_EMPTY: |
+ case PrinterReason::MEDIA_NEEDED: |
+ case PrinterReason::MEDIA_LOW: |
+ return chromeos::CupsPrintJob::ErrorCode::PAPER_JAM; |
+ case PrinterReason::TONER_EMPTY: |
+ case PrinterReason::TONER_LOW: |
+ return chromeos::CupsPrintJob::ErrorCode::OUT_OF_INK; |
+ default: |
+ break; |
+ } |
+ } |
+ return chromeos::CupsPrintJob::ErrorCode::NO_ERROR; |
+} |
+ |
+// Check if the job should timeout. Returns true if the job has timed out. |
+bool EnforceTimeout(const printing::CupsJob& job, |
+ chromeos::CupsPrintJob* print_job) { |
+ // 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); |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+// Update the current printed page. Returns true of the page has been updated. |
+bool UpdateCurrentPage(const printing::CupsJob& job, |
+ chromeos::CupsPrintJob* print_job) { |
+ 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; |
+} |
+ |
+// Updates the state of a print job based on |printer_status| and |job|. Returns |
+// true if observers need to be notified of an update. |
+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: |
+ if (ContainsReason(printer_status, PrinterReason::CONNECTING_TO_DEVICE)) { |
+ if (EnforceTimeout(job, print_job)) { |
+ LOG(WARNING) << "Connecting to printer timed out"; |
+ // TODO(skau): Purge job from queue. |
+ } |
+ break; |
+ } |
+ pages_updated = UpdateCurrentPage(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 +334,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 +363,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; |
} |