OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/profiles/profile_downloader.h" | 5 #include "chrome/browser/profiles/profile_downloader.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/json/json_reader.h" | 10 #include "base/json/json_reader.h" |
(...skipping 20 matching lines...) Expand all Loading... | |
31 #include "skia/ext/image_operations.h" | 31 #include "skia/ext/image_operations.h" |
32 | 32 |
33 using content::BrowserThread; | 33 using content::BrowserThread; |
34 | 34 |
35 namespace { | 35 namespace { |
36 | 36 |
37 // Template for optional authorization header when using an OAuth access token. | 37 // Template for optional authorization header when using an OAuth access token. |
38 const char kAuthorizationHeader[] = | 38 const char kAuthorizationHeader[] = |
39 "Authorization: Bearer %s"; | 39 "Authorization: Bearer %s"; |
40 | 40 |
41 // URL requesting Picasa API for user info. | 41 // URL requesting user info. |
42 const char kUserEntryURL[] = | 42 const char kUserEntryURL[] = |
43 "http://picasaweb.google.com/data/entry/api/user/default?alt=json"; | 43 "https://www.googleapis.com/oauth2/v1/userinfo?alt=json"; |
44 | 44 |
45 // OAuth scope for the Picasa API. | 45 // OAuth scope for the user info API. |
46 const char kPicasaScope[] = "http://picasaweb.google.com/data/"; | 46 const char kAPIScope[] = "https://www.googleapis.com/auth/userinfo.profile"; |
47 | 47 |
48 // Path in JSON dictionary to user's photo thumbnail URL. | 48 // Path in JSON dictionary to user's photo thumbnail URL. |
49 const char kPhotoThumbnailURLPath[] = "entry.gphoto$thumbnail.$t"; | 49 const char kPhotoThumbnailURLPath[] = "picture"; |
50 | 50 |
51 const char kNickNamePath[] = "entry.gphoto$nickname.$t"; | 51 const char kNickNamePath[] = "name"; |
52 | 52 |
53 // Path format for specifying thumbnail's size. | 53 // Path format for specifying thumbnail's size. |
54 const char kThumbnailSizeFormat[] = "s%d-c"; | 54 const char kThumbnailSizeFormat[] = "s%d-c"; |
55 // Default Picasa thumbnail size. | 55 // Default thumbnail size. |
56 const int kDefaultThumbnailSize = 64; | 56 const int kDefaultThumbnailSize = 64; |
57 | 57 |
58 // Separator of URL path components. | 58 // Separator of URL path components. |
59 const char kURLPathSeparator = '/'; | 59 const char kURLPathSeparator = '/'; |
60 | 60 |
61 // Photo ID of the Picasa Web Albums profile picture (base64 of 0). | 61 // Photo ID of the Picasa Web Albums profile picture (base64 of 0). |
62 const char kPicasaPhotoId[] = "AAAAAAAAAAA"; | 62 const char kPicasaPhotoId[] = "AAAAAAAAAAA"; |
63 | 63 |
64 // Photo version of the default PWA profile picture (base64 of 1). | 64 // Photo version of the default PWA profile picture (base64 of 1). |
65 const char kDefaultPicasaPhotoVersion[] = "AAAAAAAAAAE"; | 65 const char kDefaultPicasaPhotoVersion[] = "AAAAAAAAAAE"; |
66 | 66 |
67 // Photo ID of the Google+ profile picture (base64 of 2). | 67 // Photo ID of the Google+ profile picture (base64 of 2). |
68 const char kGooglePlusPhotoId[] = "AAAAAAAAAAI"; | 68 const char kGooglePlusPhotoId[] = "AAAAAAAAAAI"; |
69 | 69 |
70 // Photo version of the default Google+ profile picture (base64 of 0). | 70 // Photo version of the default Google+ profile picture (base64 of 0). |
71 const char kDefaultGooglePlusPhotoVersion[] = "AAAAAAAAAAA"; | 71 const char kDefaultGooglePlusPhotoVersion[] = "AAAAAAAAAAA"; |
72 | 72 |
73 // Number of path components in profile picture URL. | 73 // The minimum number of path components in profile picture URL. |
74 const size_t kProfileImageURLPathComponentsCount = 7; | 74 const size_t kProfileImageURLPathComponentsCount = 6; |
75 | 75 |
76 // Index of path component with photo ID. | 76 // Index of path component with photo ID. |
77 const int kPhotoIdPathComponentIndex = 2; | 77 const int kPhotoIdPathComponentIndex = 2; |
78 | 78 |
79 // Index of path component with photo version. | 79 // Index of path component with photo version. |
80 const int kPhotoVersionPathComponentIndex = 3; | 80 const int kPhotoVersionPathComponentIndex = 3; |
81 | 81 |
82 bool GetImageURLWithSize(const GURL& old_url, int size, GURL* new_url) { | |
Munjal (Google)
2011/12/03 06:02:33
Would be nice to add a comment about what this fun
sail
2011/12/04 01:48:53
Done.
| |
83 DCHECK(new_url); | |
84 std::vector<std::string> components; | |
85 base::SplitString(old_url.path(), kURLPathSeparator, &components); | |
86 if (components.size() == 0) | |
87 return false; | |
88 | |
89 std::string old_spec = old_url.spec(); | |
Munjal (Google)
2011/12/03 06:02:33
Nit: const std::string& old_spec
sail
2011/12/04 01:48:53
Done.
| |
90 std::string default_size_component( | |
91 base::StringPrintf(kThumbnailSizeFormat, kDefaultThumbnailSize)); | |
92 std::string new_size_component( | |
93 base::StringPrintf(kThumbnailSizeFormat, size)); | |
94 | |
95 // Check if a thumbnail size is already specified in the URL, e.g., | |
96 // http://lh0.ggpht.com/-abcd1aBCDEf/AAAA/AAA_A/abc12/s64-c/1234567890.jpg | |
97 size_t pos = old_spec.find(default_size_component); | |
98 if (pos != std::string::npos) { | |
99 size_t end = pos + default_size_component.size(); | |
100 std::string new_spec = old_spec.substr(0, pos) + new_size_component + | |
101 old_spec.substr(end, old_spec.size() - end); | |
Munjal (Google)
2011/12/03 06:02:33
Nit: You can skip the second parameter to substr s
sail
2011/12/04 01:48:53
Done.
| |
102 *new_url = GURL(new_spec); | |
103 return new_url->is_valid(); | |
104 } | |
105 | |
106 // A thubmnail size is not already embedded in the URL so insert it before | |
107 // the last component. | |
108 std::string file_name = old_url.ExtractFileName(); | |
Munjal (Google)
2011/12/03 06:02:33
Nit: const std::string& file_name
sail
2011/12/04 01:48:53
Done.
| |
109 pos = old_spec.find(file_name); | |
110 if (pos != std::string::npos) { | |
Munjal (Google)
2011/12/03 06:02:33
Would it ever happen that pos is npos given that f
sail
2011/12/04 01:48:53
Good point. The file name can be empty so I replac
| |
111 std::string new_spec = old_spec.substr(0, pos) + new_size_component + | |
112 old_spec.substr(pos - 1, old_spec.size() - pos + 1); | |
Munjal (Google)
2011/12/03 06:02:33
Nit: Same comment about skipping second arg to sub
sail
2011/12/04 01:48:53
Done.
| |
113 *new_url = GURL(new_spec); | |
114 return new_url->is_valid(); | |
115 } | |
Munjal (Google)
2011/12/03 06:02:33
Looking at the two code paths here, it seems like
sail
2011/12/04 01:48:53
Done.
| |
116 | |
117 // We can't set the image size, just use the default size. | |
118 *new_url = old_url; | |
119 return true; | |
120 } | |
Munjal (Google)
2011/12/03 06:02:33
It will be nice to add some unit tests for this me
sail
2011/12/04 01:48:53
Done.
| |
121 | |
82 } // namespace | 122 } // namespace |
83 | 123 |
84 bool ProfileDownloader::GetProfileNickNameAndImageURL(const std::string& data, | 124 bool ProfileDownloader::GetProfileNickNameAndImageURL(const std::string& data, |
85 string16* nick_name, | 125 string16* nick_name, |
86 std::string* url) const { | 126 std::string* url) const { |
87 DCHECK(nick_name); | 127 DCHECK(nick_name); |
88 DCHECK(url); | 128 DCHECK(url); |
89 *nick_name = string16(); | 129 *nick_name = string16(); |
90 *url = std::string(); | 130 *url = std::string(); |
91 | 131 |
(...skipping 13 matching lines...) Expand all Loading... | |
105 } | 145 } |
106 base::DictionaryValue* root_dictionary = | 146 base::DictionaryValue* root_dictionary = |
107 static_cast<base::DictionaryValue*>(root_value.get()); | 147 static_cast<base::DictionaryValue*>(root_value.get()); |
108 | 148 |
109 if (!root_dictionary->GetString(kNickNamePath, nick_name)) { | 149 if (!root_dictionary->GetString(kNickNamePath, nick_name)) { |
110 LOG(ERROR) << "Can't find nick name path in JSON data: " | 150 LOG(ERROR) << "Can't find nick name path in JSON data: " |
111 << data; | 151 << data; |
112 return false; | 152 return false; |
113 } | 153 } |
114 | 154 |
115 std::string thumbnail_url_string; | 155 std::string url_string; |
116 if (!root_dictionary->GetString( | 156 if (!root_dictionary->GetString(kPhotoThumbnailURLPath, &url_string)) { |
117 kPhotoThumbnailURLPath, &thumbnail_url_string)) { | |
118 LOG(ERROR) << "Can't find thumbnail path in JSON data: " | 157 LOG(ERROR) << "Can't find thumbnail path in JSON data: " |
119 << data; | 158 << data; |
120 return false; | 159 return false; |
121 } | 160 } |
122 | 161 |
123 // Try to change the size of thumbnail we are going to get. | 162 GURL new_url; |
124 // Typical URL looks like this: | |
125 // http://lh0.ggpht.com/-abcd1aBCDEf/AAAA/AAA_A/abc12/s64-c/1234567890.jpg | |
126 std::string default_thumbnail_size_path_component( | |
127 base::StringPrintf(kThumbnailSizeFormat, kDefaultThumbnailSize)); | |
128 int image_size = delegate_->GetDesiredImageSideLength(); | 163 int image_size = delegate_->GetDesiredImageSideLength(); |
129 std::string new_thumbnail_size_path_component( | 164 if (!GetImageURLWithSize(GURL(url_string), image_size, &new_url)) { |
130 base::StringPrintf(kThumbnailSizeFormat, image_size)); | 165 LOG(ERROR) << "GetImageURLWithSize failed for url: " << url_string; |
131 size_t thumbnail_size_pos = | 166 return false; |
132 thumbnail_url_string.find(default_thumbnail_size_path_component); | |
133 if (thumbnail_size_pos != std::string::npos) { | |
134 size_t thumbnail_size_end = | |
135 thumbnail_size_pos + default_thumbnail_size_path_component.size(); | |
136 thumbnail_url_string = | |
137 thumbnail_url_string.substr(0, thumbnail_size_pos) + | |
138 new_thumbnail_size_path_component + | |
139 thumbnail_url_string.substr( | |
140 thumbnail_size_end, | |
141 thumbnail_url_string.size() - thumbnail_size_end); | |
142 } else { | |
143 LOG(WARNING) << "Hasn't found thumbnail size part in image URL: " | |
144 << thumbnail_url_string; | |
145 // Use the thumbnail URL we have. | |
146 } | 167 } |
147 | 168 |
148 GURL thumbnail_url(thumbnail_url_string); | 169 *url = new_url.spec(); |
149 if (!thumbnail_url.is_valid()) { | |
150 LOG(ERROR) << "Thumbnail URL is not valid: " << thumbnail_url_string; | |
151 return false; | |
152 } | |
153 *url = thumbnail_url.spec(); | |
154 return true; | 170 return true; |
155 } | 171 } |
156 | 172 |
157 bool ProfileDownloader::IsDefaultProfileImageURL(const std::string& url) const { | 173 bool ProfileDownloader::IsDefaultProfileImageURL(const std::string& url) const { |
158 GURL image_url_object(url); | 174 GURL image_url_object(url); |
159 DCHECK(image_url_object.is_valid()); | 175 DCHECK(image_url_object.is_valid()); |
160 VLOG(1) << "URL to check for default image: " << image_url_object.spec(); | 176 VLOG(1) << "URL to check for default image: " << image_url_object.spec(); |
161 std::vector<std::string> path_components; | 177 std::vector<std::string> path_components; |
162 base::SplitString(image_url_object.path(), | 178 base::SplitString(image_url_object.path(), |
163 kURLPathSeparator, | 179 kURLPathSeparator, |
164 &path_components); | 180 &path_components); |
165 | 181 |
166 if (path_components.size() != kProfileImageURLPathComponentsCount) | 182 if (path_components.size() < kProfileImageURLPathComponentsCount) |
167 return false; | 183 return false; |
168 | 184 |
169 const std::string& photo_id = path_components[kPhotoIdPathComponentIndex]; | 185 const std::string& photo_id = path_components[kPhotoIdPathComponentIndex]; |
170 const std::string& photo_version = | 186 const std::string& photo_version = |
171 path_components[kPhotoVersionPathComponentIndex]; | 187 path_components[kPhotoVersionPathComponentIndex]; |
172 | 188 |
173 // There are at least two pairs of (ID, version) for the default photo: | 189 // There are at least two pairs of (ID, version) for the default photo: |
174 // the default Google+ profile photo and the default Picasa profile photo. | 190 // the default Google+ profile photo and the default Picasa profile photo. |
175 return ((photo_id == kPicasaPhotoId && | 191 return ((photo_id == kPicasaPhotoId && |
176 photo_version == kDefaultPicasaPhotoVersion) || | 192 photo_version == kDefaultPicasaPhotoVersion) || |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
236 base::StringPrintf(kAuthorizationHeader, auth_token_.c_str())); | 252 base::StringPrintf(kAuthorizationHeader, auth_token_.c_str())); |
237 } | 253 } |
238 user_entry_fetcher_->Start(); | 254 user_entry_fetcher_->Start(); |
239 } | 255 } |
240 | 256 |
241 void ProfileDownloader::StartFetchingOAuth2AccessToken() { | 257 void ProfileDownloader::StartFetchingOAuth2AccessToken() { |
242 TokenService* service = delegate_->GetBrowserProfile()->GetTokenService(); | 258 TokenService* service = delegate_->GetBrowserProfile()->GetTokenService(); |
243 DCHECK(!service->GetOAuth2LoginRefreshToken().empty()); | 259 DCHECK(!service->GetOAuth2LoginRefreshToken().empty()); |
244 | 260 |
245 std::vector<std::string> scopes; | 261 std::vector<std::string> scopes; |
246 scopes.push_back(kPicasaScope); | 262 scopes.push_back(kAPIScope); |
247 oauth2_access_token_fetcher_.reset(new OAuth2AccessTokenFetcher( | 263 oauth2_access_token_fetcher_.reset(new OAuth2AccessTokenFetcher( |
248 this, delegate_->GetBrowserProfile()->GetRequestContext())); | 264 this, delegate_->GetBrowserProfile()->GetRequestContext())); |
249 oauth2_access_token_fetcher_->Start( | 265 oauth2_access_token_fetcher_->Start( |
250 GaiaUrls::GetInstance()->oauth2_chrome_client_id(), | 266 GaiaUrls::GetInstance()->oauth2_chrome_client_id(), |
251 GaiaUrls::GetInstance()->oauth2_chrome_client_secret(), | 267 GaiaUrls::GetInstance()->oauth2_chrome_client_secret(), |
252 service->GetOAuth2LoginRefreshToken(), | 268 service->GetOAuth2LoginRefreshToken(), |
253 scopes); | 269 scopes); |
254 } | 270 } |
255 | 271 |
256 ProfileDownloader::~ProfileDownloader() {} | 272 ProfileDownloader::~ProfileDownloader() {} |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
352 void ProfileDownloader::OnGetTokenSuccess(const std::string& access_token) { | 368 void ProfileDownloader::OnGetTokenSuccess(const std::string& access_token) { |
353 auth_token_ = access_token; | 369 auth_token_ = access_token; |
354 StartFetchingImage(); | 370 StartFetchingImage(); |
355 } | 371 } |
356 | 372 |
357 // Callback for OAuth2AccessTokenFetcher on failure. | 373 // Callback for OAuth2AccessTokenFetcher on failure. |
358 void ProfileDownloader::OnGetTokenFailure(const GoogleServiceAuthError& error) { | 374 void ProfileDownloader::OnGetTokenFailure(const GoogleServiceAuthError& error) { |
359 LOG(WARNING) << "ProfileDownloader: token request using refresh token failed"; | 375 LOG(WARNING) << "ProfileDownloader: token request using refresh token failed"; |
360 delegate_->OnDownloadComplete(this, false); | 376 delegate_->OnDownloadComplete(this, false); |
361 } | 377 } |
OLD | NEW |