910e62b5创建于 1月15日历史提交
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/shell/renderer/shell_content_renderer_client.h"

#include <string>

#include "base/base_switches.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/files/file.h"
#include "base/functional/bind.h"
#include "base/immediate_crash.h"
#include "base/notreached.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/types/pass_key.h"
#include "components/cdm/renderer/external_clear_key_key_system_info.h"
#include "components/network_hints/renderer/web_prescient_networking_impl.h"
#include "components/web_cache/renderer/web_cache_impl.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/pseudonymization_util.h"
#include "content/public/common/web_identity.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/test/test_service.mojom.h"
#include "content/shell/common/main_frame_counter_test_impl.h"
#include "content/shell/common/power_monitor_test_impl.h"
#include "content/shell/common/shell_switches.h"
#include "content/shell/renderer/shell_render_frame_observer.h"
#include "mojo/public/cpp/bindings/binder_map.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "net/base/net_errors.h"
#include "sandbox/policy/sandbox.h"
#include "third_party/blink/public/platform/url_loader_throttle_provider.h"
#include "third_party/blink/public/platform/web_url_error.h"
#include "third_party/blink/public/web/modules/credentialmanagement/throttle_helper.h"
#include "third_party/blink/public/web/web_local_frame.h"
#include "third_party/blink/public/web/web_testing_support.h"
#include "third_party/blink/public/web/web_view.h"
#include "v8/include/v8-initialization.h"
#include "v8/include/v8.h"

#if BUILDFLAG(ENABLE_MOJO_CDM)
#include "base/feature_list.h"
#include "media/base/media_switches.h"
#endif

#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && \
    (defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64))
#define ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX
#include "base/debug/stack_trace.h"
#include "v8/include/v8-wasm-trap-handler-posix.h"
#endif

namespace content {

namespace {

// A test service which can be driven by browser tests for various reasons.
class TestRendererServiceImpl : public mojom::TestService {
 public:
  explicit TestRendererServiceImpl(
      mojo::PendingReceiver<mojom::TestService> receiver)
      : receiver_(this, std::move(receiver)) {
    receiver_.set_disconnect_handler(base::BindOnce(
        &TestRendererServiceImpl::OnConnectionError, base::Unretained(this)));
  }

  TestRendererServiceImpl(const TestRendererServiceImpl&) = delete;
  TestRendererServiceImpl& operator=(const TestRendererServiceImpl&) = delete;

  ~TestRendererServiceImpl() override {}

 private:
  void OnConnectionError() { delete this; }

  // mojom::TestService:
  void DoSomething(DoSomethingCallback callback) override {
    // Instead of responding normally, unbind the pipe, write some garbage,
    // and go away.
    const std::string kBadMessage = "This is definitely not a valid response!";
    mojo::ScopedMessagePipeHandle pipe = receiver_.Unbind().PassPipe();
    MojoResult rv = mojo::WriteMessageRaw(
        pipe.get(), kBadMessage.data(), kBadMessage.size(), nullptr, 0,
        MOJO_WRITE_MESSAGE_FLAG_NONE);
    DCHECK_EQ(rv, MOJO_RESULT_OK);

    // Deletes this.
    OnConnectionError();
  }

  void DoTerminateProcess(DoTerminateProcessCallback callback) override {
    NOTREACHED();
  }

  void DoCrashImmediately(DoCrashImmediatelyCallback callback) override {
    // This intentionally crashes the process and needs to be fatal regardless
    // of DCHECK level. This is unlike the other NOTREACHED()s which are not
    // expected to get called at all.
    base::ImmediateCrash();
  }

  void CreateFolder(CreateFolderCallback callback) override { NOTREACHED(); }

  void GetRequestorName(GetRequestorNameCallback callback) override {
    std::move(callback).Run("Not implemented.");
  }

  void CreateReadOnlySharedMemoryRegion(
      const std::string& message,
      CreateReadOnlySharedMemoryRegionCallback callback) override {
    NOTREACHED();
  }

  void CreateWritableSharedMemoryRegion(
      const std::string& message,
      CreateWritableSharedMemoryRegionCallback callback) override {
    NOTREACHED();
  }

  void CreateUnsafeSharedMemoryRegion(
      const std::string& message,
      CreateUnsafeSharedMemoryRegionCallback callback) override {
    NOTREACHED();
  }

  void CloneSharedMemoryContents(
      base::ReadOnlySharedMemoryRegion region,
      CloneSharedMemoryContentsCallback callback) override {
    NOTREACHED();
  }

  void IsProcessSandboxed(IsProcessSandboxedCallback callback) override {
    std::move(callback).Run(sandbox::policy::Sandbox::IsProcessSandboxed());
  }

  void PseudonymizeString(const std::string& value,
                          PseudonymizeStringCallback callback) override {
    std::move(callback).Run(
        PseudonymizationUtil::PseudonymizeStringForTesting(value));
  }

  void PassWriteableFile(base::File file,
                         PassWriteableFileCallback callback) override {
    std::move(callback).Run();
  }

  void WriteToPreloadedPipe() override { NOTREACHED(); }

  mojo::Receiver<mojom::TestService> receiver_;
};

class ShellContentRendererUrlLoaderThrottleProvider
    : public blink::URLLoaderThrottleProvider {
 public:
  ShellContentRendererUrlLoaderThrottleProvider()
      : main_thread_task_runner_(
            content::RenderThread::IsMainThread()
                ? base::SequencedTaskRunner::GetCurrentDefault()
                : nullptr) {}

  // This constructor works in conjunction with Clone().
  ShellContentRendererUrlLoaderThrottleProvider(
      const scoped_refptr<base::SequencedTaskRunner>& main_thread_task_runner,
      base::PassKey<ShellContentRendererUrlLoaderThrottleProvider>)
      : main_thread_task_runner_(std::move(main_thread_task_runner)) {}

  std::unique_ptr<URLLoaderThrottleProvider> Clone() override {
    return std::make_unique<ShellContentRendererUrlLoaderThrottleProvider>(
        main_thread_task_runner_,
        base::PassKey<ShellContentRendererUrlLoaderThrottleProvider>());
  }

  std::vector<std::unique_ptr<blink::URLLoaderThrottle>> CreateThrottles(
      base::optional_ref<const blink::LocalFrameToken> local_frame_token,
      const network::ResourceRequest& request) override {
    std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles;
    if (local_frame_token.has_value()) {
      auto throttle =
          content::MaybeCreateIdentityUrlLoaderThrottle(base::BindRepeating(
              [](const blink::LocalFrameToken& token,
                 const scoped_refptr<base::SequencedTaskRunner>
                     main_thread_task_runner,
                 const url::Origin& origin,
                 blink::mojom::IdpSigninStatus status) {
                if (content::RenderThread::IsMainThread()) {
                  blink::SetIdpSigninStatus(token, origin, status);
                  return;
                }
                if (main_thread_task_runner) {
                  main_thread_task_runner->PostTask(
                      FROM_HERE, base::BindOnce(&blink::SetIdpSigninStatus,
                                                token, origin, status));
                }
              },
              local_frame_token.value(), main_thread_task_runner_));
      if (throttle)
        throttles.push_back(std::move(throttle));
    }

    return throttles;
  }

  void SetOnline(bool is_online) override {}

 private:
  // Set only when `this` was created on the main thread, or cloned from a
  // provider which was created on the main thread.
  scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner_;
};

void CreateRendererTestService(
    mojo::PendingReceiver<mojom::TestService> receiver) {
  // Owns itself.
  new TestRendererServiceImpl(std::move(receiver));
}

}  // namespace

ShellContentRendererClient::ShellContentRendererClient() {}

ShellContentRendererClient::~ShellContentRendererClient() {
}

void ShellContentRendererClient::SetUpWebAssemblyTrapHandler() {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
  // Mac and Windows use the default implementation (where the default v8 trap
  // handler gets set up).
  ContentRendererClient::SetUpWebAssemblyTrapHandler();
  return;
#else
  base::CommandLine* const command_line =
      base::CommandLine::ForCurrentProcess();
  const bool crash_reporter_enabled =
      command_line->HasSwitch(switches::kEnableCrashReporter)
#if BUILDFLAG(IS_POSIX)
      || command_line->HasSwitch(switches::kEnableCrashReporterForTesting)
#endif  // BUILDFLAG(IS_POSIX)
      ;

  if (crash_reporter_enabled) {
    // If either --enable-crash-reporter or --enable-crash-reporter-for-testing
    // is enabled it should take care of signal handling for us, use the default
    // implementation which doesn't register an additional handler.
    ContentRendererClient::SetUpWebAssemblyTrapHandler();
    return;
  }

  const bool use_v8_default_handler =
#if defined(ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX)
      base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kDisableInProcessStackTraces)
#else
      true
#endif  // defined(ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX)
      ;

  if (use_v8_default_handler) {
    // There is no signal handler yet, but it's okay if v8 registers one.
    v8::V8::EnableWebAssemblyTrapHandler(/*use_v8_signal_handler=*/true);
    return;
  }

#if defined(ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX)
  if (base::debug::SetStackDumpFirstChanceCallback(
          v8::TryHandleWebAssemblyTrapPosix)) {
    // Crashpad and Breakpad are disabled, but the in-process stack dump
    // handlers are enabled, so set the callback on the stack dump handlers.
    v8::V8::EnableWebAssemblyTrapHandler(/*use_v8_signal_handler=*/false);
    return;
  }

  // As the registration of the callback failed, we don't enable trap
  // handlers.
#endif  // defined(ENABLE_WEB_ASSEMBLY_TRAP_HANDLER_LINUX)
#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
}

void ShellContentRendererClient::RenderThreadStarted() {
  web_cache_impl_ = std::make_unique<web_cache::WebCacheImpl>();
}

void ShellContentRendererClient::ExposeInterfacesToBrowser(
    mojo::BinderMap* binders) {
  binders->Add<mojom::TestService>(
      &CreateRendererTestService,
      base::SingleThreadTaskRunner::GetCurrentDefault());
  binders->Add<mojom::PowerMonitorTest>(
      &PowerMonitorTestImpl::MakeSelfOwnedReceiver,
      base::SingleThreadTaskRunner::GetCurrentDefault());
  binders->Add<mojom::MainFrameCounterTest>(
      &MainFrameCounterTestImpl::Bind,
      base::SingleThreadTaskRunner::GetCurrentDefault());
  binders->Add<web_cache::mojom::WebCache>(
      base::BindRepeating(&web_cache::WebCacheImpl::BindReceiver,
                          base::Unretained(web_cache_impl_.get())),
      base::SingleThreadTaskRunner::GetCurrentDefault());
}

void ShellContentRendererClient::RenderFrameCreated(RenderFrame* render_frame) {
  // TODO(danakj): The ShellRenderFrameObserver is doing stuff only for
  // browser tests. If we only create that for browser tests then the override
  // of this method in WebTestContentRendererClient would not be needed.
  new ShellRenderFrameObserver(render_frame);
}

void ShellContentRendererClient::PrepareErrorPage(
    RenderFrame* render_frame,
    const blink::WebURLError& error,
    const std::string& http_method,
    content::mojom::AlternativeErrorPageOverrideInfoPtr
        alternative_error_page_info,
    std::string* error_html) {
  if (error_html && error_html->empty()) {
    *error_html =
        "<head><title>Error</title></head><body>Could not load the requested "
        "resource.<br/>Error code: " +
        base::NumberToString(error.reason()) +
        (error.reason() < 0 ? " (" + net::ErrorToString(error.reason()) + ")"
                            : "") +
        "</body>";
  }
}

void ShellContentRendererClient::PrepareErrorPageForHttpStatusError(
    content::RenderFrame* render_frame,
    const blink::WebURLError& error,
    const std::string& http_method,
    int http_status,
    content::mojom::AlternativeErrorPageOverrideInfoPtr
        alternative_error_page_info,
    std::string* error_html) {
  if (error_html) {
    *error_html =
        "<head><title>Error</title></head><body>Server returned HTTP status " +
        base::NumberToString(http_status) + "</body>";
  }
}

void ShellContentRendererClient::DidInitializeWorkerContextOnWorkerThread(
    v8::Local<v8::Context> context) {
  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kExposeInternalsForTesting)) {
    blink::WebTestingSupport::InjectInternalsObject(context);
  }
}

std::unique_ptr<blink::URLLoaderThrottleProvider>
ShellContentRendererClient::CreateURLLoaderThrottleProvider(
    blink::URLLoaderThrottleProviderType provider_type) {
  return std::make_unique<ShellContentRendererUrlLoaderThrottleProvider>();
}

#if BUILDFLAG(ENABLE_MOJO_CDM)
std::unique_ptr<media::KeySystemSupportRegistration>
ShellContentRendererClient::GetSupportedKeySystems(
    content::RenderFrame* render_frame,
    media::GetSupportedKeySystemsCB cb) {
  media::KeySystemInfos key_systems;
  if (base::FeatureList::IsEnabled(media::kExternalClearKeyForTesting))
    key_systems.push_back(
        std::make_unique<cdm::ExternalClearKeyKeySystemInfo>());
  std::move(cb).Run(std::move(key_systems));
  return nullptr;
}
#endif

std::unique_ptr<blink::WebPrescientNetworking>
ShellContentRendererClient::CreatePrescientNetworking(
    RenderFrame* render_frame) {
  return std::make_unique<network_hints::WebPrescientNetworkingImpl>(
      render_frame);
}

}  // namespace content