15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/chromeos/ui/idle_logout_dialog_view.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ash/shell.h" 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h" 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind_helpers.h" 102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h" 11868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h" 12eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/chromeos/kiosk_mode/kiosk_mode_settings.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_list.h" 156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/grit/generated_resources.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chromeos/dbus/dbus_thread_manager.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chromeos/dbus/session_manager_client.h" 18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/aura/window_event_dispatcher.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/label.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/layout/grid_layout.h" 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/layout/layout_constants.h" 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/widget.h" 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Global singleton instance of our dialog class. 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)chromeos::IdleLogoutDialogView* g_instance = NULL; 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kIdleLogoutDialogMaxWidth = 300; 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kCountdownUpdateIntervalMs = 1000; 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace chromeos { 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IdleLogoutSettingsProvider* IdleLogoutDialogView::provider_ = NULL; 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//////////////////////////////////////////////////////////////////////////////// 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// IdleLogoutSettingsProvider public methods 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IdleLogoutSettingsProvider::IdleLogoutSettingsProvider() { 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IdleLogoutSettingsProvider::~IdleLogoutSettingsProvider() { 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)base::TimeDelta IdleLogoutSettingsProvider::GetCountdownUpdateInterval() { 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return base::TimeDelta::FromMilliseconds(kCountdownUpdateIntervalMs); 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)KioskModeSettings* IdleLogoutSettingsProvider::GetKioskModeSettings() { 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return KioskModeSettings::Get(); 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IdleLogoutSettingsProvider::LogoutCurrentUser(IdleLogoutDialogView*) { 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DBusThreadManager::Get()->GetSessionManagerClient()->StopSession(); 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//////////////////////////////////////////////////////////////////////////////// 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// IdleLogoutDialogView public static methods 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IdleLogoutDialogView::ShowDialog() { 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We only show the dialog if it is not already showing. We don't want two 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // countdowns on the screen for any reason. If the dialog is closed by using 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // CloseDialog, we reset g_instance so the next Show will work correctly; in 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // case the dialog is closed by the system, DeleteDelegate is guaranteed to be 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // called, in which case we reset g_instance there if not already reset. 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!g_instance) { 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_instance = new IdleLogoutDialogView(); 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_instance->InitAndShow(); 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IdleLogoutDialogView::CloseDialog() { 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (g_instance) 77a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) g_instance->GetWidget()->Close(); 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//////////////////////////////////////////////////////////////////////////////// 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Overridden from views::DialogDelegateView 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int IdleLogoutDialogView::GetDialogButtons() const { 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ui::DIALOG_BUTTON_NONE; 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ui::ModalType IdleLogoutDialogView::GetModalType() const { 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ui::MODAL_TYPE_WINDOW; 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 90a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)base::string16 IdleLogoutDialogView::GetWindowTitle() const { 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return l10n_util::GetStringUTF16(IDS_IDLE_LOGOUT_TITLE); 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 94a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)bool IdleLogoutDialogView::Close() { 95a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) if (timer_.IsRunning()) 96a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) timer_.Stop(); 97a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 98a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // We just closed our dialog. The global 99a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) // instance is invalid now, set it to null. 100a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) g_instance = NULL; 101a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 102a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) return true; 103a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)} 104a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//////////////////////////////////////////////////////////////////////////////// 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// IdleLogoutDialog private methods 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IdleLogoutDialogView::IdleLogoutDialogView() 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : restart_label_(NULL), 109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) weak_ptr_factory_(this) { 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!IdleLogoutDialogView::provider_) 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) IdleLogoutDialogView::provider_ = new IdleLogoutSettingsProvider(); 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IdleLogoutDialogView::~IdleLogoutDialogView() { 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this == g_instance) 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_instance = NULL; 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IdleLogoutDialogView::InitAndShow() { 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) KioskModeSettings* settings = 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) IdleLogoutDialogView::provider_->GetKioskModeSettings(); 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!settings->is_initialized()) { 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) settings->Initialize(base::Bind(&IdleLogoutDialogView::InitAndShow, 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) weak_ptr_factory_.GetWeakPtr())); 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) restart_label_ = new views::Label(); 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) restart_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) restart_label_->SetMultiLine(true); 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) views::GridLayout* layout = views::GridLayout::CreatePanel(this); 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetLayoutManager(layout); 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) views::ColumnSet* column_set = layout->AddColumnSet(0); 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 1, 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) views::GridLayout::FIXED, kIdleLogoutDialogMaxWidth, 0); 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) layout->StartRow(0, 0); 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) layout->AddPaddingRow(0, views::kRelatedControlHorizontalSpacing); 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) layout->StartRow(0, 0); 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) layout->AddView(restart_label_); 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) layout->AddPaddingRow(0, views::kRelatedControlHorizontalSpacing); 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We're initialized, show the dialog. 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Show(); 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IdleLogoutDialogView::Show() { 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) KioskModeSettings* settings = 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) IdleLogoutDialogView::provider_->GetKioskModeSettings(); 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Setup the countdown label before showing. 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) countdown_end_time_ = base::Time::Now() + 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) settings->GetIdleLogoutWarningDuration(); 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UpdateCountdown(); 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 15890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) views::DialogDelegate::CreateDialogWidget( 15990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) this, ash::Shell::GetPrimaryRootWindow(), NULL); 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetWidget()->SetAlwaysOnTop(true); 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GetWidget()->Show(); 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Update countdown every 1 second. 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) timer_.Start(FROM_HERE, 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) IdleLogoutDialogView::provider_->GetCountdownUpdateInterval(), 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this, 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &IdleLogoutDialogView::UpdateCountdown); 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IdleLogoutDialogView::UpdateCountdown() { 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::TimeDelta logout_warning_time = countdown_end_time_ - 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Time::Now(); 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int64 seconds_left = (logout_warning_time.InMillisecondsF() / 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Time::kMillisecondsPerSecond) + 0.5; 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (seconds_left > 1) { 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) restart_label_->SetText(l10n_util::GetStringFUTF16( 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) IDS_IDLE_LOGOUT_WARNING_RESTART, 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Int64ToString16(seconds_left))); 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (seconds_left > 0) { 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) restart_label_->SetText(l10n_util::GetStringUTF16( 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) IDS_IDLE_LOGOUT_WARNING_RESTART_1S)); 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Set the label - the logout probably won't be instant. 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) restart_label_->SetText(l10n_util::GetStringUTF16( 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) IDS_IDLE_LOGOUT_WARNING_RESTART_NOW)); 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We're done; stop the timer and logout. 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) timer_.Stop(); 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) IdleLogoutDialogView::provider_->LogoutCurrentUser(this); 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IdleLogoutDialogView* IdleLogoutDialogView::current_instance() { 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return g_instance; 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void IdleLogoutDialogView::set_settings_provider( 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) IdleLogoutSettingsProvider* provider) { 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) provider_ = provider; 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace chromeos 206