Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / signin / about_signin_internals.cc
blob3b8ee5d855db1e4a4e83a47b3f73a5c39e2d6e5b
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/signin/about_signin_internals.h"
7 #include "base/debug/trace_event.h"
8 #include "base/hash.h"
9 #include "base/i18n/time_formatting.h"
10 #include "base/logging.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/signin/profile_oauth2_token_service.h"
16 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
17 #include "chrome/browser/signin/signin_internals_util.h"
18 #include "chrome/browser/signin/signin_manager.h"
19 #include "chrome/common/chrome_version_info.h"
20 #include "google_apis/gaia/gaia_constants.h"
22 using base::Time;
23 using namespace signin_internals_util;
25 namespace {
27 std::string GetTimeStr(base::Time time) {
28 return base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(time));
31 base::ListValue* AddSection(base::ListValue* parent_list,
32 const std::string& title) {
33 scoped_ptr<base::DictionaryValue> section(new base::DictionaryValue());
34 base::ListValue* section_contents = new base::ListValue();
36 section->SetString("title", title);
37 section->Set("data", section_contents);
38 parent_list->Append(section.release());
39 return section_contents;
42 void AddSectionEntry(base::ListValue* section_list,
43 const std::string& field_name,
44 const std::string& field_val) {
45 scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
46 entry->SetString("label", field_name);
47 entry->SetString("value", field_val);
48 section_list->Append(entry.release());
51 std::string SigninStatusFieldToLabel(UntimedSigninStatusField field) {
52 switch (field) {
53 case USERNAME:
54 return "User Id";
55 case UNTIMED_FIELDS_END:
56 NOTREACHED();
57 return std::string();
59 NOTREACHED();
60 return std::string();
63 TimedSigninStatusValue SigninStatusFieldToLabel(
64 TimedSigninStatusField field) {
65 switch (field) {
66 case SIGNIN_TYPE:
67 return TimedSigninStatusValue("Type", "Time");
68 case CLIENT_LOGIN_STATUS:
69 return TimedSigninStatusValue("Last OnClientLogin Status",
70 "Last OnClientLogin Time");
71 case OAUTH_LOGIN_STATUS:
72 return TimedSigninStatusValue("Last OnOAuthLogin Status",
73 "Last OnOAuthLogin Time");
75 case GET_USER_INFO_STATUS:
76 return TimedSigninStatusValue("Last OnGetUserInfo Status",
77 "Last OnGetUserInfo Time");
78 case UBER_TOKEN_STATUS:
79 return TimedSigninStatusValue("Last OnUberToken Status",
80 "Last OnUberToken Time");
81 case MERGE_SESSION_STATUS:
82 return TimedSigninStatusValue("Last OnMergeSession Status",
83 "Last OnMergeSession Time");
84 case TIMED_FIELDS_END:
85 NOTREACHED();
86 return TimedSigninStatusValue("Error", std::string());
88 NOTREACHED();
89 return TimedSigninStatusValue("Error", std::string());
92 // Returns a string describing the chrome version environment. Version format:
93 // <Build Info> <OS> <Version number> (<Last change>)<channel or "-devel">
94 // If version information is unavailable, returns "invalid."
95 std::string GetVersionString() {
96 chrome::VersionInfo chrome_version;
97 if (!chrome_version.is_valid())
98 return "invalid";
99 return chrome_version.CreateVersionString();
102 } // anonymous namespace
104 AboutSigninInternals::AboutSigninInternals() : profile_(NULL) {
107 AboutSigninInternals::~AboutSigninInternals() {
110 void AboutSigninInternals::AddSigninObserver(
111 AboutSigninInternals::Observer* observer) {
112 signin_observers_.AddObserver(observer);
115 void AboutSigninInternals::RemoveSigninObserver(
116 AboutSigninInternals::Observer* observer) {
117 signin_observers_.RemoveObserver(observer);
120 void AboutSigninInternals::NotifySigninValueChanged(
121 const UntimedSigninStatusField& field,
122 const std::string& value) {
123 unsigned int field_index = field - UNTIMED_FIELDS_BEGIN;
124 DCHECK(field_index >= 0 &&
125 field_index < signin_status_.untimed_signin_fields.size());
127 signin_status_.untimed_signin_fields[field_index] = value;
129 // Also persist these values in the prefs.
130 const std::string pref_path = SigninStatusFieldToString(field);
131 profile_->GetPrefs()->SetString(pref_path.c_str(), value);
133 NotifyObservers();
136 void AboutSigninInternals::NotifySigninValueChanged(
137 const TimedSigninStatusField& field,
138 const std::string& value) {
139 unsigned int field_index = field - TIMED_FIELDS_BEGIN;
140 DCHECK(field_index >= 0 &&
141 field_index < signin_status_.timed_signin_fields.size());
143 Time now = Time::NowFromSystemTime();
144 std::string time_as_str =
145 base::UTF16ToUTF8(base::TimeFormatFriendlyDate(now));
146 TimedSigninStatusValue timed_value(value, time_as_str);
148 signin_status_.timed_signin_fields[field_index] = timed_value;
150 // Also persist these values in the prefs.
151 const std::string value_pref = SigninStatusFieldToString(field) + ".value";
152 const std::string time_pref = SigninStatusFieldToString(field) + ".time";
153 profile_->GetPrefs()->SetString(value_pref.c_str(), value);
154 profile_->GetPrefs()->SetString(time_pref.c_str(), time_as_str);
156 NotifyObservers();
159 void AboutSigninInternals::RefreshSigninPrefs() {
160 // Return if no profile exists. Can occur in unit tests.
161 if (!profile_)
162 return;
164 PrefService* pref_service = profile_->GetPrefs();
165 for (int i = UNTIMED_FIELDS_BEGIN; i < UNTIMED_FIELDS_END; ++i) {
166 const std::string pref_path =
167 SigninStatusFieldToString(static_cast<UntimedSigninStatusField>(i));
169 signin_status_.untimed_signin_fields[i - UNTIMED_FIELDS_BEGIN] =
170 pref_service->GetString(pref_path.c_str());
172 for (int i = TIMED_FIELDS_BEGIN ; i < TIMED_FIELDS_END; ++i) {
173 const std::string value_pref = SigninStatusFieldToString(
174 static_cast<TimedSigninStatusField>(i)) + ".value";
175 const std::string time_pref = SigninStatusFieldToString(
176 static_cast<TimedSigninStatusField>(i)) + ".time";
178 TimedSigninStatusValue value(pref_service->GetString(value_pref.c_str()),
179 pref_service->GetString(time_pref.c_str()));
180 signin_status_.timed_signin_fields[i - TIMED_FIELDS_BEGIN] = value;
183 // TODO(rogerta): Get status and timestamps for oauth2 tokens.
185 NotifyObservers();
188 void AboutSigninInternals::Initialize(Profile* profile) {
189 DCHECK(!profile_);
190 profile_ = profile;
192 RefreshSigninPrefs();
194 SigninManagerFactory::GetForProfile(profile)->
195 AddSigninDiagnosticsObserver(this);
196 ProfileOAuth2TokenServiceFactory::GetForProfile(profile)->
197 AddDiagnosticsObserver(this);
200 void AboutSigninInternals::Shutdown() {
201 SigninManagerFactory::GetForProfile(profile_)->
202 RemoveSigninDiagnosticsObserver(this);
203 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->
204 RemoveDiagnosticsObserver(this);
208 void AboutSigninInternals::NotifyObservers() {
209 FOR_EACH_OBSERVER(AboutSigninInternals::Observer,
210 signin_observers_,
211 OnSigninStateChanged(signin_status_.ToValue()));
214 scoped_ptr<base::DictionaryValue> AboutSigninInternals::GetSigninStatus() {
215 return signin_status_.ToValue().Pass();
218 void AboutSigninInternals::OnAccessTokenRequested(
219 const std::string& account_id,
220 const std::string& consumer_id,
221 const OAuth2TokenService::ScopeSet& scopes) {
222 TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
223 if (token) {
224 *token = TokenInfo(consumer_id, scopes);
225 } else {
226 token = new TokenInfo(consumer_id, scopes);
227 signin_status_.token_info_map[account_id].push_back(token);
230 NotifyObservers();
233 void AboutSigninInternals::OnFetchAccessTokenComplete(
234 const std::string& account_id,
235 const std::string& consumer_id,
236 const OAuth2TokenService::ScopeSet& scopes,
237 GoogleServiceAuthError error,
238 base::Time expiration_time) {
239 TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
240 if (!token) {
241 DVLOG(1) << "Can't find token: " << account_id << ", " << consumer_id;
242 return;
245 token->receive_time = base::Time::Now();
246 token->error = error;
247 token->expiration_time = expiration_time;
249 NotifyObservers();
252 void AboutSigninInternals::OnTokenRemoved(
253 const std::string& account_id,
254 const OAuth2TokenService::ScopeSet& scopes) {
255 for (size_t i = 0; i < signin_status_.token_info_map[account_id].size();
256 ++i) {
257 TokenInfo* token = signin_status_.token_info_map[account_id][i];
258 if (token->scopes == scopes)
259 token->Invalidate();
261 NotifyObservers();
264 AboutSigninInternals::TokenInfo::TokenInfo(
265 const std::string& consumer_id,
266 const OAuth2TokenService::ScopeSet& scopes)
267 : consumer_id(consumer_id),
268 scopes(scopes),
269 request_time(base::Time::Now()),
270 error(GoogleServiceAuthError::AuthErrorNone()),
271 removed_(false) {
274 AboutSigninInternals::TokenInfo::~TokenInfo() {}
276 bool AboutSigninInternals::TokenInfo::LessThan(const TokenInfo* a,
277 const TokenInfo* b) {
278 return a->consumer_id < b->consumer_id || a->scopes < b->scopes;
281 void AboutSigninInternals::TokenInfo::Invalidate() {
282 removed_ = true;
285 base::DictionaryValue* AboutSigninInternals::TokenInfo::ToValue() const {
286 scoped_ptr<base::DictionaryValue> token_info(new base::DictionaryValue());
287 token_info->SetString("service", consumer_id);
289 std::string scopes_str;
290 for (OAuth2TokenService::ScopeSet::const_iterator it = scopes.begin();
291 it != scopes.end(); ++it) {
292 scopes_str += *it + "<br/>";
294 token_info->SetString("scopes", scopes_str);
295 token_info->SetString("request_time", GetTimeStr(request_time).c_str());
297 if (removed_) {
298 token_info->SetString("status", "Token was revoked.");
299 } else if (!receive_time.is_null()) {
300 if (error == GoogleServiceAuthError::AuthErrorNone()) {
301 bool token_expired = expiration_time < base::Time::Now();
302 std::string status_str = "";
303 if (token_expired)
304 status_str = "<p style=\"color: #ffffff; background-color: #ff0000\">";
305 base::StringAppendF(&status_str,
306 "Received token at %s. Expire at %s",
307 GetTimeStr(receive_time).c_str(),
308 GetTimeStr(expiration_time).c_str());
309 if (token_expired)
310 base::StringAppendF(&status_str, "</p>");
311 token_info->SetString("status", status_str);
312 } else {
313 token_info->SetString(
314 "status",
315 base::StringPrintf("Failure: %s", error.error_message().c_str()));
317 } else {
318 token_info->SetString("status", "Waiting for response");
321 return token_info.release();
324 AboutSigninInternals::SigninStatus::SigninStatus()
325 :untimed_signin_fields(UNTIMED_FIELDS_COUNT),
326 timed_signin_fields(TIMED_FIELDS_COUNT) {
329 AboutSigninInternals::SigninStatus::~SigninStatus() {
330 for (TokenInfoMap::iterator it = token_info_map.begin();
331 it != token_info_map.end(); ++it) {
332 STLDeleteElements(&it->second);
336 AboutSigninInternals::TokenInfo*
337 AboutSigninInternals::SigninStatus::FindToken(
338 const std::string& account_id,
339 const std::string& consumer_id,
340 const OAuth2TokenService::ScopeSet& scopes) {
341 for (size_t i = 0; i < token_info_map[account_id].size();
342 ++i) {
343 TokenInfo* tmp = token_info_map[account_id][i];
344 if (tmp->consumer_id == consumer_id && tmp->scopes == scopes)
345 return tmp;
347 return NULL;
350 scoped_ptr<base::DictionaryValue>
351 AboutSigninInternals::SigninStatus::ToValue() {
352 scoped_ptr<base::DictionaryValue> signin_status(new base::DictionaryValue());
353 base::ListValue* signin_info = new base::ListValue();
354 signin_status->Set("signin_info", signin_info);
356 // A summary of signin related info first.
357 base::ListValue* basic_info = AddSection(signin_info, "Basic Information");
358 const std::string signin_status_string =
359 untimed_signin_fields[USERNAME - UNTIMED_FIELDS_BEGIN].empty() ?
360 "Not Signed In" : "Signed In";
361 AddSectionEntry(basic_info, "Chrome Version", GetVersionString());
362 AddSectionEntry(basic_info, "Signin Status", signin_status_string);
364 // Only add username. SID and LSID have moved to tokens section.
365 const std::string field =
366 SigninStatusFieldToLabel(static_cast<UntimedSigninStatusField>(USERNAME));
367 AddSectionEntry(
368 basic_info,
369 field,
370 untimed_signin_fields[USERNAME - UNTIMED_FIELDS_BEGIN]);
372 // Time and status information of the possible sign in types.
373 base::ListValue* detailed_info = AddSection(signin_info,
374 "Last Signin Details");
375 for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
376 const std::string value_field =
377 SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i)).first;
378 const std::string time_field =
379 SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i)).second;
381 AddSectionEntry(detailed_info, value_field,
382 timed_signin_fields[i - TIMED_FIELDS_BEGIN].first);
383 AddSectionEntry(detailed_info, time_field,
384 timed_signin_fields[i - TIMED_FIELDS_BEGIN].second);
387 // Token information for all services.
388 base::ListValue* token_info = new base::ListValue();
389 signin_status->Set("token_info", token_info);
390 for (TokenInfoMap::iterator it = token_info_map.begin();
391 it != token_info_map.end(); ++it) {
392 base::ListValue* token_details = AddSection(token_info, it->first);
394 std::sort(it->second.begin(), it->second.end(), TokenInfo::LessThan);
395 const std::vector<TokenInfo*>& tokens = it->second;
396 for (size_t i = 0; i < tokens.size(); ++i) {
397 base::DictionaryValue* token_info = tokens[i]->ToValue();
398 token_details->Append(token_info);
402 return signin_status.Pass();