[ntp] OneGoogleBar AsyncBarParts integration

Bug: 373569279
Change-Id: I9274845246e99d2b26b2a4e71381f49ec2e6ac73
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6177840
Commit-Queue: Roman Arora <romanarora@chromium.org>
Reviewed-by: Riley Tatum <rtatum@google.com>
Cr-Commit-Position: refs/heads/main@{#1407391}
diff --git a/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.cc b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.cc
index 635ef8d..c7217a50 100644
--- a/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.cc
+++ b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.cc
@@ -18,6 +18,7 @@
 #include "chrome/common/chrome_content_client.h"
 #include "chrome/common/webui_url_constants.h"
 #include "components/google/core/common/google_util.h"
+#include "components/search/ntp_features.h"
 #include "components/signin/public/identity_manager/tribool.h"
 #include "components/variations/net/variations_http_headers.h"
 #include "google_apis/gaia/gaia_id.h"
@@ -94,7 +95,8 @@
 
 }  // namespace safe_html
 
-std::optional<OneGoogleBarData> JsonToOGBData(const base::Value& value) {
+std::optional<OneGoogleBarData> JsonToOGBData(const base::Value& value,
+                                              bool expect_async_bar_parts) {
   if (!value.is_dict()) {
     DVLOG(1) << "Parse error: top-level dictionary not found";
     return std::nullopt;
@@ -113,16 +115,19 @@
     language_code = *maybe_language;
   }
 
-  const base::Value::Dict* one_google_bar = update->FindDict("ogb");
+  OneGoogleBarData result;
+  result.language_code = language_code;
+
+  const base::Value::Dict* one_google_bar =
+      update->FindDict(expect_async_bar_parts ? "ogb_parts" : "ogb");
   if (!one_google_bar) {
     DVLOG(1) << "Parse error: no ogb";
     return std::nullopt;
   }
 
-  OneGoogleBarData result;
-  result.language_code = language_code;
-
-  if (!safe_html::GetHtml(*one_google_bar, "html", &result.bar_html)) {
+  if (!safe_html::GetHtml(*one_google_bar,
+                          expect_async_bar_parts ? "right_html" : "html",
+                          &result.bar_html)) {
     DVLOG(1) << "Parse error: no html";
     return std::nullopt;
   }
@@ -284,8 +289,9 @@
     bool account_consistency_mirror_required)
     : url_loader_factory_(url_loader_factory),
       application_locale_(application_locale),
-      account_consistency_mirror_required_(
-          account_consistency_mirror_required) {}
+      account_consistency_mirror_required_(account_consistency_mirror_required),
+      async_bar_parts_(base::FeatureList::IsEnabled(
+          ntp_features::kNtpOneGoogleBarAsyncBarParts)) {}
 
 OneGoogleBarLoaderImpl::~OneGoogleBarLoaderImpl() = default;
 
@@ -337,6 +343,9 @@
   if (additional_query_params_.find("&async=") == std::string::npos) {
     query += "&async=fixed:0";
   }
+  if (async_bar_parts_) {
+    query += "&expflags=OgbModule__use_async_bar_parts:true";
+  }
   if (query.at(0) == '&') {
     query = query.substr(1);
   }
@@ -380,7 +389,8 @@
     return;
   }
 
-  std::optional<OneGoogleBarData> data = JsonToOGBData(*result);
+  std::optional<OneGoogleBarData> data =
+      JsonToOGBData(*result, async_bar_parts_);
   Respond(data.has_value() ? Status::OK : Status::FATAL_ERROR, data);
 }
 
diff --git a/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.h b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.h
index 06aeb257..39fd6f24 100644
--- a/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.h
+++ b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl.h
@@ -54,6 +54,7 @@
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   const std::string application_locale_;
   const bool account_consistency_mirror_required_;
+  const bool async_bar_parts_;
 
   std::vector<OneGoogleCallback> callbacks_;
   std::unique_ptr<AuthenticatedURLLoader> pending_request_;
diff --git a/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl_unittest.cc b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl_unittest.cc
index e5f2efe..0eaf0be1 100644
--- a/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl_unittest.cc
+++ b/chrome/browser/new_tab_page/one_google_bar/one_google_bar_loader_impl_unittest.cc
@@ -12,11 +12,13 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
 #include "base/test/mock_callback.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_data.h"
+#include "components/search/ntp_features.h"
 #include "components/signin/core/browser/signin_header_helper.h"
 #include "components/variations/scoped_variations_ids_provider.h"
 #include "content/public/test/browser_task_environment.h"
@@ -50,6 +52,11 @@
   "page_hooks": {}
 }}})json";
 
+const char kMinimalBarPartsValidResponse[] = R"json({"update": { "ogb_parts": {
+  "right_html": { "private_do_not_access_or_else_safe_html_wrapped_value": "" },
+  "page_hooks": {}
+}}})json";
+
 // Returns the value of the "enable_account_consistency" parameter in the
 // "X-ChromeConnected" header. The header is expected to be in the format:
 //    param1=value1,param2=value2,[...],paramN=valueN
@@ -97,6 +104,10 @@
     }
 #endif
 
+    InitOneGoogleBarLoader();
+  }
+
+  void InitOneGoogleBarLoader() {
     one_google_bar_loader_ = std::make_unique<OneGoogleBarLoaderImpl>(
         test_shared_loader_factory_, kApplicationLocale,
         account_consistency_mirror_required_);
@@ -198,6 +209,26 @@
   EXPECT_TRUE(data.has_value());
 }
 
+TEST_F(OneGoogleBarLoaderImplTest, BarPartsRequestReturns) {
+  base::test::ScopedFeatureList features;
+  features.InitWithFeatures(
+      /*enabled_features=*/{ntp_features::kNtpOneGoogleBarAsyncBarParts},
+      /*disabled_features=*/{});
+  InitOneGoogleBarLoader();
+  SetUpResponseWithData(kMinimalBarPartsValidResponse);
+
+  base::MockCallback<OneGoogleBarLoader::OneGoogleCallback> callback;
+  one_google_bar_loader()->Load(callback.Get());
+
+  std::optional<OneGoogleBarData> data;
+  base::RunLoop loop;
+  EXPECT_CALL(callback, Run(OneGoogleBarLoader::Status::OK, _))
+      .WillOnce(DoAll(SaveArg<1>(&data), Quit(&loop)));
+  loop.Run();
+
+  EXPECT_TRUE(data.has_value());
+}
+
 TEST_F(OneGoogleBarLoaderImplTest, HandlesResponsePreamble) {
   // The reponse may contain a ")]}'" prefix. The loader should ignore that
   // during parsing.
diff --git a/chrome/browser/resources/new_tab_page/untrusted/one_google_bar.html b/chrome/browser/resources/new_tab_page/untrusted/one_google_bar.html
index 3a59760..38213ad2 100644
--- a/chrome/browser/resources/new_tab_page/untrusted/one_google_bar.html
+++ b/chrome/browser/resources/new_tab_page/untrusted/one_google_bar.html
@@ -23,6 +23,7 @@
 
       $i18nRaw{inHeadStyle}
     </style>
+    <script>$i18nRaw{varsHeadScript}</script>
     <script>$i18nRaw{inHeadScript}</script>
   </head>
   <body>
diff --git a/chrome/browser/resources/new_tab_page/untrusted/one_google_bar.ts b/chrome/browser/resources/new_tab_page/untrusted/one_google_bar.ts
index 9c1eea5..3a34f07 100644
--- a/chrome/browser/resources/new_tab_page/untrusted/one_google_bar.ts
+++ b/chrome/browser/resources/new_tab_page/untrusted/one_google_bar.ts
@@ -4,6 +4,8 @@
 
 type MessageType = 'overlaysUpdated'|'click'|'loaded';
 
+declare let abp: boolean;
+
 /**
  * The following |messageType|'s are sent to the parent frame:
  *  - loaded: sent on initial load.
@@ -26,10 +28,14 @@
   setForegroundStyle(style: number): void;
 }
 
+interface AsyncBar {
+  setDarkMode(matches: boolean): void;
+}
+
 const oneGoogleBarApi = (() => {
   type IndexableApi = Record<string, Function>;
   interface Gbar {
-    gbar?: {a: Record<string, () => IndexableApi>};
+    gbar?: {a: Record<string, () => IndexableApi>, P: () => void};
   }
 
   async function callApi(
@@ -42,6 +48,17 @@
     return api[fnName]!.apply(api, args);
   }
 
+  async function callAsyncBarApi(
+      fnName: string, ...args: any[]): Promise<unknown> {
+    const {gbar} = window as Window & Gbar;
+    if (!gbar) {
+      return;
+    }
+
+    const barApi = new (gbar.P as any)();
+    return barApi[fnName]!.apply(barApi, args);
+  }
+
   interface Definition {
     name: string;
     apiName: string;
@@ -67,14 +84,25 @@
     return topLevelApi;
   }, {} as {bar: Bar});
 
+  const asyncBar: AsyncBar =
+      [['setDarkMode', 'pp']].reduce((bar: any, [name, fnName]) => {
+        bar[name!] = callAsyncBarApi.bind(null, fnName!);
+        return bar;
+      }, {} as Bar);
+
   async function updateDarkMode(): Promise<void> {
-    await api.bar.setDarkMode(
-        window.matchMedia('(prefers-color-scheme: dark)').matches);
-    // |setDarkMode(toggle)| updates the background color and foreground style.
-    // The background color should always be 'transparent'.
-    api.bar.setBackgroundColor('transparent');
-    // The foreground style is set based on NTP theme and not dark mode.
-    api.bar.setForegroundStyle(foregroundLight ? 1 : 0);
+    if (abp) {
+      await asyncBar.setDarkMode(
+          window.matchMedia('(prefers-color-scheme: dark)').matches);
+    } else {
+      await api.bar.setDarkMode(
+          window.matchMedia('(prefers-color-scheme: dark)').matches);
+      // |setDarkMode(toggle)| updates the background color and foreground
+      // style. The background color should always be 'transparent'.
+      api.bar.setBackgroundColor('transparent');
+      // The foreground style is set based on NTP theme and not dark mode.
+      api.bar.setForegroundStyle(foregroundLight ? 1 : 0);
+    }
   }
 
   let foregroundLight: boolean = false;
@@ -85,7 +113,9 @@
      * the background.
      */
     setForegroundLight: (enabled: boolean) => {
-      if (foregroundLight !== enabled) {
+      if (abp) {
+        asyncBar.setDarkMode(enabled);
+      } else if (foregroundLight !== enabled) {
         foregroundLight = enabled;
         api.bar.setForegroundStyle(foregroundLight ? 1 : 0);
       }
diff --git a/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc b/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc
index c0bf46ec..2f5b125 100644
--- a/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc
+++ b/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc
@@ -248,6 +248,11 @@
     ui::TemplateReplacements replacements;
     replacements["textdirection"] = base::i18n::IsRTL() ? "rtl" : "ltr";
     replacements["barHtml"] = data->bar_html;
+    replacements["varsHeadScript"] = base::StringPrintf(
+        "let abp = %s;", base::FeatureList::IsEnabled(
+                             ntp_features::kNtpOneGoogleBarAsyncBarParts)
+                             ? "true"
+                             : "false");
     replacements["inHeadScript"] = data->in_head_script;
     replacements["inHeadStyle"] = data->in_head_style;
     replacements["afterBarScript"] = data->after_bar_script;
diff --git a/components/search/ntp_features.cc b/components/search/ntp_features.cc
index 56cf126..10c9a41 100644
--- a/components/search/ntp_features.cc
+++ b/components/search/ntp_features.cc
@@ -283,6 +283,11 @@
              "NtpMicrosoftAuthenticationModule",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// If enabled, the OGB loader will request for the async bar parts payload type.
+BASE_FEATURE(kNtpOneGoogleBarAsyncBarParts,
+             "NtpOneGoogleBarAsyncBarParts",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 const char kNtpModuleIgnoredCriteriaThreshold[] =
     "NtpModuleIgnoredCriteriaThreshold";
 const char kNtpModuleIgnoredHaTSDelayTimeParam[] =
diff --git a/components/search/ntp_features.h b/components/search/ntp_features.h
index 187fcc0..b11017e 100644
--- a/components/search/ntp_features.h
+++ b/components/search/ntp_features.h
@@ -25,8 +25,6 @@
 BASE_DECLARE_FEATURE(kCustomizeChromeWallpaperSearch);
 BASE_DECLARE_FEATURE(kCustomizeChromeWallpaperSearchButton);
 BASE_DECLARE_FEATURE(kCustomizeChromeWallpaperSearchInspirationCard);
-BASE_DECLARE_FEATURE(kIframeOneGoogleBar);
-BASE_DECLARE_FEATURE(kOneGoogleBarModalOverlays);
 BASE_DECLARE_FEATURE(kRealboxCr23Theming);
 BASE_DECLARE_FEATURE(kRealboxMatchOmniboxTheme);
 BASE_DECLARE_FEATURE(kRealboxMatchSearchboxTheme);
@@ -77,6 +75,7 @@
 BASE_DECLARE_FEATURE(kNtpWallpaperSearchButtonAnimationShownThreshold);
 BASE_DECLARE_FEATURE(kNtpMobilePromo);
 BASE_DECLARE_FEATURE(kNtpMicrosoftAuthenticationModule);
+BASE_DECLARE_FEATURE(kNtpOneGoogleBarAsyncBarParts);
 
 // Parameter for controlling the luminosity difference for NTP elements on light
 // backgrounds.