Roll src/third_party/WebKit f298044:aa8346d (svn 202628:202629)
[chromium-blink-merge.git] / chrome / browser / profiles / profile_downloader.cc
blob4eb0c33e2cf203c7fcdf2754335f4c162cfe6f5a
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
3 // found in the LICENSE file.
5 #include "chrome/browser/profiles/profile_downloader.h"
7 #include <string>
8 #include <vector>
10 #include "base/json/json_reader.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/values.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/profiles/profile_downloader_delegate.h"
20 #include "chrome/browser/profiles/profile_manager.h"
21 #include "chrome/browser/signin/account_fetcher_service_factory.h"
22 #include "chrome/browser/signin/account_tracker_service_factory.h"
23 #include "chrome/browser/signin/chrome_signin_client_factory.h"
24 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
25 #include "chrome/browser/signin/signin_manager_factory.h"
26 #include "components/data_use_measurement/core/data_use_user_data.h"
27 #include "components/signin/core/browser/account_fetcher_service.h"
28 #include "components/signin/core/browser/profile_oauth2_token_service.h"
29 #include "components/signin/core/browser/signin_client.h"
30 #include "components/signin/core/browser/signin_manager.h"
31 #include "components/signin/core/common/profile_management_switches.h"
32 #include "content/public/browser/browser_thread.h"
33 #include "google_apis/gaia/gaia_constants.h"
34 #include "net/base/load_flags.h"
35 #include "net/url_request/url_fetcher.h"
36 #include "net/url_request/url_request_status.h"
37 #include "skia/ext/image_operations.h"
38 #include "url/gurl.h"
40 using content::BrowserThread;
42 namespace {
44 // Template for optional authorization header when using an OAuth access token.
45 const char kAuthorizationHeader[] =
46 "Authorization: Bearer %s";
48 // Path format for specifying thumbnail's size.
49 const char kThumbnailSizeFormat[] = "s%d-c";
50 // Default thumbnail size.
51 const int kDefaultThumbnailSize = 64;
53 // Separator of URL path components.
54 const char kURLPathSeparator = '/';
56 // Photo ID of the Picasa Web Albums profile picture (base64 of 0).
57 const char kPicasaPhotoId[] = "AAAAAAAAAAA";
59 // Photo version of the default PWA profile picture (base64 of 1).
60 const char kDefaultPicasaPhotoVersion[] = "AAAAAAAAAAE";
62 // The minimum number of path components in profile picture URL.
63 const size_t kProfileImageURLPathComponentsCount = 6;
65 // Index of path component with photo ID.
66 const int kPhotoIdPathComponentIndex = 2;
68 // Index of path component with photo version.
69 const int kPhotoVersionPathComponentIndex = 3;
71 // Given an image URL this function builds a new URL set to |size|.
72 // For example, if |size| was set to 256 and |old_url| was either:
73 // https://example.com/--Abc/AAAAAAAAAAI/AAAAAAAAACQ/Efg/photo.jpg
74 // or
75 // https://example.com/--Abc/AAAAAAAAAAI/AAAAAAAAACQ/Efg/s64-c/photo.jpg
76 // then return value in |new_url| would be:
77 // https://example.com/--Abc/AAAAAAAAAAI/AAAAAAAAACQ/Efg/s256-c/photo.jpg
78 bool GetImageURLWithSize(const GURL& old_url, int size, GURL* new_url) {
79 DCHECK(new_url);
80 std::vector<std::string> components = base::SplitString(
81 old_url.path(), std::string(1, kURLPathSeparator),
82 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
83 if (components.size() == 0)
84 return false;
86 const std::string& old_spec = old_url.spec();
87 std::string default_size_component(
88 base::StringPrintf(kThumbnailSizeFormat, kDefaultThumbnailSize));
89 std::string new_size_component(
90 base::StringPrintf(kThumbnailSizeFormat, size));
92 size_t pos = old_spec.find(default_size_component);
93 size_t end = std::string::npos;
94 if (pos != std::string::npos) {
95 // The default size is already specified in the URL so it needs to be
96 // replaced with the new size.
97 end = pos + default_size_component.size();
98 } else {
99 // The default size is not in the URL so try to insert it before the last
100 // component.
101 const std::string& file_name = old_url.ExtractFileName();
102 if (!file_name.empty()) {
103 pos = old_spec.find(file_name);
104 end = pos - 1;
108 if (pos != std::string::npos) {
109 std::string new_spec = old_spec.substr(0, pos) + new_size_component +
110 old_spec.substr(end);
111 *new_url = GURL(new_spec);
112 return new_url->is_valid();
115 // We can't set the image size, just use the default size.
116 *new_url = old_url;
117 return true;
120 } // namespace
122 // static
123 bool ProfileDownloader::IsDefaultProfileImageURL(const std::string& url) {
124 if (url.empty())
125 return true;
127 GURL image_url_object(url);
128 DCHECK(image_url_object.is_valid());
129 VLOG(1) << "URL to check for default image: " << image_url_object.spec();
130 std::vector<std::string> path_components = base::SplitString(
131 image_url_object.path(), std::string(1, kURLPathSeparator),
132 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
134 if (path_components.size() < kProfileImageURLPathComponentsCount)
135 return false;
137 const std::string& photo_id = path_components[kPhotoIdPathComponentIndex];
138 const std::string& photo_version =
139 path_components[kPhotoVersionPathComponentIndex];
141 // Check that the ID and version match the default Picasa profile photo.
142 return photo_id == kPicasaPhotoId &&
143 photo_version == kDefaultPicasaPhotoVersion;
146 ProfileDownloader::ProfileDownloader(ProfileDownloaderDelegate* delegate)
147 : OAuth2TokenService::Consumer("profile_downloader"),
148 delegate_(delegate),
149 picture_status_(PICTURE_FAILED),
150 account_tracker_service_(
151 AccountTrackerServiceFactory::GetForProfile(
152 delegate_->GetBrowserProfile())),
153 waiting_for_account_info_(false) {
154 DCHECK(delegate_);
155 account_tracker_service_->AddObserver(this);
158 void ProfileDownloader::Start() {
159 StartForAccount(std::string());
162 void ProfileDownloader::StartForAccount(const std::string& account_id) {
163 VLOG(1) << "Starting profile downloader...";
164 DCHECK_CURRENTLY_ON(BrowserThread::UI);
166 ProfileOAuth2TokenService* service =
167 ProfileOAuth2TokenServiceFactory::GetForProfile(
168 delegate_->GetBrowserProfile());
169 if (!service) {
170 // This can happen in some test paths.
171 LOG(WARNING) << "User has no token service";
172 delegate_->OnProfileDownloadFailure(
173 this, ProfileDownloaderDelegate::TOKEN_ERROR);
174 return;
177 SigninManagerBase* signin_manager =
178 SigninManagerFactory::GetForProfile(delegate_->GetBrowserProfile());
179 account_id_ =
180 account_id.empty() ?
181 signin_manager->GetAuthenticatedAccountId() : account_id;
182 if (service->RefreshTokenIsAvailable(account_id_))
183 StartFetchingOAuth2AccessToken();
184 else
185 service->AddObserver(this);
188 base::string16 ProfileDownloader::GetProfileHostedDomain() const {
189 return base::UTF8ToUTF16(account_info_.hosted_domain);
192 base::string16 ProfileDownloader::GetProfileFullName() const {
193 return base::UTF8ToUTF16(account_info_.full_name);
196 base::string16 ProfileDownloader::GetProfileGivenName() const {
197 return base::UTF8ToUTF16(account_info_.given_name);
200 std::string ProfileDownloader::GetProfileLocale() const {
201 return account_info_.locale;
204 SkBitmap ProfileDownloader::GetProfilePicture() const {
205 return profile_picture_;
208 ProfileDownloader::PictureStatus ProfileDownloader::GetProfilePictureStatus()
209 const {
210 return picture_status_;
213 std::string ProfileDownloader::GetProfilePictureURL() const {
214 GURL url;
215 if (GetImageURLWithSize(GURL(account_info_.picture_url),
216 delegate_->GetDesiredImageSideLength(),
217 &url)) {
218 return url.spec();
220 return account_info_.picture_url;
223 void ProfileDownloader::StartFetchingImage() {
224 VLOG(1) << "Fetching user entry with token: " << auth_token_;
225 account_info_ = account_tracker_service_->GetAccountInfo(account_id_);
227 if (delegate_->IsPreSignin()) {
228 AccountFetcherServiceFactory::GetForProfile(delegate_->GetBrowserProfile())
229 ->FetchUserInfoBeforeSignin(account_id_);
232 if (account_info_.IsValid()) {
233 // FetchImageData might call the delegate's OnProfileDownloadSuccess
234 // synchronously, causing |this| to be deleted so there should not be more
235 // code after it.
236 FetchImageData();
237 } else {
238 waiting_for_account_info_ = true;
242 void ProfileDownloader::StartFetchingOAuth2AccessToken() {
243 Profile* profile = delegate_->GetBrowserProfile();
244 OAuth2TokenService::ScopeSet scopes;
245 scopes.insert(GaiaConstants::kGoogleUserInfoProfile);
246 // Increase scope to get hd attribute to determine if lock should be enabled.
247 if (switches::IsNewProfileManagement())
248 scopes.insert(GaiaConstants::kGoogleUserInfoEmail);
249 ProfileOAuth2TokenService* token_service =
250 ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
251 oauth2_access_token_request_ = token_service->StartRequest(
252 account_id_, scopes, this);
255 ProfileDownloader::~ProfileDownloader() {
256 // Ensures PO2TS observation is cleared when ProfileDownloader is destructed
257 // before refresh token is available.
258 ProfileOAuth2TokenService* service =
259 ProfileOAuth2TokenServiceFactory::GetForProfile(
260 delegate_->GetBrowserProfile());
261 if (service)
262 service->RemoveObserver(this);
264 account_tracker_service_->RemoveObserver(this);
267 void ProfileDownloader::FetchImageData() {
268 DCHECK(account_info_.IsValid());
269 std::string image_url_with_size = GetProfilePictureURL();
271 if (!delegate_->NeedsProfilePicture()) {
272 VLOG(1) << "Skipping profile picture download";
273 delegate_->OnProfileDownloadSuccess(this);
274 return;
276 if (IsDefaultProfileImageURL(image_url_with_size)) {
277 VLOG(1) << "User has default profile picture";
278 picture_status_ = PICTURE_DEFAULT;
279 delegate_->OnProfileDownloadSuccess(this);
280 return;
282 if (!image_url_with_size.empty() &&
283 image_url_with_size == delegate_->GetCachedPictureURL()) {
284 VLOG(1) << "Picture URL matches cached picture URL";
285 picture_status_ = PICTURE_CACHED;
286 delegate_->OnProfileDownloadSuccess(this);
287 return;
290 VLOG(1) << "Fetching profile image from " << image_url_with_size;
291 profile_image_fetcher_ = net::URLFetcher::Create(
292 GURL(image_url_with_size), net::URLFetcher::GET, this);
293 data_use_measurement::DataUseUserData::AttachToFetcher(
294 profile_image_fetcher_.get(),
295 data_use_measurement::DataUseUserData::PROFILE_DOWNLOADER);
296 profile_image_fetcher_->SetRequestContext(
297 delegate_->GetBrowserProfile()->GetRequestContext());
298 profile_image_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
299 net::LOAD_DO_NOT_SAVE_COOKIES);
301 if (!auth_token_.empty()) {
302 profile_image_fetcher_->SetExtraRequestHeaders(
303 base::StringPrintf(kAuthorizationHeader, auth_token_.c_str()));
306 profile_image_fetcher_->Start();
309 void ProfileDownloader::OnURLFetchComplete(const net::URLFetcher* source) {
310 DCHECK_CURRENTLY_ON(BrowserThread::UI);
311 std::string data;
312 source->GetResponseAsString(&data);
313 bool network_error =
314 source->GetStatus().status() != net::URLRequestStatus::SUCCESS;
315 if (network_error || source->GetResponseCode() != 200) {
316 LOG(WARNING) << "Fetching profile data failed";
317 DVLOG(1) << " Status: " << source->GetStatus().status();
318 DVLOG(1) << " Error: " << source->GetStatus().error();
319 DVLOG(1) << " Response code: " << source->GetResponseCode();
320 DVLOG(1) << " Url: " << source->GetURL().spec();
321 profile_image_fetcher_.reset();
322 delegate_->OnProfileDownloadFailure(this, network_error ?
323 ProfileDownloaderDelegate::NETWORK_ERROR :
324 ProfileDownloaderDelegate::SERVICE_ERROR);
325 } else {
326 profile_image_fetcher_.reset();
327 VLOG(1) << "Decoding the image...";
328 ImageDecoder::Start(this, data);
332 void ProfileDownloader::OnImageDecoded(const SkBitmap& decoded_image) {
333 DCHECK_CURRENTLY_ON(BrowserThread::UI);
334 int image_size = delegate_->GetDesiredImageSideLength();
335 profile_picture_ = skia::ImageOperations::Resize(
336 decoded_image,
337 skia::ImageOperations::RESIZE_BEST,
338 image_size,
339 image_size);
340 picture_status_ = PICTURE_SUCCESS;
341 delegate_->OnProfileDownloadSuccess(this);
344 void ProfileDownloader::OnDecodeImageFailed() {
345 DCHECK_CURRENTLY_ON(BrowserThread::UI);
346 delegate_->OnProfileDownloadFailure(
347 this, ProfileDownloaderDelegate::IMAGE_DECODE_FAILED);
350 void ProfileDownloader::OnRefreshTokenAvailable(const std::string& account_id) {
351 ProfileOAuth2TokenService* service =
352 ProfileOAuth2TokenServiceFactory::GetForProfile(
353 delegate_->GetBrowserProfile());
354 if (account_id != account_id_)
355 return;
357 service->RemoveObserver(this);
358 StartFetchingOAuth2AccessToken();
361 // Callback for OAuth2TokenService::Request on success. |access_token| is the
362 // token used to start fetching user data.
363 void ProfileDownloader::OnGetTokenSuccess(
364 const OAuth2TokenService::Request* request,
365 const std::string& access_token,
366 const base::Time& expiration_time) {
367 DCHECK_EQ(request, oauth2_access_token_request_.get());
368 oauth2_access_token_request_.reset();
369 auth_token_ = access_token;
370 StartFetchingImage();
373 // Callback for OAuth2TokenService::Request on failure.
374 void ProfileDownloader::OnGetTokenFailure(
375 const OAuth2TokenService::Request* request,
376 const GoogleServiceAuthError& error) {
377 DCHECK_EQ(request, oauth2_access_token_request_.get());
378 oauth2_access_token_request_.reset();
379 LOG(WARNING) << "ProfileDownloader: token request using refresh token failed:"
380 << error.ToString();
381 delegate_->OnProfileDownloadFailure(
382 this, ProfileDownloaderDelegate::TOKEN_ERROR);
385 void ProfileDownloader::OnAccountUpdated(const AccountInfo& info) {
386 if (info.account_id == account_id_ && info.IsValid()) {
387 account_info_ = info;
389 // If the StartFetchingImage was called before we had valid info, the
390 // downloader has been waiting so we need to fetch the image data now.
391 if (waiting_for_account_info_) {
392 waiting_for_account_info_ = false;
393 // FetchImageData might call the delegate's OnProfileDownloadSuccess
394 // synchronously, causing |this| to be deleted so there should not be more
395 // code after it.
396 FetchImageData();