| OLD | NEW |
| 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.h" |
| 6 | 6 |
| 7 #include <cups/cups.h> | 7 #include <cups/cups.h> |
| 8 #include <algorithm> | 8 #include <algorithm> |
| 9 #include <set> | 9 #include <set> |
| 10 #include <string> | 10 #include <string> |
| 11 #include <utility> | 11 #include <utility> |
| 12 #include <vector> | 12 #include <vector> |
| 13 | 13 |
| 14 #include "base/bind.h" | 14 #include "base/bind.h" |
| 15 #include "base/memory/ptr_util.h" | 15 #include "base/memory/ptr_util.h" |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 201 break; | 201 break; |
| 202 } | 202 } |
| 203 | 203 |
| 204 return print_job->state() != old_state || pages_updated; | 204 return print_job->state() != old_state || pages_updated; |
| 205 } | 205 } |
| 206 | 206 |
| 207 } // namespace | 207 } // namespace |
| 208 | 208 |
| 209 namespace chromeos { | 209 namespace chromeos { |
| 210 | 210 |
| 211 QueryResult::QueryResult() = default; | 211 struct QueryResult { |
| 212 | 212 QueryResult() = default; |
| 213 QueryResult::QueryResult(const QueryResult& other) = default; | 213 QueryResult(const QueryResult& other) = default; |
| 214 | 214 ~QueryResult() = default; |
| 215 QueryResult::~QueryResult() = default; | 215 |
| 216 | 216 bool success; |
| 217 CupsWrapper::CupsWrapper() | 217 std::vector<::printing::QueueStatus> queues; |
| 218 : cups_connection_(GURL(), HTTP_ENCRYPT_NEVER, false) { | 218 }; |
| 219 DETACH_FROM_SEQUENCE(sequence_checker_); | 219 |
| 220 } | 220 // A wrapper around the CUPS connection to ensure that it's always accessed on |
| 221 | 221 // the same sequence. |
| 222 CupsWrapper::~CupsWrapper() = default; | 222 class CupsWrapper { |
| 223 | 223 public: |
| 224 void CupsWrapper::QueryCups(const std::vector<std::string>& printer_ids, | 224 CupsWrapper() : cups_connection_(GURL(), HTTP_ENCRYPT_NEVER, false) { |
| 225 QueryResult* result) { | 225 DETACH_FROM_SEQUENCE(sequence_checker_); |
| 226 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 226 } |
| 227 base::ThreadRestrictions::AssertIOAllowed(); | 227 |
| 228 | 228 ~CupsWrapper() = default; |
| 229 result->success = cups_connection_.GetJobs(printer_ids, &result->queues); | 229 |
| 230 } | 230 // Query CUPS for the current jobs for the given |printer_ids|. Writes result |
| 231 | 231 // to |result|. |
| 232 void CupsWrapper::CancelJobImpl(const std::string& printer_id, | 232 void QueryCups(const std::vector<std::string>& printer_ids, |
| 233 const int job_id) { | 233 QueryResult* result) { |
| 234 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 234 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 235 base::ThreadRestrictions::AssertIOAllowed(); | 235 base::ThreadRestrictions::AssertIOAllowed(); |
| 236 | 236 |
| 237 std::unique_ptr<::printing::CupsPrinter> printer = | 237 result->success = cups_connection_.GetJobs(printer_ids, &result->queues); |
| 238 cups_connection_.GetPrinter(printer_id); | 238 } |
| 239 if (!printer) { | 239 |
| 240 LOG(WARNING) << "Printer not found: " << printer_id; | 240 // Cancel the print job on the blocking thread. |
| 241 return; | 241 void CancelJobImpl(const std::string& printer_id, const int job_id) { |
| 242 } | 242 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 243 | 243 base::ThreadRestrictions::AssertIOAllowed(); |
| 244 if (!printer->CancelJob(job_id)) { | 244 |
| 245 // This is not expected to fail but log it if it does. | 245 std::unique_ptr<::printing::CupsPrinter> printer = |
| 246 LOG(WARNING) << "Cancelling job failed. Job may be stuck in queue."; | 246 cups_connection_.GetPrinter(printer_id); |
| 247 } | 247 if (!printer) { |
| 248 } | 248 LOG(WARNING) << "Printer not found: " << printer_id; |
| 249 | 249 return; |
| 250 CupsPrintJobManagerImpl::CupsPrintJobManagerImpl(Profile* profile) | 250 } |
| 251 : CupsPrintJobManager(profile), | 251 |
| 252 query_runner_(base::CreateSequencedTaskRunnerWithTraits( | 252 if (!printer->CancelJob(job_id)) { |
| 253 base::TaskTraits(base::TaskPriority::BACKGROUND, | 253 // This is not expected to fail but log it if it does. |
| 254 base::MayBlock(), | 254 LOG(WARNING) << "Cancelling job failed. Job may be stuck in queue."; |
| 255 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN))), | 255 } |
| 256 cups_wrapper_(new CupsWrapper(), | 256 } |
| 257 base::OnTaskRunnerDeleter(query_runner_)), | 257 |
| 258 weak_ptr_factory_(this) { | 258 private: |
| 259 registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, | 259 ::printing::CupsConnection cups_connection_; |
| 260 content::NotificationService::AllSources()); | 260 SEQUENCE_CHECKER(sequence_checker_); |
| 261 } | 261 |
| 262 | 262 DISALLOW_COPY_AND_ASSIGN(CupsWrapper); |
| 263 CupsPrintJobManagerImpl::~CupsPrintJobManagerImpl() {} | 263 }; |
| 264 | 264 |
| 265 // Must be run from the UI thread. | 265 class CupsPrintJobManagerImpl : public CupsPrintJobManager, |
| 266 void CupsPrintJobManagerImpl::CancelPrintJob(CupsPrintJob* job) { | 266 public content::NotificationObserver { |
| 267 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 267 public: |
| 268 | 268 explicit CupsPrintJobManagerImpl(Profile* profile) |
| 269 // Copy job_id and printer_id. |job| is about to be freed. | 269 : CupsPrintJobManager(profile), |
| 270 const int job_id = job->job_id(); | 270 query_runner_(base::CreateSequencedTaskRunnerWithTraits( |
| 271 const std::string printer_id = job->printer().id(); | 271 base::TaskTraits(base::TaskPriority::BACKGROUND, |
| 272 | 272 base::MayBlock(), |
| 273 // Stop montioring jobs after we cancel them. The user no longer cares. | 273 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN))), |
| 274 jobs_.erase(job->GetUniqueId()); | 274 cups_wrapper_(new CupsWrapper(), |
| 275 | 275 base::OnTaskRunnerDeleter(query_runner_)), |
| 276 query_runner_->PostTask( | 276 weak_ptr_factory_(this) { |
| 277 FROM_HERE, | 277 registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, |
| 278 base::Bind(&CupsWrapper::CancelJobImpl, | 278 content::NotificationService::AllSources()); |
| 279 base::Unretained(cups_wrapper_.get()), printer_id, job_id)); | 279 } |
| 280 } | 280 |
| 281 | 281 ~CupsPrintJobManagerImpl() override = default; |
| 282 bool CupsPrintJobManagerImpl::SuspendPrintJob(CupsPrintJob* job) { | 282 |
| 283 NOTREACHED() << "Pause printer is not implemented"; | 283 // CupsPrintJobManager overrides: |
| 284 return false; | 284 // Must be run from the UI thread. |
| 285 } | 285 void CancelPrintJob(CupsPrintJob* job) override { |
| 286 | 286 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 287 bool CupsPrintJobManagerImpl::ResumePrintJob(CupsPrintJob* job) { | 287 |
| 288 NOTREACHED() << "Resume printer is not implemented"; | 288 // Copy job_id and printer_id. |job| is about to be freed. |
| 289 return false; | 289 const int job_id = job->job_id(); |
| 290 } | 290 const std::string printer_id = job->printer().id(); |
| 291 | 291 |
| 292 void CupsPrintJobManagerImpl::Observe( | 292 // Stop montioring jobs after we cancel them. The user no longer cares. |
| 293 int type, | 293 jobs_.erase(job->GetUniqueId()); |
| 294 const content::NotificationSource& source, | 294 |
| 295 const content::NotificationDetails& details) { | 295 query_runner_->PostTask( |
| 296 DCHECK_EQ(chrome::NOTIFICATION_PRINT_JOB_EVENT, type); | 296 FROM_HERE, |
| 297 | 297 base::Bind(&CupsWrapper::CancelJobImpl, |
| 298 content::Details<::printing::JobEventDetails> job_details(details); | 298 base::Unretained(cups_wrapper_.get()), printer_id, job_id)); |
| 299 | 299 } |
| 300 // DOC_DONE occurs after the print job has been successfully sent to the | 300 |
| 301 // spooler which is when we begin tracking the print queue. | 301 bool SuspendPrintJob(CupsPrintJob* job) override { |
| 302 if (job_details->type() == ::printing::JobEventDetails::DOC_DONE) { | 302 NOTREACHED() << "Pause printer is not implemented"; |
| 303 const ::printing::PrintedDocument* document = job_details->document(); | |
| 304 DCHECK(document); | |
| 305 CreatePrintJob(base::UTF16ToUTF8(document->settings().device_name()), | |
| 306 base::UTF16ToUTF8(document->settings().title()), | |
| 307 job_details->job_id(), document->page_count()); | |
| 308 } | |
| 309 } | |
| 310 | |
| 311 bool CupsPrintJobManagerImpl::CreatePrintJob(const std::string& printer_name, | |
| 312 const std::string& title, | |
| 313 int job_id, | |
| 314 int total_page_number) { | |
| 315 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 316 | |
| 317 auto printer = | |
| 318 PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinter( | |
| 319 printer_name); | |
| 320 if (!printer) { | |
| 321 LOG(WARNING) << "Printer was removed while job was in progress. It cannot " | |
| 322 "be tracked"; | |
| 323 return false; | 303 return false; |
| 324 } | 304 } |
| 325 | 305 |
| 326 // Records the number of jobs we're currently tracking when a new job is | 306 bool ResumePrintJob(CupsPrintJob* job) override { |
| 327 // started. This is equivalent to print queue size in the current | 307 NOTREACHED() << "Resume printer is not implemented"; |
| 328 // implementation. | 308 return false; |
| 329 UMA_HISTOGRAM_EXACT_LINEAR("Printing.CUPS.PrintJobsQueued", jobs_.size(), 20); | 309 } |
| 330 | 310 |
| 331 // Create a new print job. | 311 // NotificationObserver overrides: |
| 332 auto cpj = base::MakeUnique<CupsPrintJob>(*printer, job_id, title, | 312 void Observe(int type, |
| 333 total_page_number); | 313 const content::NotificationSource& source, |
| 334 std::string key = cpj->GetUniqueId(); | 314 const content::NotificationDetails& details) override { |
| 335 jobs_[key] = std::move(cpj); | 315 DCHECK_EQ(chrome::NOTIFICATION_PRINT_JOB_EVENT, type); |
| 336 | 316 |
| 337 CupsPrintJob* job = jobs_[key].get(); | 317 content::Details<::printing::JobEventDetails> job_details(details); |
| 338 NotifyJobCreated(job); | 318 |
| 339 | 319 // DOC_DONE occurs after the print job has been successfully sent to the |
| 340 // Always start jobs in the waiting state. | 320 // spooler which is when we begin tracking the print queue. |
| 341 job->set_state(CupsPrintJob::State::STATE_WAITING); | 321 if (job_details->type() == ::printing::JobEventDetails::DOC_DONE) { |
| 342 NotifyJobUpdated(job); | 322 const ::printing::PrintedDocument* document = job_details->document(); |
| 343 | 323 DCHECK(document); |
| 344 ScheduleQuery(base::TimeDelta()); | 324 CreatePrintJob(base::UTF16ToUTF8(document->settings().device_name()), |
| 345 | 325 base::UTF16ToUTF8(document->settings().title()), |
| 346 return true; | 326 job_details->job_id(), document->page_count()); |
| 347 } | 327 } |
| 348 | 328 } |
| 349 void CupsPrintJobManagerImpl::ScheduleQuery() { | 329 |
| 350 ScheduleQuery(base::TimeDelta::FromMilliseconds(kPollRate)); | 330 private: |
| 351 } | 331 // Begin monitoring a print job for a given |printer_name| with the given |
| 352 | 332 // |title| with the pages |total_page_number|. |
| 353 void CupsPrintJobManagerImpl::ScheduleQuery(const base::TimeDelta& delay) { | 333 bool CreatePrintJob(const std::string& printer_name, |
| 354 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 334 const std::string& title, |
| 355 | 335 int job_id, |
| 356 if (!in_query_) { | 336 int total_page_number) { |
| 357 in_query_ = true; | 337 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 358 | 338 |
| 359 content::BrowserThread::PostDelayedTask( | 339 auto printer = |
| 360 content::BrowserThread::UI, FROM_HERE, | 340 PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinter( |
| 361 base::Bind(&CupsPrintJobManagerImpl::PostQuery, | 341 printer_name); |
| 362 weak_ptr_factory_.GetWeakPtr()), | 342 if (!printer) { |
| 363 delay); | 343 LOG(WARNING) |
| 364 } | 344 << "Printer was removed while job was in progress. It cannot " |
| 365 } | 345 "be tracked"; |
| 366 | 346 return false; |
| 367 void CupsPrintJobManagerImpl::PostQuery() { | 347 } |
| 368 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 348 |
| 369 | 349 // Records the number of jobs we're currently tracking when a new job is |
| 370 // The set of active printers is expected to be small. | 350 // started. This is equivalent to print queue size in the current |
| 371 std::set<std::string> printer_ids; | 351 // implementation. |
| 372 for (const auto& entry : jobs_) { | 352 UMA_HISTOGRAM_EXACT_LINEAR("Printing.CUPS.PrintJobsQueued", jobs_.size(), |
| 373 printer_ids.insert(entry.second->printer().id()); | 353 20); |
| 374 } | 354 |
| 375 std::vector<std::string> ids{printer_ids.begin(), printer_ids.end()}; | 355 // Create a new print job. |
| 376 | 356 auto cpj = base::MakeUnique<CupsPrintJob>(*printer, job_id, title, |
| 377 auto result = base::MakeUnique<QueryResult>(); | 357 total_page_number); |
| 378 QueryResult* result_ptr = result.get(); | 358 std::string key = cpj->GetUniqueId(); |
| 379 // Runs a query on |query_runner_| which will rejoin this sequnece on | 359 jobs_[key] = std::move(cpj); |
| 380 // completion. | 360 |
| 381 query_runner_->PostTaskAndReply( | 361 CupsPrintJob* job = jobs_[key].get(); |
| 382 FROM_HERE, | 362 NotifyJobCreated(job); |
| 383 base::Bind(&CupsWrapper::QueryCups, base::Unretained(cups_wrapper_.get()), | 363 |
| 384 ids, result_ptr), | 364 // Always start jobs in the waiting state. |
| 385 base::Bind(&CupsPrintJobManagerImpl::UpdateJobs, | 365 job->set_state(CupsPrintJob::State::STATE_WAITING); |
| 386 weak_ptr_factory_.GetWeakPtr(), base::Passed(&result))); | 366 NotifyJobUpdated(job); |
| 387 } | 367 |
| 388 | 368 ScheduleQuery(base::TimeDelta()); |
| 389 // Use job information to update local job states. Previously completed jobs | 369 |
| 390 // could be in |jobs| but those are ignored as we will not emit updates for | 370 return true; |
| 391 // them after they are completed. | 371 } |
| 392 void CupsPrintJobManagerImpl::UpdateJobs(std::unique_ptr<QueryResult> result) { | 372 |
| 393 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 373 // Schedule a query of CUPS for print job status with the default delay. |
| 394 | 374 void ScheduleQuery() { |
| 395 const std::vector<::printing::QueueStatus>& queues = result->queues; | 375 ScheduleQuery(base::TimeDelta::FromMilliseconds(kPollRate)); |
| 396 | 376 } |
| 397 // Query has completed. Allow more queries. | 377 |
| 398 in_query_ = false; | 378 // Schedule a query of CUPS for print job status with a delay of |delay|. |
| 399 | 379 void ScheduleQuery(const base::TimeDelta& delay) { |
| 400 // If the query failed, either retry or purge. | 380 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 401 if (!result->success) { | 381 |
| 402 retry_count_++; | 382 if (!in_query_) { |
| 403 LOG(WARNING) << "Failed to query CUPS for queue status. Schedule retry (" | 383 in_query_ = true; |
| 404 << retry_count_ << ")"; | 384 |
| 405 if (retry_count_ > kRetryMax) { | 385 content::BrowserThread::PostDelayedTask( |
| 406 LOG(ERROR) << "CUPS is unreachable. Giving up on all jobs."; | 386 content::BrowserThread::UI, FROM_HERE, |
| 387 base::Bind(&CupsPrintJobManagerImpl::PostQuery, |
| 388 weak_ptr_factory_.GetWeakPtr()), |
| 389 delay); |
| 390 } |
| 391 } |
| 392 |
| 393 // Schedule the CUPS query off the UI thread. Posts results back to UI thread |
| 394 // to UpdateJobs. |
| 395 void PostQuery() { |
| 396 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 397 |
| 398 // The set of active printers is expected to be small. |
| 399 std::set<std::string> printer_ids; |
| 400 for (const auto& entry : jobs_) { |
| 401 printer_ids.insert(entry.second->printer().id()); |
| 402 } |
| 403 std::vector<std::string> ids{printer_ids.begin(), printer_ids.end()}; |
| 404 |
| 405 auto result = base::MakeUnique<QueryResult>(); |
| 406 QueryResult* result_ptr = result.get(); |
| 407 // Runs a query on |query_runner_| which will rejoin this sequnece on |
| 408 // completion. |
| 409 query_runner_->PostTaskAndReply( |
| 410 FROM_HERE, |
| 411 base::Bind(&CupsWrapper::QueryCups, |
| 412 base::Unretained(cups_wrapper_.get()), ids, result_ptr), |
| 413 base::Bind(&CupsPrintJobManagerImpl::UpdateJobs, |
| 414 weak_ptr_factory_.GetWeakPtr(), base::Passed(&result))); |
| 415 } |
| 416 |
| 417 // Process jobs from CUPS and perform notifications. |
| 418 // Use job information to update local job states. Previously completed jobs |
| 419 // could be in |jobs| but those are ignored as we will not emit updates for |
| 420 // them after they are completed. |
| 421 void UpdateJobs(std::unique_ptr<QueryResult> result) { |
| 422 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 423 |
| 424 const std::vector<::printing::QueueStatus>& queues = result->queues; |
| 425 |
| 426 // Query has completed. Allow more queries. |
| 427 in_query_ = false; |
| 428 |
| 429 // If the query failed, either retry or purge. |
| 430 if (!result->success) { |
| 431 retry_count_++; |
| 432 LOG(WARNING) << "Failed to query CUPS for queue status. Schedule retry (" |
| 433 << retry_count_ << ")"; |
| 434 if (retry_count_ > kRetryMax) { |
| 435 LOG(ERROR) << "CUPS is unreachable. Giving up on all jobs."; |
| 436 PurgeJobs(); |
| 437 } else { |
| 438 // Schedule another query with a larger delay. |
| 439 DCHECK_GE(1, retry_count_); |
| 440 ScheduleQuery( |
| 441 base::TimeDelta::FromMilliseconds(kPollRate * retry_count_)); |
| 442 } |
| 443 return; |
| 444 } |
| 445 |
| 446 // A query has completed. Reset retry counter. |
| 447 retry_count_ = 0; |
| 448 |
| 449 std::vector<std::string> active_jobs; |
| 450 for (const auto& queue : queues) { |
| 451 for (auto& job : queue.jobs) { |
| 452 std::string key = CupsPrintJob::GetUniqueId(job.printer_id, job.id); |
| 453 const auto& entry = jobs_.find(key); |
| 454 if (entry == jobs_.end()) |
| 455 continue; |
| 456 |
| 457 CupsPrintJob* print_job = entry->second.get(); |
| 458 |
| 459 if (UpdatePrintJob(queue.printer_status, job, print_job)) { |
| 460 // The state of the job changed, notify observers. |
| 461 NotifyJobStateUpdate(print_job); |
| 462 } |
| 463 |
| 464 if (print_job->expired()) { |
| 465 // Job needs to be forcibly cancelled. |
| 466 RecordJobResult(TIMEOUT_CANCEL); |
| 467 CancelPrintJob(print_job); |
| 468 // Beware, print_job was removed from jobs_ and deleted. |
| 469 } else if (print_job->IsJobFinished()) { |
| 470 // Cleanup completed jobs. |
| 471 RecordJobResult(ResultForHistogram(print_job->state())); |
| 472 jobs_.erase(entry); |
| 473 } else { |
| 474 active_jobs.push_back(key); |
| 475 } |
| 476 } |
| 477 } |
| 478 |
| 479 // Keep polling until all jobs complete or error. |
| 480 if (!active_jobs.empty()) { |
| 481 // During normal operations, we poll at the default rate. |
| 482 ScheduleQuery(); |
| 483 } else if (!jobs_.empty()) { |
| 484 // We're tracking jobs that we didn't receive an update for. Something |
| 485 // bad has happened. |
| 486 LOG(ERROR) << "Lost track of (" << jobs_.size() << ") jobs"; |
| 407 PurgeJobs(); | 487 PurgeJobs(); |
| 408 } else { | 488 } |
| 409 // Schedule another query with a larger delay. | 489 } |
| 410 DCHECK_GE(1, retry_count_); | 490 |
| 411 ScheduleQuery( | 491 // Mark remaining jobs as errors and remove active jobs. |
| 412 base::TimeDelta::FromMilliseconds(kPollRate * retry_count_)); | 492 void PurgeJobs() { |
| 413 } | 493 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 414 return; | 494 |
| 415 } | 495 for (const auto& entry : jobs_) { |
| 416 | 496 // Declare all lost jobs errors. |
| 417 // A query has completed. Reset retry counter. | 497 RecordJobResult(LOST); |
| 418 retry_count_ = 0; | 498 CupsPrintJob* job = entry.second.get(); |
| 419 | 499 job->set_state(CupsPrintJob::State::STATE_ERROR); |
| 420 std::vector<std::string> active_jobs; | 500 NotifyJobStateUpdate(job); |
| 421 for (const auto& queue : queues) { | 501 } |
| 422 for (auto& job : queue.jobs) { | 502 |
| 423 std::string key = CupsPrintJob::GetUniqueId(job.printer_id, job.id); | 503 jobs_.clear(); |
| 424 const auto& entry = jobs_.find(key); | 504 } |
| 425 if (entry == jobs_.end()) | 505 |
| 426 continue; | 506 // Notify observers that a state update has occured for |job|. |
| 427 | 507 void NotifyJobStateUpdate(CupsPrintJob* job) { |
| 428 CupsPrintJob* print_job = entry->second.get(); | 508 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 429 | 509 |
| 430 if (UpdatePrintJob(queue.printer_status, job, print_job)) { | 510 switch (job->state()) { |
| 431 // The state of the job changed, notify observers. | 511 case State::STATE_NONE: |
| 432 NotifyJobStateUpdate(print_job); | 512 // State does not require notification. |
| 433 } | 513 break; |
| 434 | 514 case State::STATE_WAITING: |
| 435 if (print_job->expired()) { | 515 NotifyJobUpdated(job); |
| 436 // Job needs to be forcibly cancelled. | 516 break; |
| 437 RecordJobResult(TIMEOUT_CANCEL); | 517 case State::STATE_STARTED: |
| 438 CancelPrintJob(print_job); | 518 NotifyJobStarted(job); |
| 439 // Beware, print_job was removed from jobs_ and deleted. | 519 break; |
| 440 } else if (print_job->IsJobFinished()) { | 520 case State::STATE_PAGE_DONE: |
| 441 // Cleanup completed jobs. | 521 NotifyJobUpdated(job); |
| 442 RecordJobResult(ResultForHistogram(print_job->state())); | 522 break; |
| 443 jobs_.erase(entry); | 523 case State::STATE_RESUMED: |
| 444 } else { | 524 NotifyJobResumed(job); |
| 445 active_jobs.push_back(key); | 525 break; |
| 446 } | 526 case State::STATE_SUSPENDED: |
| 447 } | 527 NotifyJobSuspended(job); |
| 448 } | 528 break; |
| 449 | 529 case State::STATE_CANCELLED: |
| 450 // Keep polling until all jobs complete or error. | 530 NotifyJobCanceled(job); |
| 451 if (!active_jobs.empty()) { | 531 break; |
| 452 // During normal operations, we poll at the default rate. | 532 case State::STATE_ERROR: |
| 453 ScheduleQuery(); | 533 NotifyJobError(job); |
| 454 } else if (!jobs_.empty()) { | 534 break; |
| 455 // We're tracking jobs that we didn't receive an update for. Something | 535 case State::STATE_DOCUMENT_DONE: |
| 456 // bad has happened. | 536 NotifyJobDone(job); |
| 457 LOG(ERROR) << "Lost track of (" << jobs_.size() << ") jobs"; | 537 break; |
| 458 PurgeJobs(); | 538 } |
| 459 } | 539 } |
| 460 } | 540 |
| 461 | 541 // Ongoing print jobs. |
| 462 void CupsPrintJobManagerImpl::PurgeJobs() { | 542 std::map<std::string, std::unique_ptr<CupsPrintJob>> jobs_; |
| 463 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 543 |
| 464 | 544 // Prevents multiple queries from being scheduled simultaneously. |
| 465 for (const auto& entry : jobs_) { | 545 bool in_query_ = false; |
| 466 // Declare all lost jobs errors. | 546 |
| 467 RecordJobResult(LOST); | 547 // Records the number of consecutive times the GetJobs query has failed. |
| 468 CupsPrintJob* job = entry.second.get(); | 548 int retry_count_ = 0; |
| 469 job->set_state(CupsPrintJob::State::STATE_ERROR); | 549 |
| 470 NotifyJobStateUpdate(job); | 550 content::NotificationRegistrar registrar_; |
| 471 } | 551 // Task runner for queries to CUPS. |
| 472 | 552 scoped_refptr<base::SequencedTaskRunner> query_runner_; |
| 473 jobs_.clear(); | 553 std::unique_ptr<CupsWrapper, base::OnTaskRunnerDeleter> cups_wrapper_; |
| 474 } | 554 base::WeakPtrFactory<CupsPrintJobManagerImpl> weak_ptr_factory_; |
| 475 | 555 |
| 476 void CupsPrintJobManagerImpl::NotifyJobStateUpdate(CupsPrintJob* job) { | 556 DISALLOW_COPY_AND_ASSIGN(CupsPrintJobManagerImpl); |
| 477 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 557 }; |
| 478 | |
| 479 switch (job->state()) { | |
| 480 case State::STATE_NONE: | |
| 481 // State does not require notification. | |
| 482 break; | |
| 483 case State::STATE_WAITING: | |
| 484 NotifyJobUpdated(job); | |
| 485 break; | |
| 486 case State::STATE_STARTED: | |
| 487 NotifyJobStarted(job); | |
| 488 break; | |
| 489 case State::STATE_PAGE_DONE: | |
| 490 NotifyJobUpdated(job); | |
| 491 break; | |
| 492 case State::STATE_RESUMED: | |
| 493 NotifyJobResumed(job); | |
| 494 break; | |
| 495 case State::STATE_SUSPENDED: | |
| 496 NotifyJobSuspended(job); | |
| 497 break; | |
| 498 case State::STATE_CANCELLED: | |
| 499 NotifyJobCanceled(job); | |
| 500 break; | |
| 501 case State::STATE_ERROR: | |
| 502 NotifyJobError(job); | |
| 503 break; | |
| 504 case State::STATE_DOCUMENT_DONE: | |
| 505 NotifyJobDone(job); | |
| 506 break; | |
| 507 } | |
| 508 } | |
| 509 | 558 |
| 510 // static | 559 // static |
| 511 CupsPrintJobManager* CupsPrintJobManager::CreateInstance(Profile* profile) { | 560 CupsPrintJobManager* CupsPrintJobManager::CreateInstance(Profile* profile) { |
| 512 return new CupsPrintJobManagerImpl(profile); | 561 return new CupsPrintJobManagerImpl(profile); |
| 513 } | 562 } |
| 514 | 563 |
| 515 } // namespace chromeos | 564 } // namespace chromeos |
| OLD | NEW |