chromium/chrome/browser/ui/webui/ash/mako/mako_ui.cc

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

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "chrome/browser/ui/webui/ash/mako/mako_ui.h"

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_switches.h"
#include "ash/lobster/lobster_controller.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/hash/sha1.h"
#include "build/branding_buildflags.h"
#include "chrome/browser/ash/input_method/editor_helpers.h"
#include "chrome/browser/ash/input_method/editor_mediator_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/ash/mako/url_constants.h"
#include "chrome/browser/ui/webui/top_chrome/untrusted_top_chrome_web_ui_controller.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/grit/orca_resources.h"
#include "chrome/grit/orca_resources_map.h"
#include "chromeos/constants/chromeos_features.h"
#include "content/public/browser/browser_context.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"

namespace ash {
namespace {

constexpr int kEnUSResourceIds[] = {
    IDR_MAKO_ORCA_HTML, IDR_MAKO_PRIVACY_HTML, IDR_MAKO_LOBSTER_HTML,
    IDR_MAKO_ORCA_JS,   IDR_MAKO_LOBSTER_JS,   IDR_MAKO_ORCA_TRANSLATION_EN_JS};

constexpr int kLobsterResourceIds[] = {
    IDR_MAKO_LOBSTER_HTML,
    IDR_MAKO_LOBSTER_JS,
};

} // namespace

MakoUntrustedUIConfig::MakoUntrustedUIConfig()
    : DefaultTopChromeWebUIConfig(content::kChromeUIUntrustedScheme,
                                  ash::kChromeUIMakoHost) {}

MakoUntrustedUIConfig::~MakoUntrustedUIConfig() = default;

bool MakoUntrustedUIConfig::IsWebUIEnabled(
    content::BrowserContext* browser_context) {
  return chromeos::features::IsOrcaEnabled();
}

bool MakoUntrustedUIConfig::ShouldAutoResizeHost() {
  // With resizing support enabled, we should let web viewport resize according
  // to dimension of web view rather than updating the dimension of web view
  // based on inner web content.
  return !base::FeatureList::IsEnabled(ash::features::kOrcaResizingSupport);
}

MakoUntrustedUI::MakoUntrustedUI(content::WebUI* web_ui)
    : UntrustedTopChromeWebUIController(web_ui) {
  CHECK(chromeos::features::IsOrcaEnabled());

  // Setup the data source
  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
      web_ui->GetWebContents()->GetBrowserContext(), kChromeUIMakoURL);

  base::span<const webui::ResourcePath> orca_resources =
      base::make_span(kOrcaResources, kOrcaResourcesSize);

  const bool is_lobster_enabled = LobsterController::IsEnabled();
  const bool should_use_l10n_strings = input_method::ShouldUseL10nStrings();

  auto should_use_resource =
      [&](const webui::ResourcePath& resource_path) -> bool {
    // when lobster is disabled, lobster resources are not allowed.
    if (!is_lobster_enabled &&
        base::Contains(kLobsterResourceIds, resource_path.id)) {
      return false;
    }
    // when l10n is disabled, only EN-US resources are allowed.
    if (!should_use_l10n_strings &&
        !base::Contains(kEnUSResourceIds, resource_path.id)) {
      return false;
    }
    return true;
  };

  // TODO: b:333625296 - Add tests for this conditional behavior
  {
    std::vector<webui::ResourcePath> orca_en_us_resources;
    std::copy_if(orca_resources.begin(), orca_resources.end(),
                 std::back_inserter(orca_en_us_resources), should_use_resource);
    webui::SetupWebUIDataSource(source, base::make_span(orca_en_us_resources),
                                IDR_MAKO_ORCA_HTML);
  }

  source->SetDefaultResource(IDR_MAKO_ORCA_HTML);

  // Setup additional CSP overrides
  // Intentional space at end of the strings - things are appended to this.
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::TrustedTypes,
      "trusted-types goog#html polymer_resin lit-html "
      "polymer-template-event-attribute-policy polymer-html-literal; ");
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::StyleSrc,
      "style-src 'unsafe-inline'  chrome-untrusted://theme; ");
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::ImgSrc, "img-src data:; ");
}
MakoUntrustedUI::~MakoUntrustedUI() = default;

void MakoUntrustedUI::BindInterface(
    mojo::PendingReceiver<orca::mojom::EditorClient> pending_receiver) {
  // If mako ui is shown to the user, then we know that EditorMediator is
  // allowed for the current profile and will return a valid instance.
  input_method::EditorMediatorFactory::GetInstance()
      ->GetForProfile(Profile::FromWebUI(web_ui()))
      ->BindEditorClient(std::move(pending_receiver));
}

void MakoUntrustedUI::BindInterface(
    mojo::PendingReceiver<color_change_listener::mojom::PageHandler> receiver) {
  color_provider_handler_ = std::make_unique<ui::ColorChangeHandler>(
      web_ui()->GetWebContents(), std::move(receiver));
}

WEB_UI_CONTROLLER_TYPE_IMPL(MakoUntrustedUI)

}  // namespace ash