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

Side by Side Diff: chrome/browser/gaia_userinfo/profile_image_downloader.cc

Issue 8511064: GAIA Profile info prototype (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: test Created 9 years, 1 month 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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/gaia_userinfo/profile_image_downloader.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/json/json_reader.h"
11 #include "base/logging.h"
12 #include "base/utf_string_conversions.h"
13 #include "base/message_loop.h"
14 #include "base/string_split.h"
15 #include "base/string_util.h"
16 #include "base/stringprintf.h"
17 #include "chrome/browser/net/gaia/token_service.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/chrome_notification_types.h"
20 #include "chrome/common/net/gaia/gaia_constants.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/notification_details.h"
23 #include "content/public/browser/notification_observer.h"
24 #include "content/public/browser/notification_registrar.h"
25 #include "content/public/browser/notification_source.h"
26 #include "content/public/browser/notification_types.h"
27 #include "content/public/common/url_fetcher.h"
28 #include "googleurl/src/gurl.h"
29 #include "skia/ext/image_operations.h"
30 #include "third_party/skia/include/core/SkBitmap.h"
31
32 using content::BrowserThread;
33
34 namespace {
35
36 // Template for optional authorization header.
37 const char kAuthorizationHeader[] = "Authorization: GoogleLogin auth=%s";
38
39 // URL requesting Picasa API for user info.
40 const char kUserEntryURL[] =
41 "http://picasaweb.google.com/data/entry/api/user/default?alt=json";
42 // Path in JSON dictionary to user's photo thumbnail URL.
43 const char kPhotoThumbnailURLPath[] = "entry.gphoto$thumbnail.$t";
44
45 const char kFullNamePath[] = "entry.gphoto$nickname.$t";
46
47 // Path format for specifying thumbnail's size.
48 const char kThumbnailSizeFormat[] = "s%d-c";
49 // Default Picasa thumbnail size.
50 const int kDefaultThumbnailSize = 64;
51
52 // Separator of URL path components.
53 const char kURLPathSeparator = '/';
54
55 // Photo ID of the Picasa Web Albums profile picture (base64 of 0).
56 const char kPicasaPhotoId[] = "AAAAAAAAAAA";
57
58 // Photo version of the default PWA profile picture (base64 of 1).
59 const char kDefaultPicasaPhotoVersion[] = "AAAAAAAAAAE";
60
61 // Photo ID of the Google+ profile picture (base64 of 2).
62 const char kGooglePlusPhotoId[] = "AAAAAAAAAAI";
63
64 // Photo version of the default Google+ profile picture (base64 of 0).
65 const char kDefaultGooglePlusPhotoVersion[] = "AAAAAAAAAAA";
66
67 // Number of path components in profile picture URL.
68 const size_t kProfileImageURLPathComponentsCount = 7;
69
70 // Index of path component with photo ID.
71 const int kPhotoIdPathComponentIndex = 2;
72
73 // Index of path component with photo version.
74 const int kPhotoVersionPathComponentIndex = 3;
75
76 } // namespace
77
78 std::string ProfileImageDownloader::GetProfileImageURL(
79 const std::string& data) {
80 int error_code = -1;
81 std::string error_message;
82 scoped_ptr<base::Value> root_value(base::JSONReader::ReadAndReturnError(
83 data, false, &error_code, &error_message));
84 if (!root_value.get()) {
85 LOG(ERROR) << "Error while parsing Picasa user entry response: "
86 << error_message;
87 return std::string();
88 }
89 if (!root_value->IsType(base::Value::TYPE_DICTIONARY)) {
90 LOG(ERROR) << "JSON root is not a dictionary: "
91 << root_value->GetType();
92 return std::string();
93 }
94 base::DictionaryValue* root_dictionary =
95 static_cast<base::DictionaryValue*>(root_value.get());
96
97 std::string thumbnail_url_string;
98 if (!root_dictionary->GetString(
99 kPhotoThumbnailURLPath, &thumbnail_url_string)) {
100 LOG(ERROR) << "Can't find thumbnail path in JSON data: "
101 << data;
102 return std::string();
103 }
104
105 if (!root_dictionary->GetString(kFullNamePath, &full_name_)) {
106 } else {
107 }
108
109 fprintf(stderr, "%s - got name: %s\n",
110 __func__, UTF16ToUTF8(full_name_).c_str());
111
112 // Try to change the size of thumbnail we are going to get.
113 // Typical URL looks like this:
114 // http://lh0.ggpht.com/-abcd1aBCDEf/AAAA/AAA_A/abc12/s64-c/1234567890.jpg
115 std::string default_thumbnail_size_path_component(
116 base::StringPrintf(kThumbnailSizeFormat, kDefaultThumbnailSize));
117 std::string new_thumbnail_size_path_component(
118 base::StringPrintf(kThumbnailSizeFormat,
119 delegate_->GetDesiredImageSize()));
120 size_t thumbnail_size_pos =
121 thumbnail_url_string.find(default_thumbnail_size_path_component);
122 if (thumbnail_size_pos != std::string::npos) {
123 size_t thumbnail_size_end =
124 thumbnail_size_pos + default_thumbnail_size_path_component.size();
125 thumbnail_url_string =
126 thumbnail_url_string.substr(0, thumbnail_size_pos) +
127 new_thumbnail_size_path_component +
128 thumbnail_url_string.substr(
129 thumbnail_size_end,
130 thumbnail_url_string.size() - thumbnail_size_end);
131 } else {
132 LOG(WARNING) << "Hasn't found thumbnail size part in image URL: "
133 << thumbnail_url_string;
134 // Use the thumbnail URL we have.
135 }
136
137 fprintf(stderr, "%s - thumbnail URL is : %s\n",
138 __func__, thumbnail_url_string.c_str());
139
140 GURL thumbnail_url(thumbnail_url_string);
141 if (!thumbnail_url.is_valid()) {
142 LOG(ERROR) << "Thumbnail URL is not valid: " << thumbnail_url_string;
143 return std::string();
144 }
145 return thumbnail_url.spec();
146 }
147
148 bool ProfileImageDownloader::IsDefaultProfileImageURL(
149 const std::string& url) const {
150
151 GURL image_url_object(url);
152 DCHECK(image_url_object.is_valid());
153 VLOG(1) << "URL to check for default image: " << image_url_object.spec();
154 std::vector<std::string> path_components;
155 base::SplitString(image_url_object.path(),
156 kURLPathSeparator,
157 &path_components);
158
159 if (path_components.size() != kProfileImageURLPathComponentsCount)
160 return false;
161
162 const std::string& photo_id = path_components[kPhotoIdPathComponentIndex];
163 const std::string& photo_version =
164 path_components[kPhotoVersionPathComponentIndex];
165
166 // There are at least two pairs of (ID, version) for the default photo:
167 // the default Google+ profile photo and the default Picasa profile photo.
168 return ((photo_id == kPicasaPhotoId &&
169 photo_version == kDefaultPicasaPhotoVersion) ||
170 (photo_id == kGooglePlusPhotoId &&
171 photo_version == kDefaultGooglePlusPhotoVersion));
172 }
173
174 ProfileImageDownloader::ProfileImageDownloader(Delegate* delegate)
175 : delegate_(delegate) {
176 DCHECK(delegate_);
177 }
178
179 void ProfileImageDownloader::Start() {
180 VLOG(1) << "Starting profile image downloader...";
181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
182
183 TokenService* service =
184 delegate_->GetProfile()->GetTokenService();
185 if (!service) {
186 // This can happen in some test paths.
187 fprintf(stderr, "%s no token service\n", __func__);
188 LOG(WARNING) << "User has no token service";
189 delegate_->OnDownloadFailure();
190 return;
191 }
192 if (service->HasTokenForService(GaiaConstants::kPicasaService)) {
193 auth_token_ =
194 service->GetTokenForService(GaiaConstants::kPicasaService);
195 StartFetchingImage();
196 } else {
197 fprintf(stderr, "%s does not have picasa token, waiting\n", __func__);
198 }
199
200 registrar_.Add(this,
201 chrome::NOTIFICATION_TOKEN_AVAILABLE,
202 content::Source<TokenService>(service));
203 registrar_.Add(this,
204 chrome::NOTIFICATION_TOKEN_REQUEST_FAILED,
205 content::Source<TokenService>(service));
206 }
207
208 void ProfileImageDownloader::StartFetchingImage() {
209 std::string email = delegate_->GetProfileUserName();
210 if (email.empty())
211 return;
212 VLOG(1) << "Fetching user entry with token: " << auth_token_;
213 user_entry_fetcher_.reset(content::URLFetcher::Create(
214 GURL(kUserEntryURL), content::URLFetcher::GET, this));
215 user_entry_fetcher_->SetRequestContext(
216 delegate_->GetProfile()->GetRequestContext());
217 if (!auth_token_.empty()) {
218 user_entry_fetcher_->SetExtraRequestHeaders(
219 base::StringPrintf(kAuthorizationHeader, auth_token_.c_str()));
220 }
221 user_entry_fetcher_->Start();
222 }
223
224 ProfileImageDownloader::~ProfileImageDownloader() {}
225
226 void ProfileImageDownloader::OnURLFetchComplete(
227 const content::URLFetcher* source) {
228 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
229 std::string data;
230 source->GetResponseAsString(&data);
231 if (source->GetResponseCode() != 200) {
232 fprintf(stderr, "%s, URL fetch error\n", __func__);
233 LOG(ERROR) << "Response code is " << source->GetResponseCode();
234 LOG(ERROR) << "Url is " << source->GetURL().spec();
235 LOG(ERROR) << "Data is " << data;
236 delegate_->OnDownloadFailure();
237
238 /*
239 if (source->GetResponseCode() == 403) {
240 TokenService* service =
241 delegate_->GetProfile()->GetTokenService();
242 service->ClearTokenForService(GaiaConstants::kPicasaService);
243 service->StartFetchingMissingTokens();
244 }
245 */
246
247 return;
248 }
249
250 if (source == user_entry_fetcher_.get()) {
251 std::string image_url = GetProfileImageURL(data);
252 if (image_url.empty()) {
253 delegate_->OnDownloadFailure();
254 return;
255 }
256 if (IsDefaultProfileImageURL(image_url)) {
257 delegate_->OnDownloadDefaultImage();
258 return;
259 }
260 VLOG(1) << "Fetching profile image from " << image_url;
261 profile_image_fetcher_.reset(content::URLFetcher::Create(
262 GURL(image_url), content::URLFetcher::GET, this));
263 profile_image_fetcher_->SetRequestContext(
264 delegate_->GetProfile()->GetRequestContext());
265 if (!auth_token_.empty()) {
266 profile_image_fetcher_->SetExtraRequestHeaders(
267 base::StringPrintf(kAuthorizationHeader, auth_token_.c_str()));
268 }
269 profile_image_fetcher_->Start();
270 } else if (source == profile_image_fetcher_.get()) {
271 VLOG(1) << "Decoding the image...";
272 scoped_refptr<ImageDecoder> image_decoder = new ImageDecoder(
273 this, data);
274 image_decoder->Start();
275 }
276 }
277
278 void ProfileImageDownloader::OnImageDecoded(const ImageDecoder* decoder,
279 const SkBitmap& decoded_image) {
280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
281 SkBitmap resized_image = skia::ImageOperations::Resize(
282 decoded_image,
283 skia::ImageOperations::RESIZE_BEST,
284 delegate_->GetDesiredImageSize(),
285 delegate_->GetDesiredImageSize());
286 delegate_->OnDownloadSuccess(resized_image, full_name_);
287 }
288
289 void ProfileImageDownloader::OnDecodeImageFailed(const ImageDecoder* decoder) {
290 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
291 delegate_->OnDownloadFailure();
292 }
293
294 void ProfileImageDownloader::Observe(
295 int type,
296 const content::NotificationSource& source,
297 const content::NotificationDetails& details) {
298 DCHECK(type == chrome::NOTIFICATION_TOKEN_AVAILABLE ||
299 type == chrome::NOTIFICATION_TOKEN_REQUEST_FAILED);
300
301 fprintf(stderr, "%s\n", __func__);
302
303 TokenService::TokenAvailableDetails* token_details =
304 content::Details<TokenService::TokenAvailableDetails>(details).ptr();
305 if (type == chrome::NOTIFICATION_TOKEN_AVAILABLE) {
306 if (token_details->service() == GaiaConstants::kPicasaService) {
307 registrar_.RemoveAll();
308 auth_token_ = token_details->token();
309 StartFetchingImage();
310 }
311 } else {
312 if (token_details->service() == GaiaConstants::kPicasaService) {
313 LOG(WARNING) << "ProfileImageDownloader: token request failed";
314 delegate_->OnDownloadFailure();
315 }
316 }
317 }
OLDNEW
« no previous file with comments | « chrome/browser/gaia_userinfo/profile_image_downloader.h ('k') | chrome/browser/profiles/avatar_menu_model.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698