| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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/ui/webui/settings/chromeos/cups_printers_handler.h" | 5 #include "chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
| 11 #include "base/files/file_util.h" | 11 #include "base/files/file_util.h" |
| 12 #include "base/json/json_string_value_serializer.h" | 12 #include "base/json/json_string_value_serializer.h" |
| 13 #include "base/memory/ptr_util.h" | 13 #include "base/memory/ptr_util.h" |
| 14 #include "base/metrics/histogram_macros.h" | 14 #include "base/metrics/histogram_macros.h" |
| 15 #include "base/path_service.h" | 15 #include "base/path_service.h" |
| 16 #include "base/strings/string_util.h" | 16 #include "base/strings/string_util.h" |
| 17 #include "base/threading/sequenced_task_runner_handle.h" | 17 #include "base/threading/sequenced_task_runner_handle.h" |
| 18 #include "base/values.h" | 18 #include "base/values.h" |
| 19 #include "chrome/browser/browser_process.h" | 19 #include "chrome/browser/browser_process.h" |
| 20 #include "chrome/browser/chromeos/printing/combining_printer_detector.h" |
| 20 #include "chrome/browser/chromeos/printing/ppd_provider_factory.h" | 21 #include "chrome/browser/chromeos/printing/ppd_provider_factory.h" |
| 21 #include "chrome/browser/chromeos/printing/printer_configurer.h" | 22 #include "chrome/browser/chromeos/printing/printer_configurer.h" |
| 22 #include "chrome/browser/chromeos/printing/printer_discoverer.h" | |
| 23 #include "chrome/browser/chromeos/printing/printer_info.h" | 23 #include "chrome/browser/chromeos/printing/printer_info.h" |
| 24 #include "chrome/browser/chromeos/printing/printers_manager_factory.h" | 24 #include "chrome/browser/chromeos/printing/printers_manager_factory.h" |
| 25 #include "chrome/browser/chromeos/printing/usb_printer_detector.h" |
| 26 #include "chrome/browser/chromeos/printing/usb_printer_detector_factory.h" |
| 25 #include "chrome/browser/download/download_prefs.h" | 27 #include "chrome/browser/download/download_prefs.h" |
| 26 #include "chrome/browser/profiles/profile.h" | 28 #include "chrome/browser/profiles/profile.h" |
| 27 #include "chrome/browser/ui/browser_finder.h" | 29 #include "chrome/browser/ui/browser_finder.h" |
| 28 #include "chrome/browser/ui/browser_window.h" | 30 #include "chrome/browser/ui/browser_window.h" |
| 29 #include "chrome/browser/ui/chrome_select_file_policy.h" | 31 #include "chrome/browser/ui/chrome_select_file_policy.h" |
| 30 #include "chrome/common/chrome_paths.h" | 32 #include "chrome/common/chrome_paths.h" |
| 31 #include "chromeos/dbus/dbus_thread_manager.h" | 33 #include "chromeos/dbus/dbus_thread_manager.h" |
| 32 #include "chromeos/dbus/debug_daemon_client.h" | 34 #include "chromeos/dbus/debug_daemon_client.h" |
| 33 #include "chromeos/printing/ppd_cache.h" | 35 #include "chromeos/printing/ppd_cache.h" |
| 34 #include "chromeos/printing/ppd_provider.h" | 36 #include "chromeos/printing/ppd_provider.h" |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 119 // empty string if the input is just a backslash. | 121 // empty string if the input is just a backslash. |
| 120 queue = queue.substr(1); | 122 queue = queue.substr(1); |
| 121 } | 123 } |
| 122 | 124 |
| 123 return queue; | 125 return queue; |
| 124 } | 126 } |
| 125 | 127 |
| 126 } // namespace | 128 } // namespace |
| 127 | 129 |
| 128 CupsPrintersHandler::CupsPrintersHandler(content::WebUI* webui) | 130 CupsPrintersHandler::CupsPrintersHandler(content::WebUI* webui) |
| 129 : printer_discoverer_(nullptr), | 131 : printer_detector_(nullptr), |
| 130 profile_(Profile::FromWebUI(webui)), | 132 profile_(Profile::FromWebUI(webui)), |
| 131 weak_factory_(this) { | 133 weak_factory_(this) { |
| 132 ppd_provider_ = printing::CreateProvider(profile_); | 134 ppd_provider_ = printing::CreateProvider(profile_); |
| 133 printer_configurer_ = chromeos::PrinterConfigurer::Create(profile_); | 135 printer_configurer_ = PrinterConfigurer::Create(profile_); |
| 134 } | 136 } |
| 135 | 137 |
| 136 CupsPrintersHandler::~CupsPrintersHandler() {} | 138 CupsPrintersHandler::~CupsPrintersHandler() {} |
| 137 | 139 |
| 138 void CupsPrintersHandler::RegisterMessages() { | 140 void CupsPrintersHandler::RegisterMessages() { |
| 139 web_ui()->RegisterMessageCallback( | 141 web_ui()->RegisterMessageCallback( |
| 140 "getCupsPrintersList", | 142 "getCupsPrintersList", |
| 141 base::Bind(&CupsPrintersHandler::HandleGetCupsPrintersList, | 143 base::Bind(&CupsPrintersHandler::HandleGetCupsPrintersList, |
| 142 base::Unretained(this))); | 144 base::Unretained(this))); |
| 143 web_ui()->RegisterMessageCallback( | 145 web_ui()->RegisterMessageCallback( |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 217 CHECK(args->GetString(1, &printer_name)); | 219 CHECK(args->GetString(1, &printer_name)); |
| 218 PrintersManager* prefs = | 220 PrintersManager* prefs = |
| 219 PrintersManagerFactory::GetForBrowserContext(profile_); | 221 PrintersManagerFactory::GetForBrowserContext(profile_); |
| 220 auto printer = prefs->GetPrinter(printer_id); | 222 auto printer = prefs->GetPrinter(printer_id); |
| 221 if (!printer) | 223 if (!printer) |
| 222 return; | 224 return; |
| 223 | 225 |
| 224 Printer::PrinterProtocol protocol = printer->GetProtocol(); | 226 Printer::PrinterProtocol protocol = printer->GetProtocol(); |
| 225 prefs->RemovePrinter(printer_id); | 227 prefs->RemovePrinter(printer_id); |
| 226 | 228 |
| 227 chromeos::DebugDaemonClient* client = | 229 DebugDaemonClient* client = DBusThreadManager::Get()->GetDebugDaemonClient(); |
| 228 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient(); | |
| 229 client->CupsRemovePrinter(printer_name, | 230 client->CupsRemovePrinter(printer_name, |
| 230 base::Bind(&OnRemovedPrinter, protocol), | 231 base::Bind(&OnRemovedPrinter, protocol), |
| 231 base::Bind(&base::DoNothing)); | 232 base::Bind(&base::DoNothing)); |
| 232 } | 233 } |
| 233 | 234 |
| 234 void CupsPrintersHandler::HandleGetPrinterInfo(const base::ListValue* args) { | 235 void CupsPrintersHandler::HandleGetPrinterInfo(const base::ListValue* args) { |
| 235 DCHECK(args); | 236 DCHECK(args); |
| 236 std::string callback_id; | 237 std::string callback_id; |
| 237 if (!args->GetString(0, &callback_id)) { | 238 if (!args->GetString(0, &callback_id)) { |
| 238 NOTREACHED() << "Expected request for a promise"; | 239 NOTREACHED() << "Expected request for a promise"; |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 283 if (printer_protocol == kIppScheme) { | 284 if (printer_protocol == kIppScheme) { |
| 284 port = kIppPort; | 285 port = kIppPort; |
| 285 } else if (printer_protocol == kIppsScheme) { | 286 } else if (printer_protocol == kIppsScheme) { |
| 286 // ipps is ipp over https so it uses the https port. | 287 // ipps is ipp over https so it uses the https port. |
| 287 port = kIppsPort; | 288 port = kIppsPort; |
| 288 } else { | 289 } else { |
| 289 NOTREACHED() << "Unrecognized protocol. Port was not set."; | 290 NOTREACHED() << "Unrecognized protocol. Port was not set."; |
| 290 } | 291 } |
| 291 } | 292 } |
| 292 | 293 |
| 293 ::chromeos::QueryIppPrinter( | 294 QueryIppPrinter(host.as_string(), port, printer_queue, |
| 294 host.as_string(), port, printer_queue, | 295 base::Bind(&CupsPrintersHandler::OnPrinterInfo, |
| 295 base::Bind(&CupsPrintersHandler::OnPrinterInfo, | 296 weak_factory_.GetWeakPtr(), callback_id)); |
| 296 weak_factory_.GetWeakPtr(), callback_id)); | |
| 297 } | 297 } |
| 298 | 298 |
| 299 void CupsPrintersHandler::OnPrinterInfo(const std::string& callback_id, | 299 void CupsPrintersHandler::OnPrinterInfo(const std::string& callback_id, |
| 300 bool success, | 300 bool success, |
| 301 const std::string& make, | 301 const std::string& make, |
| 302 const std::string& model, | 302 const std::string& model, |
| 303 bool ipp_everywhere) { | 303 bool ipp_everywhere) { |
| 304 if (!success) { | 304 if (!success) { |
| 305 base::DictionaryValue reject; | 305 base::DictionaryValue reject; |
| 306 reject.SetString("message", "Querying printer failed"); | 306 reject.SetString("message", "Querying printer failed"); |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 383 | 383 |
| 384 // Copy the printer for the configurer. Ownership needs to be transfered to | 384 // Copy the printer for the configurer. Ownership needs to be transfered to |
| 385 // the receiver of the callback. | 385 // the receiver of the callback. |
| 386 const Printer printer_copy = *printer; | 386 const Printer printer_copy = *printer; |
| 387 printer_configurer_->SetUpPrinter( | 387 printer_configurer_->SetUpPrinter( |
| 388 printer_copy, | 388 printer_copy, |
| 389 base::Bind(&CupsPrintersHandler::OnAddedPrinter, | 389 base::Bind(&CupsPrintersHandler::OnAddedPrinter, |
| 390 weak_factory_.GetWeakPtr(), base::Passed(&printer))); | 390 weak_factory_.GetWeakPtr(), base::Passed(&printer))); |
| 391 } | 391 } |
| 392 | 392 |
| 393 void CupsPrintersHandler::OnAddedPrinter( | 393 void CupsPrintersHandler::OnAddedPrinter(std::unique_ptr<Printer> printer, |
| 394 std::unique_ptr<Printer> printer, | 394 PrinterSetupResult result_code) { |
| 395 chromeos::PrinterSetupResult result_code) { | |
| 396 std::string printer_name = printer->display_name(); | 395 std::string printer_name = printer->display_name(); |
| 397 UMA_HISTOGRAM_ENUMERATION("Printing.CUPS.PrinterSetupResult", result_code, | 396 UMA_HISTOGRAM_ENUMERATION("Printing.CUPS.PrinterSetupResult", result_code, |
| 398 chromeos::PrinterSetupResult::kMaxValue); | 397 PrinterSetupResult::kMaxValue); |
| 399 switch (result_code) { | 398 switch (result_code) { |
| 400 case chromeos::PrinterSetupResult::kSuccess: { | 399 case PrinterSetupResult::kSuccess: { |
| 401 UMA_HISTOGRAM_ENUMERATION("Printing.CUPS.PrinterAdded", | 400 UMA_HISTOGRAM_ENUMERATION("Printing.CUPS.PrinterAdded", |
| 402 printer->GetProtocol(), Printer::kProtocolMax); | 401 printer->GetProtocol(), Printer::kProtocolMax); |
| 403 auto* manager = PrintersManagerFactory::GetForBrowserContext(profile_); | 402 auto* manager = PrintersManagerFactory::GetForBrowserContext(profile_); |
| 404 manager->PrinterInstalled(*printer); | 403 manager->PrinterInstalled(*printer); |
| 405 manager->RegisterPrinter(std::move(printer)); | 404 manager->RegisterPrinter(std::move(printer)); |
| 406 break; | 405 break; |
| 407 } | 406 } |
| 408 case chromeos::PrinterSetupResult::kPpdNotFound: | 407 case PrinterSetupResult::kPpdNotFound: |
| 409 LOG(WARNING) << "Could not locate requested PPD"; | 408 LOG(WARNING) << "Could not locate requested PPD"; |
| 410 break; | 409 break; |
| 411 case chromeos::PrinterSetupResult::kPpdTooLarge: | 410 case PrinterSetupResult::kPpdTooLarge: |
| 412 LOG(WARNING) << "PPD is too large"; | 411 LOG(WARNING) << "PPD is too large"; |
| 413 break; | 412 break; |
| 414 case chromeos::PrinterSetupResult::kPpdUnretrievable: | 413 case PrinterSetupResult::kPpdUnretrievable: |
| 415 LOG(WARNING) << "Could not retrieve PPD from server"; | 414 LOG(WARNING) << "Could not retrieve PPD from server"; |
| 416 break; | 415 break; |
| 417 case chromeos::PrinterSetupResult::kInvalidPpd: | 416 case PrinterSetupResult::kInvalidPpd: |
| 418 LOG(WARNING) << "Provided PPD is invalid."; | 417 LOG(WARNING) << "Provided PPD is invalid."; |
| 419 break; | 418 break; |
| 420 case chromeos::PrinterSetupResult::kPrinterUnreachable: | 419 case PrinterSetupResult::kPrinterUnreachable: |
| 421 LOG(WARNING) << "Could not contact printer for configuration"; | 420 LOG(WARNING) << "Could not contact printer for configuration"; |
| 422 break; | 421 break; |
| 423 case chromeos::PrinterSetupResult::kDbusError: | 422 case PrinterSetupResult::kDbusError: |
| 424 case chromeos::PrinterSetupResult::kFatalError: | 423 case PrinterSetupResult::kFatalError: |
| 425 LOG(ERROR) << "Unrecoverable error. Reboot required."; | 424 LOG(ERROR) << "Unrecoverable error. Reboot required."; |
| 426 break; | 425 break; |
| 427 case chromeos::PrinterSetupResult::kMaxValue: | 426 case PrinterSetupResult::kMaxValue: |
| 428 NOTREACHED() << "This is not an expected value"; | 427 NOTREACHED() << "This is not an expected value"; |
| 429 break; | 428 break; |
| 430 } | 429 } |
| 431 CallJavascriptFunction( | 430 CallJavascriptFunction( |
| 432 "cr.webUIListenerCallback", base::Value("on-add-cups-printer"), | 431 "cr.webUIListenerCallback", base::Value("on-add-cups-printer"), |
| 433 base::Value(result_code == chromeos::PrinterSetupResult::kSuccess), | 432 base::Value(result_code == PrinterSetupResult::kSuccess), |
| 434 base::Value(printer_name)); | 433 base::Value(printer_name)); |
| 435 } | 434 } |
| 436 | 435 |
| 437 void CupsPrintersHandler::OnAddPrinterError() { | 436 void CupsPrintersHandler::OnAddPrinterError() { |
| 438 FireWebUIListener("on-add-cups-printer", base::Value(false), base::Value("")); | 437 FireWebUIListener("on-add-cups-printer", base::Value(false), base::Value("")); |
| 439 } | 438 } |
| 440 | 439 |
| 441 void CupsPrintersHandler::HandleGetCupsPrinterManufacturers( | 440 void CupsPrintersHandler::HandleGetCupsPrinterManufacturers( |
| 442 const base::ListValue* args) { | 441 const base::ListValue* args) { |
| 443 AllowJavascript(); | 442 AllowJavascript(); |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 488 chrome::FindBrowserWithWebContents(web_ui()->GetWebContents()) | 487 chrome::FindBrowserWithWebContents(web_ui()->GetWebContents()) |
| 489 ->window() | 488 ->window() |
| 490 ->GetNativeWindow(); | 489 ->GetNativeWindow(); |
| 491 select_file_dialog_->SelectFile( | 490 select_file_dialog_->SelectFile( |
| 492 ui::SelectFileDialog::SELECT_OPEN_FILE, base::string16(), downloads_path, | 491 ui::SelectFileDialog::SELECT_OPEN_FILE, base::string16(), downloads_path, |
| 493 nullptr, 0, FILE_PATH_LITERAL(""), owning_window, nullptr); | 492 nullptr, 0, FILE_PATH_LITERAL(""), owning_window, nullptr); |
| 494 } | 493 } |
| 495 | 494 |
| 496 void CupsPrintersHandler::ResolveManufacturersDone( | 495 void CupsPrintersHandler::ResolveManufacturersDone( |
| 497 const std::string& js_callback, | 496 const std::string& js_callback, |
| 498 chromeos::printing::PpdProvider::CallbackResultCode result_code, | 497 printing::PpdProvider::CallbackResultCode result_code, |
| 499 const std::vector<std::string>& manufacturers) { | 498 const std::vector<std::string>& manufacturers) { |
| 500 auto manufacturers_value = base::MakeUnique<base::ListValue>(); | 499 auto manufacturers_value = base::MakeUnique<base::ListValue>(); |
| 501 if (result_code == chromeos::printing::PpdProvider::SUCCESS) { | 500 if (result_code == printing::PpdProvider::SUCCESS) { |
| 502 manufacturers_value->AppendStrings(manufacturers); | 501 manufacturers_value->AppendStrings(manufacturers); |
| 503 } | 502 } |
| 504 base::DictionaryValue response; | 503 base::DictionaryValue response; |
| 505 response.SetBoolean("success", | 504 response.SetBoolean("success", result_code == printing::PpdProvider::SUCCESS); |
| 506 result_code == chromeos::printing::PpdProvider::SUCCESS); | |
| 507 response.Set("manufacturers", std::move(manufacturers_value)); | 505 response.Set("manufacturers", std::move(manufacturers_value)); |
| 508 ResolveJavascriptCallback(base::Value(js_callback), response); | 506 ResolveJavascriptCallback(base::Value(js_callback), response); |
| 509 } | 507 } |
| 510 | 508 |
| 511 void CupsPrintersHandler::ResolvePrintersDone( | 509 void CupsPrintersHandler::ResolvePrintersDone( |
| 512 const std::string& js_callback, | 510 const std::string& js_callback, |
| 513 chromeos::printing::PpdProvider::CallbackResultCode result_code, | 511 printing::PpdProvider::CallbackResultCode result_code, |
| 514 const std::vector<std::string>& printers) { | 512 const std::vector<std::string>& printers) { |
| 515 auto printers_value = base::MakeUnique<base::ListValue>(); | 513 auto printers_value = base::MakeUnique<base::ListValue>(); |
| 516 if (result_code == chromeos::printing::PpdProvider::SUCCESS) { | 514 if (result_code == printing::PpdProvider::SUCCESS) { |
| 517 printers_value->AppendStrings(printers); | 515 printers_value->AppendStrings(printers); |
| 518 } | 516 } |
| 519 base::DictionaryValue response; | 517 base::DictionaryValue response; |
| 520 response.SetBoolean("success", | 518 response.SetBoolean("success", result_code == printing::PpdProvider::SUCCESS); |
| 521 result_code == chromeos::printing::PpdProvider::SUCCESS); | |
| 522 response.Set("models", std::move(printers_value)); | 519 response.Set("models", std::move(printers_value)); |
| 523 ResolveJavascriptCallback(base::Value(js_callback), response); | 520 ResolveJavascriptCallback(base::Value(js_callback), response); |
| 524 } | 521 } |
| 525 | 522 |
| 526 void CupsPrintersHandler::FileSelected(const base::FilePath& path, | 523 void CupsPrintersHandler::FileSelected(const base::FilePath& path, |
| 527 int index, | 524 int index, |
| 528 void* params) { | 525 void* params) { |
| 529 DCHECK(!webui_callback_id_.empty()); | 526 DCHECK(!webui_callback_id_.empty()); |
| 530 ResolveJavascriptCallback(base::Value(webui_callback_id_), | 527 ResolveJavascriptCallback(base::Value(webui_callback_id_), |
| 531 base::Value(path.value())); | 528 base::Value(path.value())); |
| 532 webui_callback_id_.clear(); | 529 webui_callback_id_.clear(); |
| 533 } | 530 } |
| 534 | 531 |
| 535 void CupsPrintersHandler::HandleStartDiscovery(const base::ListValue* args) { | 532 void CupsPrintersHandler::HandleStartDiscovery(const base::ListValue* args) { |
| 536 if (!printer_discoverer_.get()) { | 533 // Create (or recreate) the printer_detector_. If one already existed, we |
| 537 printer_discoverer_ = | 534 // want to start over anyways. |
| 538 chromeos::PrinterDiscoverer::CreateForProfile(profile_); | 535 printer_detector_ = CombiningPrinterDetector::Create(); |
| 539 } | 536 PrinterDetector* usb_detector = |
| 540 | 537 UsbPrinterDetectorFactory::GetInstance()->Get(profile_); |
| 541 printer_discoverer_->AddObserver(this); | 538 DCHECK(usb_detector); |
| 539 printer_detector_->AddDetector(usb_detector); |
| 540 printer_detector_->AddObserver(this); |
| 541 OnPrintersFound(printer_detector_->GetPrinters()); |
| 542 printer_detector_->Start(); |
| 542 } | 543 } |
| 543 | 544 |
| 544 void CupsPrintersHandler::HandleStopDiscovery(const base::ListValue* args) { | 545 void CupsPrintersHandler::HandleStopDiscovery(const base::ListValue* args) { |
| 545 printer_discoverer_.reset(); | 546 printer_detector_.reset(); |
| 546 } | 547 } |
| 547 | 548 |
| 548 void CupsPrintersHandler::OnPrintersFound( | 549 void CupsPrintersHandler::OnPrintersFound( |
| 549 const std::vector<Printer>& printers) { | 550 const std::vector<Printer>& printers) { |
| 550 std::unique_ptr<base::ListValue> printers_list = | 551 std::unique_ptr<base::ListValue> printers_list = |
| 551 base::MakeUnique<base::ListValue>(); | 552 base::MakeUnique<base::ListValue>(); |
| 552 for (const auto& printer : printers) { | 553 // Filter out already-configured printers as we go. |
| 553 printers_list->Append(GetPrinterInfo(printer)); | 554 PrintersManager* printers_manager = |
| 555 PrintersManagerFactory::GetForBrowserContext(profile_); |
| 556 if (printers_manager != nullptr) { |
| 557 for (const auto& printer : printers) { |
| 558 if (printers_manager->GetPrinter(printer.id()).get() == nullptr) { |
| 559 printers_list->Append(GetPrinterInfo(printer)); |
| 560 } |
| 561 } |
| 562 } else { |
| 563 LOG(WARNING) << "Failing to get available printers because no " |
| 564 "PrintersManager exists."; |
| 554 } | 565 } |
| 555 | |
| 556 FireWebUIListener("on-printer-discovered", *printers_list); | 566 FireWebUIListener("on-printer-discovered", *printers_list); |
| 557 } | 567 } |
| 558 | 568 |
| 559 void CupsPrintersHandler::OnDiscoveryInitialScanDone(int printer_count) { | 569 void CupsPrintersHandler::OnPrinterScanComplete() { |
| 560 UMA_HISTOGRAM_COUNTS_100("Printing.CUPS.PrintersDiscovered", printer_count); | 570 UMA_HISTOGRAM_COUNTS_100("Printing.CUPS.PrintersDiscovered", |
| 571 printer_detector_->GetPrinters().size()); |
| 561 FireWebUIListener("on-printer-discovery-done"); | 572 FireWebUIListener("on-printer-discovery-done"); |
| 562 } | 573 } |
| 563 | 574 |
| 564 } // namespace settings | 575 } // namespace settings |
| 565 } // namespace chromeos | 576 } // namespace chromeos |
| OLD | NEW |