Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(82)

Side by Side Diff: chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc

Issue 296823009: ChromeOS login webui refactoring: split user selection/gaia login screens. (Closed) Base URL: http://git.chromium.org/chromium/src.git@refactoring-1
Patch Set: Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 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/chromeos/login/signin_screen_handler.h" 5 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/bind_helpers.h" 10 #include "base/bind_helpers.h"
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
78 namespace { 78 namespace {
79 79
80 // User dictionary keys. 80 // User dictionary keys.
81 const char kKeyUsername[] = "username"; 81 const char kKeyUsername[] = "username";
82 const char kKeyDisplayName[] = "displayName"; 82 const char kKeyDisplayName[] = "displayName";
83 const char kKeyEmailAddress[] = "emailAddress"; 83 const char kKeyEmailAddress[] = "emailAddress";
84 const char kKeyEnterpriseDomain[] = "enterpriseDomain"; 84 const char kKeyEnterpriseDomain[] = "enterpriseDomain";
85 const char kKeyPublicAccount[] = "publicAccount"; 85 const char kKeyPublicAccount[] = "publicAccount";
86 const char kKeyLocallyManagedUser[] = "locallyManagedUser"; 86 const char kKeyLocallyManagedUser[] = "locallyManagedUser";
87 const char kKeySignedIn[] = "signedIn"; 87 const char kKeySignedIn[] = "signedIn";
88 const char kKeyCanRemove[] = "canRemove";
89 const char kKeyIsOwner[] = "isOwner"; 88 const char kKeyIsOwner[] = "isOwner";
90 const char kKeyInitialAuthType[] = "initialAuthType"; 89 const char kKeyInitialAuthType[] = "initialAuthType";
91 const char kKeyMultiProfilesAllowed[] = "isMultiProfilesAllowed"; 90 const char kKeyMultiProfilesAllowed[] = "isMultiProfilesAllowed";
92 const char kKeyMultiProfilesPolicy[] = "multiProfilesPolicy"; 91 const char kKeyMultiProfilesPolicy[] = "multiProfilesPolicy";
93 92
94 // Max number of users to show. 93 // Max number of users to show.
95 const size_t kMaxUsers = 18; 94 const size_t kMaxUsers = 18;
96 95
97 // Timeout to delay first notification about offline state for a 96 // Timeout to delay first notification about offline state for a
98 // current network. 97 // current network.
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
222 } 221 }
223 manager->ChangeInputMethod(input_method); 222 manager->ChangeInputMethod(input_method);
224 223
225 return true; 224 return true;
226 } 225 }
227 226
228 void RecordSAMLScrapingVerificationResultInHistogram(bool success) { 227 void RecordSAMLScrapingVerificationResultInHistogram(bool success) {
229 UMA_HISTOGRAM_BOOLEAN("ChromeOS.SAML.Scraping.VerificationResult", success); 228 UMA_HISTOGRAM_BOOLEAN("ChromeOS.SAML.Scraping.VerificationResult", success);
230 } 229 }
231 230
232 bool ShouldForceOnlineSignIn(const User* user) {
233 // Public sessions are always allowed to log in offline.
234 // Supervised user are allowed to log in offline if their OAuth token status
235 // is unknown or valid.
236 // For all other users, force online sign in if:
237 // * The flag to force online sign-in is set for the user.
238 // * The user's OAuth token is invalid.
239 // * The user's OAuth token status is unknown (except supervised users,
240 // see above).
241 if (user->is_logged_in())
242 return false;
243
244 const User::OAuthTokenStatus token_status = user->oauth_token_status();
245 const bool is_locally_managed_user =
246 user->GetType() == User::USER_TYPE_LOCALLY_MANAGED;
247 const bool is_public_session =
248 user->GetType() == User::USER_TYPE_PUBLIC_ACCOUNT;
249
250 if (is_locally_managed_user &&
251 token_status == User::OAUTH_TOKEN_STATUS_UNKNOWN) {
252 return false;
253 }
254
255 if (is_public_session)
256 return false;
257
258 return user->force_online_signin() ||
259 (token_status == User::OAUTH2_TOKEN_STATUS_INVALID) ||
260 (token_status == User::OAUTH_TOKEN_STATUS_UNKNOWN);
261 }
262
263 } // namespace 231 } // namespace
264 232
265 // LoginScreenContext implementation ------------------------------------------ 233 // LoginScreenContext implementation ------------------------------------------
266 234
267 LoginScreenContext::LoginScreenContext() { 235 LoginScreenContext::LoginScreenContext() {
268 Init(); 236 Init();
269 } 237 }
270 238
271 LoginScreenContext::LoginScreenContext(const base::ListValue* args) { 239 LoginScreenContext::LoginScreenContext(const base::ListValue* args) {
272 Init(); 240 Init();
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after
493 show_on_init_ = true; 461 show_on_init_ = true;
494 return; 462 return;
495 } 463 }
496 464
497 if (oobe_ui_) { 465 if (oobe_ui_) {
498 // Shows new user sign-in for OOBE. 466 // Shows new user sign-in for OOBE.
499 OnShowAddUser(email_); 467 OnShowAddUser(email_);
500 } else { 468 } else {
501 // Populates account picker. Animation is turned off for now until we 469 // Populates account picker. Animation is turned off for now until we
502 // figure out how to make it fast enough. 470 // figure out how to make it fast enough.
503 SendUserList(false); 471 delegate_->HandleGetUsers();
504 472
505 // Reset Caps Lock state when login screen is shown. 473 // Reset Caps Lock state when login screen is shown.
506 input_method::InputMethodManager::Get() 474 input_method::InputMethodManager::Get()
507 ->GetImeKeyboard() 475 ->GetImeKeyboard()
508 ->SetCapsLockEnabled(false); 476 ->SetCapsLockEnabled(false);
509 477
510 base::DictionaryValue params; 478 base::DictionaryValue params;
511 params.SetBoolean("disableAddUser", AllWhitelistedUsersPresent()); 479 params.SetBoolean("disableAddUser", AllWhitelistedUsersPresent());
512 UpdateUIState(UI_STATE_ACCOUNT_PICKER, &params); 480 UpdateUIState(UI_STATE_ACCOUNT_PICKER, &params);
513 } 481 }
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after
800 // This message is sent by the kiosk app menu, but is handled here 768 // This message is sent by the kiosk app menu, but is handled here
801 // so we can tell the delegate to launch the app. 769 // so we can tell the delegate to launch the app.
802 AddCallback("launchKioskApp", &SigninScreenHandler::HandleLaunchKioskApp); 770 AddCallback("launchKioskApp", &SigninScreenHandler::HandleLaunchKioskApp);
803 } 771 }
804 772
805 void SigninScreenHandler::RegisterPrefs(PrefRegistrySimple* registry) { 773 void SigninScreenHandler::RegisterPrefs(PrefRegistrySimple* registry) {
806 registry->RegisterDictionaryPref(prefs::kUsersLRUInputMethod); 774 registry->RegisterDictionaryPref(prefs::kUsersLRUInputMethod);
807 } 775 }
808 776
809 void SigninScreenHandler::HandleGetUsers() { 777 void SigninScreenHandler::HandleGetUsers() {
810 SendUserList(false); 778 if (delegate_)
779 delegate_->HandleGetUsers();
811 } 780 }
812 781
813 void SigninScreenHandler::ClearAndEnablePassword() { 782 void SigninScreenHandler::ClearAndEnablePassword() {
814 core_oobe_actor_->ResetSignInUI(false); 783 core_oobe_actor_->ResetSignInUI(false);
815 } 784 }
816 785
817 void SigninScreenHandler::ClearUserPodPassword() { 786 void SigninScreenHandler::ClearUserPodPassword() {
818 core_oobe_actor_->ClearUserPodPassword(); 787 core_oobe_actor_->ClearUserPodPassword();
819 } 788 }
820 789
(...skipping 17 matching lines...) Expand all
838 // preferences update would be picked up next time it will be shown. 807 // preferences update would be picked up next time it will be shown.
839 if (!webui_visible_) { 808 if (!webui_visible_) {
840 LOG(WARNING) << "Login UI is not active - postponed prefs change."; 809 LOG(WARNING) << "Login UI is not active - postponed prefs change.";
841 preferences_changed_delayed_ = true; 810 preferences_changed_delayed_ = true;
842 return; 811 return;
843 } 812 }
844 813
845 if (delegate_ && !delegate_->IsShowUsers()) { 814 if (delegate_ && !delegate_->IsShowUsers()) {
846 HandleShowAddUser(NULL); 815 HandleShowAddUser(NULL);
847 } else { 816 } else {
848 SendUserList(false); 817 if (delegate_)
818 delegate_->HandleGetUsers();
849 UpdateUIState(UI_STATE_ACCOUNT_PICKER, NULL); 819 UpdateUIState(UI_STATE_ACCOUNT_PICKER, NULL);
850 } 820 }
851 preferences_changed_delayed_ = false; 821 preferences_changed_delayed_ = false;
852 } 822 }
853 823
854 void SigninScreenHandler::ResetSigninScreenHandlerDelegate() { 824 void SigninScreenHandler::ResetSigninScreenHandlerDelegate() {
855 SetDelegate(NULL); 825 SetDelegate(NULL);
856 } 826 }
857 827
858 void SigninScreenHandler::ShowBannerMessage(const std::string& message) { 828 void SigninScreenHandler::ShowBannerMessage(const std::string& message) {
859 CallJS("login.AccountPickerScreen.showBannerMessage", message); 829 CallJS("login.AccountPickerScreen.showBannerMessage", message);
860 } 830 }
861 831
862 void SigninScreenHandler::ShowUserPodButton( 832 void SigninScreenHandler::ShowUserPodButton(const std::string& username,
863 const std::string& username, 833 const std::string& iconURL) {
864 const std::string& iconURL,
865 const base::Closure& click_callback) {
866 user_pod_button_callback_map_[username] = click_callback;
867 CallJS("login.AccountPickerScreen.showUserPodButton", username, iconURL); 834 CallJS("login.AccountPickerScreen.showUserPodButton", username, iconURL);
835 }
868 836
869 // TODO(tengs): Move this code once we move unlocking to native code. 837 void SigninScreenHandler::ShowEasyUnlockBubble() {
870 if (ScreenLocker::default_screen_locker()) { 838 CallJS("login.AccountPickerScreen.showEasyUnlockBubble");
871 UserManager* user_manager = UserManager::Get();
872 const User* user = user_manager->FindUser(username);
873 if (!user)
874 return;
875 PrefService* profile_prefs =
876 user_manager->GetProfileByUser(user)->GetPrefs();
877 if (profile_prefs->GetBoolean(prefs::kEasyUnlockShowTutorial)) {
878 CallJS("login.AccountPickerScreen.showEasyUnlockBubble");
879 profile_prefs->SetBoolean(prefs::kEasyUnlockShowTutorial, false);
880 }
881 }
882 } 839 }
883 840
884 void SigninScreenHandler::HideUserPodButton(const std::string& username) { 841 void SigninScreenHandler::HideUserPodButton(const std::string& username) {
885 CallJS("login.AccountPickerScreen.hideUserPodButton", username); 842 CallJS("login.AccountPickerScreen.hideUserPodButton", username);
886 } 843 }
887 844
888 void SigninScreenHandler::SetAuthType(const std::string& username, 845 void SigninScreenHandler::SetAuthType(const std::string& username,
889 LoginDisplay::AuthType auth_type, 846 LoginDisplay::AuthType auth_type,
890 const std::string& initial_value) { 847 const std::string& initial_value) {
891 user_auth_type_map_[username] = auth_type;
892 CallJS("login.AccountPickerScreen.setAuthType", 848 CallJS("login.AccountPickerScreen.setAuthType",
893 username, 849 username,
894 static_cast<int>(auth_type), 850 static_cast<int>(auth_type),
895 base::StringValue(initial_value)); 851 base::StringValue(initial_value));
896 } 852 }
897 853
898 LoginDisplay::AuthType SigninScreenHandler::GetAuthType(
899 const std::string& username) const {
900 if (user_auth_type_map_.find(username) == user_auth_type_map_.end())
901 return LoginDisplay::OFFLINE_PASSWORD;
902 return user_auth_type_map_.find(username)->second;
903 }
904
905 void SigninScreenHandler::ShowError(int login_attempts, 854 void SigninScreenHandler::ShowError(int login_attempts,
906 const std::string& error_text, 855 const std::string& error_text,
907 const std::string& help_link_text, 856 const std::string& help_link_text,
908 HelpAppLauncher::HelpTopic help_topic_id) { 857 HelpAppLauncher::HelpTopic help_topic_id) {
909 core_oobe_actor_->ShowSignInError(login_attempts, error_text, help_link_text, 858 core_oobe_actor_->ShowSignInError(login_attempts, error_text, help_link_text,
910 help_topic_id); 859 help_topic_id);
911 } 860 }
912 861
913 void SigninScreenHandler::ShowErrorScreen(LoginDisplay::SigninError error_id) { 862 void SigninScreenHandler::ShowErrorScreen(LoginDisplay::SigninError error_id) {
914 switch (error_id) { 863 switch (error_id) {
(...skipping 358 matching lines...) Expand 10 before | Expand all | Expand 10 after
1273 } 1222 }
1274 } 1223 }
1275 1224
1276 void SigninScreenHandler::HandleToggleKioskAutolaunchScreen() { 1225 void SigninScreenHandler::HandleToggleKioskAutolaunchScreen() {
1277 policy::BrowserPolicyConnectorChromeOS* connector = 1226 policy::BrowserPolicyConnectorChromeOS* connector =
1278 g_browser_process->platform_part()->browser_policy_connector_chromeos(); 1227 g_browser_process->platform_part()->browser_policy_connector_chromeos();
1279 if (delegate_ && !connector->IsEnterpriseManaged()) 1228 if (delegate_ && !connector->IsEnterpriseManaged())
1280 delegate_->ShowKioskAutolaunchScreen(); 1229 delegate_->ShowKioskAutolaunchScreen();
1281 } 1230 }
1282 1231
1232 // ToDo(antrim) : move to screen
1283 void SigninScreenHandler::FillUserDictionary(User* user, 1233 void SigninScreenHandler::FillUserDictionary(User* user,
Nikita (slow) 2014/05/23 15:52:15 You can delete this method.
Nikita (slow) 2014/05/27 17:03:24 What about this method?
Denis Kuznetsov (DE-MUC) 2014/05/27 17:35:28 Done.
1284 bool is_owner, 1234 bool is_owner,
1285 bool is_signin_to_add, 1235 bool is_signin_to_add,
1286 LoginDisplay::AuthType auth_type, 1236 LoginDisplay::AuthType auth_type,
1287 base::DictionaryValue* user_dict) { 1237 base::DictionaryValue* user_dict) {
1288 const std::string& email = user->email(); 1238 const std::string& email = user->email();
1289 const bool is_public_account = 1239 const bool is_public_account =
1290 user->GetType() == User::USER_TYPE_PUBLIC_ACCOUNT; 1240 user->GetType() == User::USER_TYPE_PUBLIC_ACCOUNT;
1291 const bool is_locally_managed_user = 1241 const bool is_locally_managed_user =
1292 user->GetType() == User::USER_TYPE_LOCALLY_MANAGED; 1242 user->GetType() == User::USER_TYPE_LOCALLY_MANAGED;
1293 1243
(...skipping 24 matching lines...) Expand all
1318 policy::BrowserPolicyConnectorChromeOS* policy_connector = 1268 policy::BrowserPolicyConnectorChromeOS* policy_connector =
1319 g_browser_process->platform_part()->browser_policy_connector_chromeos(); 1269 g_browser_process->platform_part()->browser_policy_connector_chromeos();
1320 1270
1321 if (policy_connector->IsEnterpriseManaged()) { 1271 if (policy_connector->IsEnterpriseManaged()) {
1322 user_dict->SetString(kKeyEnterpriseDomain, 1272 user_dict->SetString(kKeyEnterpriseDomain,
1323 policy_connector->GetEnterpriseDomain()); 1273 policy_connector->GetEnterpriseDomain());
1324 } 1274 }
1325 } 1275 }
1326 } 1276 }
1327 1277
1328 void SigninScreenHandler::SendUserList(bool animated) { 1278 void SigninScreenHandler::LoadUsers(const base::ListValue& users_list,
1329 if (!delegate_) 1279 bool animated,
1330 return; 1280 bool showGuest) {
1331 TRACE_EVENT_ASYNC_STEP_INTO0("ui",
1332 "ShowLoginWebUI",
1333 LoginDisplayHostImpl::kShowLoginWebUIid,
1334 "SendUserList");
1335 BootTimesLoader::Get()->RecordCurrentStats("login-send-user-list");
1336
1337 base::ListValue users_list;
1338 const UserList& users = delegate_->GetUsers();
1339
1340 // TODO(nkostylev): Move to a separate method in UserManager.
1341 // http://crbug.com/230852
1342 bool is_signin_to_add = LoginDisplayHostImpl::default_host() &&
1343 UserManager::Get()->IsUserLoggedIn();
1344
1345 user_pod_button_callback_map_.clear();
1346 user_auth_type_map_.clear();
1347
1348 bool single_user = users.size() == 1;
1349 std::string owner;
1350 chromeos::CrosSettings::Get()->GetString(chromeos::kDeviceOwner, &owner);
1351 bool has_owner = owner.size() > 0;
1352 size_t max_non_owner_users = has_owner ? kMaxUsers - 1 : kMaxUsers;
1353 size_t non_owner_count = 0;
1354 policy::BrowserPolicyConnectorChromeOS* connector =
1355 g_browser_process->platform_part()->
1356 browser_policy_connector_chromeos();
1357 bool is_enterprise_managed = connector->IsEnterpriseManaged();
1358
1359
1360 for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) {
1361 const std::string& email = (*it)->email();
1362 bool is_owner = (email == owner);
1363 bool is_public_account =
1364 ((*it)->GetType() == User::USER_TYPE_PUBLIC_ACCOUNT);
1365
1366 if ((is_public_account && !is_signin_to_add) ||
1367 is_owner ||
1368 (!is_public_account && non_owner_count < max_non_owner_users)) {
1369 LoginDisplay::AuthType initial_auth_type =
1370 ShouldForceOnlineSignIn(*it) ? LoginDisplay::ONLINE_SIGN_IN
1371 : LoginDisplay::OFFLINE_PASSWORD;
1372 user_auth_type_map_[email] = initial_auth_type;
1373
1374 base::DictionaryValue* user_dict = new base::DictionaryValue();
1375 FillUserDictionary(
1376 *it, is_owner, is_signin_to_add, initial_auth_type, user_dict);
1377 bool signed_in = (*it)->is_logged_in();
1378 // Single user check here is necessary because owner info might not be
1379 // available when running into login screen on first boot.
1380 // See http://crosbug.com/12723
1381 bool can_remove_user = ((!single_user || is_enterprise_managed) &&
1382 !email.empty() && !is_owner && !is_public_account &&
1383 !signed_in && !is_signin_to_add);
1384 user_dict->SetBoolean(kKeyCanRemove, can_remove_user);
1385
1386 if (!is_owner)
1387 ++non_owner_count;
1388 if (is_owner && users_list.GetSize() > kMaxUsers) {
1389 // Owner is always in the list.
1390 users_list.Insert(kMaxUsers - 1, user_dict);
1391 } else {
1392 users_list.Append(user_dict);
1393 }
1394 }
1395 }
1396 while (users_list.GetSize() > kMaxUsers)
1397 users_list.Remove(kMaxUsers, NULL);
1398
1399 CallJS("login.AccountPickerScreen.loadUsers", users_list, animated, 1281 CallJS("login.AccountPickerScreen.loadUsers", users_list, animated,
1400 delegate_->IsShowGuest()); 1282 delegate_->IsShowGuest());
1401 } 1283 }
1402 1284
1403 void SigninScreenHandler::HandleAccountPickerReady() { 1285 void SigninScreenHandler::HandleAccountPickerReady() {
1404 VLOG(0) << "Login WebUI >> AccountPickerReady"; 1286 VLOG(0) << "Login WebUI >> AccountPickerReady";
1405 1287
1406 if (delegate_ && !ScreenLocker::default_screen_locker() && 1288 if (delegate_ && !ScreenLocker::default_screen_locker() &&
1407 !chromeos::IsMachineHWIDCorrect() && 1289 !chromeos::IsMachineHWIDCorrect() &&
1408 !oobe_ui_) { 1290 !oobe_ui_) {
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after
1561 void SigninScreenHandler::HandleUpdateOfflineLogin(bool offline_login_active) { 1443 void SigninScreenHandler::HandleUpdateOfflineLogin(bool offline_login_active) {
1562 offline_login_active_ = offline_login_active; 1444 offline_login_active_ = offline_login_active;
1563 } 1445 }
1564 1446
1565 void SigninScreenHandler::HandleFocusPod(const std::string& user_id) { 1447 void SigninScreenHandler::HandleFocusPod(const std::string& user_id) {
1566 SetUserInputMethod(user_id); 1448 SetUserInputMethod(user_id);
1567 WallpaperManager::Get()->SetUserWallpaperDelayed(user_id); 1449 WallpaperManager::Get()->SetUserWallpaperDelayed(user_id);
1568 } 1450 }
1569 1451
1570 void SigninScreenHandler::HandleCustomButtonClicked( 1452 void SigninScreenHandler::HandleCustomButtonClicked(
1571 const std::string& username) { 1453 const std::string& user_id) {
1572 if (user_pod_button_callback_map_.find(username) 1454 if (delegate_)
1573 == user_pod_button_callback_map_.end()) { 1455 delegate_->HandleCustomButtonClicked(user_id);
1574 LOG(WARNING) << "User pod custom button clicked but no callback found";
1575 return;
1576 }
1577 user_pod_button_callback_map_[username].Run();
1578 } 1456 }
1579 1457
1580 void SigninScreenHandler::HandleRetrieveAuthenticatedUserEmail( 1458 void SigninScreenHandler::HandleRetrieveAuthenticatedUserEmail(
1581 double attempt_token) { 1459 double attempt_token) {
1582 email_retriever_.reset(new AuthenticatedUserEmailRetriever( 1460 email_retriever_.reset(new AuthenticatedUserEmailRetriever(
1583 base::Bind(&SigninScreenHandler::CallJS<double, std::string>, 1461 base::Bind(&SigninScreenHandler::CallJS<double, std::string>,
1584 base::Unretained(this), 1462 base::Unretained(this),
1585 "login.GaiaSigninScreen.setAuthenticatedUserEmail", 1463 "login.GaiaSigninScreen.setAuthenticatedUserEmail",
1586 attempt_token), 1464 attempt_token),
1587 Profile::FromWebUI(web_ui())->GetRequestContext())); 1465 Profile::FromWebUI(web_ui())->GetRequestContext()));
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after
1781 DCHECK(gaia_screen_handler_); 1659 DCHECK(gaia_screen_handler_);
1782 return gaia_screen_handler_->frame_state(); 1660 return gaia_screen_handler_->frame_state();
1783 } 1661 }
1784 1662
1785 net::Error SigninScreenHandler::FrameError() const { 1663 net::Error SigninScreenHandler::FrameError() const {
1786 DCHECK(gaia_screen_handler_); 1664 DCHECK(gaia_screen_handler_);
1787 return gaia_screen_handler_->frame_error(); 1665 return gaia_screen_handler_->frame_error();
1788 } 1666 }
1789 1667
1790 } // namespace chromeos 1668 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698