chromium/ash/webui/web_applications/webui_test_prod_util.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.

#include "ash/webui/web_applications/webui_test_prod_util.h"

#include "ash/constants/ash_switches.h"
#include "base/command_line.h"
#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/memory/ref_counted_memory.h"
#include "base/no_destructor.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_restrictions.h"
#include "content/public/common/content_switches.h"

using content::WebUIDataSource;

namespace {

WebUIDataSource::ShouldHandleRequestCallback& GetTestShouldHandleRequest() {
  static base::NoDestructor<WebUIDataSource::ShouldHandleRequestCallback>
      callback;
  return *callback;
}

WebUIDataSource::HandleRequestCallback& GetTestRequestFilterHandler() {
  static base::NoDestructor<WebUIDataSource::HandleRequestCallback> callback;
  return *callback;
}

bool InvokeTestShouldHandleRequestCallback(
    const WebUIDataSource::ShouldHandleRequestCallback&
        real_should_handle_request_callback,
    const std::string& path) {
  const auto& test_callback = GetTestShouldHandleRequest();
  if (test_callback && test_callback.Run(path)) {
    return true;
  }
  return real_should_handle_request_callback &&
         real_should_handle_request_callback.Run(path);
}

void InvokeTestFileRequestFilterCallback(
    const WebUIDataSource::HandleRequestCallback& real_handle_request_callback,
    const std::string& path,
    WebUIDataSource::GotDataCallback callback) {
  // First, check whether this was request for a test-only resource. This
  // requires the test handler to be installed by
  // SetTestableDataSourceRequestHandlerForTesting() and for it to have returned
  // true. Otherwise assume, that since the request was directed to the filter,
  // that it is a request for the "real" filter installed by
  // MaybeConfigureTestableDataSource().
  const auto& test_callback = GetTestShouldHandleRequest();
  if (test_callback && test_callback.Run(path)) {
    GetTestRequestFilterHandler().Run(path, std::move(callback));
  } else {
    DCHECK(!real_handle_request_callback.is_null());
    real_handle_request_callback.Run(path, std::move(callback));
  }
}

// Determines whether, when attempting to load a path, we want to, instead of
// using the regular handler, load it from a file on disk.
bool ShouldLoadResponseFromDisk(const base::FilePath& root,
                                const std::string& path) {
  const base::FilePath expanded = root.Append(path);
  base::ScopedAllowBlockingForTesting allow_blocking;
  const bool exists = base::PathExists(expanded);
  if (exists) {
    VLOG(1) << "Loading test data from " << expanded << " for " << path;
  } else {
    VLOG(1) << "Unable to load test data from " << expanded << " for " << path
            << ", as the file doesn't exist.";
  }
  return exists;
}

void LoadFileFromDisk(const base::FilePath& path,
                      content::WebUIDataSource::GotDataCallback callback) {
  std::string result;
  CHECK(base::ReadFileToString(path, &result));

  std::move(callback).Run(
      new base::RefCountedBytes(base::as_byte_span(result)));
}

void LoadResponseFromDisk(const base::FilePath& root,
                          const std::string& path,
                          content::WebUIDataSource::GotDataCallback callback) {
  base::ThreadPool::PostTask(
      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
      base::BindOnce(LoadFileFromDisk, root.Append(path), std::move(callback)));
}

}  // namespace

bool MaybeConfigureTestableDataSource(
    WebUIDataSource* host_source,
    const std::string& handler_name,
    const WebUIDataSource::ShouldHandleRequestCallback&
        real_should_handle_request_callback,
    const WebUIDataSource::HandleRequestCallback&
        real_handle_request_callback) {
  const base::CommandLine& cmd = *base::CommandLine::ForCurrentProcess();
  bool has_test_handler = false;
  if (cmd.HasSwitch(::ash::switches::kWebUiDataSourcePathForTesting) &&
      !handler_name.empty()) {
    const base::FilePath root =
        cmd.GetSwitchValuePath(::ash::switches::kWebUiDataSourcePathForTesting)
            .Append(handler_name);
    GetTestShouldHandleRequest() =
        base::BindRepeating(ShouldLoadResponseFromDisk, root);
    GetTestRequestFilterHandler() =
        base::BindRepeating(LoadResponseFromDisk, root);
    has_test_handler = true;
  } else if (cmd.HasSwitch(::switches::kTestType)) {
    has_test_handler = true;
  }

  if (has_test_handler) {
    host_source->SetRequestFilter(
        base::BindRepeating(&InvokeTestShouldHandleRequestCallback,
                            real_should_handle_request_callback),
        base::BindRepeating(&InvokeTestFileRequestFilterCallback,
                            real_handle_request_callback));
  } else {
    host_source->SetRequestFilter(real_should_handle_request_callback,
                                  real_handle_request_callback);
  }
  return has_test_handler;
}

void SetTestableDataSourceRequestHandlerForTesting(  // IN-TEST
    WebUIDataSource::ShouldHandleRequestCallback should_handle,
    WebUIDataSource::HandleRequestCallback handler) {
  GetTestShouldHandleRequest() = std::move(should_handle);
  GetTestRequestFilterHandler() = std::move(handler);
}