WebUI NTP: inline the OneGoogleBar on chrome://new-tab-page

The plan is to inline the OneGoogleBar for M84. The OneGoogleBar and
NTP teams will work towards iframing the OGBOneGoogleBarfor M85.

To support inlining, chrome://new-tab-page needs to be able to make
network request, inline HTML/CSS/javascript and iframe *.google.com (for
the app launcher and profile picture picker).

The feature flag ntp-iframe-one-google-bar can be enabled on
chrome://flags to load the OneGoogleBar in an iframe. When disabled, the
OneGoogleBar is inlined which is the default value.

Bug: 1039913
Change-Id: Ic78c79c761b41ea46f3c96876f03136dc6d44748
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2171712
Reviewed-by: Lei Zhang <[email protected]>
Reviewed-by: Tibor Goldschwendt <[email protected]>
Reviewed-by: Alex Gough <[email protected]>
Commit-Queue: Esmael Elmoslimany <[email protected]>
Cr-Commit-Position: refs/heads/master@{#764837}
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 37fc262..e607fb0 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3600,6 +3600,10 @@
      flag_descriptions::kNtpDismissPromosDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(ntp_features::kDismissPromos)},
 
+    {"ntp-iframe-one-google-bar", flag_descriptions::kNtpIframeOneGoogleBarName,
+     flag_descriptions::kNtpIframeOneGoogleBarDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(ntp_features::kIframeOneGoogleBar)},
+
     {"ntp-realbox", flag_descriptions::kNtpRealboxName,
      flag_descriptions::kNtpRealboxDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(ntp_features::kRealbox)},
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 75492d1e..c13458d 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3002,27 +3002,32 @@
   },
   {
     "name": "ntp-confirm-suggestion-removals",
-    "owners": ["dbeam"],
+    "owners": ["aee", "mahmadi", "tiborg"],
     "expiry_milestone": 82
   },
   {
     "name": "ntp-disable-initial-most-visited-fade-in",
-    "owners": ["dbeam"],
+    "owners": ["aee", "mahmadi", "tiborg"],
     "expiry_milestone": 79
   },
   {
     "name": "ntp-dismiss-promos",
-    "owners": ["dbeam"],
+    "owners": ["aee", "mahmadi", "tiborg"],
     "expiry_milestone": 82
   },
   {
+    "name": "ntp-iframe-one-google-bar",
+    "owners": ["aee", "mahmadi", "tiborg"],
+    "expiry_milestone": 86
+  },
+  {
     "name": "ntp-realbox",
-    "owners": ["dbeam", "mahmadi"],
+    "owners": ["aee", "mahmadi", "tiborg"],
     "expiry_milestone": 85
   },
   {
     "name": "ntp-realbox-match-omnibox-theme",
-    "owners": ["dbeam", "mahmadi"],
+    "owners": ["aee", "mahmadi", "tiborg"],
     "expiry_milestone": 85
   },
   {
@@ -3312,7 +3317,7 @@
   },
   {
     "name": "omnibox-zero-suggestions-on-ntp-realbox",
-    "owners": [ "dbeam", "mahmadi" ],
+    "owners": [ "mahmadi" ],
     "expiry_milestone": 85
   },
   {
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 8487f189..3c69605 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2798,6 +2798,11 @@
     "Enables a UI to persistently dismiss [non-emergency] promos on the "
     "bottom/middle of the New Tab Page";
 
+const char kNtpIframeOneGoogleBarName[] = "Load OneGoogleBar in an iframe";
+const char kNtpIframeOneGoogleBarDescription[] =
+    "Enables loading the OneGoogleBar in an iframe. Otherwise, the "
+    "OneGoogleBar is loaded inline on chrome://new-tab-page.";
+
 const char kNtpRealboxName[] = "Real search box in New Tab Page";
 const char kNtpRealboxDescription[] =
     "Enables a search box in the middle of the NTP that will accept input "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 9911fb58..b04e52f7 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1609,6 +1609,9 @@
 extern const char kNtpDismissPromosName[];
 extern const char kNtpDismissPromosDescription[];
 
+extern const char kNtpIframeOneGoogleBarName[];
+extern const char kNtpIframeOneGoogleBarDescription[];
+
 extern const char kNtpRealboxName[];
 extern const char kNtpRealboxDescription[];
 
diff --git a/chrome/browser/resources/new_tab_page/app.html b/chrome/browser/resources/new_tab_page/app.html
index 08d3b7c9..18d84d22 100644
--- a/chrome/browser/resources/new_tab_page/app.html
+++ b/chrome/browser/resources/new_tab_page/app.html
@@ -22,6 +22,7 @@
   #backgroundGradient {
     height: 100%;
     position: fixed;
+    top: 0;
     width: 100%;
   }
 
@@ -45,14 +46,17 @@
     align-items: center;
     display: flex;
     flex-direction: column;
-    min-height: calc(100vh - var(--ntp-one-google-bar-height));
-    padding-top: var(--ntp-one-google-bar-height);
+    height: calc(100vh - var(--ntp-one-google-bar-height));
     position: relative;
   }
 
+  :host([iframe-one-google-bar-enabled_]) #content {
+    padding-top: var(--ntp-one-google-bar-height);
+  }
+
   #logo {
     margin-bottom: 8px;
-    z-index: 1;  /* Needed so it layers on top of OGB. */
+    z-index: 1;  /* Needed so it layers on top of OneGoogleBar. */
   }
 
   ntp-fakebox,
@@ -220,7 +224,7 @@
         --ntp-theme-shortcut-background-color:
               [[rgbaOrInherit_(theme_.shortcutBackgroundColor)]];
         --ntp-logo-color: [[rgbaOrInherit_(logoColor_)]];">
-  <dom-if if="[[lazyRender_]]">
+  <dom-if if="[[showIframedOneGoogleBar_]]">
     <template>
       <ntp-untrusted-iframe id="oneGoogleBar" path="one-google-bar"
           hidden$="[[!oneGoogleBarLoaded_]]">
diff --git a/chrome/browser/resources/new_tab_page/app.js b/chrome/browser/resources/new_tab_page/app.js
index 305088e3..e8fe00af 100644
--- a/chrome/browser/resources/new_tab_page/app.js
+++ b/chrome/browser/resources/new_tab_page/app.js
@@ -35,6 +35,13 @@
   static get properties() {
     return {
       /** @private */
+      iframeOneGoogleBarEnabled_: {
+        type: Boolean,
+        value: () => loadTimeData.getBoolean('iframeOneGoogleBarEnabled'),
+        reflectToAttribute: true,
+      },
+
+      /** @private */
       oneGoogleBarLoaded_: {
         observer: 'oneGoogleBarLoadedChange_',
         type: Boolean,
@@ -50,6 +57,14 @@
       },
 
       /** @private */
+      showIframedOneGoogleBar_: {
+        type: Boolean,
+        value: false,
+        computed: `computeShowIframedOneGoogleBar_(iframeOneGoogleBarEnabled_,
+            lazyRender_)`,
+      },
+
+      /** @private */
       promoLoaded_: {
         type: Boolean,
         value: false,
@@ -143,6 +158,7 @@
     this.setThemeListenerId_ = null;
     /** @private {!EventTracker} */
     this.eventTracker_ = new EventTracker();
+    this.loadOneGoogleBar_();
   }
 
   /** @override */
@@ -204,15 +220,82 @@
     }
   }
 
+  /**
+   * @return {!Promise}
+   * @private
+   */
+  async loadOneGoogleBar_() {
+    if (this.iframeOneGoogleBarEnabled_) {
+      const oneGoogleBar = document.querySelector('#oneGoogleBar');
+      oneGoogleBar.remove();
+      return;
+    }
+
+    const {parts} =
+        await BrowserProxy.getInstance().handler.getOneGoogleBarParts();
+    if (!parts) {
+      return;
+    }
+
+    const inHeadStyle = document.createElement('style');
+    inHeadStyle.type = 'text/css';
+    inHeadStyle.appendChild(document.createTextNode(parts.inHeadStyle));
+    document.head.appendChild(inHeadStyle);
+
+    const inHeadScript = document.createElement('script');
+    inHeadScript.type = 'text/javascript';
+    inHeadScript.appendChild(document.createTextNode(parts.inHeadScript));
+    document.head.appendChild(inHeadScript);
+
+    this.oneGoogleBarLoaded_ = true;
+    const oneGoogleBar = document.querySelector('#oneGoogleBar');
+    oneGoogleBar.innerHTML = parts.barHtml;
+
+    const afterBarScript = document.createElement('script');
+    afterBarScript.type = 'text/javascript';
+    afterBarScript.appendChild(document.createTextNode(parts.afterBarScript));
+    oneGoogleBar.parentNode.insertBefore(
+        afterBarScript, oneGoogleBar.nextSibling);
+
+    document.querySelector('#oneGoogleBarEndOfBody').innerHTML =
+        parts.endOfBodyHtml;
+
+    const endOfBodyScript = document.createElement('script');
+    endOfBodyScript.type = 'text/javascript';
+    endOfBodyScript.appendChild(document.createTextNode(parts.endOfBodyScript));
+    document.body.appendChild(endOfBodyScript);
+  }
+
   /** @private */
-  onOneGoogleBarDarkThemeEnabledChange_() {
+  async onOneGoogleBarDarkThemeEnabledChange_() {
     if (!this.oneGoogleBarLoaded_) {
       return;
     }
-    $$(this, '#oneGoogleBar').postMessage({
-      type: 'enableDarkTheme',
-      enabled: this.oneGoogleBarDarkThemeEnabled_,
-    });
+    if (this.iframeOneGoogleBarEnabled_) {
+      $$(this, '#oneGoogleBar').postMessage({
+        type: 'enableDarkTheme',
+        enabled: this.oneGoogleBarDarkThemeEnabled_,
+      });
+      return;
+    }
+    const {gbar} = /** @type {{gbar}} */ (window);
+    if (!gbar) {
+      return;
+    }
+    const oneGoogleBar =
+        await /** @type {!{a: {bf: function(): !Promise<{pc: !Function}>}}} */ (
+            gbar)
+            .a.bf();
+    oneGoogleBar.pc.call(
+        oneGoogleBar, this.oneGoogleBarDarkThemeEnabled_ ? 1 : 0);
+  }
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  computeShowIframedOneGoogleBar_() {
+    return this.iframeOneGoogleBarEnabled_ && this.lazyRender_;
   }
 
   /**
@@ -457,10 +540,9 @@
 
   /** @private */
   oneGoogleBarLoadedChange_() {
-    if (!this.oneGoogleBarLoaded_) {
-      return;
+    if (this.oneGoogleBarLoaded_ && this.iframeOneGoogleBarEnabled_) {
+      this.setupShortcutDragDropOneGoogleBarWorkaround_();
     }
-    this.setupShortcutDragDropOgbWorkaround_();
   }
 
   /**
@@ -475,7 +557,7 @@
    * fires if the pointer has left ntp-most-visited.
    * @private
    */
-  setupShortcutDragDropOgbWorkaround_() {
+  setupShortcutDragDropOneGoogleBarWorkaround_() {
     const iframe = $$(this, '#oneGoogleBar');
     let resetAtDragEnd = false;
     let dragging = false;
diff --git a/chrome/browser/resources/new_tab_page/new_tab_page.html b/chrome/browser/resources/new_tab_page/new_tab_page.html
index bb691bde..5ea55362 100644
--- a/chrome/browser/resources/new_tab_page/new_tab_page.html
+++ b/chrome/browser/resources/new_tab_page/new_tab_page.html
@@ -8,12 +8,18 @@
         background: $i18n{backgroundColor};
         margin: 0;
       }
+
+      #oneGoogleBar {
+        height: 56px;
+      }
     </style>
   </head>
   <body>
+    <div id="oneGoogleBar"></div>
     <ntp-app></ntp-app>
     <script type="module" src="new_tab_page.js"></script>
     <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
     <link rel="stylesheet" href="shared_vars.css">
+    <div id="oneGoogleBarEndOfBody"></div>
   </body>
 </html>
diff --git a/chrome/browser/search/ntp_features.cc b/chrome/browser/search/ntp_features.cc
index 97e9835e..08164c1 100644
--- a/chrome/browser/search/ntp_features.cc
+++ b/chrome/browser/search/ntp_features.cc
@@ -22,6 +22,10 @@
 const base::Feature kDismissPromos{"DismissNtpPromos",
                                    base::FEATURE_DISABLED_BY_DEFAULT};
 
+// If enabled, the OneGooleBar is loaded in an iframe. Otherwise, it is inlined.
+const base::Feature kIframeOneGoogleBar{"IframeOneGoogleBar",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Depends on kRealbox being enabled. If enabled, the NTP "realbox" will be
 // themed like the omnibox (same background/text/selected/hover colors).
 const base::Feature kRealboxMatchOmniboxTheme{
diff --git a/chrome/browser/search/ntp_features.h b/chrome/browser/search/ntp_features.h
index 5131b407..5573a11 100644
--- a/chrome/browser/search/ntp_features.h
+++ b/chrome/browser/search/ntp_features.h
@@ -14,6 +14,7 @@
 
 extern const base::Feature kConfirmSuggestionRemovals;
 extern const base::Feature kDismissPromos;
+extern const base::Feature kIframeOneGoogleBar;
 extern const base::Feature kRealboxMatchOmniboxTheme;
 extern const base::Feature kRealboxUseGoogleGIcon;
 extern const base::Feature kWebUI;
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 7ca445f9..6bef12e7 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -956,7 +956,9 @@
       // https://crbug.com/831813
       origin.host() == chrome::kChromeUIInspectHost ||
       // https://crbug.com/859345
-      origin.host() == chrome::kChromeUIDownloadsHost;
+      origin.host() == chrome::kChromeUIDownloadsHost ||
+      // TODO(crbug.com/1076506): remove when change to iframed OneGoogleBar.
+      origin.host() == chrome::kChromeUINewTabPageHost;
 }
 
 ChromeWebUIControllerFactory::ChromeWebUIControllerFactory() = default;
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom b/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
index b1aff4a..0308745 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
@@ -11,6 +11,15 @@
 import "mojo/public/mojom/base/string16.mojom";
 import "chrome/common/search/omnibox.mojom";
 
+struct OneGoogleBarParts {
+  string bar_html;
+  string in_head_script;
+  string in_head_style;
+  string after_bar_script;
+  string end_of_body_html;
+  string end_of_body_script;
+};
+
 struct MostVisitedTile {
   string title;
   mojo_base.mojom.TextDirection title_direction;
@@ -256,6 +265,8 @@
   GetDoodle() => (Doodle? doodle);
   // Choose custom background from local file system.
   ChooseLocalCustomBackground() => (bool success);
+  // Get the OneGoogleBarParts to be inlined in the NTP.
+  GetOneGoogleBarParts() => (OneGoogleBarParts? parts);
 
   // ======= METRICS =======
   // Logs that |tiles| were displayed / updated at |time|. The first instance of
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
index e759407..360de19 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/search/chrome_colors/chrome_colors_service.h"
 #include "chrome/browser/search/instant_service.h"
 #include "chrome/browser/search/instant_service_factory.h"
+#include "chrome/browser/search/one_google_bar/one_google_bar_service_factory.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/search_provider_logos/logo_service_factory.h"
 #include "chrome/browser/ui/bookmarks/bookmark_stats.h"
@@ -177,6 +178,8 @@
       ntp_background_service_(
           NtpBackgroundServiceFactory::GetForProfile(profile)),
       logo_service_(LogoServiceFactory::GetForProfile(profile)),
+      one_google_bar_service_(
+          OneGoogleBarServiceFactory::GetForProfile(profile)),
       page_{std::move(pending_page)},
       profile_(profile),
       receiver_{this, std::move(pending_page_handler)},
@@ -197,6 +200,11 @@
   instant_service_->UpdateNtpTheme();
   OmniboxTabHelper::CreateForWebContents(web_contents);
   OmniboxTabHelper::FromWebContents(web_contents_)->AddObserver(this);
+  // |one_google_bar_service_| is null in incognito, or when the feature is
+  // disabled.
+  if (one_google_bar_service_) {
+    one_google_bar_service_observer_.Add(one_google_bar_service_);
+  }
 }
 
 NewTabPageHandler::~NewTabPageHandler() {
@@ -428,6 +436,18 @@
   choose_local_custom_background_callback_ = std::move(callback);
 }
 
+void NewTabPageHandler::GetOneGoogleBarParts(
+    GetOneGoogleBarPartsCallback callback) {
+  if (!one_google_bar_service_) {
+    return;
+  }
+  one_google_bar_parts_callbacks_.push_back(std::move(callback));
+  if (one_google_bar_service_->one_google_bar_data().has_value()) {
+    OnOneGoogleBarDataUpdated();
+  }
+  one_google_bar_service_->Refresh();
+}
+
 void NewTabPageHandler::OnMostVisitedTilesRendered(
     std::vector<new_tab_page::mojom::MostVisitedTilePtr> tiles,
     double time) {
@@ -718,6 +738,31 @@
   }
 }
 
+void NewTabPageHandler::OnOneGoogleBarDataUpdated() {
+  base::Optional<OneGoogleBarData> data =
+      one_google_bar_service_->one_google_bar_data();
+  for (auto& callback : one_google_bar_parts_callbacks_) {
+    if (data.has_value()) {
+      auto parts = new_tab_page::mojom::OneGoogleBarParts::New();
+      parts->bar_html = data->bar_html;
+      parts->in_head_script = data->in_head_script;
+      parts->in_head_style = data->in_head_style;
+      parts->after_bar_script = data->after_bar_script;
+      parts->end_of_body_html = data->end_of_body_html;
+      parts->end_of_body_script = data->end_of_body_script;
+      std::move(callback).Run(std::move(parts));
+    } else {
+      std::move(callback).Run(nullptr);
+    }
+  }
+  one_google_bar_parts_callbacks_.clear();
+}
+
+void NewTabPageHandler::OnOneGoogleBarServiceShuttingDown() {
+  one_google_bar_service_observer_.RemoveAll();
+  one_google_bar_service_ = nullptr;
+}
+
 void NewTabPageHandler::FileSelected(const base::FilePath& path,
                                      int index,
                                      void* params) {
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
index 16df797e..f94f38b 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
@@ -7,9 +7,12 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/scoped_observer.h"
 #include "base/time/time.h"
 #include "chrome/browser/search/background/ntp_background_service_observer.h"
 #include "chrome/browser/search/instant_service_observer.h"
+#include "chrome/browser/search/one_google_bar/one_google_bar_service.h"
+#include "chrome/browser/search/one_google_bar/one_google_bar_service_observer.h"
 #include "chrome/browser/ui/omnibox/omnibox_tab_helper.h"
 #include "chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom.h"
 #include "chrome/common/search/instant_types.h"
@@ -44,6 +47,7 @@
                           public InstantServiceObserver,
                           public NtpBackgroundServiceObserver,
                           public OmniboxTabHelper::Observer,
+                          public OneGoogleBarServiceObserver,
                           public ui::SelectFileDialog::Listener,
                           public AutocompleteController::Observer {
  public:
@@ -90,6 +94,7 @@
   void GetDoodle(GetDoodleCallback callback) override;
   void ChooseLocalCustomBackground(
       ChooseLocalCustomBackgroundCallback callback) override;
+  void GetOneGoogleBarParts(GetOneGoogleBarPartsCallback callback) override;
   void OnMostVisitedTilesRendered(
       std::vector<new_tab_page::mojom::MostVisitedTilePtr> tiles,
       double time) override;
@@ -127,6 +132,10 @@
   void OnOmniboxFocusChanged(OmniboxFocusState state,
                              OmniboxFocusChangeReason reason) override;
 
+  // OneGoogleBarServiceObserver:
+  void OnOneGoogleBarDataUpdated() override;
+  void OnOneGoogleBarServiceShuttingDown() override;
+
   // SelectFileDialog::Listener:
   void FileSelected(const base::FilePath& path,
                     int index,
@@ -158,6 +167,10 @@
   GetBackgroundCollectionsCallback background_collections_callback_;
   std::string images_request_collection_id_;
   GetBackgroundImagesCallback background_images_callback_;
+  std::vector<GetOneGoogleBarPartsCallback> one_google_bar_parts_callbacks_;
+  OneGoogleBarService* one_google_bar_service_;
+  ScopedObserver<OneGoogleBarService, OneGoogleBarServiceObserver>
+      one_google_bar_service_observer_{this};
   mojo::Remote<new_tab_page::mojom::Page> page_;
   Profile* profile_;
   mojo::Receiver<new_tab_page::mojom::PageHandler> receiver_;
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
index 3f9b72f..88987665 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -23,6 +23,7 @@
 #include "chrome/grit/new_tab_page_resources.h"
 #include "chrome/grit/new_tab_page_resources_map.h"
 #include "components/favicon_base/favicon_url_parser.h"
+#include "components/google/core/common/google_util.h"
 #include "components/omnibox/common/omnibox_features.h"
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/url_data_source.h"
@@ -43,8 +44,6 @@
 content::WebUIDataSource* CreateNewTabPageUiHtmlSource(Profile* profile) {
   content::WebUIDataSource* source =
       content::WebUIDataSource::Create(chrome::kChromeUINewTabPageHost);
-  source->OverrideContentSecurityPolicyChildSrc(base::StringPrintf(
-      "frame-src %s;", chrome::kChromeUIUntrustedNewTabPageUrl));
 
   ui::Accelerator undo_accelerator(ui::VKEY_Z, ui::EF_PLATFORM_ACCELERATOR);
   source->AddString("undoDescription", l10n_util::GetStringFUTF16(
@@ -70,6 +69,10 @@
           ? omnibox::kGoogleGIconResourceName
           : omnibox::kSearchIconResourceName);
 
+  source->AddBoolean(
+      "iframeOneGoogleBarEnabled",
+      base::FeatureList::IsEnabled(ntp_features::kIframeOneGoogleBar));
+
   static constexpr webui::LocalizedString kStrings[] = {
       {"doneButton", IDS_DONE},
       {"title", IDS_NEW_TAB_TITLE},
@@ -183,6 +186,21 @@
       source, base::make_span(kNewTabPageResources, kNewTabPageResourcesSize),
       kGeneratedPath, IDR_NEW_TAB_PAGE_NEW_TAB_PAGE_HTML);
 
+  // Allows creating <script> and inlining as well as network requests to
+  // support inlining the OneGoogleBar.
+  // TODO(crbug.com/1076506): remove when changing to iframed OneGoogleBar.
+  // Needs to happen after |webui::SetupWebUIDataSource()| since also overrides
+  // script-src.
+  source->OverrideContentSecurityPolicyScriptSrc(
+      "script-src chrome://resources chrome://test 'self' 'unsafe-inline' "
+      "https:;");
+  // Allow embedding of iframes from the One Google Bar and
+  // chrome-untrusted://new-tab-page for other external content and resources.
+  source->OverrideContentSecurityPolicyChildSrc(
+      base::StringPrintf("child-src https://*.google.com/ %s %s;",
+                         google_util::CommandLineGoogleBaseURL().spec().c_str(),
+                         chrome::kChromeUIUntrustedNewTabPageUrl));
+
   return source;
 }
 
diff --git a/chrome/test/data/webui/new_tab_page/app_test.js b/chrome/test/data/webui/new_tab_page/app_test.js
index b19b1ea..d91d0a2d 100644
--- a/chrome/test/data/webui/new_tab_page/app_test.js
+++ b/chrome/test/data/webui/new_tab_page/app_test.js
@@ -36,6 +36,9 @@
     testProxy.handler.setResultFor('getDoodle', Promise.resolve({
       doodle: null,
     }));
+    testProxy.handler.setResultFor('getOneGoogleBarParts', Promise.resolve({
+      parts: null,
+    }));
     testProxy.setResultMapperFor('matchMedia', () => ({
                                                  addListener() {},
                                                  removeListener() {},
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 2068805..c632c02 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -39436,6 +39436,7 @@
       label="AutofillSaveCreditCardUsesImprovedMessaging:disabled"/>
   <int value="-449465495" label="disable-browser-task-scheduler"/>
   <int value="-444867364" label="Metal:enabled"/>
+  <int value="-442352394" label="IframeOneGoogleBar:disabled"/>
   <int value="-438379844" label="SwapSideVolumeButtonsForOrientation:enabled"/>
   <int value="-436470115" label="TouchpadAndWheelScrollLatching:enabled"/>
   <int value="-435914745" label="ClipboardContentSetting:disabled"/>
@@ -39466,6 +39467,7 @@
   <int value="-400584764" label="ChromeHomeNtpRedesign:enabled"/>
   <int value="-400572959" label="UseDownloadOfflineContentProvider:enabled"/>
   <int value="-400098787" label="QuietNotificationPrompts:enabled"/>
+  <int value="-398922143" label="IframeOneGoogleBar:enabled"/>
   <int value="-398623652" label="CCTTargetTranslateLanguage:enabled"/>
   <int value="-397392156" label="FtpProtocol:disabled"/>
   <int value="-396994784" label="enable-vr-shell"/>