Notify ShelfItem if IconImage is a placeholder

Create a field on the ShelfItem for the Shelf UI to be able to identify
a placeholder icon from a regular icon.

Bug: b:304383596
Change-Id: Ic085a31b277b7fa7d48d519a8f6fceda14622fe9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5001948
Reviewed-by: Scott Violet <[email protected]>
Reviewed-by: Ana Salazar <[email protected]>
Commit-Queue: Ana Salazar <[email protected]>
Reviewed-by: Toni Barzic <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1220624}
diff --git a/chrome/browser/ash/app_list/app_service/app_service_app_icon_loader.cc b/chrome/browser/ash/app_list/app_service/app_service_app_icon_loader.cc
index 41d4c179..d9f10af5 100644
--- a/chrome/browser/ash/app_list/app_service/app_service_app_icon_loader.cc
+++ b/chrome/browser/ash/app_list/app_service/app_service_app_icon_loader.cc
@@ -82,6 +82,7 @@
   if (it != icon_map_.end()) {
     if (!it->second.isNull()) {
       delegate()->OnAppImageUpdated(id, it->second,
+                                    /*is_placeholder_icon=*/false,
                                     /*badge_image=*/absl::nullopt);
     }
     return;
@@ -111,7 +112,9 @@
     return;
   }
 
-  delegate()->OnAppImageUpdated(id, it->second, /*badge_image=*/absl::nullopt);
+  delegate()->OnAppImageUpdated(id, it->second,
+                                /*is_placeholder_icon=*/false,
+                                /*badge_image=*/absl::nullopt);
 }
 
 void AppServiceAppIconLoader::OnAppUpdate(const apps::AppUpdate& update) {
@@ -180,7 +183,8 @@
     }
     gfx::ImageSkia image = icon_value->uncompressed;
     icon_map_[id] = image;
-    delegate()->OnAppImageUpdated(id, image, /*badge_image=*/absl::nullopt);
+    delegate()->OnAppImageUpdated(id, image, icon_value->is_placeholder_icon,
+                                  /*badge_image=*/absl::nullopt);
   }
 
   if (icon_value->is_placeholder_icon) {
diff --git a/chrome/browser/ash/app_list/app_service/app_service_promise_app_icon_loader.cc b/chrome/browser/ash/app_list/app_service/app_service_promise_app_icon_loader.cc
index 149301e..66e51125 100644
--- a/chrome/browser/ash/app_list/app_service/app_service_promise_app_icon_loader.cc
+++ b/chrome/browser/ash/app_list/app_service/app_service_promise_app_icon_loader.cc
@@ -106,5 +106,6 @@
     apps::IconValuePtr icon_value) {
   gfx::ImageSkia image = icon_value->uncompressed;
   delegate()->OnAppImageUpdated(package_id.ToString(), image,
+                                icon_value->is_placeholder_icon,
                                 /*badge_image=*/absl::nullopt);
 }
diff --git a/chrome/browser/ash/app_list/app_service/app_service_shortcut_icon_loader.cc b/chrome/browser/ash/app_list/app_service/app_service_shortcut_icon_loader.cc
index e8798a51..754073f 100644
--- a/chrome/browser/ash/app_list/app_service/app_service_shortcut_icon_loader.cc
+++ b/chrome/browser/ash/app_list/app_service/app_service_shortcut_icon_loader.cc
@@ -52,7 +52,9 @@
   ShortcutIDToIconMap::const_iterator it = icon_map_.find(id);
   if (it != icon_map_.end()) {
     if (!it->second.image.isNull()) {
-      delegate()->OnAppImageUpdated(id, it->second.image, it->second.badge);
+      delegate()->OnAppImageUpdated(id, it->second.image,
+                                    /*is_placeholder_icon=*/false,
+                                    it->second.badge);
     }
     return;
   }
@@ -71,7 +73,9 @@
     return;
   }
 
-  delegate()->OnAppImageUpdated(id, it->second.image, it->second.badge);
+  delegate()->OnAppImageUpdated(id, it->second.image,
+                                /*is_placeholder_icon=*/false,
+                                it->second.badge);
 }
 
 void AppServiceShortcutIconLoader::OnShortcutUpdated(
@@ -121,5 +125,6 @@
           : gfx::ImageSkia();
 
   icon_map_[shortcut_id.value()] = {.image = image, .badge = badge};
-  delegate()->OnAppImageUpdated(shortcut_id.value(), image, badge);
+  delegate()->OnAppImageUpdated(shortcut_id.value(), image,
+                                /*is_placeholder_icon=*/false, badge);
 }
diff --git a/chrome/browser/ash/app_list/arc/arc_app_unittest.cc b/chrome/browser/ash/app_list/arc/arc_app_unittest.cc
index 14a4987..6673d63f 100644
--- a/chrome/browser/ash/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ash/app_list/arc/arc_app_unittest.cc
@@ -171,6 +171,7 @@
   void OnAppImageUpdated(
       const std::string& app_id,
       const gfx::ImageSkia& image,
+      bool is_placeholder_icon,
       const absl::optional<gfx::ImageSkia>& badge_image) override {
     app_id_ = app_id;
     image_ = image;
diff --git a/chrome/browser/ash/app_list/search/arc/arc_app_shortcut_search_result.cc b/chrome/browser/ash/app_list/search/arc/arc_app_shortcut_search_result.cc
index 4a5ed8d..73a66c4 100644
--- a/chrome/browser/ash/app_list/search/arc/arc_app_shortcut_search_result.cc
+++ b/chrome/browser/ash/app_list/search/arc/arc_app_shortcut_search_result.cc
@@ -112,6 +112,7 @@
 void ArcAppShortcutSearchResult::OnAppImageUpdated(
     const std::string& app_id,
     const gfx::ImageSkia& image,
+    bool is_placeholder_icon,
     const absl::optional<gfx::ImageSkia>& badge_image) {
   SetBadgeIcon(ui::ImageModel::FromImageSkia(image));
 }
diff --git a/chrome/browser/ash/app_list/search/arc/arc_app_shortcut_search_result.h b/chrome/browser/ash/app_list/search/arc/arc_app_shortcut_search_result.h
index fe1d7ae..40ddc18b 100644
--- a/chrome/browser/ash/app_list/search/arc/arc_app_shortcut_search_result.h
+++ b/chrome/browser/ash/app_list/search/arc/arc_app_shortcut_search_result.h
@@ -56,6 +56,7 @@
   void OnAppImageUpdated(
       const std::string& app_id,
       const gfx::ImageSkia& image,
+      bool is_placeholder_icon,
       const absl::optional<gfx::ImageSkia>& badge_image) override;
 
   // Gets app id of the app that publishes this app shortcut.
diff --git a/chrome/browser/ash/file_system_provider/notification_manager.cc b/chrome/browser/ash/file_system_provider/notification_manager.cc
index 302f55e..60baacc 100644
--- a/chrome/browser/ash/file_system_provider/notification_manager.cc
+++ b/chrome/browser/ash/file_system_provider/notification_manager.cc
@@ -75,6 +75,7 @@
 void NotificationManager::OnAppImageUpdated(
     const std::string& id,
     const gfx::ImageSkia& image,
+    bool is_placeholder_icon,
     const absl::optional<gfx::ImageSkia>& badge_image) {
   extension_icon_ = ui::ImageModel::FromImageSkia(image);
   ShowNotification();
diff --git a/chrome/browser/ash/file_system_provider/notification_manager.h b/chrome/browser/ash/file_system_provider/notification_manager.h
index 87cf8c5..2e74613 100644
--- a/chrome/browser/ash/file_system_provider/notification_manager.h
+++ b/chrome/browser/ash/file_system_provider/notification_manager.h
@@ -52,6 +52,7 @@
   void OnAppImageUpdated(
       const std::string& id,
       const gfx::ImageSkia& image,
+      bool is_placeholder_icon,
       const absl::optional<gfx::ImageSkia>& badge_image) override;
 
   // message_center::NotificationObserver overrides:
diff --git a/chrome/browser/extensions/api/file_system/request_file_system_notification.cc b/chrome/browser/extensions/api/file_system/request_file_system_notification.cc
index f93abf4..5bca2416 100644
--- a/chrome/browser/extensions/api/file_system/request_file_system_notification.cc
+++ b/chrome/browser/extensions/api/file_system/request_file_system_notification.cc
@@ -61,6 +61,7 @@
   void OnAppImageUpdated(
       const std::string& id,
       const gfx::ImageSkia& image,
+      bool is_placeholder_icon,
       const absl::optional<gfx::ImageSkia>& badge_image) override {
     pending_notification_->set_icon(ui::ImageModel::FromImageSkia(image));
     auto* notification_display_service =
diff --git a/chrome/browser/extensions/chrome_app_icon_loader.cc b/chrome/browser/extensions/chrome_app_icon_loader.cc
index 2108b80..74a087c 100644
--- a/chrome/browser/extensions/chrome_app_icon_loader.cc
+++ b/chrome/browser/extensions/chrome_app_icon_loader.cc
@@ -91,6 +91,7 @@
 
 void ChromeAppIconLoader::OnIconUpdated(ChromeAppIcon* icon) {
   delegate()->OnAppImageUpdated(icon->app_id(), icon->image_skia(),
+                                /*is_placeholder_icon=*/false,
                                 /*badge_image=*/absl::nullopt);
 }
 
diff --git a/chrome/browser/extensions/chrome_app_icon_unittest.cc b/chrome/browser/extensions/chrome_app_icon_unittest.cc
index 8fa058f6..93b1e09 100644
--- a/chrome/browser/extensions/chrome_app_icon_unittest.cc
+++ b/chrome/browser/extensions/chrome_app_icon_unittest.cc
@@ -116,6 +116,7 @@
   void OnAppImageUpdated(
       const std::string& app_id,
       const gfx::ImageSkia& image,
+      bool is_placeholder_icon,
       const absl::optional<gfx::ImageSkia>& badge_image) override {
     image_skia_ = image;
   }
diff --git a/chrome/browser/notifications/extension_notifier_controller.cc b/chrome/browser/notifications/extension_notifier_controller.cc
index 3292b49..62ba48a 100644
--- a/chrome/browser/notifications/extension_notifier_controller.cc
+++ b/chrome/browser/notifications/extension_notifier_controller.cc
@@ -78,6 +78,7 @@
 void ExtensionNotifierController::OnAppImageUpdated(
     const std::string& id,
     const gfx::ImageSkia& image,
+    bool is_placeholder_icon,
     const absl::optional<gfx::ImageSkia>& badge_image) {
   observer_->OnIconImageUpdated(
       message_center::NotifierId(message_center::NotifierType::APPLICATION, id),
diff --git a/chrome/browser/notifications/extension_notifier_controller.h b/chrome/browser/notifications/extension_notifier_controller.h
index 7e74f61..ec52165 100644
--- a/chrome/browser/notifications/extension_notifier_controller.h
+++ b/chrome/browser/notifications/extension_notifier_controller.h
@@ -30,6 +30,7 @@
   void OnAppImageUpdated(
       const std::string& id,
       const gfx::ImageSkia& image,
+      bool is_placeholder_icon,
       const absl::optional<gfx::ImageSkia>& badge_image) override;
 
   std::unique_ptr<AppIconLoader> app_icon_loader_;
diff --git a/chrome/browser/ui/app_icon_loader_delegate.h b/chrome/browser/ui/app_icon_loader_delegate.h
index 0d0c8cc4..4c4bc76 100644
--- a/chrome/browser/ui/app_icon_loader_delegate.h
+++ b/chrome/browser/ui/app_icon_loader_delegate.h
@@ -19,9 +19,13 @@
   // 'image' is the main app image, `badge_image` if set is the badge that
   // should be painted on top of the main image for certain app types
   // (currently, `badge_image` will be set for app shortcuts).
+  // 'is_placeholder_icon' is true if the main app image is a placeholder icon.
+  // A promise app may have a placeholder icon while the IconLoader finishes
+  // resolving the resource or is unable to fetch one.
   virtual void OnAppImageUpdated(
       const std::string& app_id,
       const gfx::ImageSkia& image,
+      bool is_placeholder_icon,
       const absl::optional<gfx::ImageSkia>& badge_image) = 0;
 
  protected:
diff --git a/chrome/browser/ui/ash/shelf/arc_app_window.cc b/chrome/browser/ui/ash/shelf/arc_app_window.cc
index 65d3dbe9..38cfdc4 100644
--- a/chrome/browser/ui/ash/shelf/arc_app_window.cc
+++ b/chrome/browser/ui/ash/shelf/arc_app_window.cc
@@ -89,6 +89,7 @@
 void ArcAppWindow::OnAppImageUpdated(
     const std::string& app_id,
     const gfx::ImageSkia& image,
+    bool is_placeholder_icon,
     const absl::optional<gfx::ImageSkia>& badge_image) {
   if (image_fetching_) {
     // This is default app icon. Don't assign it right now to avoid flickering.
diff --git a/chrome/browser/ui/ash/shelf/arc_app_window.h b/chrome/browser/ui/ash/shelf/arc_app_window.h
index 6f725ee..6a1224b 100644
--- a/chrome/browser/ui/ash/shelf/arc_app_window.h
+++ b/chrome/browser/ui/ash/shelf/arc_app_window.h
@@ -61,6 +61,7 @@
   void OnAppImageUpdated(
       const std::string& app_id,
       const gfx::ImageSkia& image,
+      bool is_placeholder_icon,
       const absl::optional<gfx::ImageSkia>& badge_image) override;
 
  private:
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc
index 653ed65e..7f4c77e 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc
@@ -1192,6 +1192,7 @@
 void ChromeShelfController::OnAppImageUpdated(
     const std::string& app_id,
     const gfx::ImageSkia& image,
+    bool is_placeholder_icon,
     const absl::optional<gfx::ImageSkia>& badge_image) {
   TRACE_EVENT0("ui", "ChromeShelfController::OnAppImageUpdated");
   bool is_standard_icon = true;
@@ -1204,7 +1205,7 @@
   }
 
   if (is_standard_icon) {
-    UpdateAppImage(app_id, badge_image, image);
+    UpdateAppImage(app_id, badge_image, is_placeholder_icon, image);
     return;
   }
 
@@ -1225,12 +1226,14 @@
   standard_icon_task_runner_->PostTaskAndReplyWithResult(
       FROM_HERE, base::BindOnce(&CreateStandardImageOnWorkerThread, copy),
       base::BindOnce(&ChromeShelfController::UpdateAppImage,
-                     weak_ptr_factory_.GetWeakPtr(), app_id, badge_image));
+                     weak_ptr_factory_.GetWeakPtr(), app_id, badge_image,
+                     is_placeholder_icon));
 }
 
 void ChromeShelfController::UpdateAppImage(
     const std::string& app_id,
     const absl::optional<gfx::ImageSkia>& badge_image,
+    bool is_placeholder_icon,
     const gfx::ImageSkia& image) {
   TRACE_EVENT0("ui", "ChromeShelfController::UpdateAppImage");
   // TODO: need to get this working for shortcuts.
@@ -1243,6 +1246,7 @@
     }
     item.image = image;
     item.badge_image = badge_image.value_or(gfx::ImageSkia());
+    item.has_placeholder_icon = is_placeholder_icon;
     shelf_spinner_controller_->MaybeApplySpinningEffect(app_id, &item.image);
     item.notification_badge_color =
         ash::AppIconColorCache::GetInstance().GetLightVibrantColorForApp(app_id,
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller.h b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.h
index 0cdf157..085b9ca 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller.h
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.h
@@ -289,6 +289,7 @@
   void OnAppImageUpdated(
       const std::string& app_id,
       const gfx::ImageSkia& image,
+      bool is_placeholder_icon,
       const absl::optional<gfx::ImageSkia>& badge_image) override;
 
   // Inserts a shelf item for an app at |index|. Note that |index| may be
@@ -313,6 +314,7 @@
   // Updates images of shelf items representing the app.
   void UpdateAppImage(const std::string& app_id,
                       const absl::optional<gfx::ImageSkia>& badge_image,
+                      bool is_placeholder_icon,
                       const gfx::ImageSkia& image);
 
   // Remembers / restores list of running applications.
diff --git a/chrome/browser/ui/ash/shelf/crostini_app_window.cc b/chrome/browser/ui/ash/shelf/crostini_app_window.cc
index 85df1d0a..62ed8b17 100644
--- a/chrome/browser/ui/ash/shelf/crostini_app_window.cc
+++ b/chrome/browser/ui/ash/shelf/crostini_app_window.cc
@@ -41,6 +41,7 @@
   void OnAppImageUpdated(
       const std::string& app_id,
       const gfx::ImageSkia& image,
+      bool is_placeholder_icon,
       const absl::optional<gfx::ImageSkia>& badge_image) override {
     if (!widget_ || !widget_->widget_delegate())
       return;
diff --git a/chrome/browser/ui/views/arc_app_dialog_view.cc b/chrome/browser/ui/views/arc_app_dialog_view.cc
index d526d7b..cd48ea6 100644
--- a/chrome/browser/ui/views/arc_app_dialog_view.cc
+++ b/chrome/browser/ui/views/arc_app_dialog_view.cc
@@ -64,6 +64,7 @@
   void OnAppImageUpdated(
       const std::string& app_id,
       const gfx::ImageSkia& image,
+      bool is_placeholder_icon,
       const absl::optional<gfx::ImageSkia>& badge_image) override;
 
   void AddMultiLineLabel(views::View* parent, const std::u16string& label_text);
@@ -184,6 +185,7 @@
 void ArcAppDialogView::OnAppImageUpdated(
     const std::string& app_id,
     const gfx::ImageSkia& image,
+    bool is_placeholder_icon,
     const absl::optional<gfx::ImageSkia>& badge_image) {
   DCHECK_EQ(app_id, app_id_);
   DCHECK(!image.isNull());
diff --git a/chrome/browser/ui/views/arc_data_removal_dialog_view.cc b/chrome/browser/ui/views/arc_data_removal_dialog_view.cc
index cf70aca..59235ca 100644
--- a/chrome/browser/ui/views/arc_data_removal_dialog_view.cc
+++ b/chrome/browser/ui/views/arc_data_removal_dialog_view.cc
@@ -55,6 +55,7 @@
   void OnAppImageUpdated(
       const std::string& app_id,
       const gfx::ImageSkia& image,
+      bool is_placeholder_icon,
       const absl::optional<gfx::ImageSkia>& badge_image) override;
 
   // ArcSessionManagerObserver:
@@ -135,6 +136,7 @@
 void DataRemovalConfirmationDialog::OnAppImageUpdated(
     const std::string& app_id,
     const gfx::ImageSkia& image,
+    bool is_placeholder_icon,
     const absl::optional<gfx::ImageSkia>& badge_image) {
   DCHECK(!image.isNull());
   DCHECK_EQ(image.width(), kArcAppIconSize);