chromium/chrome/browser/dev_ui/android/dev_ui_loader_throttle.cc

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/dev_ui/android/dev_ui_loader_throttle.h"

#include <string>
#include <utility>

#include "base/check_op.h"
#include "build/chromeos_buildflags.h"
#include "chrome/android/modules/dev_ui/provider/dev_ui_module_provider.h"
#include "chrome/browser/dev_ui/android/dev_ui_loader_error_page.h"
#include "chrome/common/webui_url_constants.h"
#include "components/commerce/core/commerce_constants.h"
#include "components/history_clusters/history_clusters_internals/webui/url_constants.h"
#include "components/optimization_guide/optimization_guide_internals/webui/url_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/common/url_constants.h"
#include "device/vr/buildflags/buildflags.h"
#include "net/base/net_errors.h"
#include "url/gurl.h"

namespace {

// Decides whether WebUI |host| (assumed for chrome://) is in the DevUI DFM.
// These should exclude WebUI hosts that are used outside of Chrome, e.g., those
// used by Android WebView (see AwWebUIControllerFactory). The test
// DevUiLoaderThrottleTest.PreventAccidentalInclusion safeguards against the
// inclusion of WebUI hosts that are purposefully left in the base module (i.e.,
// installed by default, and not in the DevUI DFM).
bool IsWebUiHostInDevUiDfm(const std::string& host) {
  // Each WebUI host (including synonyms) in the DevUI DFM must have an entry.
  // Assume linear search is fast enough. Can optimize later if needed.
  return host == chrome::kChromeUIAccessibilityHost ||
         host == chrome::kChromeUIAutofillInternalsHost ||
         host == chrome::kChromeUIBluetoothInternalsHost ||
         host == chrome::kChromeUIBrowsingTopicsInternalsHost ||
         host == chrome::kChromeUIComponentsHost ||
         host == chrome::kChromeUICrashesHost ||
         host == chrome::kChromeUIDeviceLogHost ||
         host == chrome::kChromeUIDownloadInternalsHost ||
         host == chrome::kChromeUIFamilyLinkUserInternalsHost ||
         host == chrome::kChromeUIGCMInternalsHost ||
         host == chrome::kChromeUIInternalsHost ||
         host == chrome::kChromeUIInterstitialHost ||
         host == chrome::kChromeUILocalStateHost ||
         host == chrome::kChromeUIMediaEngagementHost ||
         host == chrome::kChromeUIMemoryInternalsHost ||
         host == chrome::kChromeUIMetricsInternalsHost ||
         host == chrome::kChromeUINTPTilesInternalsHost ||
         host == chrome::kChromeUINetExportHost ||
         host == chrome::kChromeUINetInternalsHost ||
         host == chrome::kChromeUIOmniboxHost ||
         host == chrome::kChromeUIPasswordManagerInternalsHost ||
         host == chrome::kChromeUIPolicyHost ||
         host == chrome::kChromeUIPredictorsHost ||
         host == chrome::kChromeUISandboxHost ||
         host == chrome::kChromeUISignInInternalsHost ||
         host == chrome::kChromeUISiteEngagementHost ||
         host == chrome::kChromeUISnippetsInternalsHost ||
         host == chrome::kChromeUISyncInternalsHost ||
         host == chrome::kChromeUITranslateInternalsHost ||
         host == chrome::kChromeUIUsbInternalsHost ||
         host == chrome::kChromeUIUserActionsHost ||
         host == chrome::kChromeUIWebApksHost ||
         host == chrome::kChromeUIWebRtcLogsHost ||
         host == commerce::kChromeUICommerceInternalsHost ||
         host == content::kChromeUIPrivateAggregationInternalsHost ||
         host == content::kChromeUIAttributionInternalsHost ||
         host == content::kChromeUIBlobInternalsHost ||
         host == content::kChromeUIGpuHost ||
         host == content::kChromeUIHistogramHost ||
         host == content::kChromeUIIndexedDBInternalsHost ||
         host == content::kChromeUIMediaInternalsHost ||
         host == content::kChromeUINetworkErrorsListingHost ||
         host == content::kChromeUIProcessInternalsHost ||
         host == content::kChromeUIQuotaInternalsHost ||
         host == content::kChromeUIServiceWorkerInternalsHost ||
         host == content::kChromeUIUkmHost ||
         host == content::kChromeUIWebRTCInternalsHost ||
#if BUILDFLAG(ENABLE_VR)
         host == content::kChromeUIWebXrInternalsHost ||
#endif
         host == history_clusters_internals::
                     kChromeUIHistoryClustersInternalsHost ||
         host == optimization_guide_internals::
                     kChromeUIOptimizationGuideInternalsHost;
}

}  // namespace

namespace dev_ui {

// static
bool DevUiLoaderThrottle::ShouldInstallDevUiDfm(const GURL& url) {
  return url.SchemeIs(content::kChromeUIScheme) &&
         IsWebUiHostInDevUiDfm(url.host());
}

// static
std::unique_ptr<content::NavigationThrottle>
DevUiLoaderThrottle::MaybeCreateThrottleFor(content::NavigationHandle* handle) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  DCHECK(handle);
  if (!handle->IsInPrimaryMainFrame())
    return nullptr;

  if (!ShouldInstallDevUiDfm(handle->GetURL()))
    return nullptr;

  if (dev_ui::DevUiModuleProvider::GetInstance()->ModuleInstalled()) {
    dev_ui::DevUiModuleProvider::GetInstance()->EnsureLoaded();
    return nullptr;
  }

  return std::make_unique<DevUiLoaderThrottle>(handle);
}

DevUiLoaderThrottle::DevUiLoaderThrottle(
    content::NavigationHandle* navigation_handle)
    : content::NavigationThrottle(navigation_handle) {}

DevUiLoaderThrottle::~DevUiLoaderThrottle() = default;

const char* DevUiLoaderThrottle::GetNameForLogging() {
  return "DevUiLoaderThrottle";
}

content::NavigationThrottle::ThrottleCheckResult
DevUiLoaderThrottle::WillStartRequest() {
  if (!dev_ui::DevUiModuleProvider::GetInstance()->ModuleInstalled()) {
    // Can handle multiple install requests.
    dev_ui::DevUiModuleProvider::GetInstance()->InstallModule(
        base::BindOnce(&DevUiLoaderThrottle::OnDevUiDfmInstallWithStatus,
                       weak_ptr_factory_.GetWeakPtr()));
    return DEFER;
  }
  return PROCEED;
}

void DevUiLoaderThrottle::OnDevUiDfmInstallWithStatus(bool success) {
  if (success) {
    dev_ui::DevUiModuleProvider::GetInstance()->EnsureLoaded();
    Resume();
  } else {
    std::string html = BuildErrorPageHtml();
    CancelDeferredNavigation({CANCEL, net::ERR_CONNECTION_FAILED, html});
  }
}

}  // namespace dev_ui