chromium/ash/webui/web_applications/test/js_library_test.cc

// Copyright 2020 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/web_applications/test/js_library_test.h"

#include <string>
#include <utility>

#include "base/base_paths.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/memory/ref_counted_memory.h"
#include "base/path_service.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/web_ui_controller_factory.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/scoped_web_ui_controller_factory_registration.h"
#include "services/network/public/mojom/content_security_policy.mojom.h"
#include "ui/webui/mojo_web_ui_controller.h"
#include "url/gurl.h"

namespace {

constexpr base::FilePath::CharType kRootDir[] =
    FILE_PATH_LITERAL("ash/webui/system_apps/public/js/");

constexpr char kSystemAppTestHost[] = "system-app-test";
constexpr char kSystemAppTestURL[] = "chrome://system-app-test";
constexpr char kUntrustedSystemAppTestURL[] =
    "chrome-untrusted://system-app-test/";

bool IsSystemAppTestURL(const GURL& url) {
  return url.SchemeIs(content::kChromeUIScheme) &&
         url.host() == kSystemAppTestHost;
}

void HandleRequest(const std::string& url_path,
                   content::WebUIDataSource::GotDataCallback callback) {
  const auto& path_for_key = [url_path](base::BasePathKey key) {
    base::FilePath path;
    CHECK(base::PathService::Get(key, &path));
    path = path.Append(kRootDir);
    path = path.AppendASCII(url_path.substr(0, url_path.find('?')));
    return path;
  };
  // First try the source dir, then try generated files.
  base::FilePath path = path_for_key(base::BasePathKey::DIR_SRC_TEST_DATA_ROOT);
  std::string contents;
  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    if (!base::PathExists(path)) {
      path = path_for_key(base::BasePathKey::DIR_GEN_TEST_DATA_ROOT);
    }
    CHECK(base::ReadFileToString(path, &contents)) << path.value();
  }

  std::move(callback).Run(
      base::MakeRefCounted<base::RefCountedString>(std::move(contents)));
}

void SetRequestFilterForDataSource(content::WebUIDataSource& data_source) {
  data_source.SetRequestFilter(
      base::BindRepeating([](const std::string& path) { return true; }),
      base::BindRepeating(&HandleRequest));
}

void CreateAndAddTrustedSystemAppTestDataSource(
    content::BrowserContext* browser_context) {
  content::WebUIDataSource* trusted_source =
      content::WebUIDataSource::CreateAndAdd(browser_context,
                                             kSystemAppTestHost);

  // We need a CSP override to be able to embed a chrome-untrusted:// iframe.
  std::string csp =
      std::string("frame-src ") + kUntrustedSystemAppTestURL + ";";
  trusted_source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::FrameSrc, csp);
  trusted_source->OverrideContentSecurityPolicy(
      network::mojom::CSPDirectiveName::ScriptSrc,
      "script-src chrome://resources chrome://webui-test 'self';");

  SetRequestFilterForDataSource(*trusted_source);
}

void CreateAndAddUntrustedSystemAppTestDataSource(
    content::BrowserContext* browser_context) {
  content::WebUIDataSource* untrusted_source =
      content::WebUIDataSource::CreateAndAdd(browser_context,
                                             kUntrustedSystemAppTestURL);
  untrusted_source->AddFrameAncestor(GURL(kSystemAppTestURL));

  SetRequestFilterForDataSource(*untrusted_source);
}

class JsLibraryTestWebUIController : public ui::MojoWebUIController {
 public:
  explicit JsLibraryTestWebUIController(content::WebUI* web_ui)
      : ui::MojoWebUIController(web_ui) {
    auto* browser_context = web_ui->GetWebContents()->GetBrowserContext();
    CreateAndAddTrustedSystemAppTestDataSource(browser_context);
    CreateAndAddUntrustedSystemAppTestDataSource(browser_context);

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

class JsLibraryTestWebUIControllerFactory
    : public content::WebUIControllerFactory {
 public:
  JsLibraryTestWebUIControllerFactory() = default;
  ~JsLibraryTestWebUIControllerFactory() override = default;

  std::unique_ptr<content::WebUIController> CreateWebUIControllerForURL(
      content::WebUI* web_ui,
      const GURL& url) override {
    return std::make_unique<JsLibraryTestWebUIController>(web_ui);
  }

  content::WebUI::TypeID GetWebUIType(content::BrowserContext* browser_context,
                                      const GURL& url) override {
    if (IsSystemAppTestURL(url)) {
      return reinterpret_cast<content::WebUI::TypeID>(this);
    }
    return content::WebUI::kNoWebUI;
  }

  bool UseWebUIForURL(content::BrowserContext* browser_context,
                      const GURL& url) override {
    return IsSystemAppTestURL(url);
  }

 private:
  content::ScopedWebUIControllerFactoryRegistration scoped_registration_{this};
};

}  // namespace

JsLibraryTest::JsLibraryTest()
    : factory_(std::make_unique<JsLibraryTestWebUIControllerFactory>()) {}

JsLibraryTest::~JsLibraryTest() = default;