OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/android/logo_bridge.h" | 5 #include "chrome/browser/android/logo_bridge.h" |
6 | 6 |
7 #include <jni.h> | 7 #include <jni.h> |
8 #include <stdint.h> | 8 #include <stdint.h> |
9 | 9 |
10 #include "base/android/jni_android.h" | 10 #include "base/android/jni_android.h" |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
70 JNIEnv* env, | 70 JNIEnv* env, |
71 const search_provider_logos::Logo* logo) { | 71 const search_provider_logos::Logo* logo) { |
72 if (!logo) | 72 if (!logo) |
73 return ScopedJavaLocalRef<jobject>(); | 73 return ScopedJavaLocalRef<jobject>(); |
74 | 74 |
75 return MakeJavaLogo(env, &logo->image, GURL(logo->metadata.on_click_url), | 75 return MakeJavaLogo(env, &logo->image, GURL(logo->metadata.on_click_url), |
76 logo->metadata.alt_text, | 76 logo->metadata.alt_text, |
77 GURL(logo->metadata.animated_url)); | 77 GURL(logo->metadata.animated_url)); |
78 } | 78 } |
79 | 79 |
| 80 class LoadTimeMetricRecorderImpl : public LogoBridge::LoadTimeMetricRecorder { |
| 81 public: |
| 82 LoadTimeMetricRecorderImpl() |
| 83 : logo_load_start_(base::TimeTicks::Now()), is_recording_(true) {} |
| 84 |
| 85 void Cancel() override { is_recording_ = false; } |
| 86 |
| 87 void Finish() override { |
| 88 if (!is_recording_) { |
| 89 return; |
| 90 } |
| 91 is_recording_ = false; |
| 92 UMA_HISTOGRAM_MEDIUM_TIMES("NewTabPage.LogoShownTime", |
| 93 base::TimeTicks::Now() - logo_load_start_); |
| 94 } |
| 95 |
| 96 private: |
| 97 base::TimeTicks logo_load_start_; |
| 98 bool is_recording_; |
| 99 bool waiting_for_result_; |
| 100 }; |
| 101 |
| 102 class LoadTimeMetricRecorderNullImpl |
| 103 : public LogoBridge::LoadTimeMetricRecorder { |
| 104 public: |
| 105 LoadTimeMetricRecorderNullImpl() {} |
| 106 void Cancel() override {} |
| 107 void Finish() override {} |
| 108 }; |
| 109 |
80 class LogoObserverAndroid : public search_provider_logos::LogoObserver { | 110 class LogoObserverAndroid : public search_provider_logos::LogoObserver { |
81 public: | 111 public: |
82 LogoObserverAndroid(base::WeakPtr<LogoBridge> logo_bridge, | 112 LogoObserverAndroid(base::WeakPtr<LogoBridge> logo_bridge, |
83 JNIEnv* env, | 113 JNIEnv* env, |
84 jobject j_logo_observer) | 114 jobject j_logo_observer) |
85 : logo_bridge_(logo_bridge) { | 115 : logo_bridge_(logo_bridge), |
| 116 load_time_recorder_(new LoadTimeMetricRecorderImpl()) { |
86 j_logo_observer_.Reset(env, j_logo_observer); | 117 j_logo_observer_.Reset(env, j_logo_observer); |
87 } | 118 } |
88 | 119 |
89 ~LogoObserverAndroid() override {} | 120 ~LogoObserverAndroid() override {} |
90 | 121 |
91 // seach_provider_logos::LogoObserver: | 122 // seach_provider_logos::LogoObserver: |
92 void OnLogoAvailable(const search_provider_logos::Logo* logo, | 123 void OnLogoAvailable(const search_provider_logos::Logo* logo, |
93 bool from_cache) override { | 124 bool from_cache) override { |
94 if (!logo_bridge_) | 125 if (!logo_bridge_) |
95 return; | 126 return; |
96 | 127 |
97 JNIEnv* env = base::android::AttachCurrentThread(); | 128 JNIEnv* env = base::android::AttachCurrentThread(); |
98 ScopedJavaLocalRef<jobject> j_logo = ConvertLogoToJavaObject(env, logo); | 129 ScopedJavaLocalRef<jobject> j_logo = ConvertLogoToJavaObject(env, logo); |
| 130 load_time_recorder_->Finish(); |
99 Java_LogoObserver_onLogoAvailable(env, j_logo_observer_, j_logo, | 131 Java_LogoObserver_onLogoAvailable(env, j_logo_observer_, j_logo, |
100 from_cache); | 132 from_cache); |
101 } | 133 } |
102 | 134 |
103 void OnObserverRemoved() override { delete this; } | 135 void OnObserverRemoved() override { delete this; } |
104 | 136 |
105 private: | 137 private: |
106 // The associated LogoBridge. We won't call back to Java if the LogoBridge has | 138 // The associated LogoBridge. We won't call back to Java if the LogoBridge has |
107 // been destroyed. | 139 // been destroyed. |
108 base::WeakPtr<LogoBridge> logo_bridge_; | 140 base::WeakPtr<LogoBridge> logo_bridge_; |
109 | 141 |
| 142 std::unique_ptr<LogoBridge::LoadTimeMetricRecorder> load_time_recorder_; |
110 base::android::ScopedJavaGlobalRef<jobject> j_logo_observer_; | 143 base::android::ScopedJavaGlobalRef<jobject> j_logo_observer_; |
111 | 144 |
112 DISALLOW_COPY_AND_ASSIGN(LogoObserverAndroid); | 145 DISALLOW_COPY_AND_ASSIGN(LogoObserverAndroid); |
113 }; | 146 }; |
114 | 147 |
115 } // namespace | 148 } // namespace |
116 | 149 |
117 class LogoBridge::AnimatedLogoFetcher : public net::URLFetcherDelegate { | 150 class LogoBridge::AnimatedLogoFetcher : public net::URLFetcherDelegate { |
118 public: | 151 public: |
119 AnimatedLogoFetcher( | 152 AnimatedLogoFetcher( |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
213 animated_logo_fetcher_ = base::MakeUnique<AnimatedLogoFetcher>( | 246 animated_logo_fetcher_ = base::MakeUnique<AnimatedLogoFetcher>( |
214 profile->GetRequestContext()); | 247 profile->GetRequestContext()); |
215 } | 248 } |
216 | 249 |
217 LogoBridge::~LogoBridge() {} | 250 LogoBridge::~LogoBridge() {} |
218 | 251 |
219 void LogoBridge::Destroy(JNIEnv* env, const JavaParamRef<jobject>& obj) { | 252 void LogoBridge::Destroy(JNIEnv* env, const JavaParamRef<jobject>& obj) { |
220 delete this; | 253 delete this; |
221 } | 254 } |
222 | 255 |
| 256 LogoBridge::LoadTimeMetricRecorder* LogoBridge::load_time_recorder() { |
| 257 if (!load_time_recorder_) { |
| 258 load_time_recorder_ = base::MakeUnique<LoadTimeMetricRecorderNullImpl>(); |
| 259 } |
| 260 return load_time_recorder_.get(); |
| 261 } |
| 262 |
223 void LogoBridge::GetCurrentLogo(JNIEnv* env, | 263 void LogoBridge::GetCurrentLogo(JNIEnv* env, |
224 const JavaParamRef<jobject>& obj, | 264 const JavaParamRef<jobject>& obj, |
225 const JavaParamRef<jobject>& j_logo_observer) { | 265 const JavaParamRef<jobject>& j_logo_observer) { |
226 if (doodle_service_) { | 266 if (doodle_service_) { |
227 j_logo_observer_.Reset(j_logo_observer); | 267 j_logo_observer_.Reset(j_logo_observer); |
| 268 load_time_recorder_ = base::MakeUnique<LoadTimeMetricRecorderImpl>(); |
228 | 269 |
229 // Immediately hand out any current cached config. | 270 // Immediately hand out any current cached config. |
230 DoodleConfigReceived(doodle_service_->config(), /*from_cache=*/true); | 271 bool cache_hit = |
| 272 DoodleConfigReceived(doodle_service_->config(), /*from_cache=*/true); |
231 // Also request a refresh, in case something changed. | 273 // Also request a refresh, in case something changed. |
232 doodle_service_->Refresh(); | 274 bool refresh_triggered = |
| 275 doodle_service_->Refresh() if (!cache_hit && !refresh_triggered) { |
| 276 load_time_recorder()->Cancel(); |
| 277 } |
233 } else { | 278 } else { |
234 // |observer| is deleted in LogoObserverAndroid::OnObserverRemoved(). | 279 // |observer| is deleted in LogoObserverAndroid::OnObserverRemoved(). |
235 LogoObserverAndroid* observer = new LogoObserverAndroid( | 280 LogoObserverAndroid* observer = new LogoObserverAndroid( |
236 weak_ptr_factory_.GetWeakPtr(), env, j_logo_observer); | 281 weak_ptr_factory_.GetWeakPtr(), env, j_logo_observer); |
237 logo_service_->GetLogo(observer); | 282 logo_service_->GetLogo(observer); |
238 } | 283 } |
239 } | 284 } |
240 | 285 |
241 void LogoBridge::GetAnimatedLogo(JNIEnv* env, | 286 void LogoBridge::GetAnimatedLogo(JNIEnv* env, |
242 const JavaParamRef<jobject>& obj, | 287 const JavaParamRef<jobject>& obj, |
243 const JavaParamRef<jobject>& j_callback, | 288 const JavaParamRef<jobject>& j_callback, |
244 const JavaParamRef<jstring>& j_url) { | 289 const JavaParamRef<jstring>& j_url) { |
245 GURL url = GURL(ConvertJavaStringToUTF8(env, j_url)); | 290 GURL url = GURL(ConvertJavaStringToUTF8(env, j_url)); |
246 animated_logo_fetcher_->Start(env, url, j_callback); | 291 animated_logo_fetcher_->Start(env, url, j_callback); |
247 } | 292 } |
248 | 293 |
249 void LogoBridge::OnDoodleConfigUpdated( | 294 void LogoBridge::OnDoodleConfigUpdated( |
250 const base::Optional<doodle::DoodleConfig>& maybe_doodle_config) { | 295 const base::Optional<doodle::DoodleConfig>& maybe_doodle_config) { |
251 if (j_logo_observer_.is_null()) { | 296 if (j_logo_observer_.is_null()) { |
252 return; | 297 return; |
253 } | 298 } |
254 DoodleConfigReceived(maybe_doodle_config, /*from_cache=*/false); | 299 DoodleConfigReceived(maybe_doodle_config, /*from_cache=*/false); |
255 } | 300 } |
256 | 301 |
257 void LogoBridge::DoodleConfigReceived( | 302 bool LogoBridge::DoodleConfigReceived( |
258 const base::Optional<doodle::DoodleConfig>& maybe_doodle_config, | 303 const base::Optional<doodle::DoodleConfig>& maybe_doodle_config, |
259 bool from_cache) { | 304 bool from_cache) { |
260 DCHECK(!j_logo_observer_.is_null()); | 305 DCHECK(!j_logo_observer_.is_null()); |
261 | 306 |
262 if (!maybe_doodle_config.has_value()) { | 307 if (!maybe_doodle_config.has_value()) { |
263 JNIEnv* env = base::android::AttachCurrentThread(); | 308 JNIEnv* env = base::android::AttachCurrentThread(); |
264 Java_LogoObserver_onLogoAvailable( | 309 Java_LogoObserver_onLogoAvailable( |
265 env, j_logo_observer_, ScopedJavaLocalRef<jobject>(), from_cache); | 310 env, j_logo_observer_, ScopedJavaLocalRef<jobject>(), from_cache); |
266 return; | 311 return false; |
267 } | 312 } |
| 313 load_time_recorder()->WaitForResult(); |
268 const doodle::DoodleConfig& doodle_config = maybe_doodle_config.value(); | 314 const doodle::DoodleConfig& doodle_config = maybe_doodle_config.value(); |
269 // If there is a CTA image, that means the main image is animated. Show the | 315 // If there is a CTA image, that means the main image is animated. Show the |
270 // non-animated CTA image first, and load the animated one only when the | 316 // non-animated CTA image first, and load the animated one only when the |
271 // user requests it. | 317 // user requests it. |
272 bool has_cta = doodle_config.large_cta_image.has_value(); | 318 bool has_cta = doodle_config.large_cta_image.has_value(); |
273 const GURL& image_url = has_cta ? doodle_config.large_cta_image->url | 319 const GURL& image_url = has_cta ? doodle_config.large_cta_image->url |
274 : doodle_config.large_image.url; | 320 : doodle_config.large_image.url; |
275 const GURL& animated_image_url = | 321 const GURL& animated_image_url = |
276 has_cta ? doodle_config.large_image.url : GURL::EmptyGURL(); | 322 has_cta ? doodle_config.large_image.url : GURL::EmptyGURL(); |
277 // TODO(treib): For interactive doodles, use |fullpage_interactive_url| | 323 // TODO(treib): For interactive doodles, use |fullpage_interactive_url| |
278 // instead of |target_url|? | 324 // instead of |target_url|? |
279 const GURL& on_click_url = doodle_config.target_url; | 325 const GURL& on_click_url = doodle_config.target_url; |
280 const std::string& alt_text = doodle_config.alt_text; | 326 const std::string& alt_text = doodle_config.alt_text; |
281 image_fetcher_->StartOrQueueNetworkRequest( | 327 image_fetcher_->StartOrQueueNetworkRequest( |
282 image_url.spec(), image_url, | 328 image_url.spec(), image_url, |
283 base::Bind(&LogoBridge::DoodleImageFetched, base::Unretained(this), | 329 base::Bind(&LogoBridge::DoodleImageFetched, base::Unretained(this), |
284 from_cache, on_click_url, alt_text, animated_image_url)); | 330 from_cache, on_click_url, alt_text, animated_image_url)); |
| 331 return true; |
285 } | 332 } |
286 | 333 |
287 void LogoBridge::DoodleImageFetched( | 334 void LogoBridge::DoodleImageFetched( |
288 bool config_from_cache, | 335 bool config_from_cache, |
289 const GURL& on_click_url, | 336 const GURL& on_click_url, |
290 const std::string& alt_text, | 337 const std::string& alt_text, |
291 const GURL& animated_image_url, | 338 const GURL& animated_image_url, |
292 const std::string& image_fetch_id, | 339 const std::string& image_fetch_id, |
293 const gfx::Image& image, | 340 const gfx::Image& image, |
294 const image_fetcher::RequestMetadata& metadata) { | 341 const image_fetcher::RequestMetadata& metadata) { |
295 JNIEnv* env = base::android::AttachCurrentThread(); | 342 JNIEnv* env = base::android::AttachCurrentThread(); |
296 | 343 |
297 if (image.IsEmpty()) { | 344 if (image.IsEmpty()) { |
298 DLOG(WARNING) << "Failed to download doodle image"; | 345 DLOG(WARNING) << "Failed to download doodle image"; |
| 346 load_time_recorder()->Cancel(); |
299 Java_LogoObserver_onLogoAvailable(env, j_logo_observer_, | 347 Java_LogoObserver_onLogoAvailable(env, j_logo_observer_, |
300 ScopedJavaLocalRef<jobject>(), | 348 ScopedJavaLocalRef<jobject>(), |
301 config_from_cache); | 349 config_from_cache); |
302 return; | 350 return; |
303 } | 351 } |
304 | 352 |
305 UMA_HISTOGRAM_BOOLEAN("NewTabPage.LogoImageDownloaded", | 353 UMA_HISTOGRAM_BOOLEAN("NewTabPage.LogoImageDownloaded", |
306 metadata.from_http_cache); | 354 metadata.from_http_cache); |
307 | 355 |
| 356 load_time_recorder()->Finish(); |
308 ScopedJavaLocalRef<jobject> j_logo = MakeJavaLogo( | 357 ScopedJavaLocalRef<jobject> j_logo = MakeJavaLogo( |
309 env, image.ToSkBitmap(), on_click_url, alt_text, animated_image_url); | 358 env, image.ToSkBitmap(), on_click_url, alt_text, animated_image_url); |
310 Java_LogoObserver_onLogoAvailable(env, j_logo_observer_, j_logo, | 359 Java_LogoObserver_onLogoAvailable(env, j_logo_observer_, j_logo, |
311 config_from_cache); | 360 config_from_cache); |
312 } | 361 } |
313 | 362 |
314 // static | 363 // static |
315 bool RegisterLogoBridge(JNIEnv* env) { | 364 bool RegisterLogoBridge(JNIEnv* env) { |
316 return RegisterNativesImpl(env); | 365 return RegisterNativesImpl(env); |
317 } | 366 } |
OLD | NEW |