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 |