chromium/ash/webui/projector_app/untrusted_projector_ui.cc

// Copyright 2021 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 "ash/webui/projector_app/untrusted_projector_ui.h"

#include "ash/strings/grit/ash_strings.h"
#include "ash/webui/grit/ash_projector_app_untrusted_resources.h"
#include "ash/webui/grit/ash_projector_app_untrusted_resources_map.h"
#include "ash/webui/grit/ash_projector_common_resources.h"
#include "ash/webui/grit/ash_projector_common_resources_map.h"
#include "ash/webui/media_app_ui/buildflags.h"
#include "ash/webui/projector_app/projector_app_client.h"
#include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
#include "ash/webui/projector_app/untrusted_projector_page_handler_impl.h"
#include "chromeos/grit/chromeos_projector_app_bundle_resources.h"
#include "chromeos/grit/chromeos_projector_app_bundle_resources_map.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 "ui/webui/color_change_listener/color_change_handler.h"
#include "ui/webui/webui_allowlist.h"
#include "url/gurl.h"

namespace ash {

namespace {

void CreateAndAddProjectorHTMLSource(content::WebUI* web_ui,
                                     UntrustedProjectorUIDelegate* delegate) {
  auto* browser_context = web_ui->GetWebContents()->GetBrowserContext();
  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
      browser_context, kChromeUIUntrustedProjectorUrl);

  source->AddResourcePaths(
      base::make_span(kAshProjectorAppUntrustedResources,
                      kAshProjectorAppUntrustedResourcesSize));
  source->AddResourcePaths(base::make_span(kAshProjectorCommonResources,
                                           kAshProjectorCommonResourcesSize));
  source->AddResourcePaths(
      base::make_span(kChromeosProjectorAppBundleResources,
                      kChromeosProjectorAppBundleResourcesSize));

  source->AddResourcePath("", IDR_ASH_PROJECTOR_APP_UNTRUSTED_INDEX_HTML);
  source->AddLocalizedString("appTitle", IDS_ASH_PROJECTOR_DISPLAY_SOURCE);

  // Provide a list of specific script resources (javascript files and inlined
  // scripts inside html) or their sha-256 hashes to allow to be executed.
  // "wasm-eval" is added to allow wasm. "chrome-untrusted://resources" is
  // needed to allow the post message api.
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::ScriptSrc,
      "script-src 'self' chrome-untrusted://resources;");
  // Allow fonts.
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::FontSrc,
      "font-src https://fonts.gstatic.com;");
  // Allow styles to include inline styling needed for Polymer elements.
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::StyleSrc,
      "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;");
  std::string mediaCSP =
      std::string("media-src 'self' https://*.drive.google.com ") +
      kChromeUIUntrustedProjectorPwaUrl + " blob:;";
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::MediaSrc,
      // Allows streaming video.
      mediaCSP);
  // Allow images to also handle data urls.
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::ImgSrc,
      "img-src blob: data: 'self' https://*.googleusercontent.com;");
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::ConnectSrc,
      "connect-src 'self' https://www.googleapis.com "
      "https://drive.google.com;");
  // Allow styles to include inline styling needed for Polymer elements and
  // the material 3 dynamic palette.
  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::StyleSrc,
      "style-src 'self' 'unsafe-inline' chrome-untrusted://theme;");

  source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::TrustedTypes,
      "trusted-types polymer_resin lit-html goog#html polymer-html-literal "
      "polymer-template-event-attribute-policy;");

  delegate->PopulateLoadTimeData(source);
  source->UseStringsJs();

  auto* webui_allowlist = WebUIAllowlist::GetOrCreate(browser_context);
  const url::Origin untrusted_origin =
      url::Origin::Create(GURL(kChromeUIUntrustedProjectorUrl));
  webui_allowlist->RegisterAutoGrantedPermission(untrusted_origin,
                                                 ContentSettingsType::COOKIES);
  webui_allowlist->RegisterAutoGrantedPermission(
      untrusted_origin, ContentSettingsType::JAVASCRIPT);
  webui_allowlist->RegisterAutoGrantedPermission(untrusted_origin,
                                                 ContentSettingsType::IMAGES);
}

}  // namespace

UntrustedProjectorUI::UntrustedProjectorUI(
    content::WebUI* web_ui,
    UntrustedProjectorUIDelegate* delegate,
    PrefService* pref_service)
    : UntrustedWebUIController(web_ui), pref_service_(pref_service) {
  CreateAndAddProjectorHTMLSource(web_ui, delegate);
  ProjectorAppClient::Get()->NotifyAppUIActive(true);
}

UntrustedProjectorUI::~UntrustedProjectorUI() {
  ProjectorAppClient::Get()->NotifyAppUIActive(false);
}

void UntrustedProjectorUI::BindInterface(
    mojo::PendingReceiver<
        projector::mojom::UntrustedProjectorPageHandlerFactory> factory) {
  if (receiver_.is_bound()) {
    receiver_.reset();
  }
  receiver_.Bind(std::move(factory));
}

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

void UntrustedProjectorUI::Create(
    mojo::PendingReceiver<projector::mojom::UntrustedProjectorPageHandler>
        projector_handler,
    mojo::PendingRemote<projector::mojom::UntrustedProjectorPage> projector) {
  page_handler_ = std::make_unique<UntrustedProjectorPageHandlerImpl>(
      std::move(projector_handler), std::move(projector), pref_service_);
}

WEB_UI_CONTROLLER_TYPE_IMPL(UntrustedProjectorUI)

}  // namespace ash