Chromium Code Reviews| 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_impl.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" |
| 16 #include "base/stl_util.h" | 16 #include "base/stl_util.h" |
| 17 #include "base/strings/utf_string_conversions.h" | 17 #include "base/strings/utf_string_conversions.h" |
| 18 #include "base/threading/sequenced_task_runner_handle.h" | 18 #include "base/threading/sequenced_task_runner_handle.h" |
| 19 #include "base/threading/sequenced_worker_pool.h" | |
| 19 #include "chrome/browser/chrome_notification_types.h" | 20 #include "chrome/browser/chrome_notification_types.h" |
| 20 #include "chrome/browser/chromeos/printing/cups_print_job.h" | 21 #include "chrome/browser/chromeos/printing/cups_print_job.h" |
| 21 #include "chrome/browser/chromeos/printing/printers_manager.h" | 22 #include "chrome/browser/chromeos/printing/printers_manager.h" |
| 22 #include "chrome/browser/chromeos/printing/printers_manager_factory.h" | 23 #include "chrome/browser/chromeos/printing/printers_manager_factory.h" |
| 23 #include "chrome/browser/printing/print_job.h" | 24 #include "chrome/browser/printing/print_job.h" |
| 24 #include "chrome/browser/profiles/profile.h" | 25 #include "chrome/browser/profiles/profile.h" |
| 25 #include "content/public/browser/browser_context.h" | 26 #include "content/public/browser/browser_context.h" |
| 26 #include "content/public/browser/browser_thread.h" | 27 #include "content/public/browser/browser_thread.h" |
| 27 #include "content/public/browser/notification_registrar.h" | 28 #include "content/public/browser/notification_registrar.h" |
| 28 #include "content/public/browser/notification_service.h" | 29 #include "content/public/browser/notification_service.h" |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 134 print_job->set_state(State::STATE_STARTED); | 135 print_job->set_state(State::STATE_STARTED); |
| 135 } else { | 136 } else { |
| 136 pages_updated = job.current_pages != print_job->printed_page_number(); | 137 pages_updated = job.current_pages != print_job->printed_page_number(); |
| 137 print_job->set_printed_page_number(job.current_pages); | 138 print_job->set_printed_page_number(job.current_pages); |
| 138 print_job->set_state(State::STATE_PAGE_DONE); | 139 print_job->set_state(State::STATE_PAGE_DONE); |
| 139 } | 140 } |
| 140 | 141 |
| 141 return pages_updated; | 142 return pages_updated; |
| 142 } | 143 } |
| 143 | 144 |
| 144 // Updates the state of a print job based on |printer_status| and |job|. Returns | |
| 145 // true if observers need to be notified of an update. | |
| 146 bool UpdatePrintJob(const printing::PrinterStatus& printer_status, | |
| 147 const printing::CupsJob& job, | |
| 148 chromeos::CupsPrintJob* print_job) { | |
| 149 DCHECK_EQ(job.id, print_job->job_id()); | |
| 150 | |
| 151 State old_state = print_job->state(); | |
| 152 | |
| 153 bool pages_updated = false; | |
| 154 switch (job.state) { | |
| 155 case printing::CupsJob::PROCESSING: | |
| 156 if (ContainsReason(printer_status, PrinterReason::CONNECTING_TO_DEVICE)) { | |
| 157 if (EnforceTimeout(job, print_job)) { | |
| 158 LOG(WARNING) << "Connecting to printer timed out"; | |
| 159 // TODO(skau): Purge job from queue. | |
| 160 } | |
| 161 break; | |
| 162 } | |
| 163 pages_updated = UpdateCurrentPage(job, print_job); | |
| 164 break; | |
| 165 case printing::CupsJob::COMPLETED: | |
| 166 DCHECK_GE(job.current_pages, print_job->total_page_number()); | |
| 167 print_job->set_state(State::STATE_DOCUMENT_DONE); | |
| 168 break; | |
| 169 case printing::CupsJob::ABORTED: | |
| 170 case printing::CupsJob::CANCELED: | |
| 171 print_job->set_error_code(ErrorCodeFromReasons(printer_status)); | |
| 172 // fall through | |
| 173 default: | |
| 174 print_job->set_state(ConvertState(job.state)); | |
| 175 break; | |
| 176 } | |
| 177 | |
| 178 return print_job->state() != old_state || pages_updated; | |
| 179 } | |
| 180 | |
| 181 } // namespace | 145 } // namespace |
| 182 | 146 |
| 183 namespace chromeos { | 147 namespace chromeos { |
| 184 | 148 |
| 185 CupsPrintJobManagerImpl::CupsPrintJobManagerImpl(Profile* profile) | 149 CupsPrintJobManagerImpl::CupsPrintJobManagerImpl(Profile* profile) |
| 186 : CupsPrintJobManager(profile), | 150 : CupsPrintJobManager(profile), |
| 187 cups_connection_(GURL(), HTTP_ENCRYPT_NEVER, false), | 151 cups_connection_(GURL(), HTTP_ENCRYPT_NEVER, false), |
| 188 weak_ptr_factory_(this) { | 152 weak_ptr_factory_(this) { |
| 189 registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, | 153 registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, |
| 190 content::NotificationService::AllSources()); | 154 content::NotificationService::AllSources()); |
| 191 } | 155 } |
| 192 | 156 |
| 193 CupsPrintJobManagerImpl::~CupsPrintJobManagerImpl() {} | 157 CupsPrintJobManagerImpl::~CupsPrintJobManagerImpl() {} |
| 194 | 158 |
| 195 bool CupsPrintJobManagerImpl::CancelPrintJob(CupsPrintJob* job) { | 159 // Must be run from the UI thread or jobs_ could become inconsistent. |
|
Carlson
2017/03/16 18:36:48
the "or jobs_ could become inconsistent" suffix he
skau
2017/03/16 22:14:36
Done.
| |
| 196 std::string printer_id = job->printer().id(); | 160 void CupsPrintJobManagerImpl::CancelPrintJob(CupsPrintJob* job) { |
| 197 std::unique_ptr<::printing::CupsPrinter> printer = | 161 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 198 cups_connection_.GetPrinter(printer_id); | |
| 199 if (!printer) { | |
| 200 LOG(WARNING) << "Printer not found"; | |
| 201 return false; | |
| 202 } | |
| 203 | 162 |
| 204 return printer->CancelJob(job->job_id()); | 163 const int job_id = job->job_id(); |
| 164 | |
| 165 // Copy printer_id. |job| is about to be freed. | |
| 166 const std::string printer_id = job->printer().id(); | |
| 167 | |
| 168 // Stop montioring jobs after we cancel them. The user no longer cares. | |
| 169 jobs_.erase(job->GetUniqueId()); | |
| 170 | |
| 171 // Be sure to copy out all relevant fields. |job| may be destroyed after we | |
| 172 // exit this scope. | |
| 173 content::BrowserThread::GetBlockingPool()->PostTask( | |
| 174 FROM_HERE, | |
| 175 base::Bind(&CupsPrintJobManagerImpl::CancelJobImpl, | |
| 176 weak_ptr_factory_.GetWeakPtr(), printer_id, job_id)); | |
| 205 } | 177 } |
| 206 | 178 |
| 207 bool CupsPrintJobManagerImpl::SuspendPrintJob(CupsPrintJob* job) { | 179 bool CupsPrintJobManagerImpl::SuspendPrintJob(CupsPrintJob* job) { |
| 208 NOTREACHED() << "Pause printer is not implemented"; | 180 NOTREACHED() << "Pause printer is not implemented"; |
| 209 return false; | 181 return false; |
| 210 } | 182 } |
| 211 | 183 |
| 212 bool CupsPrintJobManagerImpl::ResumePrintJob(CupsPrintJob* job) { | 184 bool CupsPrintJobManagerImpl::ResumePrintJob(CupsPrintJob* job) { |
| 213 NOTREACHED() << "Resume printer is not implemented"; | 185 NOTREACHED() << "Resume printer is not implemented"; |
| 214 return false; | 186 return false; |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 231 base::UTF16ToUTF8(document->settings().title()), | 203 base::UTF16ToUTF8(document->settings().title()), |
| 232 job_details->job_id(), document->page_count()); | 204 job_details->job_id(), document->page_count()); |
| 233 } | 205 } |
| 234 } | 206 } |
| 235 | 207 |
| 236 bool CupsPrintJobManagerImpl::CreatePrintJob(const std::string& printer_name, | 208 bool CupsPrintJobManagerImpl::CreatePrintJob(const std::string& printer_name, |
| 237 const std::string& title, | 209 const std::string& title, |
| 238 int job_id, | 210 int job_id, |
| 239 int total_page_number) { | 211 int total_page_number) { |
| 240 auto printer = | 212 auto printer = |
| 241 chromeos::PrintersManagerFactory::GetForBrowserContext(profile_) | 213 PrintersManagerFactory::GetForBrowserContext(profile_)->GetPrinter( |
| 242 ->GetPrinter(printer_name); | 214 printer_name); |
| 243 if (!printer) { | 215 if (!printer) { |
| 244 LOG(WARNING) << "Printer was removed while job was in progress. It cannot " | 216 LOG(WARNING) << "Printer was removed while job was in progress. It cannot " |
| 245 "be tracked"; | 217 "be tracked"; |
| 246 return false; | 218 return false; |
| 247 } | 219 } |
| 248 | 220 |
| 249 // Create a new print job. | 221 // Create a new print job. |
| 250 auto cpj = base::MakeUnique<CupsPrintJob>(*printer, job_id, title, | 222 auto cpj = base::MakeUnique<CupsPrintJob>(*printer, job_id, title, |
| 251 total_page_number); | 223 total_page_number); |
| 252 std::string key = cpj->GetUniqueId(); | 224 std::string key = cpj->GetUniqueId(); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 288 } | 260 } |
| 289 std::vector<std::string> ids{printer_ids.begin(), printer_ids.end()}; | 261 std::vector<std::string> ids{printer_ids.begin(), printer_ids.end()}; |
| 290 | 262 |
| 291 content::BrowserThread::PostTaskAndReplyWithResult( | 263 content::BrowserThread::PostTaskAndReplyWithResult( |
| 292 content::BrowserThread::FILE_USER_BLOCKING, FROM_HERE, | 264 content::BrowserThread::FILE_USER_BLOCKING, FROM_HERE, |
| 293 base::Bind(&QueryCups, &cups_connection_, ids), | 265 base::Bind(&QueryCups, &cups_connection_, ids), |
| 294 base::Bind(&CupsPrintJobManagerImpl::UpdateJobs, | 266 base::Bind(&CupsPrintJobManagerImpl::UpdateJobs, |
| 295 weak_ptr_factory_.GetWeakPtr())); | 267 weak_ptr_factory_.GetWeakPtr())); |
| 296 } | 268 } |
| 297 | 269 |
| 270 bool CupsPrintJobManagerImpl::UpdatePrintJob( | |
| 271 const ::printing::PrinterStatus& printer_status, | |
| 272 const ::printing::CupsJob& job, | |
| 273 CupsPrintJob* print_job) { | |
| 274 DCHECK_EQ(job.id, print_job->job_id()); | |
| 275 | |
| 276 State old_state = print_job->state(); | |
| 277 | |
| 278 bool pages_updated = false; | |
| 279 switch (job.state) { | |
| 280 case ::printing::CupsJob::PROCESSING: | |
| 281 if (ContainsReason(printer_status, PrinterReason::CONNECTING_TO_DEVICE)) { | |
| 282 if (EnforceTimeout(job, print_job)) { | |
| 283 LOG(WARNING) << "Connecting to printer timed out"; | |
| 284 print_job->set_expired(true); | |
| 285 } | |
| 286 break; | |
|
Carlson
2017/03/16 18:36:48
Nit: I think it would be slightly more natural to
skau
2017/03/16 22:14:36
Done.
| |
| 287 } | |
| 288 pages_updated = UpdateCurrentPage(job, print_job); | |
| 289 break; | |
| 290 case ::printing::CupsJob::COMPLETED: | |
| 291 DCHECK_GE(job.current_pages, print_job->total_page_number()); | |
| 292 print_job->set_state(State::STATE_DOCUMENT_DONE); | |
| 293 break; | |
| 294 case ::printing::CupsJob::ABORTED: | |
| 295 case ::printing::CupsJob::CANCELED: | |
| 296 print_job->set_error_code(ErrorCodeFromReasons(printer_status)); | |
| 297 // fall through | |
| 298 default: | |
| 299 print_job->set_state(ConvertState(job.state)); | |
| 300 break; | |
| 301 } | |
| 302 | |
| 303 return print_job->state() != old_state || pages_updated; | |
| 304 } | |
| 305 | |
| 298 // Use job information to update local job states. Previously completed jobs | 306 // Use job information to update local job states. Previously completed jobs |
| 299 // could be in |jobs| but those are ignored as we will not emit updates for them | 307 // could be in |jobs| but those are ignored as we will not emit updates for them |
| 300 // after they are completed. | 308 // after they are completed. |
| 301 void CupsPrintJobManagerImpl::UpdateJobs(const QueryResult& result) { | 309 void CupsPrintJobManagerImpl::UpdateJobs(const QueryResult& result) { |
| 302 const std::vector<::printing::QueueStatus>& queues = result.queues; | 310 const std::vector<::printing::QueueStatus>& queues = result.queues; |
| 303 | 311 |
| 304 // Query has completed. Allow more queries. | 312 // Query has completed. Allow more queries. |
| 305 in_query_ = false; | 313 in_query_ = false; |
| 306 | 314 |
| 307 // If the query failed, either retry or purge. | 315 // If the query failed, either retry or purge. |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 332 if (entry == jobs_.end()) | 340 if (entry == jobs_.end()) |
| 333 continue; | 341 continue; |
| 334 | 342 |
| 335 CupsPrintJob* print_job = entry->second.get(); | 343 CupsPrintJob* print_job = entry->second.get(); |
| 336 | 344 |
| 337 if (UpdatePrintJob(queue.printer_status, job, print_job)) { | 345 if (UpdatePrintJob(queue.printer_status, job, print_job)) { |
| 338 // The state of the job changed, notify observers. | 346 // The state of the job changed, notify observers. |
| 339 NotifyJobStateUpdate(print_job); | 347 NotifyJobStateUpdate(print_job); |
| 340 } | 348 } |
| 341 | 349 |
| 342 // Cleanup completed jobs. | 350 if (print_job->expired()) { |
| 343 if (print_job->IsJobFinished()) { | 351 // Job needs to be forcibly cancelled. |
| 352 CancelPrintJob(print_job); | |
| 353 // Beware, print_job was removed from jobs_ and deleted. | |
| 354 } else if (print_job->IsJobFinished()) { | |
| 355 // Cleanup completed jobs. | |
| 344 jobs_.erase(entry); | 356 jobs_.erase(entry); |
| 345 } else { | 357 } else { |
| 346 active_jobs.push_back(key); | 358 active_jobs.push_back(key); |
| 347 } | 359 } |
| 348 } | 360 } |
| 349 } | 361 } |
| 350 | 362 |
| 351 // Keep polling until all jobs complete or error. | 363 // Keep polling until all jobs complete or error. |
| 352 if (!active_jobs.empty()) { | 364 if (!active_jobs.empty()) { |
| 353 // During normal operations, we poll at the default rate. | 365 // During normal operations, we poll at the default rate. |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 364 for (const auto& entry : jobs_) { | 376 for (const auto& entry : jobs_) { |
| 365 // Declare all lost jobs errors. | 377 // Declare all lost jobs errors. |
| 366 CupsPrintJob* job = entry.second.get(); | 378 CupsPrintJob* job = entry.second.get(); |
| 367 job->set_state(CupsPrintJob::State::STATE_ERROR); | 379 job->set_state(CupsPrintJob::State::STATE_ERROR); |
| 368 NotifyJobStateUpdate(job); | 380 NotifyJobStateUpdate(job); |
| 369 } | 381 } |
| 370 | 382 |
| 371 jobs_.clear(); | 383 jobs_.clear(); |
| 372 } | 384 } |
| 373 | 385 |
| 386 void CupsPrintJobManagerImpl::CancelJobImpl(const std::string& printer_id, | |
| 387 const int job_id) { | |
| 388 std::unique_ptr<::printing::CupsPrinter> printer = | |
| 389 cups_connection_.GetPrinter(printer_id); | |
| 390 if (!printer) { | |
| 391 LOG(WARNING) << "Printer not found"; | |
|
Carlson
2017/03/16 18:36:48
Would it be useful to add printer_id and/or job_id
skau
2017/03/16 22:14:36
I'll log the printer_id. I don't think the job_id
| |
| 392 return; | |
| 393 } | |
| 394 | |
| 395 if (!printer->CancelJob(job_id)) { | |
| 396 // This is not expected to fail but log it if it does. | |
| 397 LOG(WARNING) << "Cancelling job failed. Job may be stuck in queue."; | |
| 398 } | |
| 399 } | |
| 400 | |
| 374 void CupsPrintJobManagerImpl::NotifyJobStateUpdate(CupsPrintJob* job) { | 401 void CupsPrintJobManagerImpl::NotifyJobStateUpdate(CupsPrintJob* job) { |
| 375 switch (job->state()) { | 402 switch (job->state()) { |
| 376 case State::STATE_NONE: | 403 case State::STATE_NONE: |
| 377 // State does not require notification. | 404 // State does not require notification. |
| 378 break; | 405 break; |
| 379 case State::STATE_WAITING: | 406 case State::STATE_WAITING: |
| 380 NotifyJobUpdated(job); | 407 NotifyJobUpdated(job); |
| 381 break; | 408 break; |
| 382 case State::STATE_STARTED: | 409 case State::STATE_STARTED: |
| 383 NotifyJobStarted(job); | 410 NotifyJobStarted(job); |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 402 break; | 429 break; |
| 403 } | 430 } |
| 404 } | 431 } |
| 405 | 432 |
| 406 // static | 433 // static |
| 407 CupsPrintJobManager* CupsPrintJobManager::CreateInstance(Profile* profile) { | 434 CupsPrintJobManager* CupsPrintJobManager::CreateInstance(Profile* profile) { |
| 408 return new CupsPrintJobManagerImpl(profile); | 435 return new CupsPrintJobManagerImpl(profile); |
| 409 } | 436 } |
| 410 | 437 |
| 411 } // namespace chromeos | 438 } // namespace chromeos |
| OLD | NEW |