blob: c890db3ed08c02faefe2f039158ffcd242a50774 [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// 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.
4
5#include "chrome/browser/ui/webui/ntp/ntp_login_handler.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/metrics/histogram.h"
12#include "base/prefs/pref_notifier.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000013#include "base/prefs/pref_service.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010014#include "base/strings/utf_string_conversions.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000015#include "base/values.h"
16#include "chrome/app/chrome_command_ids.h"
17#include "chrome/browser/browser_process.h"
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010018#include "chrome/browser/chrome_notification_types.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000019#include "chrome/browser/profiles/profile.h"
20#include "chrome/browser/profiles/profile_info_cache.h"
21#include "chrome/browser/profiles/profile_manager.h"
22#include "chrome/browser/profiles/profile_metrics.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000023#include "chrome/browser/signin/signin_manager.h"
24#include "chrome/browser/signin/signin_manager_factory.h"
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010025#include "chrome/browser/signin/signin_promo.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000026#include "chrome/browser/sync/profile_sync_service.h"
27#include "chrome/browser/sync/profile_sync_service_factory.h"
28#include "chrome/browser/ui/browser.h"
29#include "chrome/browser/ui/browser_commands.h"
30#include "chrome/browser/ui/browser_finder.h"
31#include "chrome/browser/ui/browser_window.h"
32#include "chrome/browser/ui/chrome_pages.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000033#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000034#include "chrome/browser/web_resource/promo_resource_service.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000035#include "chrome/common/pref_names.h"
36#include "chrome/common/url_constants.h"
37#include "content/public/browser/notification_details.h"
38#include "content/public/browser/notification_service.h"
39#include "content/public/browser/web_contents.h"
40#include "content/public/browser/web_ui.h"
Ben Murdoch58e6fbe2013-07-26 10:20:38 +010041#include "content/public/common/page_zoom.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000042#include "grit/chromium_strings.h"
43#include "grit/generated_resources.h"
44#include "net/base/escape.h"
45#include "skia/ext/image_operations.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000046#include "ui/base/l10n/l10n_util.h"
47#include "ui/gfx/canvas.h"
48#include "ui/gfx/image/image.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000049#include "ui/webui/web_ui_util.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000050
51using content::OpenURLParams;
52using content::Referrer;
53
54namespace {
55
56SkBitmap GetGAIAPictureForNTP(const gfx::Image& image) {
57 // This value must match the width and height value of login-status-icon
58 // in new_tab.css.
59 const int kLength = 27;
60 SkBitmap bmp = skia::ImageOperations::Resize(*image.ToSkBitmap(),
61 skia::ImageOperations::RESIZE_BEST, kLength, kLength);
62
63 gfx::Canvas canvas(gfx::Size(kLength, kLength), ui::SCALE_FACTOR_100P,
64 false);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000065 canvas.DrawImageInt(gfx::ImageSkia::CreateFrom1xBitmap(bmp), 0, 0);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000066
67 // Draw a gray border on the inside of the icon.
68 SkColor color = SkColorSetARGB(83, 0, 0, 0);
69 canvas.DrawRect(gfx::Rect(0, 0, kLength - 1, kLength - 1), color);
70
71 return canvas.ExtractImageRep().sk_bitmap();
72}
73
74// Puts the |content| into a span with the given CSS class.
75string16 CreateSpanWithClass(const string16& content,
76 const std::string& css_class) {
77 return ASCIIToUTF16("<span class='" + css_class + "'>") +
78 net::EscapeForHTML(content) + ASCIIToUTF16("</span>");
79}
80
81} // namespace
82
83NTPLoginHandler::NTPLoginHandler() {
84}
85
86NTPLoginHandler::~NTPLoginHandler() {
87}
88
89void NTPLoginHandler::RegisterMessages() {
90 PrefService* pref_service = Profile::FromWebUI(web_ui())->GetPrefs();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000091 username_pref_.Init(prefs::kGoogleServicesUsername,
92 pref_service,
93 base::Bind(&NTPLoginHandler::UpdateLogin,
94 base::Unretained(this)));
95 signin_allowed_pref_.Init(prefs::kSigninAllowed,
96 pref_service,
97 base::Bind(&NTPLoginHandler::UpdateLogin,
98 base::Unretained(this)));
Torne (Richard Coles)58218062012-11-14 11:43:16 +000099
100 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
101 content::NotificationService::AllSources());
102
103 web_ui()->RegisterMessageCallback("initializeSyncLogin",
104 base::Bind(&NTPLoginHandler::HandleInitializeSyncLogin,
105 base::Unretained(this)));
106 web_ui()->RegisterMessageCallback("showSyncLoginUI",
107 base::Bind(&NTPLoginHandler::HandleShowSyncLoginUI,
108 base::Unretained(this)));
109 web_ui()->RegisterMessageCallback("loginMessageSeen",
110 base::Bind(&NTPLoginHandler::HandleLoginMessageSeen,
111 base::Unretained(this)));
112 web_ui()->RegisterMessageCallback("showAdvancedLoginUI",
113 base::Bind(&NTPLoginHandler::HandleShowAdvancedLoginUI,
114 base::Unretained(this)));
115}
116
117void NTPLoginHandler::Observe(int type,
118 const content::NotificationSource& source,
119 const content::NotificationDetails& details) {
120 if (type == chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED) {
121 UpdateLogin();
122 } else {
123 NOTREACHED();
124 }
125}
126
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000127void NTPLoginHandler::HandleInitializeSyncLogin(const ListValue* args) {
128 UpdateLogin();
129}
130
131void NTPLoginHandler::HandleShowSyncLoginUI(const ListValue* args) {
132 Profile* profile = Profile::FromWebUI(web_ui());
133 std::string username = profile->GetPrefs()->GetString(
134 prefs::kGoogleServicesUsername);
135 content::WebContents* web_contents = web_ui()->GetWebContents();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000136 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000137 if (!browser)
138 return;
139
140 if (username.empty()) {
141#if !defined(OS_ANDROID)
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100142 // The user isn't signed in, show the sign in promo.
143 if (signin::ShouldShowPromo(profile)) {
144 signin::Source source =
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100145 (web_contents->GetURL().spec() == chrome::kChromeUIAppsURL) ?
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100146 signin::SOURCE_APPS_PAGE_LINK :
147 signin::SOURCE_NTP_LINK;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100148 chrome::ShowBrowserSignin(browser, source);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000149 RecordInHistogram(NTP_SIGN_IN_PROMO_CLICKED);
150 }
151#endif
152 } else if (args->GetSize() == 4 &&
153 chrome::IsCommandEnabled(browser, IDC_SHOW_AVATAR_MENU)) {
154 // The user is signed in, show the profiles menu.
155 double x = 0;
156 double y = 0;
157 double width = 0;
158 double height = 0;
159 bool success = args->GetDouble(0, &x);
160 DCHECK(success);
161 success = args->GetDouble(1, &y);
162 DCHECK(success);
163 success = args->GetDouble(2, &width);
164 DCHECK(success);
165 success = args->GetDouble(3, &height);
166 DCHECK(success);
167
Ben Murdoch58e6fbe2013-07-26 10:20:38 +0100168 double zoom = content::ZoomLevelToZoomFactor(web_contents->GetZoomLevel());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000169 gfx::Rect rect(x * zoom, y * zoom, width * zoom, height * zoom);
170
171 browser->window()->ShowAvatarBubble(web_ui()->GetWebContents(), rect);
172 ProfileMetrics::LogProfileOpenMethod(ProfileMetrics::NTP_AVATAR_BUBBLE);
173 }
174}
175
176void NTPLoginHandler::RecordInHistogram(int type) {
177 // Invalid type to record.
178 if (type < NTP_SIGN_IN_PROMO_VIEWED ||
179 type > NTP_SIGN_IN_PROMO_CLICKED) {
180 NOTREACHED();
181 } else {
182 UMA_HISTOGRAM_ENUMERATION("SyncPromo.NTPPromo", type,
183 NTP_SIGN_IN_PROMO_BUCKET_BOUNDARY);
184 }
185}
186
187void NTPLoginHandler::HandleLoginMessageSeen(const ListValue* args) {
188 Profile::FromWebUI(web_ui())->GetPrefs()->SetBoolean(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100189 prefs::kSignInPromoShowNTPBubble, false);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000190 NewTabUI* ntp_ui = NewTabUI::FromWebUIController(web_ui()->GetController());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000191 // When instant extended is enabled, there may not be a NewTabUI object.
192 if (ntp_ui)
193 ntp_ui->set_showing_sync_bubble(true);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000194}
195
196void NTPLoginHandler::HandleShowAdvancedLoginUI(const ListValue* args) {
197 Browser* browser =
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000198 chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000199 if (browser)
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100200 chrome::ShowBrowserSignin(browser, signin::SOURCE_NTP_LINK);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000201}
202
203void NTPLoginHandler::UpdateLogin() {
204 Profile* profile = Profile::FromWebUI(web_ui());
205 std::string username = profile->GetPrefs()->GetString(
206 prefs::kGoogleServicesUsername);
207
208 string16 header, sub_header;
209 std::string icon_url;
210 if (!username.empty()) {
211 ProfileInfoCache& cache =
212 g_browser_process->profile_manager()->GetProfileInfoCache();
213 size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath());
214 if (profile_index != std::string::npos) {
215 // Only show the profile picture and full name for the single profile
216 // case. In the multi-profile case the profile picture is visible in the
217 // title bar and the full name can be ambiguous.
218 if (cache.GetNumberOfProfiles() == 1) {
219 string16 name = cache.GetGAIANameOfProfileAtIndex(profile_index);
220 if (!name.empty())
221 header = CreateSpanWithClass(name, "profile-name");
222 const gfx::Image* image =
223 cache.GetGAIAPictureOfProfileAtIndex(profile_index);
224 if (image)
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000225 icon_url = webui::GetBitmapDataUrl(GetGAIAPictureForNTP(*image));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000226 }
227 if (header.empty())
228 header = CreateSpanWithClass(UTF8ToUTF16(username), "profile-name");
229 }
230 } else {
231#if !defined(OS_ANDROID)
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100232 // Android uses a custom sign in promo.
233 if (signin::ShouldShowPromo(profile)) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000234 string16 signed_in_link = l10n_util::GetStringUTF16(
235 IDS_SYNC_PROMO_NOT_SIGNED_IN_STATUS_LINK);
236 signed_in_link = CreateSpanWithClass(signed_in_link, "link-span");
237 header = l10n_util::GetStringFUTF16(
238 IDS_SYNC_PROMO_NOT_SIGNED_IN_STATUS_HEADER,
239 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME));
240 sub_header = l10n_util::GetStringFUTF16(
241 IDS_SYNC_PROMO_NOT_SIGNED_IN_STATUS_SUB_HEADER, signed_in_link);
242 // Record that the user was shown the promo.
243 RecordInHistogram(NTP_SIGN_IN_PROMO_VIEWED);
244 }
245#endif
246 }
247
248 StringValue header_value(header);
249 StringValue sub_header_value(sub_header);
250 StringValue icon_url_value(icon_url);
251 base::FundamentalValue is_user_signed_in(!username.empty());
252 web_ui()->CallJavascriptFunction("ntp.updateLogin",
253 header_value, sub_header_value, icon_url_value, is_user_signed_in);
254}
255
256// static
257bool NTPLoginHandler::ShouldShow(Profile* profile) {
258#if defined(OS_CHROMEOS)
259 // For now we don't care about showing sync status on Chrome OS. The promo
260 // UI and the avatar menu don't exist on that platform.
261 return false;
262#else
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000263 SigninManager* signin = SigninManagerFactory::GetForProfile(profile);
264 return !profile->IsOffTheRecord() && signin && signin->IsSigninAllowed();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000265#endif
266}
267
268// static
269void NTPLoginHandler::GetLocalizedValues(Profile* profile,
270 DictionaryValue* values) {
271 PrefService* prefs = profile->GetPrefs();
Ben Murdochbb1529c2013-08-08 10:24:53 +0100272 bool hide_sync = !prefs->GetBoolean(prefs::kSignInPromoShowNTPBubble);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000273
Ben Murdochbb1529c2013-08-08 10:24:53 +0100274 string16 message = hide_sync ? string16() :
275 l10n_util::GetStringFUTF16(IDS_SYNC_PROMO_NTP_BUBBLE_MESSAGE,
276 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000277
278 values->SetString("login_status_message", message);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000279 values->SetString("login_status_url",
280 hide_sync ? std::string() : chrome::kSyncLearnMoreURL);
281 values->SetString("login_status_advanced",
Ben Murdochbb1529c2013-08-08 10:24:53 +0100282 hide_sync ? string16() :
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000283 l10n_util::GetStringUTF16(IDS_SYNC_PROMO_NTP_BUBBLE_ADVANCED));
284 values->SetString("login_status_dismiss",
285 hide_sync ? string16() :
286 l10n_util::GetStringUTF16(IDS_SYNC_PROMO_NTP_BUBBLE_OK));
287}