Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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/webapps/add_to_homescreen_data_fetcher.h" | 5 #include "chrome/browser/android/webapps/add_to_homescreen_data_fetcher.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/location.h" | 10 #include "base/location.h" |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 41 const int kDataTimeoutInMilliseconds = 4000; | 41 const int kDataTimeoutInMilliseconds = 4000; |
| 42 | 42 |
| 43 // Looks up the original, online URL of the site requested. The URL from the | 43 // Looks up the original, online URL of the site requested. The URL from the |
| 44 // WebContents may be a distilled article which is not appropriate for a home | 44 // WebContents may be a distilled article which is not appropriate for a home |
| 45 // screen shortcut. | 45 // screen shortcut. |
| 46 GURL GetShortcutUrl(content::BrowserContext* browser_context, | 46 GURL GetShortcutUrl(content::BrowserContext* browser_context, |
| 47 const GURL& actual_url) { | 47 const GURL& actual_url) { |
| 48 return dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(actual_url); | 48 return dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(actual_url); |
| 49 } | 49 } |
| 50 | 50 |
| 51 InstallableParams ParamsToPerformManifestAndIconFetch( | |
| 52 int ideal_icon_size_in_px, | |
| 53 int minimum_icon_size_in_px) { | |
| 54 InstallableParams params; | |
| 55 params.ideal_primary_icon_size_in_px = ideal_icon_size_in_px; | |
| 56 params.minimum_primary_icon_size_in_px = minimum_icon_size_in_px; | |
| 57 params.fetch_valid_primary_icon = true; | |
| 58 return params; | |
| 59 } | |
| 60 | |
| 51 InstallableParams ParamsToPerformInstallableCheck( | 61 InstallableParams ParamsToPerformInstallableCheck( |
| 52 int ideal_icon_size_in_px, | 62 int ideal_icon_size_in_px, |
| 53 int minimum_icon_size_in_px, | 63 int minimum_icon_size_in_px, |
| 54 int badge_size_in_px, | 64 int badge_size_in_px, |
| 55 bool check_webapk_compatibility) { | 65 bool check_webapk_compatibility) { |
| 56 InstallableParams params; | 66 InstallableParams params = ParamsToPerformManifestAndIconFetch( |
| 57 params.ideal_primary_icon_size_in_px = ideal_icon_size_in_px; | 67 ideal_icon_size_in_px, minimum_icon_size_in_px); |
| 58 params.minimum_primary_icon_size_in_px = minimum_icon_size_in_px; | |
| 59 params.check_installable = check_webapk_compatibility; | |
| 60 params.fetch_valid_primary_icon = true; | |
| 61 if (check_webapk_compatibility) { | 68 if (check_webapk_compatibility) { |
| 69 params.check_installable = check_webapk_compatibility; | |
| 62 params.ideal_badge_icon_size_in_px = badge_size_in_px; | 70 params.ideal_badge_icon_size_in_px = badge_size_in_px; |
| 63 params.minimum_badge_icon_size_in_px = badge_size_in_px; | 71 params.minimum_badge_icon_size_in_px = badge_size_in_px; |
| 64 params.fetch_valid_badge_icon = true; | 72 params.fetch_valid_badge_icon = true; |
| 65 } | 73 } |
| 66 return params; | 74 return params; |
| 67 } | 75 } |
| 68 | 76 |
| 69 } // namespace | 77 } // namespace |
| 70 | 78 |
| 71 AddToHomescreenDataFetcher::AddToHomescreenDataFetcher( | 79 AddToHomescreenDataFetcher::AddToHomescreenDataFetcher( |
| 72 content::WebContents* web_contents, | 80 content::WebContents* web_contents, |
| 73 int ideal_icon_size_in_px, | 81 int ideal_icon_size_in_px, |
| 74 int minimum_icon_size_in_px, | 82 int minimum_icon_size_in_px, |
| 75 int ideal_splash_image_size_in_px, | 83 int ideal_splash_image_size_in_px, |
| 76 int minimum_splash_image_size_in_px, | 84 int minimum_splash_image_size_in_px, |
| 77 int badge_size_in_px, | 85 int badge_size_in_px, |
| 78 bool check_webapk_compatibility, | 86 bool check_webapk_compatibility, |
| 79 Observer* observer) | 87 Observer* observer) |
| 80 : WebContentsObserver(web_contents), | 88 : WebContentsObserver(web_contents), |
| 81 background_task_runner_( | 89 background_task_runner_( |
| 82 content::BrowserThread::GetBlockingPool() | 90 content::BrowserThread::GetBlockingPool() |
| 83 ->GetTaskRunnerWithShutdownBehavior( | 91 ->GetTaskRunnerWithShutdownBehavior( |
| 84 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)), | 92 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)), |
| 93 installable_manager_(InstallableManager::FromWebContents(web_contents)), | |
| 85 weak_observer_(observer), | 94 weak_observer_(observer), |
| 86 shortcut_info_(GetShortcutUrl(web_contents->GetBrowserContext(), | 95 shortcut_info_(GetShortcutUrl(web_contents->GetBrowserContext(), |
| 87 web_contents->GetLastCommittedURL())), | 96 web_contents->GetLastCommittedURL())), |
| 88 ideal_icon_size_in_px_(ideal_icon_size_in_px), | 97 ideal_icon_size_in_px_(ideal_icon_size_in_px), |
| 89 minimum_icon_size_in_px_(minimum_icon_size_in_px), | 98 minimum_icon_size_in_px_(minimum_icon_size_in_px), |
| 90 ideal_splash_image_size_in_px_(ideal_splash_image_size_in_px), | 99 ideal_splash_image_size_in_px_(ideal_splash_image_size_in_px), |
| 91 minimum_splash_image_size_in_px_(minimum_splash_image_size_in_px), | 100 minimum_splash_image_size_in_px_(minimum_splash_image_size_in_px), |
| 92 badge_size_in_px_(badge_size_in_px), | 101 badge_size_in_px_(badge_size_in_px), |
| 93 check_webapk_compatibility_(check_webapk_compatibility), | 102 check_webapk_compatibility_(check_webapk_compatibility), |
| 94 is_waiting_for_web_application_info_(true), | 103 is_waiting_for_web_application_info_(true), |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 137 case WebApplicationInfo::MOBILE_CAPABLE_APPLE: | 146 case WebApplicationInfo::MOBILE_CAPABLE_APPLE: |
| 138 base::RecordAction( | 147 base::RecordAction( |
| 139 base::UserMetricsAction("webapps.AddShortcut.AppShortcutApple")); | 148 base::UserMetricsAction("webapps.AddShortcut.AppShortcutApple")); |
| 140 break; | 149 break; |
| 141 case WebApplicationInfo::MOBILE_CAPABLE_UNSPECIFIED: | 150 case WebApplicationInfo::MOBILE_CAPABLE_UNSPECIFIED: |
| 142 base::RecordAction( | 151 base::RecordAction( |
| 143 base::UserMetricsAction("webapps.AddShortcut.Bookmark")); | 152 base::UserMetricsAction("webapps.AddShortcut.Bookmark")); |
| 144 break; | 153 break; |
| 145 } | 154 } |
| 146 | 155 |
| 147 InstallableManager::CreateForWebContents(web_contents()); | |
| 148 InstallableManager* manager = | |
| 149 InstallableManager::FromWebContents(web_contents()); | |
| 150 DCHECK(manager); | |
| 151 | |
| 152 // Kick off a timeout for downloading data. If we haven't finished within the | 156 // Kick off a timeout for downloading data. If we haven't finished within the |
| 153 // timeout, fall back to using a dynamically-generated launcher icon. | 157 // timeout, fall back to using a dynamically-generated launcher icon. |
| 154 data_timeout_timer_.Start( | 158 data_timeout_timer_.Start( |
| 155 FROM_HERE, base::TimeDelta::FromMilliseconds(kDataTimeoutInMilliseconds), | 159 FROM_HERE, base::TimeDelta::FromMilliseconds(kDataTimeoutInMilliseconds), |
| 156 base::Bind(&AddToHomescreenDataFetcher::OnDataTimedout, this)); | 160 base::Bind(&AddToHomescreenDataFetcher::OnDataTimedout, this)); |
| 157 | 161 |
| 158 manager->GetData( | 162 installable_manager_->GetData( |
| 159 ParamsToPerformInstallableCheck(ideal_icon_size_in_px_, | 163 ParamsToPerformManifestAndIconFetch(ideal_icon_size_in_px_, |
| 160 minimum_icon_size_in_px_, | 164 minimum_icon_size_in_px_), |
| 161 badge_size_in_px_, | 165 base::Bind(&AddToHomescreenDataFetcher::OnDidGetManifestAndIcon, this)); |
| 162 check_webapk_compatibility_), | |
| 163 base::Bind(&AddToHomescreenDataFetcher::OnDidPerformInstallableCheck, | |
| 164 this)); | |
| 165 } | 166 } |
| 166 | 167 |
| 167 AddToHomescreenDataFetcher::~AddToHomescreenDataFetcher() { | 168 AddToHomescreenDataFetcher::~AddToHomescreenDataFetcher() { |
| 168 DCHECK(!weak_observer_); | 169 DCHECK(!weak_observer_); |
| 169 } | 170 } |
| 170 | 171 |
| 171 bool AddToHomescreenDataFetcher::OnMessageReceived( | 172 bool AddToHomescreenDataFetcher::OnMessageReceived( |
| 172 const IPC::Message& message, | 173 const IPC::Message& message, |
| 173 content::RenderFrameHost* sender) { | 174 content::RenderFrameHost* sender) { |
| 174 if (!is_waiting_for_web_application_info_) | 175 if (!is_waiting_for_web_application_info_) |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 190 return; | 191 return; |
| 191 | 192 |
| 192 if (!is_installable_check_complete_) { | 193 if (!is_installable_check_complete_) { |
| 193 is_installable_check_complete_ = true; | 194 is_installable_check_complete_ = true; |
| 194 if (check_webapk_compatibility_) | 195 if (check_webapk_compatibility_) |
| 195 weak_observer_->OnDidDetermineWebApkCompatibility(false); | 196 weak_observer_->OnDidDetermineWebApkCompatibility(false); |
| 196 weak_observer_->OnUserTitleAvailable(shortcut_info_.user_title); | 197 weak_observer_->OnUserTitleAvailable(shortcut_info_.user_title); |
| 197 } | 198 } |
| 198 | 199 |
| 199 badge_icon_.reset(); | 200 badge_icon_.reset(); |
| 200 CreateLauncherIcon(SkBitmap()); | 201 CreateLauncherIcon(raw_icon_); |
|
pkotwicz
2017/06/22 17:49:27
We should probably rename |raw_icon_| to |primary_
dominickn
2017/06/23 01:52:03
Done.
| |
| 202 } | |
| 203 | |
| 204 void AddToHomescreenDataFetcher::OnDidGetManifestAndIcon( | |
| 205 const InstallableData& data) { | |
| 206 if (!web_contents() || !weak_observer_ || is_installable_check_complete_) | |
| 207 return; | |
| 208 | |
| 209 if (!data.manifest.IsEmpty()) { | |
| 210 base::RecordAction(base::UserMetricsAction("webapps.AddShortcut.Manifest")); | |
| 211 shortcut_info_.UpdateFromManifest(data.manifest); | |
| 212 shortcut_info_.manifest_url = data.manifest_url; | |
| 213 | |
| 214 if (data.primary_icon && !data.primary_icon->drawsNothing()) { | |
|
pkotwicz
2017/06/22 17:49:27
Nit: Based on InstallableManager::OnIconFetched(),
dominickn
2017/06/23 01:52:03
Good point, can't even remember code that I wrote.
| |
| 215 raw_icon_ = *data.primary_icon; | |
| 216 shortcut_info_.best_primary_icon_url = data.primary_icon_url; | |
| 217 | |
| 218 // Save the splash screen URL for the later download. | |
| 219 shortcut_info_.splash_image_url = | |
| 220 content::ManifestIconSelector::FindBestMatchingIcon( | |
| 221 data.manifest.icons, ideal_splash_image_size_in_px_, | |
| 222 minimum_splash_image_size_in_px_, | |
| 223 content::Manifest::Icon::IconPurpose::ANY); | |
| 224 shortcut_info_.ideal_splash_image_size_in_px = | |
| 225 ideal_splash_image_size_in_px_; | |
| 226 shortcut_info_.minimum_splash_image_size_in_px = | |
| 227 minimum_splash_image_size_in_px_; | |
| 228 } | |
| 229 } | |
| 230 | |
| 231 installable_manager_->GetData( | |
| 232 ParamsToPerformInstallableCheck( | |
| 233 ideal_icon_size_in_px_, minimum_icon_size_in_px_, badge_size_in_px_, | |
| 234 check_webapk_compatibility_), | |
| 235 base::Bind(&AddToHomescreenDataFetcher::OnDidPerformInstallableCheck, | |
| 236 this)); | |
| 201 } | 237 } |
| 202 | 238 |
| 203 void AddToHomescreenDataFetcher::OnDidPerformInstallableCheck( | 239 void AddToHomescreenDataFetcher::OnDidPerformInstallableCheck( |
| 204 const InstallableData& data) { | 240 const InstallableData& data) { |
| 205 data_timeout_timer_.Stop(); | 241 data_timeout_timer_.Stop(); |
| 206 badge_icon_.reset(); | 242 badge_icon_.reset(); |
| 207 | 243 |
| 208 if (!web_contents() || !weak_observer_ || is_installable_check_complete_) | 244 if (!web_contents() || !weak_observer_ || is_installable_check_complete_) |
| 209 return; | 245 return; |
| 210 | 246 |
| 211 is_installable_check_complete_ = true; | 247 is_installable_check_complete_ = true; |
| 212 | 248 |
| 213 bool webapk_compatible = false; | 249 bool webapk_compatible = false; |
| 214 if (check_webapk_compatibility_) { | 250 if (check_webapk_compatibility_) { |
| 215 webapk_compatible = (data.error_code == NO_ERROR_DETECTED && | 251 webapk_compatible = (data.error_code == NO_ERROR_DETECTED && |
| 216 AreWebManifestUrlsWebApkCompatible(data.manifest)); | 252 AreWebManifestUrlsWebApkCompatible(data.manifest)); |
|
pkotwicz
2017/06/22 17:49:27
I think that it might be more intuitive if ParamsT
dominickn
2017/06/23 01:52:04
That won't work: setting check_installable to true
pkotwicz
2017/06/23 19:23:18
Sorry for the confusion. I meant it would be more
pkotwicz
2017/06/23 19:30:45
Oh, I see you have in fact made this change :)
dominickn
2017/06/26 02:39:16
When WebAPKs are enabled by default we'll always w
| |
| 217 weak_observer_->OnDidDetermineWebApkCompatibility(webapk_compatible); | 253 weak_observer_->OnDidDetermineWebApkCompatibility(webapk_compatible); |
| 254 } | |
| 218 | 255 |
| 219 if (webapk_compatible) { | 256 if (webapk_compatible) { |
| 220 // WebAPKs are wholly defined by the Web Manifest. Ignore the <meta> tag | 257 shortcut_info_.UpdateSource(ShortcutInfo::SOURCE_ADD_TO_HOMESCREEN_PWA); |
| 221 // data received in OnDidGetWebApplicationInfo(). | 258 |
| 222 shortcut_info_ = ShortcutInfo(GURL()); | 259 if (data.badge_icon && !data.badge_icon->drawsNothing()) { |
| 260 shortcut_info_.best_badge_icon_url = data.badge_icon_url; | |
| 261 badge_icon_ = *data.badge_icon; | |
| 223 } | 262 } |
| 224 } | 263 } |
| 225 | 264 |
| 226 if (!data.manifest.IsEmpty()) { | |
| 227 base::RecordAction(base::UserMetricsAction("webapps.AddShortcut.Manifest")); | |
| 228 shortcut_info_.UpdateFromManifest(data.manifest); | |
| 229 shortcut_info_.manifest_url = data.manifest_url; | |
| 230 | |
| 231 if (webapk_compatible) { | |
| 232 shortcut_info_.UpdateSource(ShortcutInfo::SOURCE_ADD_TO_HOMESCREEN_PWA); | |
| 233 | |
| 234 if (data.badge_icon && !data.badge_icon->drawsNothing()) { | |
| 235 shortcut_info_.best_badge_icon_url = data.badge_icon_url; | |
| 236 badge_icon_ = *data.badge_icon; | |
| 237 } | |
| 238 } | |
| 239 } | |
| 240 | |
| 241 // Save the splash screen URL for the later download. | |
| 242 shortcut_info_.splash_image_url = | |
| 243 content::ManifestIconSelector::FindBestMatchingIcon( | |
| 244 data.manifest.icons, ideal_splash_image_size_in_px_, | |
| 245 minimum_splash_image_size_in_px_, | |
| 246 content::Manifest::Icon::IconPurpose::ANY); | |
| 247 shortcut_info_.ideal_splash_image_size_in_px = ideal_splash_image_size_in_px_; | |
| 248 shortcut_info_.minimum_splash_image_size_in_px = | |
| 249 minimum_splash_image_size_in_px_; | |
| 250 | |
| 251 weak_observer_->OnUserTitleAvailable(shortcut_info_.user_title); | 265 weak_observer_->OnUserTitleAvailable(shortcut_info_.user_title); |
| 252 | 266 |
| 253 if (data.primary_icon) { | 267 if (!raw_icon_.drawsNothing()) { |
| 254 shortcut_info_.best_primary_icon_url = data.primary_icon_url; | |
| 255 | |
| 256 if (webapk_compatible) | 268 if (webapk_compatible) |
| 257 NotifyObserver(*data.primary_icon); | 269 NotifyObserver(raw_icon_); |
| 258 else | 270 else |
| 259 CreateLauncherIcon(*(data.primary_icon)); | 271 CreateLauncherIcon(raw_icon_); |
| 260 return; | 272 return; |
| 261 } | 273 } |
| 262 | 274 |
| 263 FetchFavicon(); | 275 FetchFavicon(); |
| 264 } | 276 } |
| 265 | 277 |
| 266 void AddToHomescreenDataFetcher::FetchFavicon() { | 278 void AddToHomescreenDataFetcher::FetchFavicon() { |
| 267 if (!web_contents() || !weak_observer_) | 279 if (!web_contents() || !weak_observer_) |
| 268 return; | 280 return; |
| 269 | 281 |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 299 CreateLauncherIconFromFaviconInBackground, | 311 CreateLauncherIconFromFaviconInBackground, |
| 300 base::Unretained(this), bitmap_result), | 312 base::Unretained(this), bitmap_result), |
| 301 base::Bind(&AddToHomescreenDataFetcher::NotifyObserver, | 313 base::Bind(&AddToHomescreenDataFetcher::NotifyObserver, |
| 302 base::RetainedRef(this))); | 314 base::RetainedRef(this))); |
| 303 } | 315 } |
| 304 | 316 |
| 305 SkBitmap AddToHomescreenDataFetcher::CreateLauncherIconFromFaviconInBackground( | 317 SkBitmap AddToHomescreenDataFetcher::CreateLauncherIconFromFaviconInBackground( |
| 306 const favicon_base::FaviconRawBitmapResult& bitmap_result) { | 318 const favicon_base::FaviconRawBitmapResult& bitmap_result) { |
| 307 base::ThreadRestrictions::AssertIOAllowed(); | 319 base::ThreadRestrictions::AssertIOAllowed(); |
| 308 | 320 |
| 309 SkBitmap raw_icon; | |
| 310 if (bitmap_result.is_valid()) { | 321 if (bitmap_result.is_valid()) { |
| 311 gfx::PNGCodec::Decode(bitmap_result.bitmap_data->front(), | 322 gfx::PNGCodec::Decode(bitmap_result.bitmap_data->front(), |
| 312 bitmap_result.bitmap_data->size(), &raw_icon); | 323 bitmap_result.bitmap_data->size(), &raw_icon_); |
| 313 } | 324 } |
| 314 | 325 |
| 315 shortcut_info_.best_primary_icon_url = bitmap_result.icon_url; | 326 shortcut_info_.best_primary_icon_url = bitmap_result.icon_url; |
| 316 return CreateLauncherIconInBackground(raw_icon); | 327 return CreateLauncherIconInBackground(raw_icon_); |
| 317 } | 328 } |
| 318 | 329 |
| 319 void AddToHomescreenDataFetcher::CreateLauncherIcon(const SkBitmap& raw_icon) { | 330 void AddToHomescreenDataFetcher::CreateLauncherIcon(const SkBitmap& icon) { |
| 320 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 331 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 321 base::PostTaskAndReplyWithResult( | 332 base::PostTaskAndReplyWithResult( |
| 322 background_task_runner_.get(), FROM_HERE, | 333 background_task_runner_.get(), FROM_HERE, |
| 323 base::Bind(&AddToHomescreenDataFetcher::CreateLauncherIconInBackground, | 334 base::Bind(&AddToHomescreenDataFetcher::CreateLauncherIconInBackground, |
| 324 base::Unretained(this), raw_icon), | 335 base::Unretained(this), icon), |
| 325 base::Bind(&AddToHomescreenDataFetcher::NotifyObserver, | 336 base::Bind(&AddToHomescreenDataFetcher::NotifyObserver, |
| 326 base::RetainedRef(this))); | 337 base::RetainedRef(this))); |
| 327 } | 338 } |
| 328 | 339 |
| 329 SkBitmap AddToHomescreenDataFetcher::CreateLauncherIconInBackground( | 340 SkBitmap AddToHomescreenDataFetcher::CreateLauncherIconInBackground( |
| 330 const SkBitmap& raw_icon) { | 341 const SkBitmap& icon) { |
| 331 base::ThreadRestrictions::AssertIOAllowed(); | 342 base::ThreadRestrictions::AssertIOAllowed(); |
| 332 | 343 |
| 333 SkBitmap primary_icon; | 344 SkBitmap primary_icon; |
| 334 bool is_generated = false; | 345 bool is_generated = false; |
| 335 if (weak_observer_) { | 346 if (weak_observer_) { |
| 336 primary_icon = weak_observer_->FinalizeLauncherIconInBackground( | 347 primary_icon = weak_observer_->FinalizeLauncherIconInBackground( |
| 337 raw_icon, shortcut_info_.url, &is_generated); | 348 icon, shortcut_info_.url, &is_generated); |
| 338 } | 349 } |
| 339 | 350 |
| 340 if (is_generated) | 351 if (is_generated) |
| 341 shortcut_info_.best_primary_icon_url = GURL(); | 352 shortcut_info_.best_primary_icon_url = GURL(); |
| 342 | 353 |
| 343 return primary_icon; | 354 return primary_icon; |
| 344 } | 355 } |
| 345 | 356 |
| 346 void AddToHomescreenDataFetcher::NotifyObserver(const SkBitmap& primary_icon) { | 357 void AddToHomescreenDataFetcher::NotifyObserver(const SkBitmap& primary_icon) { |
| 347 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 358 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 348 if (!web_contents() || !weak_observer_ || is_icon_saved_) | 359 if (!web_contents() || !weak_observer_ || is_icon_saved_) |
| 349 return; | 360 return; |
| 350 | 361 |
| 351 is_icon_saved_ = true; | 362 is_icon_saved_ = true; |
| 352 primary_icon_ = primary_icon; | 363 primary_icon_ = primary_icon; |
| 353 weak_observer_->OnDataAvailable(shortcut_info_, primary_icon_, badge_icon_); | 364 weak_observer_->OnDataAvailable(shortcut_info_, primary_icon_, badge_icon_); |
| 354 } | 365 } |
| OLD | NEW |