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

Side by Side Diff: chrome/browser/chromeos/login/profile_image_downloader.cc

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

Powered by Google App Engine
This is Rietveld 408576698