chromium/ash/webui/help_app_ui/help_app_ui.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 "ash/webui/help_app_ui/help_app_ui.h"

#include <utility>

#include "ash/constants/ash_features.h"
#include "ash/webui/grit/ash_help_app_resources.h"
#include "ash/webui/help_app_ui/help_app_manager.h"
#include "ash/webui/help_app_ui/help_app_manager_factory.h"
#include "ash/webui/help_app_ui/help_app_page_handler.h"
#include "ash/webui/help_app_ui/help_app_untrusted_ui.h"
#include "ash/webui/help_app_ui/search/search_handler.h"
#include "ash/webui/web_applications/webui_test_prod_util.h"
#include "base/strings/strcat.h"
#include "chromeos/ash/components/local_search_service/public/cpp/local_search_service_proxy.h"
#include "chromeos/ash/components/local_search_service/public/cpp/local_search_service_proxy_factory.h"
#include "chromeos/ash/components/local_search_service/public/mojom/types.mojom.h"
#include "chromeos/constants/url_constants.h"
#include "chromeos/strings/grit/chromeos_strings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/common/url_constants.h"
#include "services/network/public/mojom/content_security_policy.mojom.h"
#include "ui/webui/webui_allowlist.h"

namespace ash {

namespace {
content::WebUIDataSource* CreateAndAddHostDataSource(
    content::BrowserContext* browser_context) {
  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
      browser_context, kChromeUIHelpAppHost);

  source->SetDefaultResource(IDR_HELP_APP_HOST_INDEX_DARK_LIGHT_HTML);
  source->AddResourcePath("app_icon_192.png", IDR_HELP_APP_ICON_192);
  source->AddResourcePath("app_icon_512.png", IDR_HELP_APP_ICON_512);
  source->AddResourcePath("browser_proxy.js", IDR_HELP_APP_BROWSER_PROXY_JS);

  source->AddLocalizedString("appTitle", IDS_HELP_APP_EXPLORE);
  return source;
}
}  // namespace

HelpAppUI::HelpAppUI(content::WebUI* web_ui,
                     std::unique_ptr<HelpAppUIDelegate> delegate)
    : MojoWebUIController(web_ui), delegate_(std::move(delegate)) {
  content::BrowserContext* browser_context =
      web_ui->GetWebContents()->GetBrowserContext();
  content::WebUIDataSource* host_source =
      CreateAndAddHostDataSource(browser_context);
  // We need a CSP override to use the chrome-untrusted://, almanac:// and
  // cros-apps:// schemes in the host.
  std::string csp = base::StrCat({"frame-src ", kChromeUIHelpAppUntrustedURL,
                                  " ", chromeos::kAppInstallUriScheme, ": ",
                                  chromeos::kLegacyAppInstallUriScheme, ":;"});
  host_source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::FrameSrc, csp);

  // Add ability to request chrome-untrusted: URLs.
  web_ui->AddRequestableScheme(content::kChromeUIUntrustedScheme);

  // Register common permissions for chrome-untrusted:// pages.
  // TODO(crbug.com/40710326): Remove this after common permissions are
  // granted by default.
  auto* permissions_allowlist = WebUIAllowlist::GetOrCreate(browser_context);
  const url::Origin untrusted_origin =
      url::Origin::Create(GURL(kChromeUIHelpAppUntrustedURL));
  permissions_allowlist->RegisterAutoGrantedPermissions(
      untrusted_origin, {
                            ContentSettingsType::COOKIES,
                            ContentSettingsType::IMAGES,
                            ContentSettingsType::JAVASCRIPT,
                            ContentSettingsType::SOUND,
                        });

  if (MaybeConfigureTestableDataSource(host_source)) {
    host_source->OverrideContentSecurityPolicy(
        network::mojom::CSPDirectiveName::ScriptSrc,
        "script-src chrome://resources chrome://webui-test 'self';");
    host_source->OverrideContentSecurityPolicy(
        network::mojom::CSPDirectiveName::TrustedTypes,
        std::string("trusted-types test-harness;"));
  }

  // Register common permissions for chrome-untrusted:// pages.
  // TODO(crbug.com/40710326): Remove this after common permissions are
  // granted by default.
  auto* magazine_permissions_allowlist =
      WebUIAllowlist::GetOrCreate(browser_context);
  const url::Origin magazine_untrusted_origin =
      url::Origin::Create(GURL(kChromeUIHelpAppKidsMagazineUntrustedURL));
  magazine_permissions_allowlist->RegisterAutoGrantedPermissions(
      magazine_untrusted_origin, {
                                     ContentSettingsType::COOKIES,
                                     ContentSettingsType::IMAGES,
                                     ContentSettingsType::JAVASCRIPT,
                                     ContentSettingsType::SOUND,
                                 });
}

HelpAppUI::~HelpAppUI() = default;

void HelpAppUI::BindInterface(
    mojo::PendingReceiver<help_app::mojom::PageHandlerFactory> receiver) {
  page_factory_receiver_.reset();
  page_factory_receiver_.Bind(std::move(receiver));
}

void HelpAppUI::BindInterface(
    mojo::PendingReceiver<local_search_service::mojom::Index> index_receiver) {
  if (base::FeatureList::IsEnabled(features::kEnableLocalSearchService)) {
    auto* const factory = local_search_service::LocalSearchServiceProxyFactory::
        GetForBrowserContext(web_ui()->GetWebContents()->GetBrowserContext());
    factory->SetLocalState(delegate_->GetLocalState());
    factory->GetIndex(local_search_service::IndexId::kHelpApp,
                      local_search_service::Backend::kInvertedIndex,
                      std::move(index_receiver));
  }
}

void HelpAppUI::BindInterface(
    mojo::PendingReceiver<help_app::mojom::SearchHandler> receiver) {
  if (base::FeatureList::IsEnabled(features::kHelpAppLauncherSearch)) {
    help_app::HelpAppManagerFactory::GetForBrowserContext(
        web_ui()->GetWebContents()->GetBrowserContext())
        ->search_handler()
        ->BindInterface(std::move(receiver));
  }
}

void HelpAppUI::CreatePageHandler(
    mojo::PendingReceiver<help_app::mojom::PageHandler> receiver) {
  page_handler_ =
      std::make_unique<HelpAppPageHandler>(this, std::move(receiver));
}

bool HelpAppUI::IsJavascriptErrorReportingEnabled() {
  // JavaScript errors are reported via CrashReportPrivate.reportError. Don't
  // send duplicate reports via WebUI.
  return false;
}

WEB_UI_CONTROLLER_TYPE_IMPL(HelpAppUI)

}  // namespace ash