diff --git a/content/web_test/browser/web_test_content_browser_client.cc b/content/web_test/browser/web_test_content_browser_client.cc
index e0f55d75e7f39..4bb1004a8a06d 100644
--- a/content/web_test/browser/web_test_content_browser_client.cc
+++ b/content/web_test/browser/web_test_content_browser_client.cc
@@ -366,6 +366,12 @@ void WebTestContentBrowserClient::ExposeInterfacesToRenderer(
       base::BindRepeating(&WebTestContentBrowserClient::BindWebTestControlHost,
                           base::Unretained(this),
                           render_process_host->GetID()));
+
+  registry->AddInterface(
+      base::BindRepeating(
+          &WebTestContentBrowserClient::BindNonAssociatedWebTestControlHost,
+          base::Unretained(this)),
+      ui_task_runner);
 }
 
 void WebTestContentBrowserClient::BindPermissionAutomation(
@@ -657,6 +663,14 @@ void WebTestContentBrowserClient::BindWebTestControlHost(
         render_process_id, std::move(receiver));
 }
 
+void WebTestContentBrowserClient::BindNonAssociatedWebTestControlHost(
+    mojo::PendingReceiver<mojom::NonAssociatedWebTestControlHost> receiver) {
+  if (WebTestControlHost::Get()) {
+    WebTestControlHost::Get()->BindNonAssociatedWebTestControlHost(
+        std::move(receiver));
+  }
+}
+
 #if BUILDFLAG(IS_WIN)
 bool WebTestContentBrowserClient::PreSpawnChild(
     sandbox::TargetConfig* config,
diff --git a/content/web_test/browser/web_test_content_browser_client.h b/content/web_test/browser/web_test_content_browser_client.h
index e6b2df55b872f..cb46d458e9713 100644
--- a/content/web_test/browser/web_test_content_browser_client.h
+++ b/content/web_test/browser/web_test_content_browser_client.h
@@ -160,6 +160,9 @@ class WebTestContentBrowserClient : public ShellContentBrowserClient {
       int render_process_id,
       mojo::PendingAssociatedReceiver<mojom::WebTestControlHost> receiver);
 
+  void BindNonAssociatedWebTestControlHost(
+      mojo::PendingReceiver<mojom::NonAssociatedWebTestControlHost> receiver);
+
   bool block_popups_ = true;
   bool screen_orientation_changed_ = false;
 
diff --git a/content/web_test/browser/web_test_control_host.cc b/content/web_test/browser/web_test_control_host.cc
index 962a792c08d71..96297e20b0348 100644
--- a/content/web_test/browser/web_test_control_host.cc
+++ b/content/web_test/browser/web_test_control_host.cc
@@ -685,6 +685,7 @@ bool WebTestControlHost::ResetBrowserAfterWebTest() {
   expected_pixel_hash_.clear();
   test_url_ = GURL();
   prefs_ = blink::web_pref::WebPreferences();
+  lcpp_hint_ = absl::nullopt;
   should_override_prefs_ = false;
   WebTestContentBrowserClient::Get()->SetPopupBlockingEnabled(true);
   WebTestContentBrowserClient::Get()->ResetMockClipboardHosts();
@@ -1065,6 +1066,13 @@ void WebTestControlHost::RenderViewDeleted(RenderViewHost* render_view_host) {
   main_window_render_view_hosts_.erase(render_view_host);
 }
 
+void WebTestControlHost::DidStartNavigation(
+    NavigationHandle* navigation_handle) {
+  if (lcpp_hint_) {
+    navigation_handle->SetLCPPNavigationHint(lcpp_hint_.value());
+  }
+}
+
 void WebTestControlHost::ReadyToCommitNavigation(
     NavigationHandle* navigation_handle) {
   NavigationRequest* request = NavigationRequest::From(navigation_handle);
@@ -1783,6 +1791,11 @@ void WebTestControlHost::DisableAutoResize(const gfx::Size& new_size) {
   main_window_->ResizeWebContentForTests(new_size);
 }
 
+void WebTestControlHost::SetLCPPNavigationHint(
+    blink::mojom::LCPCriticalPathPredictorNavigationTimeHintPtr hint) {
+  lcpp_hint_ = *hint.get();
+}
+
 void WebTestControlHost::GoToOffset(int offset) {
   main_window_->GoBackOrForward(offset);
 }
@@ -1844,6 +1857,10 @@ void WebTestControlHost::PrepareRendererForNextWebTest() {
   //
   // Note: this navigation might happen in a new process, depending on the
   // COOP policy of the previous document.
+
+  // Avoid sending LCPP hint on the about:blank navigation.
+  lcpp_hint_ = absl::nullopt;
+
   NavigationController::LoadURLParams params((GURL(kAboutBlankResetWebTest)));
   params.transition_type = ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED);
   params.should_clear_history_list = true;
@@ -2001,6 +2018,11 @@ void WebTestControlHost::BindWebTestControlHostForRenderer(
   receiver_bindings_.Add(this, std::move(receiver), render_process_id);
 }
 
+void WebTestControlHost::BindNonAssociatedWebTestControlHost(
+    mojo::PendingReceiver<mojom::NonAssociatedWebTestControlHost> receiver) {
+  non_associated_receiver_bindings_.Add(this, std::move(receiver));
+}
+
 mojo::AssociatedRemote<mojom::WebTestRenderFrame>&
 WebTestControlHost::GetWebTestRenderFrameRemote(RenderFrameHost* frame) {
   GlobalRenderFrameHostId key(frame->GetProcess()->GetID(),
diff --git a/content/web_test/browser/web_test_control_host.h b/content/web_test/browser/web_test_control_host.h
index b0efbb4fe321a..1876233fc4cc7 100644
--- a/content/web_test/browser/web_test_control_host.h
+++ b/content/web_test/browser/web_test_control_host.h
@@ -38,6 +38,7 @@
 #include "mojo/public/cpp/bindings/associated_receiver_set.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "third_party/blink/public/common/web_preferences/web_preferences.h"
+#include "third_party/blink/public/mojom/lcp_critical_path_predictor/lcp_critical_path_predictor.mojom.h"
 #include "ui/gfx/geometry/size.h"
 
 class SkBitmap;
@@ -114,7 +115,8 @@ class WebTestResultPrinter {
 class WebTestControlHost : public WebContentsObserver,
                            public RenderProcessHostObserver,
                            public GpuDataManagerObserver,
-                           public mojom::WebTestControlHost {
+                           public mojom::WebTestControlHost,
+                           public mojom::NonAssociatedWebTestControlHost {
  public:
   static WebTestControlHost* Get();
 
@@ -153,6 +155,9 @@ class WebTestControlHost : public WebContentsObserver,
       int render_process_id,
       mojo::PendingAssociatedReceiver<mojom::WebTestControlHost> receiver);
 
+  void BindNonAssociatedWebTestControlHost(
+      mojo::PendingReceiver<mojom::NonAssociatedWebTestControlHost> receiver);
+
   const WebTestRuntimeFlags& web_test_runtime_flags() const {
     return web_test_runtime_flags_;
   }
@@ -190,6 +195,7 @@ class WebTestControlHost : public WebContentsObserver,
   void RenderFrameHostChanged(RenderFrameHost* old_host,
                               RenderFrameHost* new_host) override;
   void RenderViewDeleted(RenderViewHost* render_view_host) override;
+  void DidStartNavigation(NavigationHandle* navigation_handle) override;
   void ReadyToCommitNavigation(NavigationHandle* navigation_handle) override;
   void DidFinishNavigation(NavigationHandle* navigation) override;
 
@@ -261,6 +267,9 @@ class WebTestControlHost : public WebContentsObserver,
   void EnableAutoResize(const gfx::Size& min_size,
                         const gfx::Size& max_size) override;
   void DisableAutoResize(const gfx::Size& new_size) override;
+  void SetLCPPNavigationHint(
+      blink::mojom::LCPCriticalPathPredictorNavigationTimeHintPtr hint)
+      override;
 
   void DiscardMainWindow();
   // Closes all windows opened by the test. This is every window but the main
@@ -358,6 +367,14 @@ class WebTestControlHost : public WebContentsObserver,
   bool should_override_prefs_ = false;
   blink::web_pref::WebPreferences prefs_;
 
+  // When populated, simulate a LCPP backend by sending this hint data along
+  // navigations (typically reload of the same page).
+  // This is set by the LCPP web_tests via
+  // NonAssociatedWebTestControlHost::SetLCPPNavigationHint mojom interface.
+  // This is reset before switching to the next test page.
+  absl::optional<blink::mojom::LCPCriticalPathPredictorNavigationTimeHint>
+      lcpp_hint_;
+
   bool crash_when_leak_found_ = false;
   std::unique_ptr<LeakDetector> leak_detector_;
 
@@ -426,6 +443,9 @@ class WebTestControlHost : public WebContentsObserver,
                               int /*render_process_id*/>
       receiver_bindings_;
 
+  mojo::ReceiverSet<mojom::NonAssociatedWebTestControlHost>
+      non_associated_receiver_bindings_;
+
   base::ScopedTempDir writable_directory_for_tests_;
 
   enum class NextPointerLockAction {
diff --git a/content/web_test/common/web_test.mojom b/content/web_test/common/web_test.mojom
index ca2e1d509497b..6b07671be92c3 100644
--- a/content/web_test/common/web_test.mojom
+++ b/content/web_test/common/web_test.mojom
@@ -8,6 +8,7 @@ import "mojo/public/mojom/base/file_path.mojom";
 import "mojo/public/mojom/base/string16.mojom";
 import "mojo/public/mojom/base/values.mojom";
 import "skia/public/mojom/bitmap.mojom";
+import "third_party/blink/public/mojom/lcp_critical_path_predictor/lcp_critical_path_predictor.mojom"; // presubmit: ignore-long-line
 import "third_party/blink/public/mojom/permissions/permission_status.mojom";
 import "third_party/blink/public/mojom/webpreferences/web_preferences.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
@@ -338,3 +339,15 @@ interface WebTestControlHost {
   // Disable Auto Resize mode, resizing the contents to `new_size`.
   DisableAutoResize(gfx.mojom.Size new_size);
 };
+
+// Web test messages sent from the renderer process to the browser.
+// This provides a message pipe from each renderer to the global
+// WebTestControlHost in the browser.
+// This is very similar to WebTestControlHost, but the interface is not
+// associated to the legacy IPC channel, so can be used from mojo JS binding
+// directly.
+interface NonAssociatedWebTestControlHost {
+  // Provide the specified LCPP navigation time hint to next navigation.
+  SetLCPPNavigationHint(
+      blink.mojom.LCPCriticalPathPredictorNavigationTimeHint hint);
+};
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 9c691b0936ac1..a3fb39396ebf1 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -6495,6 +6495,9 @@ crbug.com/626703 virtual/css-highlight-overlay-painting/external/wpt/css/css-hig
 # Gardening 2023-07-06
 crbug.com/1462500 [ Win ] media/picture-in-picture/picture-in-picture-interstitial-sizing.html [ Failure Pass ]
 
+# Until LCPP is unflagged, LCPP tests run in virtual tests.
+crbug.com/1419756 http/tests/lcp_critical_path_predictor/* [ Crash ]
+
 # Gardnener 2023-07-07
 crbug.com/1462462 http/tests/history/replacestate-post-to-get-2.html [ Failure Pass Timeout ]
 
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index a7e11d7ac3df2..d611a438d0a34 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -2230,5 +2230,24 @@
     "args": ["--enable-features=ServiceWorkerStaticRouter"],
     "expires": "Jul 14, 2024",
     "owners": ["yyanagisawa@chromium.org", "sisidovski@chromium.org"]
+  },
+  {
+    "prefix": "lcpp",
+    "platforms": [
+      "Linux",
+      "Mac",
+      "Win"
+    ],
+    "bases": [
+      "http/tests/lcp_critical_path_predictor"
+    ],
+    "args": [
+      "--enable-features=LCPCriticalPathPredictor"
+    ],
+    "expires": "Jul 10, 2024",
+    "owners": [
+      "chikamune@chromium.org",
+      "kouhei@chromium.org"
+    ]
   }
 ]
diff --git a/third_party/blink/web_tests/http/tests/lcp_critical_path_predictor/prioritize_lcp_image.php b/third_party/blink/web_tests/http/tests/lcp_critical_path_predictor/prioritize_lcp_image.php
new file mode 100644
index 0000000000000..5d60f585513cb
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/lcp_critical_path_predictor/prioritize_lcp_image.php
@@ -0,0 +1,56 @@
+<!doctype html>
+<script src="/priorities/resources/common.js"></script>
+<script type=module>
+import {mojo} from "/gen/mojo/public/js/bindings.js";
+import {NonAssociatedWebTestControlHostRemote} from "/gen/content/web_test/common/web_test.mojom.m.js";
+import {ByteString} from "/gen/mojo/public/mojom/base/byte_string.mojom.m.js";
+import {LCPCriticalPathPredictorNavigationTimeHint} from "/gen/third_party/blink/public/mojom/lcp_critical_path_predictor/lcp_critical_path_predictor.mojom.m.js";
+
+if (!window.testRunner) {
+  console.log("This test requires window.testRunner.")
+}
+
+testRunner.dumpAsText();
+testRunner.waitUntilDone();
+if (window.location.search != "?start") {
+  const hint = new LCPCriticalPathPredictorNavigationTimeHint();
+
+  const resp = await fetch("/gen/third_party/blink/renderer/core/lcp_critical_path_predictor/test_proto/lcp_image_id.pb");
+
+  const bytes = new ByteString;
+  bytes.data = new Uint8Array(await resp.arrayBuffer());
+
+  hint.lcpElementLocators = [bytes];
+
+  const web_test_control_host_remote = new NonAssociatedWebTestControlHostRemote();
+  web_test_control_host_remote.$.bindNewPipeAndPassReceiver().bindInBrowser('process');
+  web_test_control_host_remote.setLCPPNavigationHint(hint);
+
+  window.location.search = '?start';
+}
+</script>
+<?php
+// Do not output the HTML below this PHP block until the test is reloaded with
+// "?start" to avoid it being picked up by the HTMLPreloadScanner.
+if ($_SERVER['QUERY_STRING'] != "start")
+  exit;
+?>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  promise_test(async t => {
+    const url = new URL('/resources/square.png', location).toString();
+    const hint_matched_img_priority = await internals.getInitialResourcePriority(url, document);
+
+    assert_equals(hint_matched_img_priority, kVeryHigh);
+  }, "Ensure LCPP hinted images were loaded with VeryHigh priority.")
+
+  promise_test(async t => {
+    const url = new URL('/resources/square100.png', location).toString();
+    const hint_matched_img_priority = await internals.getInitialResourcePriority(url, document);
+
+    assert_equals(hint_matched_img_priority, kMedium);
+  }, "Ensure non-LCPP hinted images were loaded unaffected with Medium priority.")
+</script>
+<img src="/resources/square.png" id="lcp_image">
+<img src="/resources/square100.png">
diff --git a/third_party/blink/web_tests/virtual/lcpp/README.md b/third_party/blink/web_tests/virtual/lcpp/README.md
new file mode 100644
index 0000000000000..6cf05d65812d7
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/lcpp/README.md
@@ -0,0 +1,2 @@
+A virtual test suite for LCP Critical Path Predictor.
+See crbug.com/1419756