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

Side by Side Diff: chrome/browser/android/webapps/add_to_homescreen_data_fetcher.cc

Issue 2949993002: Don't ignore manifest icons for sites that don't have a service worker. (Closed)
Patch Set: Comments, clean up Created 3 years, 6 months 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
OLDNEW
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 25 matching lines...) Expand all
36 const int kDataTimeoutInMilliseconds = 4000; 36 const int kDataTimeoutInMilliseconds = 4000;
37 37
38 // Looks up the original, online URL of the site requested. The URL from the 38 // Looks up the original, online URL of the site requested. The URL from the
39 // WebContents may be a distilled article which is not appropriate for a home 39 // WebContents may be a distilled article which is not appropriate for a home
40 // screen shortcut. 40 // screen shortcut.
41 GURL GetShortcutUrl(content::BrowserContext* browser_context, 41 GURL GetShortcutUrl(content::BrowserContext* browser_context,
42 const GURL& actual_url) { 42 const GURL& actual_url) {
43 return dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(actual_url); 43 return dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(actual_url);
44 } 44 }
45 45
46 InstallableParams ParamsToPerformInstallableCheck( 46 InstallableParams ParamsToPerformManifestAndIconFetch(
47 int ideal_icon_size_in_px, 47 int ideal_icon_size_in_px,
48 int minimum_icon_size_in_px, 48 int minimum_icon_size_in_px,
49 int badge_size_in_px, 49 int badge_size_in_px,
50 bool check_webapk_compatibility) { 50 bool check_webapk_compatibility) {
51 InstallableParams params; 51 InstallableParams params;
52 params.ideal_primary_icon_size_in_px = ideal_icon_size_in_px; 52 params.ideal_primary_icon_size_in_px = ideal_icon_size_in_px;
53 params.minimum_primary_icon_size_in_px = minimum_icon_size_in_px; 53 params.minimum_primary_icon_size_in_px = minimum_icon_size_in_px;
54 params.check_installable = check_webapk_compatibility;
55 params.fetch_valid_primary_icon = true; 54 params.fetch_valid_primary_icon = true;
56 if (check_webapk_compatibility) { 55 if (check_webapk_compatibility) {
56 params.fetch_valid_badge_icon = true;
57 params.ideal_badge_icon_size_in_px = badge_size_in_px; 57 params.ideal_badge_icon_size_in_px = badge_size_in_px;
58 params.minimum_badge_icon_size_in_px = badge_size_in_px; 58 params.minimum_badge_icon_size_in_px = badge_size_in_px;
59 params.fetch_valid_badge_icon = true;
60 } 59 }
61 return params; 60 return params;
62 } 61 }
63 62
63 InstallableParams ParamsToPerformInstallableCheck(
64 bool check_webapk_compatibility) {
65 InstallableParams params;
66 params.check_installable = check_webapk_compatibility;
67 return params;
68 }
69
64 } // namespace 70 } // namespace
65 71
66 AddToHomescreenDataFetcher::AddToHomescreenDataFetcher( 72 AddToHomescreenDataFetcher::AddToHomescreenDataFetcher(
67 content::WebContents* web_contents, 73 content::WebContents* web_contents,
68 int ideal_icon_size_in_px, 74 int ideal_icon_size_in_px,
69 int minimum_icon_size_in_px, 75 int minimum_icon_size_in_px,
70 int ideal_splash_image_size_in_px, 76 int ideal_splash_image_size_in_px,
71 int minimum_splash_image_size_in_px, 77 int minimum_splash_image_size_in_px,
72 int badge_size_in_px, 78 int badge_size_in_px,
73 bool check_webapk_compatibility, 79 bool check_webapk_compatibility,
74 Observer* observer) 80 Observer* observer)
75 : content::WebContentsObserver(web_contents), 81 : content::WebContentsObserver(web_contents),
82 installable_manager_(InstallableManager::FromWebContents(web_contents)),
76 weak_observer_(observer), 83 weak_observer_(observer),
77 shortcut_info_(GetShortcutUrl(web_contents->GetBrowserContext(), 84 shortcut_info_(GetShortcutUrl(web_contents->GetBrowserContext(),
78 web_contents->GetLastCommittedURL())), 85 web_contents->GetLastCommittedURL())),
79 ideal_icon_size_in_px_(ideal_icon_size_in_px), 86 ideal_icon_size_in_px_(ideal_icon_size_in_px),
80 minimum_icon_size_in_px_(minimum_icon_size_in_px), 87 minimum_icon_size_in_px_(minimum_icon_size_in_px),
81 ideal_splash_image_size_in_px_(ideal_splash_image_size_in_px), 88 ideal_splash_image_size_in_px_(ideal_splash_image_size_in_px),
82 minimum_splash_image_size_in_px_(minimum_splash_image_size_in_px), 89 minimum_splash_image_size_in_px_(minimum_splash_image_size_in_px),
83 badge_size_in_px_(badge_size_in_px), 90 badge_size_in_px_(badge_size_in_px),
84 check_webapk_compatibility_(check_webapk_compatibility), 91 check_webapk_compatibility_(check_webapk_compatibility),
85 is_waiting_for_web_application_info_(true), 92 is_waiting_for_web_application_info_(true),
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
128 case WebApplicationInfo::MOBILE_CAPABLE_APPLE: 135 case WebApplicationInfo::MOBILE_CAPABLE_APPLE:
129 base::RecordAction( 136 base::RecordAction(
130 base::UserMetricsAction("webapps.AddShortcut.AppShortcutApple")); 137 base::UserMetricsAction("webapps.AddShortcut.AppShortcutApple"));
131 break; 138 break;
132 case WebApplicationInfo::MOBILE_CAPABLE_UNSPECIFIED: 139 case WebApplicationInfo::MOBILE_CAPABLE_UNSPECIFIED:
133 base::RecordAction( 140 base::RecordAction(
134 base::UserMetricsAction("webapps.AddShortcut.Bookmark")); 141 base::UserMetricsAction("webapps.AddShortcut.Bookmark"));
135 break; 142 break;
136 } 143 }
137 144
138 InstallableManager::CreateForWebContents(web_contents());
139 InstallableManager* manager =
140 InstallableManager::FromWebContents(web_contents());
141 DCHECK(manager);
142
143 // Kick off a timeout for downloading data. If we haven't finished within the 145 // Kick off a timeout for downloading data. If we haven't finished within the
144 // timeout, fall back to using a dynamically-generated launcher icon. 146 // timeout, fall back to using a dynamically-generated launcher icon.
145 data_timeout_timer_.Start( 147 data_timeout_timer_.Start(
146 FROM_HERE, base::TimeDelta::FromMilliseconds(kDataTimeoutInMilliseconds), 148 FROM_HERE, base::TimeDelta::FromMilliseconds(kDataTimeoutInMilliseconds),
147 base::Bind(&AddToHomescreenDataFetcher::OnDataTimedout, this)); 149 base::Bind(&AddToHomescreenDataFetcher::OnDataTimedout, this));
148 150
149 manager->GetData( 151 installable_manager_->GetData(
150 ParamsToPerformInstallableCheck(ideal_icon_size_in_px_, 152 ParamsToPerformManifestAndIconFetch(
151 minimum_icon_size_in_px_, 153 ideal_icon_size_in_px_, minimum_icon_size_in_px_, badge_size_in_px_,
152 badge_size_in_px_, 154 check_webapk_compatibility_),
153 check_webapk_compatibility_), 155 base::Bind(&AddToHomescreenDataFetcher::OnDidGetManifestAndIcons, this));
154 base::Bind(&AddToHomescreenDataFetcher::OnDidPerformInstallableCheck,
155 this));
156 } 156 }
157 157
158 AddToHomescreenDataFetcher::~AddToHomescreenDataFetcher() { 158 AddToHomescreenDataFetcher::~AddToHomescreenDataFetcher() {
159 DCHECK(!weak_observer_); 159 DCHECK(!weak_observer_);
160 } 160 }
161 161
162 bool AddToHomescreenDataFetcher::OnMessageReceived( 162 bool AddToHomescreenDataFetcher::OnMessageReceived(
163 const IPC::Message& message, 163 const IPC::Message& message,
164 content::RenderFrameHost* sender) { 164 content::RenderFrameHost* sender) {
165 if (!is_waiting_for_web_application_info_) 165 if (!is_waiting_for_web_application_info_)
(...skipping 15 matching lines...) Expand all
181 return; 181 return;
182 182
183 if (!is_installable_check_complete_) { 183 if (!is_installable_check_complete_) {
184 is_installable_check_complete_ = true; 184 is_installable_check_complete_ = true;
185 if (check_webapk_compatibility_) 185 if (check_webapk_compatibility_)
186 weak_observer_->OnDidDetermineWebApkCompatibility(false); 186 weak_observer_->OnDidDetermineWebApkCompatibility(false);
187 weak_observer_->OnUserTitleAvailable(shortcut_info_.user_title); 187 weak_observer_->OnUserTitleAvailable(shortcut_info_.user_title);
188 } 188 }
189 189
190 badge_icon_.reset(); 190 badge_icon_.reset();
191 CreateLauncherIcon(SkBitmap()); 191 CreateLauncherIcon(raw_primary_icon_);
192 } 192 }
193 193
194 void AddToHomescreenDataFetcher::OnDidPerformInstallableCheck( 194 void AddToHomescreenDataFetcher::OnDidGetManifestAndIcons(
195 const InstallableData& data) { 195 const InstallableData& data) {
196 data_timeout_timer_.Stop();
197 badge_icon_.reset();
198
199 if (!web_contents() || !weak_observer_ || is_installable_check_complete_) 196 if (!web_contents() || !weak_observer_ || is_installable_check_complete_)
200 return; 197 return;
201 198
202 is_installable_check_complete_ = true;
203
204 bool webapk_compatible = false;
205 if (check_webapk_compatibility_) {
206 webapk_compatible = (data.error_code == NO_ERROR_DETECTED &&
207 AreWebManifestUrlsWebApkCompatible(data.manifest));
208 weak_observer_->OnDidDetermineWebApkCompatibility(webapk_compatible);
209
210 if (webapk_compatible) {
211 // WebAPKs are wholly defined by the Web Manifest. Ignore the <meta> tag
212 // data received in OnDidGetWebApplicationInfo().
213 shortcut_info_ = ShortcutInfo(GURL());
214 }
215 }
216
217 if (!data.manifest.IsEmpty()) { 199 if (!data.manifest.IsEmpty()) {
218 base::RecordAction(base::UserMetricsAction("webapps.AddShortcut.Manifest")); 200 base::RecordAction(base::UserMetricsAction("webapps.AddShortcut.Manifest"));
219 shortcut_info_.UpdateFromManifest(data.manifest); 201 shortcut_info_.UpdateFromManifest(data.manifest);
220 shortcut_info_.manifest_url = data.manifest_url; 202 shortcut_info_.manifest_url = data.manifest_url;
203 }
221 204
222 if (webapk_compatible) { 205 // Do this after updating from the manifest for the case where a site has
223 shortcut_info_.UpdateSource(ShortcutInfo::SOURCE_ADD_TO_HOMESCREEN_PWA); 206 // a manifest with name and standalone specified, but no icons.
207 if (data.manifest.IsEmpty() || !data.primary_icon) {
208 FetchFavicon();
209 return;
210 }
224 211
225 if (data.badge_icon && !data.badge_icon->drawsNothing()) { 212 raw_primary_icon_ = *data.primary_icon;
226 shortcut_info_.best_badge_icon_url = data.badge_icon_url; 213 shortcut_info_.best_primary_icon_url = data.primary_icon_url;
227 badge_icon_ = *data.badge_icon;
228 }
229 }
230 }
231 214
232 // Save the splash screen URL for the later download. 215 // Save the splash screen URL for the later download.
233 shortcut_info_.splash_image_url = 216 shortcut_info_.splash_image_url =
234 content::ManifestIconSelector::FindBestMatchingIcon( 217 content::ManifestIconSelector::FindBestMatchingIcon(
235 data.manifest.icons, ideal_splash_image_size_in_px_, 218 data.manifest.icons, ideal_splash_image_size_in_px_,
236 minimum_splash_image_size_in_px_, 219 minimum_splash_image_size_in_px_,
237 content::Manifest::Icon::IconPurpose::ANY); 220 content::Manifest::Icon::IconPurpose::ANY);
238 shortcut_info_.ideal_splash_image_size_in_px = ideal_splash_image_size_in_px_; 221 shortcut_info_.ideal_splash_image_size_in_px = ideal_splash_image_size_in_px_;
239 shortcut_info_.minimum_splash_image_size_in_px = 222 shortcut_info_.minimum_splash_image_size_in_px =
240 minimum_splash_image_size_in_px_; 223 minimum_splash_image_size_in_px_;
224 if (data.badge_icon) {
225 shortcut_info_.best_badge_icon_url = data.badge_icon_url;
226 badge_icon_ = *data.badge_icon;
227 }
228
229 installable_manager_->GetData(
230 ParamsToPerformInstallableCheck(check_webapk_compatibility_),
231 base::Bind(&AddToHomescreenDataFetcher::OnDidPerformInstallableCheck,
232 this));
233 }
234
235 void AddToHomescreenDataFetcher::OnDidPerformInstallableCheck(
236 const InstallableData& data) {
237 data_timeout_timer_.Stop();
238 badge_icon_.reset();
pkotwicz 2017/06/23 19:23:19 Shouldn't this be removed?
dominickn 2017/06/26 02:39:17 Good catch, thanks.
239
240 if (!web_contents() || !weak_observer_ || is_installable_check_complete_)
241 return;
242
243 is_installable_check_complete_ = true;
244
245 bool webapk_compatible = false;
246 if (check_webapk_compatibility_) {
247 webapk_compatible = (data.error_code == NO_ERROR_DETECTED &&
248 AreWebManifestUrlsWebApkCompatible(data.manifest));
249 weak_observer_->OnDidDetermineWebApkCompatibility(webapk_compatible);
250 }
241 251
242 weak_observer_->OnUserTitleAvailable(shortcut_info_.user_title); 252 weak_observer_->OnUserTitleAvailable(shortcut_info_.user_title);
243 253 if (webapk_compatible) {
244 if (data.primary_icon) { 254 shortcut_info_.UpdateSource(ShortcutInfo::SOURCE_ADD_TO_HOMESCREEN_PWA);
245 shortcut_info_.best_primary_icon_url = data.primary_icon_url; 255 NotifyObserver(raw_primary_icon_);
246 256 } else {
247 if (webapk_compatible) 257 CreateLauncherIcon(raw_primary_icon_);
248 NotifyObserver(*data.primary_icon);
249 else
250 CreateLauncherIcon(*(data.primary_icon));
251 return;
252 } 258 }
253
254 FetchFavicon();
255 } 259 }
256 260
257 void AddToHomescreenDataFetcher::FetchFavicon() { 261 void AddToHomescreenDataFetcher::FetchFavicon() {
258 if (!web_contents() || !weak_observer_) 262 if (!web_contents() || !weak_observer_)
259 return; 263 return;
260 264
261 // Grab the best, largest icon we can find to represent this bookmark. 265 // Grab the best, largest icon we can find to represent this bookmark.
262 // TODO(dfalcantara): Try combining with the new BookmarksHandler once its 266 // TODO(dfalcantara): Try combining with the new BookmarksHandler once its
263 // rewrite is further along. 267 // rewrite is further along.
264 std::vector<int> icon_types{ 268 std::vector<int> icon_types{
(...skipping 10 matching lines...) Expand all
275 int threshold_to_get_any_largest_icon = ideal_icon_size_in_px_ - 1; 279 int threshold_to_get_any_largest_icon = ideal_icon_size_in_px_ - 1;
276 favicon_service->GetLargestRawFaviconForPageURL( 280 favicon_service->GetLargestRawFaviconForPageURL(
277 shortcut_info_.url, icon_types, threshold_to_get_any_largest_icon, 281 shortcut_info_.url, icon_types, threshold_to_get_any_largest_icon,
278 base::Bind(&AddToHomescreenDataFetcher::OnFaviconFetched, this), 282 base::Bind(&AddToHomescreenDataFetcher::OnFaviconFetched, this),
279 &favicon_task_tracker_); 283 &favicon_task_tracker_);
280 } 284 }
281 285
282 void AddToHomescreenDataFetcher::OnFaviconFetched( 286 void AddToHomescreenDataFetcher::OnFaviconFetched(
283 const favicon_base::FaviconRawBitmapResult& bitmap_result) { 287 const favicon_base::FaviconRawBitmapResult& bitmap_result) {
284 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 288 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
289 data_timeout_timer_.Stop();
pkotwicz 2017/06/23 19:23:19 Won't this cause OnUserTitleAvailable() never to b
dominickn 2017/06/26 02:39:16 Added an explicit call in OnDidFetchManifestAndIco
285 290
286 if (!web_contents() || !weak_observer_ || is_icon_saved_) 291 if (!web_contents() || !weak_observer_ || is_icon_saved_)
287 return; 292 return;
288 293
289 // The user is waiting for the icon to be processed before they can proceed 294 // The user is waiting for the icon to be processed before they can proceed
290 // with add to homescreen. But if we shut down, there's no point starting the 295 // with add to homescreen. But if we shut down, there's no point starting the
291 // image processing. Use USER_VISIBLE with MayBlock and SKIP_ON_SHUTDOWN. 296 // image processing. Use USER_VISIBLE with MayBlock and SKIP_ON_SHUTDOWN.
292 base::PostTaskWithTraitsAndReplyWithResult( 297 base::PostTaskWithTraitsAndReplyWithResult(
293 FROM_HERE, 298 FROM_HERE,
294 {base::MayBlock(), base::TaskPriority::USER_VISIBLE, 299 {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
295 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}, 300 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
296 base::BindOnce(&AddToHomescreenDataFetcher:: 301 base::BindOnce(&AddToHomescreenDataFetcher::
297 CreateLauncherIconFromFaviconInBackground, 302 CreateLauncherIconFromFaviconInBackground,
298 base::Unretained(this), bitmap_result), 303 base::Unretained(this), bitmap_result),
299 base::BindOnce(&AddToHomescreenDataFetcher::NotifyObserver, 304 base::BindOnce(&AddToHomescreenDataFetcher::NotifyObserver,
300 base::RetainedRef(this))); 305 base::RetainedRef(this)));
301 } 306 }
302 307
303 SkBitmap AddToHomescreenDataFetcher::CreateLauncherIconFromFaviconInBackground( 308 SkBitmap AddToHomescreenDataFetcher::CreateLauncherIconFromFaviconInBackground(
304 const favicon_base::FaviconRawBitmapResult& bitmap_result) { 309 const favicon_base::FaviconRawBitmapResult& bitmap_result) {
305 base::ThreadRestrictions::AssertIOAllowed(); 310 base::ThreadRestrictions::AssertIOAllowed();
306 311
307 SkBitmap raw_icon;
308 if (bitmap_result.is_valid()) { 312 if (bitmap_result.is_valid()) {
309 gfx::PNGCodec::Decode(bitmap_result.bitmap_data->front(), 313 gfx::PNGCodec::Decode(bitmap_result.bitmap_data->front(),
310 bitmap_result.bitmap_data->size(), &raw_icon); 314 bitmap_result.bitmap_data->size(),
315 &raw_primary_icon_);
311 } 316 }
312 317
313 shortcut_info_.best_primary_icon_url = bitmap_result.icon_url; 318 shortcut_info_.best_primary_icon_url = bitmap_result.icon_url;
314 return CreateLauncherIconInBackground(raw_icon); 319 return CreateLauncherIconInBackground(raw_primary_icon_);
315 } 320 }
316 321
317 void AddToHomescreenDataFetcher::CreateLauncherIcon(const SkBitmap& raw_icon) { 322 void AddToHomescreenDataFetcher::CreateLauncherIcon(const SkBitmap& icon) {
318 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 323 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
319 324
320 // The user is waiting for the icon to be processed before they can proceed 325 // The user is waiting for the icon to be processed before they can proceed
321 // with add to homescreen. But if we shut down, there's no point starting the 326 // with add to homescreen. But if we shut down, there's no point starting the
322 // image processing. Use USER_VISIBLE with MayBlock and SKIP_ON_SHUTDOWN. 327 // image processing. Use USER_VISIBLE with MayBlock and SKIP_ON_SHUTDOWN.
323 base::PostTaskWithTraitsAndReplyWithResult( 328 base::PostTaskWithTraitsAndReplyWithResult(
324 FROM_HERE, 329 FROM_HERE,
325 {base::MayBlock(), base::TaskPriority::USER_VISIBLE, 330 {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
326 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}, 331 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
327 base::BindOnce( 332 base::BindOnce(
328 &AddToHomescreenDataFetcher::CreateLauncherIconInBackground, 333 &AddToHomescreenDataFetcher::CreateLauncherIconInBackground,
329 base::Unretained(this), raw_icon), 334 base::Unretained(this), raw_primary_icon_),
330 base::BindOnce(&AddToHomescreenDataFetcher::NotifyObserver, 335 base::BindOnce(&AddToHomescreenDataFetcher::NotifyObserver,
331 base::RetainedRef(this))); 336 base::RetainedRef(this)));
332 } 337 }
333 338
334 SkBitmap AddToHomescreenDataFetcher::CreateLauncherIconInBackground( 339 SkBitmap AddToHomescreenDataFetcher::CreateLauncherIconInBackground(
335 const SkBitmap& raw_icon) { 340 const SkBitmap& icon) {
336 base::ThreadRestrictions::AssertIOAllowed(); 341 base::ThreadRestrictions::AssertIOAllowed();
337 342
338 SkBitmap primary_icon; 343 SkBitmap primary_icon;
339 bool is_generated = false; 344 bool is_generated = false;
340 if (weak_observer_) { 345 if (weak_observer_) {
341 primary_icon = weak_observer_->FinalizeLauncherIconInBackground( 346 primary_icon = weak_observer_->FinalizeLauncherIconInBackground(
342 raw_icon, shortcut_info_.url, &is_generated); 347 icon, shortcut_info_.url, &is_generated);
343 } 348 }
344 349
345 if (is_generated) 350 if (is_generated)
346 shortcut_info_.best_primary_icon_url = GURL(); 351 shortcut_info_.best_primary_icon_url = GURL();
347 352
348 return primary_icon; 353 return primary_icon;
349 } 354 }
350 355
351 void AddToHomescreenDataFetcher::NotifyObserver(const SkBitmap& primary_icon) { 356 void AddToHomescreenDataFetcher::NotifyObserver(const SkBitmap& primary_icon) {
352 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 357 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
353 if (!web_contents() || !weak_observer_ || is_icon_saved_) 358 if (!web_contents() || !weak_observer_ || is_icon_saved_)
354 return; 359 return;
355 360
356 is_icon_saved_ = true; 361 is_icon_saved_ = true;
357 primary_icon_ = primary_icon; 362 primary_icon_ = primary_icon;
358 weak_observer_->OnDataAvailable(shortcut_info_, primary_icon_, badge_icon_); 363 weak_observer_->OnDataAvailable(shortcut_info_, primary_icon_, badge_icon_);
359 } 364 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698