[NTP] Calc and store custom background color V3.

This CL relands https://chromium-review.googlesource.com/c/chromium/src/+/1612235
with following changes:
- calculates image color async, as it can take 1-2 seconds,
- fixes the image attribution bug.

The seconds patch corresponds to the reverted CL.

Bug: 874325
Change-Id: Iec9812c93dc383043b22048ec4387f1f08a28e5a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1618502
Reviewed-by: Nicolas Ouellet-Payeur <nicolaso@chromium.org>
Reviewed-by: Kyle Milka <kmilka@chromium.org>
Commit-Queue: Gayane Petrosyan <gayane@chromium.org>
Cr-Commit-Position: refs/heads/master@{#661606}
diff --git a/chrome/browser/search/background/ntp_background_service.cc b/chrome/browser/search/background/ntp_background_service.cc
index 6cefbba..8124da2 100644
--- a/chrome/browser/search/background/ntp_background_service.cc
+++ b/chrome/browser/search/background/ntp_background_service.cc
@@ -267,6 +267,23 @@
   collection_images_.push_back(image);
 }
 
+void NtpBackgroundService::AddValidBackdropUrlWithThumbnailForTesting(
+    const GURL& url,
+    const GURL& thumbnail_url) {
+  CollectionImage image;
+  image.image_url = url;
+  image.thumbnail_image_url = thumbnail_url;
+  collection_images_.push_back(image);
+}
+
+const GURL& NtpBackgroundService::GetThumbnailUrl(const GURL& image_url) {
+  for (auto& image : collection_images_) {
+    if (image.image_url == image_url)
+      return image.thumbnail_image_url;
+  }
+  return GURL::EmptyGURL();
+}
+
 void NtpBackgroundService::NotifyObservers(FetchComplete fetch_complete) {
   for (auto& observer : observers_) {
     switch (fetch_complete) {
diff --git a/chrome/browser/search/background/ntp_background_service.h b/chrome/browser/search/background/ntp_background_service.h
index de41780..64a7163 100644
--- a/chrome/browser/search/background/ntp_background_service.h
+++ b/chrome/browser/search/background/ntp_background_service.h
@@ -55,6 +55,13 @@
 
   void AddValidBackdropUrlForTesting(const GURL& url);
 
+  void AddValidBackdropUrlWithThumbnailForTesting(const GURL& url,
+                                                  const GURL& thumbnail_url);
+
+  // Returns thumbnail url for the given image url if its valid. Otherwise,
+  // returns empty url.
+  const GURL& GetThumbnailUrl(const GURL& image_url);
+
   // Returns the currently cached CollectionInfo, if any.
   const std::vector<CollectionInfo>& collection_info() const {
     return collection_info_;
diff --git a/chrome/browser/search/background/ntp_background_service_unittest.cc b/chrome/browser/search/background/ntp_background_service_unittest.cc
index e7d5a73..5d04323 100644
--- a/chrome/browser/search/background/ntp_background_service_unittest.cc
+++ b/chrome/browser/search/background/ntp_background_service_unittest.cc
@@ -261,3 +261,14 @@
   EXPECT_FALSE(service()->IsValidBackdropUrl(
       GURL("https://wallpapers.co/another_image")));
 }
+
+TEST_F(NtpBackgroundServiceTest, GetThumbnailUrl) {
+  const GURL kInvalidUrl("foo");
+  const GURL kValidUrl("https://www.foo.com");
+  const GURL kValidThumbnailUrl("https://www.foo.com/thumbnail");
+  service()->AddValidBackdropUrlWithThumbnailForTesting(kValidUrl,
+                                                        kValidThumbnailUrl);
+
+  EXPECT_EQ(kValidThumbnailUrl, service()->GetThumbnailUrl(kValidUrl));
+  EXPECT_EQ(GURL::EmptyGURL(), service()->GetThumbnailUrl(kInvalidUrl));
+}
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index 449a5bb..12f07f9 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -17,6 +17,7 @@
 #include "base/task/post_task.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/image_fetcher/image_decoder_impl.h"
 #include "chrome/browser/ntp_tiles/chrome_most_visited_sites_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/background/ntp_background_service.h"
@@ -51,7 +52,9 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/browser/url_data_source.h"
+#include "ui/gfx/color_analysis.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/native_theme/dark_mode_observer.h"
 
@@ -63,6 +66,8 @@
 const char kNtpCustomBackgroundAttributionActionURL[] =
     "attribution_action_url";
 
+const char kImageFetcherUmaClientName[] = "NtpCustomBackgrounds";
+
 base::DictionaryValue GetBackgroundInfoAsDict(
     const GURL& background_url,
     const std::string& attribution_line_1,
@@ -81,6 +86,33 @@
   return background_info;
 }
 
+// |GetBackgroundInfoWithColor| has to return new object so that updated version
+// gets synced.
+base::DictionaryValue GetBackgroundInfoWithColor(
+    const base::DictionaryValue* background_info,
+    const SkColor color) {
+  base::DictionaryValue new_background_info;
+  auto url = const_cast<base::Value&&>(
+      *background_info->FindKey(kNtpCustomBackgroundURL));
+  auto attribution_line_1 = const_cast<base::Value&&>(
+      *background_info->FindKey(kNtpCustomBackgroundAttributionLine1));
+  auto attribution_line_2 = const_cast<base::Value&&>(
+      *background_info->FindKey(kNtpCustomBackgroundAttributionLine2));
+  auto action_url = const_cast<base::Value&&>(
+      *background_info->FindKey(kNtpCustomBackgroundAttributionActionURL));
+
+  new_background_info.SetKey(kNtpCustomBackgroundURL, url.Clone());
+  new_background_info.SetKey(kNtpCustomBackgroundAttributionLine1,
+                             attribution_line_1.Clone());
+  new_background_info.SetKey(kNtpCustomBackgroundAttributionLine2,
+                             attribution_line_2.Clone());
+  new_background_info.SetKey(kNtpCustomBackgroundAttributionActionURL,
+                             action_url.Clone());
+  new_background_info.SetKey(kNtpCustomBackgroundMainColor,
+                             base::Value((int)color));
+  return new_background_info;
+}
+
 base::Value NtpCustomBackgroundDefaults() {
   base::Value defaults(base::Value::Type::DICTIONARY);
   defaults.SetKey(kNtpCustomBackgroundURL,
@@ -113,8 +145,17 @@
     std::move(*callback).Run(result);
 }
 
+// |GetBitmapMainColor| just wraps |CalculateKMeanColorOfBitmap|.
+// As |CalculateKMeanColorOfBitmap| is overloaded, it cannot be bind for async
+// call.
+SkColor GetBitmapMainColor(const SkBitmap& bitmap) {
+  return color_utils::CalculateKMeanColorOfBitmap(bitmap);
+}
+
 }  // namespace
 
+const char kNtpCustomBackgroundMainColor[] = "background_main_color";
+
 // Keeps track of any changes in search engine provider and notifies
 // InstantService if a third-party search provider (i.e. a third-party NTP) is
 // being used.
@@ -231,6 +272,11 @@
       prefs::kNtpCustomBackgroundDict,
       base::BindRepeating(&InstantService::UpdateBackgroundFromSync,
                           weak_ptr_factory_.GetWeakPtr()));
+
+  image_fetcher_ = std::make_unique<image_fetcher::ImageFetcherImpl>(
+      std::make_unique<ImageDecoderImpl>(),
+      content::BrowserContext::GetDefaultStoragePartition(profile_)
+          ->GetURLLoaderFactoryForBrowserProcess());
 }
 
 InstantService::~InstantService() = default;
@@ -383,6 +429,12 @@
   RemoveLocalBackgroundImageCopy();
 
   if (background_url.is_valid() && is_backdrop_url) {
+    const GURL& thumbnail_url =
+        background_service_->GetThumbnailUrl(background_url);
+    FetchCustomBackground(background_url, thumbnail_url.is_valid()
+                                              ? thumbnail_url
+                                              : background_url);
+
     base::DictionaryValue background_info = GetBackgroundInfoAsDict(
         background_url, attribution_line_1, attribution_line_2, action_url);
     pref_service_->Set(prefs::kNtpCustomBackgroundDict, background_info);
@@ -723,6 +775,52 @@
   ResetCustomBackgroundThemeInfo();
 }
 
+void InstantService::UpdateCustomBackgroundColorAsync(
+    const GURL& image_url,
+    const gfx::Image& fetched_image,
+    const image_fetcher::RequestMetadata& metadata) {
+  // Calculate the bitmap color asynchronously as it is slow (1-2 seconds for
+  // the thumbnail). However, prefs should be updated on the main thread.
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE, {base::TaskPriority::BEST_EFFORT},
+      base::BindOnce(&GetBitmapMainColor, *fetched_image.ToSkBitmap()),
+      base::BindOnce(&InstantService::UpdateCustomBackgroundPrefsWithColor,
+                     weak_ptr_factory_.GetWeakPtr(), image_url));
+}
+
+void InstantService::FetchCustomBackground(const GURL& image_url,
+                                           const GURL& fetch_url) {
+  DCHECK(!fetch_url.is_empty());
+
+  net::NetworkTrafficAnnotationTag traffic_annotation =
+      net::DefineNetworkTrafficAnnotation("ntp_custom_background",
+                                          R"(
+    semantics {
+      sender: "Desktop Chrome background fetcher"
+      description:
+        "Fetch New Tab Page custom background for color calculation."
+      trigger:
+        "User selects new background on the New Tab Page."
+      data: "The only data sent is the path to an image"
+      destination: GOOGLE_OWNED_SERVICE
+    }
+    policy {
+      cookies_allowed: NO
+      setting:
+        "Users cannot disable this feature. The feature is enabled by "
+        "default."
+      policy_exception_justification: "Not implemented."
+    })");
+
+  image_fetcher::ImageFetcherParams params(traffic_annotation,
+                                           kImageFetcherUmaClientName);
+  image_fetcher_->FetchImage(
+      image_url,
+      base::BindOnce(&InstantService::UpdateCustomBackgroundColorAsync,
+                     weak_ptr_factory_.GetWeakPtr(), image_url),
+      std::move(params));
+}
+
 bool InstantService::IsCustomBackgroundPrefValid(GURL& custom_background_url) {
   const base::DictionaryValue* background_info =
       profile_->GetPrefs()->GetDictionary(prefs::kNtpCustomBackgroundDict);
@@ -777,3 +875,24 @@
                                 false);
   registry->RegisterBooleanPref(prefs::kNtpUseMostVisitedTiles, false);
 }
+
+void InstantService::UpdateCustomBackgroundPrefsWithColor(const GURL& image_url,
+                                                          SkColor color) {
+  // Update background color only if the selected background is still the same.
+  const base::DictionaryValue* background_info =
+      pref_service_->GetDictionary(prefs::kNtpCustomBackgroundDict);
+  if (!background_info)
+    return;
+
+  GURL current_bg_url(
+      background_info->FindKey(kNtpCustomBackgroundURL)->GetString());
+  if (current_bg_url == image_url) {
+    pref_service_->Set(prefs::kNtpCustomBackgroundDict,
+                       GetBackgroundInfoWithColor(background_info, color));
+  }
+}
+
+void InstantService::SetImageFetcherForTesting(
+    image_fetcher::ImageFetcher* image_fetcher) {
+  image_fetcher_ = base::WrapUnique(image_fetcher);
+}
diff --git a/chrome/browser/search/instant_service.h b/chrome/browser/search/instant_service.h
index 319bab81..c40e247e 100644
--- a/chrome/browser/search/instant_service.h
+++ b/chrome/browser/search/instant_service.h
@@ -18,6 +18,7 @@
 #include "base/optional.h"
 #include "build/build_config.h"
 #include "components/history/core/browser/history_types.h"
+#include "components/image_fetcher/core/image_fetcher_impl.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/ntp_tiles/most_visited_sites.h"
 #include "components/ntp_tiles/ntp_tile.h"
@@ -47,6 +48,8 @@
 class DarkModeObserver;
 }  // namespace ui
 
+extern const char kNtpCustomBackgroundMainColor[];
+
 // Tracks render process host IDs that are associated with Instant, i.e.
 // processes that are used to render an NTP. Also responsible for keeping
 // necessary information (most visited tiles and theme info) updated in those
@@ -154,11 +157,22 @@
   // tests.
   virtual void ResetToDefault();
 
+  // Calculates the most frequent color of the image and stores it in prefs.
+  void UpdateCustomBackgroundColorAsync(
+      const GURL& image_url,
+      const gfx::Image& fetched_image,
+      const image_fetcher::RequestMetadata& metadata);
+
+  // Fetches the image for the given |fetch_url|.
+  void FetchCustomBackground(const GURL& image_url, const GURL& fetch_url);
+
  private:
   class SearchProviderObserver;
 
   friend class InstantExtendedTest;
   friend class InstantUnitTestBase;
+  friend class LocalNTPBackgroundsAndDarkModeTest;
+  friend class TestInstantService;
 
   FRIEND_TEST_ALL_PREFIXES(InstantExtendedTest, ProcessIsolation);
   FRIEND_TEST_ALL_PREFIXES(InstantServiceTest, DeleteThumbnailDataIfExists);
@@ -229,6 +243,12 @@
 
   void CreateDarkModeObserver(ui::NativeTheme* theme);
 
+  // Updates custom background prefs with color for the given |image_url|.
+  void UpdateCustomBackgroundPrefsWithColor(const GURL& image_url,
+                                            SkColor color);
+
+  void SetImageFetcherForTesting(image_fetcher::ImageFetcher* image_fetcher);
+
   Profile* const profile_;
 
   // The process ids associated with Instant processes.
@@ -261,6 +281,8 @@
 
   NtpBackgroundService* background_service_;
 
+  std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher_;
+
   base::WeakPtrFactory<InstantService> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(InstantService);
diff --git a/chrome/browser/search/instant_service_unittest.cc b/chrome/browser/search/instant_service_unittest.cc
index 2e09ed5..b2dfc78 100644
--- a/chrome/browser/search/instant_service_unittest.cc
+++ b/chrome/browser/search/instant_service_unittest.cc
@@ -55,6 +55,18 @@
   MOCK_METHOD0(ResetCustomBackgroundThemeInfo, void());
 };
 
+bool CheckBackgroundColor(SkColor color,
+                          const base::DictionaryValue* background_info) {
+  if (!background_info)
+    return false;
+
+  const base::Value* background_color =
+      background_info->FindKey(kNtpCustomBackgroundMainColor);
+  if (!background_color)
+    return false;
+
+  return color == static_cast<uint32_t>(background_color->GetInt());
+}
 }  // namespace
 
 using InstantServiceTest = InstantUnitTestBase;
@@ -576,3 +588,48 @@
   EXPECT_EQ(std::string(), theme_info->custom_background_attribution_line_2);
   EXPECT_EQ(GURL(), theme_info->custom_background_attribution_action_url);
 }
+
+TEST_F(InstantServiceTest, TestUpdateCustomBackgroundColor) {
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(32, 32);
+  bitmap.eraseColor(SK_ColorRED);
+  gfx::Image image = gfx::Image::CreateFrom1xBitmap(bitmap);
+  sync_preferences::TestingPrefServiceSyncable* pref_service =
+      profile()->GetTestingPrefService();
+
+  ASSERT_FALSE(instant_service_->IsCustomBackgroundSet());
+
+  // Background color will not update if no background is set.
+  instant_service_->UpdateCustomBackgroundColorAsync(
+      GURL(), image, image_fetcher::RequestMetadata());
+  thread_bundle()->RunUntilIdle();
+  EXPECT_FALSE(CheckBackgroundColor(
+      SK_ColorRED,
+      pref_service->GetDictionary(prefs::kNtpCustomBackgroundDict)));
+
+  const GURL kUrl("https://www.foo.com");
+  const std::string kAttributionLine1 = "foo";
+  const std::string kAttributionLine2 = "bar";
+  const GURL kActionUrl("https://www.bar.com");
+
+  SetUserSelectedDefaultSearchProvider("{google:baseURL}");
+  instant_service_->AddValidBackdropUrlForTesting(kUrl);
+  instant_service_->SetCustomBackgroundURLWithAttributions(
+      kUrl, kAttributionLine1, kAttributionLine2, kActionUrl);
+
+  // Background color will not update if current background url changed.
+  instant_service_->UpdateCustomBackgroundColorAsync(
+      GURL("different_url"), image, image_fetcher::RequestMetadata());
+  thread_bundle()->RunUntilIdle();
+  EXPECT_FALSE(CheckBackgroundColor(
+      SK_ColorRED,
+      pref_service->GetDictionary(prefs::kNtpCustomBackgroundDict)));
+
+  // Background color should update.
+  instant_service_->UpdateCustomBackgroundColorAsync(
+      kUrl, image, image_fetcher::RequestMetadata());
+  thread_bundle()->RunUntilIdle();
+  EXPECT_TRUE(CheckBackgroundColor(
+      SK_ColorRED,
+      pref_service->GetDictionary(prefs::kNtpCustomBackgroundDict)));
+}
diff --git a/chrome/browser/search/instant_unittest_base.cc b/chrome/browser/search/instant_unittest_base.cc
index 9c2df763..69f1e02 100644
--- a/chrome/browser/search/instant_unittest_base.cc
+++ b/chrome/browser/search/instant_unittest_base.cc
@@ -20,6 +20,7 @@
 #include "chrome/test/base/search_test_utils.h"
 #include "components/google/core/browser/google_pref_names.h"
 #include "components/google/core/browser/google_url_tracker.h"
+#include "components/image_fetcher/core/mock_image_fetcher.h"
 #include "components/search/search.h"
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_service.h"
@@ -42,6 +43,9 @@
   UIThreadSearchTermsData::SetGoogleBaseURL("https://www.google.com/");
   SetUserSelectedDefaultSearchProvider("{google:baseURL}");
   instant_service_ = InstantServiceFactory::GetForProfile(profile());
+
+  instant_service_->SetImageFetcherForTesting(
+      new testing::NiceMock<image_fetcher::MockImageFetcher>());
 }
 
 void InstantUnitTestBase::TearDown() {
diff --git a/chrome/browser/ui/search/local_ntp_backgrounds_browsertest.cc b/chrome/browser/ui/search/local_ntp_backgrounds_browsertest.cc
index 4fdcc11..b35c9ed 100644
--- a/chrome/browser/ui/search/local_ntp_backgrounds_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_backgrounds_browsertest.cc
@@ -21,15 +21,27 @@
 #include "chrome/common/search/instant_types.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "components/image_fetcher/core/mock_image_fetcher.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/extension_registry.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace {
-
 using LocalNTPCustomBackgroundsTest = InProcessBrowserTest;
 
+class TestInstantService {
+ public:
+  explicit TestInstantService(Profile* profile) {
+    instant_service = InstantServiceFactory::GetForProfile(profile);
+    instant_service->SetImageFetcherForTesting(
+        new testing::NiceMock<image_fetcher::MockImageFetcher>());
+  }
+  InstantService* get_instant_service() { return instant_service; }
+
+ private:
+  InstantService* instant_service;
+};
+
 IN_PROC_BROWSER_TEST_F(LocalNTPCustomBackgroundsTest,
                        EmbeddedSearchAPIEndToEnd) {
   content::WebContents* active_tab =
@@ -41,9 +53,9 @@
   local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
 
   // Check that a URL with no attributions can be set.
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(browser()->profile());
-  instant_service->AddValidBackdropUrlForTesting(GURL("https://www.test.com/"));
+  TestInstantService test_instant_service(browser()->profile());
+  test_instant_service.get_instant_service()->AddValidBackdropUrlForTesting(
+      GURL("https://www.test.com/"));
   EXPECT_TRUE(content::ExecuteScript(active_tab,
                                      "window.chrome.embeddedSearch.newTabPage."
                                      "setBackgroundURL('https://www.test.com/"
@@ -76,9 +88,9 @@
   local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
 
   // Set a custom background attribution via the EmbeddedSearch API.
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(browser()->profile());
-  instant_service->AddValidBackdropUrlForTesting(GURL("https://www.test.com/"));
+  TestInstantService test_instant_service(browser()->profile());
+  test_instant_service.get_instant_service()->AddValidBackdropUrlForTesting(
+      GURL("https://www.test.com/"));
   EXPECT_TRUE(content::ExecuteScript(active_tab,
                                      "window.chrome.embeddedSearch.newTabPage."
                                      "setBackgroundURLWithAttributions('https:/"
@@ -120,9 +132,8 @@
   local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
 
   // Set a custom background image via the EmbeddedSearch API.
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(browser()->profile());
-  instant_service->AddValidBackdropUrlForTesting(
+  TestInstantService test_instant_service(browser()->profile());
+  test_instant_service.get_instant_service()->AddValidBackdropUrlForTesting(
       GURL("chrome-search://local-ntp/background1.jpg"));
   EXPECT_TRUE(content::ExecuteScript(
       active_tab,
@@ -166,9 +177,8 @@
   local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
 
   // Set a custom background image via the EmbeddedSearch API.
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(browser()->profile());
-  instant_service->AddValidBackdropUrlForTesting(
+  TestInstantService test_instant_service(browser()->profile());
+  test_instant_service.get_instant_service()->AddValidBackdropUrlForTesting(
       GURL("chrome-search://local-ntp/background1.jpg"));
   ASSERT_TRUE(content::ExecuteScript(
       active_tab,
@@ -268,9 +278,9 @@
   local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
 
   // Set a custom background attribution via the EmbeddedSearch API.
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(profile());
-  instant_service->AddValidBackdropUrlForTesting(GURL("https://www.test.com/"));
+  TestInstantService test_instant_service(browser()->profile());
+  test_instant_service.get_instant_service()->AddValidBackdropUrlForTesting(
+      GURL("https://www.test.com/"));
   ASSERT_TRUE(content::ExecuteScript(active_tab,
                                      "window.chrome.embeddedSearch.newTabPage."
                                      "setBackgroundURLWithAttributions('https:/"
@@ -327,9 +337,8 @@
   local_ntp_test_utils::NavigateToNTPAndWaitUntilLoaded(browser());
 
   // Set a custom background image via the EmbeddedSearch API.
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(profile());
-  instant_service->AddValidBackdropUrlForTesting(
+  TestInstantService test_instant_service(browser()->profile());
+  test_instant_service.get_instant_service()->AddValidBackdropUrlForTesting(
       GURL("chrome-search://local-ntp/background1.jpg"));
   ASSERT_TRUE(content::ExecuteScript(
       active_tab,
@@ -392,9 +401,8 @@
   EXPECT_TRUE(result);
 
   // Set a custom background image via the EmbeddedSearch API.
-  InstantService* instant_service =
-      InstantServiceFactory::GetForProfile(profile());
-  instant_service->AddValidBackdropUrlForTesting(
+  TestInstantService test_instant_service(browser()->profile());
+  test_instant_service.get_instant_service()->AddValidBackdropUrlForTesting(
       GURL("chrome-search://local-ntp/background1.jpg"));
   ASSERT_TRUE(content::ExecuteScript(
       active_tab,
@@ -440,6 +448,8 @@
         InstantServiceFactory::GetForProfile(browser()->profile());
     theme()->SetDarkMode(true);
     instant_service->SetDarkModeThemeForTesting(theme());
+    instant_service->SetImageFetcherForTesting(
+        new testing::NiceMock<image_fetcher::MockImageFetcher>());
   }
 
   InstantService* instant_service;
@@ -534,5 +544,3 @@
   EXPECT_TRUE(GetIsDarkModeApplied(active_tab));
   EXPECT_TRUE(GetIsLightChipsApplied(active_tab));
 }
-
-}  // namespace
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index bd13b26..a9e8e21e 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -157,6 +157,7 @@
  <item id="network_location_request" hash_code="96590038" type="2" content_hash_code="80741011" os_list="linux,windows" semantics_fields="2,3,4,5" policy_fields="-1" file_path="services/device/geolocation/network_location_request.cc"/>
  <item id="network_time_component" hash_code="46188932" type="0" content_hash_code="28051857" os_list="linux,windows" file_path="components/network_time/network_time_tracker.cc"/>
  <item id="ntp_contextual_suggestions_fetch" hash_code="95711309" type="0" deprecated="2019-04-18" content_hash_code="107035434" file_path=""/>
+ <item id="ntp_custom_background" hash_code="92125886" type="0" content_hash_code="61176452" os_list="linux,windows" file_path="chrome/browser/search/instant_service.cc"/>
  <item id="ntp_custom_link_checker_request" hash_code="78408551" type="0" deprecated="2018-10-26" content_hash_code="13407730" file_path=""/>
  <item id="ntp_icon_source" hash_code="29197139" type="0" content_hash_code="16399294" os_list="linux,windows" file_path="chrome/browser/search/ntp_icon_source.cc"/>
  <item id="ntp_snippets_fetch" hash_code="15418154" type="0" content_hash_code="10078959" os_list="linux,windows" file_path="components/ntp_snippets/remote/json_request.cc"/>