BundledExchanges: Implement navigation to a BundledExchanges

This patch plumbs BundledExchanges information for navigation, and
loading.

With this patch, Chrome can recognize a given BundledExchanges file
by specifying following command line flags.
./out/Release/chrome --user-data-dir=<test_profile_path> \
  --trustable-bundled-exchanges-file=<file_abs_path> file://<file_abs_path>

BundledExchangesFactory is now empty, and follow-up patches will
implement it to make the navigation work.

Bug: 966753
Change-Id: I22fd0f1eb4cbbf8e8509694ee3dd00279ff7acc7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1690635
Reviewed-by: Tsuyoshi Horo <horo@chromium.org>
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
Reviewed-by: Scott Violet <sky@chromium.org>
Reviewed-by: Kunihiko Sakamoto <ksakamoto@chromium.org>
Commit-Queue: Takashi Toyoshima <toyoshim@chromium.org>
Auto-Submit: Takashi Toyoshima <toyoshim@chromium.org>
Cr-Commit-Position: refs/heads/master@{#679549}
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index f3e8885..b323a120 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -5541,7 +5541,7 @@
       error_reason, handle);
 }
 
-bool ChromeContentBrowserClient::CanIgnoreCertificateErrorIfNeeded() {
+bool ChromeContentBrowserClient::CanAcceptUntrustedExchangesIfNeeded() {
   // We require --user-data-dir flag too so that no dangerous changes are made
   // in the user's regular profile.
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 975b61f..d0e2ef2 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -565,7 +565,7 @@
   base::Optional<std::string> GetOriginPolicyErrorPage(
       network::OriginPolicyState error_reason,
       content::NavigationHandle* handle) override;
-  bool CanIgnoreCertificateErrorIfNeeded() override;
+  bool CanAcceptUntrustedExchangesIfNeeded() override;
   void OnNetworkServiceDataUseUpdate(int32_t network_traffic_annotation_id_hash,
                                      int64_t recv_bytes,
                                      int64_t sent_bytes) override;
diff --git a/chrome/browser/ui/startup/bad_flags_prompt.cc b/chrome/browser/ui/startup/bad_flags_prompt.cc
index 2eb96fb..1aade5e 100644
--- a/chrome/browser/ui/startup/bad_flags_prompt.cc
+++ b/chrome/browser/ui/startup/bad_flags_prompt.cc
@@ -137,6 +137,10 @@
     // GPU sanboxing isn't implemented for the Web GPU API yet meaning it would
     // be possible to read GPU data for other Chromium processes.
     switches::kEnableUnsafeWebGPU,
+
+    // A flag to support local file based BundledExchanges loading, only for
+    // testing purpose.
+    switches::kTrustableBundledExchangesFile,
 };
 #endif  // OS_ANDROID
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 8120647..b9507444 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1919,6 +1919,10 @@
     "web_contents/web_drag_dest_mac.mm",
     "web_contents/web_drag_utils_win.cc",
     "web_contents/web_drag_utils_win.h",
+    "web_package/bundled_exchanges_factory.cc",
+    "web_package/bundled_exchanges_factory.h",
+    "web_package/bundled_exchanges_source.cc",
+    "web_package/bundled_exchanges_source.h",
     "web_package/prefetched_signed_exchange_cache.cc",
     "web_package/prefetched_signed_exchange_cache.h",
     "web_package/prefetched_signed_exchange_cache_adapter.cc",
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index cdacabc..91980a81 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -11,6 +11,7 @@
 #include "base/debug/alias.h"
 #include "base/debug/dump_without_crashing.h"
 #include "base/feature_list.h"
+#include "base/files/file_path.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/optional.h"
@@ -69,6 +70,7 @@
 #include "content/public/common/url_utils.h"
 #include "content/public/common/web_preferences.h"
 #include "mojo/public/cpp/system/data_pipe.h"
+#include "net/base/filename_util.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
@@ -658,6 +660,23 @@
                            frame_tree_node_->frame_tree_node_id(), "url",
                            common_params_.url.possibly_invalid_spec());
 
+  // Setup for navigation to the BundledExchanges.
+  if (GetContentClient()->browser()->CanAcceptUntrustedExchangesIfNeeded() &&
+      common_params_.url.SchemeIsFile() &&
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kTrustableBundledExchangesFile)) {
+    // A user intends navigation to the BundledExchanges for testing.
+    const base::FilePath specified_path =
+        base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
+            switches::kTrustableBundledExchangesFile);
+    base::FilePath url_path;
+    if (net::FileURLToFilePath(common_params_.url, &url_path) &&
+        url_path == specified_path) {
+      bundled_exchanges_factory_ = std::make_unique<BundledExchangesFactory>(
+          BundledExchangesSource(specified_path));
+    }
+  }
+
   // Sanitize the referrer.
   common_params_.referrer =
       Referrer::SanitizeForRequest(common_params_.url, common_params_.referrer);
@@ -1718,6 +1737,9 @@
   headers.MergeFrom(navigation_handle_->TakeModifiedRequestHeaders());
   begin_params_->headers = headers.ToString();
 
+  std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptor;
+  if (bundled_exchanges_factory_)
+    interceptor.push_back(bundled_exchanges_factory_->CreateInterceptor());
   loader_ = NavigationURLLoader::Create(
       browser_context, browser_context->GetResourceContext(), partition,
       std::make_unique<NavigationRequestInfo>(
@@ -1736,7 +1758,8 @@
           frame_tree_node_->devtools_frame_token()),
       std::move(navigation_ui_data),
       navigation_handle_->service_worker_handle(), appcache_handle_.get(),
-      std::move(prefetched_signed_exchange_cache_), this);
+      std::move(prefetched_signed_exchange_cache_), this,
+      std::move(interceptor));
   DCHECK(!render_frame_host_);
 }
 
@@ -2024,7 +2047,8 @@
       std::move(response_body_), std::move(url_loader_client_endpoints_),
       is_view_source_, std::move(subresource_loader_params_),
       std::move(subresource_overrides_),
-      std::move(service_worker_provider_info), devtools_navigation_token_);
+      std::move(service_worker_provider_info), devtools_navigation_token_,
+      std::move(bundled_exchanges_factory_));
 
   // Give SpareRenderProcessHostManager a heads-up about the most recently used
   // BrowserContext.  This is mostly needed to make sure the spare is warmed-up
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index e1a89aa..f5aa353 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -19,6 +19,7 @@
 #include "content/browser/initiator_csp_context.h"
 #include "content/browser/loader/navigation_url_loader_delegate.h"
 #include "content/browser/navigation_subresource_loader_params.h"
+#include "content/browser/web_package/bundled_exchanges_factory.h"
 #include "content/common/content_export.h"
 #include "content/common/frame_message_enums.h"
 #include "content/common/navigation_params.h"
@@ -940,6 +941,11 @@
   // TODO(clamy): Revisit the unit test architecture.
   ThrottleChecksFinishedCallback complete_callback_for_testing_;
 
+  // The factory to handle the BundledExchanges that's bound to this request.
+  // Used to navigate to the main resource URL of the BundledExchanges, and
+  // load it from the corresponding entry.
+  std::unique_ptr<BundledExchangesFactory> bundled_exchanges_factory_;
+
   base::WeakPtrFactory<NavigationRequest> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(NavigationRequest);
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index da8e5d7..783925a 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -107,6 +107,7 @@
 #include "content/browser/speech/speech_recognition_dispatcher_host.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/browser/wake_lock/wake_lock_service_impl.h"
+#include "content/browser/web_package/bundled_exchanges_factory.h"
 #include "content/browser/web_package/prefetched_signed_exchange_cache.h"
 #include "content/browser/webauth/authenticator_environment_impl.h"
 #include "content/browser/webauth/authenticator_impl.h"
@@ -1655,6 +1656,7 @@
   document_interface_broker_content_binding_.Close();
   document_interface_broker_blink_binding_.Close();
   SetLastCommittedUrl(GURL());
+  bundled_exchanges_factory_.reset();
 
   // Execute any pending AX tree snapshot callbacks with an empty response,
   // since we're never going to get a response from this renderer.
@@ -4428,7 +4430,8 @@
       nullptr /* response_head */, mojo::ScopedDataPipeConsumerHandle(),
       network::mojom::URLLoaderClientEndpointsPtr(), false, base::nullopt,
       base::nullopt /* subresource_overrides */, nullptr /* provider_info */,
-      base::UnguessableToken::Create() /* not traced */);
+      base::UnguessableToken::Create() /* not traced */,
+      nullptr /* bundled_exchanges_factory */);
 }
 
 void RenderFrameHostImpl::Stop() {
@@ -4781,7 +4784,10 @@
     base::Optional<std::vector<mojom::TransferrableURLLoaderPtr>>
         subresource_overrides,
     blink::mojom::ServiceWorkerProviderInfoForClientPtr provider_info,
-    const base::UnguessableToken& devtools_navigation_token) {
+    const base::UnguessableToken& devtools_navigation_token,
+    std::unique_ptr<BundledExchangesFactory> bundled_exchanges_factory) {
+  bundled_exchanges_factory_ = std::move(bundled_exchanges_factory);
+
   TRACE_EVENT2("navigation", "RenderFrameHostImpl::CommitNavigation",
                "frame_tree_node", frame_tree_node_->frame_tree_node_id(), "url",
                common_params.url.possibly_invalid_spec());
@@ -4983,6 +4989,14 @@
           bypass_redirect_checks);
     }
 
+    if (bundled_exchanges_factory_) {
+      mojo::Remote<network::mojom::URLLoaderFactory> fallback_factory(
+          std::move(pending_default_factory));
+      bundled_exchanges_factory_->CreateURLLoaderFactory(
+          pending_default_factory.InitWithNewPipeAndPassReceiver(),
+          std::move(fallback_factory));
+    }
+
     DCHECK(pending_default_factory);
     subresource_loader_factories->pending_default_factory() =
         std::move(pending_default_factory);
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 3884d4a..d0f858a 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -140,6 +140,7 @@
 namespace content {
 class AppCacheNavigationHandle;
 class AuthenticatorImpl;
+class BundledExchangesFactory;
 class FrameTree;
 class FrameTreeNode;
 class GeolocationServiceImpl;
@@ -692,7 +693,8 @@
       base::Optional<std::vector<mojom::TransferrableURLLoaderPtr>>
           subresource_overrides,
       blink::mojom::ServiceWorkerProviderInfoForClientPtr provider_info,
-      const base::UnguessableToken& devtools_navigation_token);
+      const base::UnguessableToken& devtools_navigation_token,
+      std::unique_ptr<BundledExchangesFactory> bundled_exchanges_factory);
 
   // Indicates that a navigation failed and that this RenderFrame should display
   // an error page.
@@ -2222,6 +2224,10 @@
   blink::mojom::FrameLifecycleState frame_lifecycle_state_ =
       blink::mojom::FrameLifecycleState::kRunning;
 
+  // The factory to load resources from the BundledExchanges source bound to
+  // this file.
+  std::unique_ptr<BundledExchangesFactory> bundled_exchanges_factory_;
+
   // NOTE: This must be the last member.
   base::WeakPtrFactory<RenderFrameHostImpl> weak_ptr_factory_{this};
 
diff --git a/content/browser/loader/navigation_url_loader.cc b/content/browser/loader/navigation_url_loader.cc
index 3f3714f4b..a800bed 100644
--- a/content/browser/loader/navigation_url_loader.cc
+++ b/content/browser/loader/navigation_url_loader.cc
@@ -8,7 +8,6 @@
 
 #include "base/command_line.h"
 #include "content/browser/frame_host/navigation_request_info.h"
-#include "content/browser/loader/navigation_loader_interceptor.h"
 #include "content/browser/loader/navigation_url_loader_factory.h"
 #include "content/browser/loader/navigation_url_loader_impl.h"
 #include "content/browser/web_package/prefetched_signed_exchange_cache.h"
@@ -29,7 +28,9 @@
     AppCacheNavigationHandle* appcache_handle,
     scoped_refptr<PrefetchedSignedExchangeCache>
         prefetched_signed_exchange_cache,
-    NavigationURLLoaderDelegate* delegate) {
+    NavigationURLLoaderDelegate* delegate,
+    std::vector<std::unique_ptr<NavigationLoaderInterceptor>>
+        initial_interceptors) {
   if (g_loader_factory) {
     return g_loader_factory->CreateLoader(
         resource_context, storage_partition, std::move(request_info),
@@ -40,7 +41,7 @@
       std::move(request_info), std::move(navigation_ui_data),
       service_worker_handle, appcache_handle,
       std::move(prefetched_signed_exchange_cache), delegate,
-      std::vector<std::unique_ptr<NavigationLoaderInterceptor>>());
+      std::move(initial_interceptors));
 }
 
 void NavigationURLLoader::SetFactoryForTesting(
diff --git a/content/browser/loader/navigation_url_loader.h b/content/browser/loader/navigation_url_loader.h
index 160f5de..7123394 100644
--- a/content/browser/loader/navigation_url_loader.h
+++ b/content/browser/loader/navigation_url_loader.h
@@ -11,6 +11,7 @@
 
 #include "base/macros.h"
 #include "base/optional.h"
+#include "content/browser/loader/navigation_loader_interceptor.h"
 #include "content/common/content_export.h"
 #include "content/public/common/previews_state.h"
 
@@ -55,7 +56,9 @@
       AppCacheNavigationHandle* appcache_handle,
       scoped_refptr<PrefetchedSignedExchangeCache>
           prefetched_signed_exchange_cache,
-      NavigationURLLoaderDelegate* delegate);
+      NavigationURLLoaderDelegate* delegate,
+      std::vector<std::unique_ptr<NavigationLoaderInterceptor>>
+          initial_interceptors = {});
 
   // For testing purposes; sets the factory for use in testing.
   static void SetFactoryForTesting(NavigationURLLoaderFactory* factory);
diff --git a/content/browser/web_package/bundled_exchanges_factory.cc b/content/browser/web_package/bundled_exchanges_factory.cc
new file mode 100644
index 0000000..c946f08
--- /dev/null
+++ b/content/browser/web_package/bundled_exchanges_factory.cc
@@ -0,0 +1,61 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/web_package/bundled_exchanges_factory.h"
+
+#include "content/browser/loader/navigation_loader_interceptor.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace {
+
+// A class to inherit NavigationLoaderInterceptor for the navigation to a
+// BundledExchanges.
+// TODO(crbug.com/966753): Implement.
+class Interceptor final : public content::NavigationLoaderInterceptor {
+ public:
+  Interceptor() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); }
+  ~Interceptor() override { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); }
+
+ private:
+  // NavigationLoaderInterceptor implementation
+  void MaybeCreateLoader(const network::ResourceRequest& resource_request,
+                         content::BrowserContext* browser_context,
+                         content::ResourceContext* resource_context,
+                         LoaderCallback callback,
+                         FallbackCallback fallback_callback) override {
+    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+
+    std::move(callback).Run({});
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(Interceptor);
+};
+
+}  // namespace
+
+namespace content {
+
+BundledExchangesFactory::BundledExchangesFactory(
+    const BundledExchangesSource& bundled_exchanges_source) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+// Can be destructed on IO or UI thread.
+BundledExchangesFactory::~BundledExchangesFactory() = default;
+
+std::unique_ptr<NavigationLoaderInterceptor>
+BundledExchangesFactory::CreateInterceptor() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  return std::make_unique<Interceptor>();
+}
+
+void BundledExchangesFactory::CreateURLLoaderFactory(
+    mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver,
+    mojo::Remote<network::mojom::URLLoaderFactory> fallback_factory) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  NOTREACHED();
+}
+
+}  // namespace content
diff --git a/content/browser/web_package/bundled_exchanges_factory.h b/content/browser/web_package/bundled_exchanges_factory.h
new file mode 100644
index 0000000..23abc5f
--- /dev/null
+++ b/content/browser/web_package/bundled_exchanges_factory.h
@@ -0,0 +1,44 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WEB_PACKAGE_BUNDLED_EXCHANGES_FACTORY_H_
+#define CONTENT_BROWSER_WEB_PACKAGE_BUNDLED_EXCHANGES_FACTORY_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "content/browser/web_package/bundled_exchanges_source.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "url/gurl.h"
+
+namespace content {
+
+class NavigationLoaderInterceptor;
+
+// A class to provide interfaces to communicate with a BundledExchanges for
+// loading.
+class BundledExchangesFactory final {
+ public:
+  explicit BundledExchangesFactory(const BundledExchangesSource& source);
+  ~BundledExchangesFactory();
+
+  // Creates a NavigationLoaderInterceptor instance to handle the request for
+  // a BundledExchanges, to redirect to the entry URL of the BundledExchanges,
+  // and to load the main exchange from the BundledExchanges.
+  std::unique_ptr<NavigationLoaderInterceptor> CreateInterceptor();
+
+  // Creates a URLLoaderFactory to load resources from the BundledExchanges.
+  void CreateURLLoaderFactory(
+      mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver,
+      mojo::Remote<network::mojom::URLLoaderFactory> fallback_factory);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BundledExchangesFactory);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_WEB_PACKAGE_BUNDLED_EXCHANGES_FACTORY_H_
diff --git a/content/browser/web_package/bundled_exchanges_source.cc b/content/browser/web_package/bundled_exchanges_source.cc
new file mode 100644
index 0000000..ecd3d288c
--- /dev/null
+++ b/content/browser/web_package/bundled_exchanges_source.cc
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/web_package/bundled_exchanges_source.h"
+
+namespace content {
+
+BundledExchangesSource::BundledExchangesSource(const base::FilePath& path)
+    : file_path(path) {
+  DCHECK(!file_path.empty());
+}
+
+BundledExchangesSource::BundledExchangesSource(
+    const BundledExchangesSource& src) = default;
+
+}  // namespace content
diff --git a/content/browser/web_package/bundled_exchanges_source.h b/content/browser/web_package/bundled_exchanges_source.h
new file mode 100644
index 0000000..c470267
--- /dev/null
+++ b/content/browser/web_package/bundled_exchanges_source.h
@@ -0,0 +1,24 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WEB_PACKAGE_BUNDLED_EXCHANGES_SOURCE_H_
+#define CONTENT_BROWSER_WEB_PACKAGE_BUNDLED_EXCHANGES_SOURCE_H_
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+// A struct to abstract required information to access a BundledExchanges.
+struct CONTENT_EXPORT BundledExchangesSource {
+  explicit BundledExchangesSource(const base::FilePath& file_path);
+  explicit BundledExchangesSource(const BundledExchangesSource& src);
+
+  const base::FilePath file_path;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_WEB_PACKAGE_BUNDLED_EXCHANGES_SOURCE_H_
diff --git a/content/browser/web_package/signed_exchange_certificate_chain.cc b/content/browser/web_package/signed_exchange_certificate_chain.cc
index 7237f87..947af81 100644
--- a/content/browser/web_package/signed_exchange_certificate_chain.cc
+++ b/content/browser/web_package/signed_exchange_certificate_chain.cc
@@ -204,7 +204,7 @@
 
 SignedExchangeCertificateChain::IgnoreErrorsSPKIList::IgnoreErrorsSPKIList(
     const base::CommandLine& command_line) {
-  if (!GetContentClient()->browser()->CanIgnoreCertificateErrorIfNeeded())
+  if (!GetContentClient()->browser()->CanAcceptUntrustedExchangesIfNeeded())
     return;
   Parse(command_line.GetSwitchValueASCII(
       network::switches::kIgnoreCertificateErrorsSPKIList));
diff --git a/content/browser/web_package/signed_exchange_certificate_chain.h b/content/browser/web_package/signed_exchange_certificate_chain.h
index 520f614..25e76b90 100644
--- a/content/browser/web_package/signed_exchange_certificate_chain.h
+++ b/content/browser/web_package/signed_exchange_certificate_chain.h
@@ -81,7 +81,7 @@
 
   // Returns true if SPKI hash of |cert_| is included in the
   // --ignore-certificate-errors-spki-list command line flag, and
-  // ContentBrowserClient::CanIgnoreCertificateErrorIfNeeded() returns true.
+  // ContentBrowserClient::CanAcceptUntrustedExchangesIfNeeded() returns true.
   bool ShouldIgnoreErrors() const;
 
  private:
diff --git a/content/browser/web_package/signed_exchange_handler_unittest.cc b/content/browser/web_package/signed_exchange_handler_unittest.cc
index ca783ed7..5d763b9f 100644
--- a/content/browser/web_package/signed_exchange_handler_unittest.cc
+++ b/content/browser/web_package/signed_exchange_handler_unittest.cc
@@ -68,7 +68,7 @@
                                           sizeof(kDummySCTBytes));
 
 class TestBrowserClient : public ContentBrowserClient {
-  bool CanIgnoreCertificateErrorIfNeeded() override { return true; }
+  bool CanAcceptUntrustedExchangesIfNeeded() override { return true; }
 };
 
 std::string GetTestFileContents(base::StringPiece name) {
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 88a60f5..72ca266 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -944,7 +944,7 @@
   return base::nullopt;
 }
 
-bool ContentBrowserClient::CanIgnoreCertificateErrorIfNeeded() {
+bool ContentBrowserClient::CanAcceptUntrustedExchangesIfNeeded() {
   return false;
 }
 
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 2ecbd10..aecf782 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1538,11 +1538,11 @@
       network::OriginPolicyState error_reason,
       content::NavigationHandle* navigation_handle);
 
-  // Returns true if it is OK to ignore errors for certificates specified by the
-  // --ignore-certificate-errors-spki-list command line flag. The embedder may
-  // perform additional checks, such as requiring --user-data-dir flag too to
-  // make sure that insecure contents will not persist accidentally.
-  virtual bool CanIgnoreCertificateErrorIfNeeded();
+  // Returns true if it is OK to accept untrusted exchanges, such as expired
+  // signed exchanges, and unsigned bundled exchanges.
+  // The embedder may require --user-data-dir flag and so on to accept it in
+  // order to make sure that insecure contents will not persist accidentally.
+  virtual bool CanAcceptUntrustedExchangesIfNeeded();
 
   // Called on every request completion to update the data use when network
   // service is enabled.
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 12f4984..cf8464ec 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -810,6 +810,11 @@
 // the platform default is used.
 const char kTouchTextSelectionStrategy[]    = "touch-selection-strategy";
 
+// Accepts specified file as a trustable BundledExchanges file. This flag should
+// be used only for testing purpose.
+const char kTrustableBundledExchangesFile[] =
+    "trustable-bundled-exchanges-file";
+
 // Replaces the existing codecs supported in peer connection with a single fake
 // codec entry that create a fake video encoder and decoder.
 const char kUseFakeCodecForPeerConnection[] =
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 13ad701a..6a21e9b 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -227,6 +227,7 @@
 CONTENT_EXPORT extern const char kTouchEventFeatureDetectionEnabled[];
 CONTENT_EXPORT extern const char kTouchEventFeatureDetectionDisabled[];
 CONTENT_EXPORT extern const char kTouchTextSelectionStrategy[];
+CONTENT_EXPORT extern const char kTrustableBundledExchangesFile[];
 CONTENT_EXPORT extern const char kUseFakeCodecForPeerConnection[];
 CONTENT_EXPORT extern const char kUseFakeUIForMediaStream[];
 CONTENT_EXPORT extern const char kVideoImageTextureTarget[];
diff --git a/content/shell/browser/web_test/web_test_content_browser_client.cc b/content/shell/browser/web_test/web_test_content_browser_client.cc
index c8aa2f9..dacecdd 100644
--- a/content/shell/browser/web_test/web_test_content_browser_client.cc
+++ b/content/shell/browser/web_test/web_test_content_browser_client.cc
@@ -315,7 +315,7 @@
   return !block_popups_ || user_gesture;
 }
 
-bool WebTestContentBrowserClient::CanIgnoreCertificateErrorIfNeeded() {
+bool WebTestContentBrowserClient::CanAcceptUntrustedExchangesIfNeeded() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kRunWebTests);
 }
diff --git a/content/shell/browser/web_test/web_test_content_browser_client.h b/content/shell/browser/web_test/web_test_content_browser_client.h
index c07734f..4f00217 100644
--- a/content/shell/browser/web_test/web_test_content_browser_client.h
+++ b/content/shell/browser/web_test/web_test_content_browser_client.h
@@ -72,7 +72,7 @@
                        bool user_gesture,
                        bool opener_suppressed,
                        bool* no_javascript_access) override;
-  bool CanIgnoreCertificateErrorIfNeeded() override;
+  bool CanAcceptUntrustedExchangesIfNeeded() override;
 
   // ShellContentBrowserClient overrides.
   void ExposeInterfacesToFrame(