910e62b5创建于 1月15日历史提交
// Copyright 2019 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/renderer/browser_exposed_renderer_interfaces.h"

#include <stdint.h>

#include <memory>
#include <utility>

#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/task_runner.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "content/common/features.h"
#include "content/common/frame.mojom.h"
#include "content/public/common/content_client.h"
#include "content/public/common/resource_usage_reporter.mojom.h"
#include "content/public/common/resource_usage_reporter_type_converters.h"
#include "content/public/renderer/content_renderer_client.h"
#include "content/renderer/render_thread_impl.h"
#include "content/renderer/service_worker/embedded_worker_instance_client_impl.h"
#include "content/renderer/worker/shared_worker_factory_impl.h"
#include "content/services/auction_worklet/auction_worklet_service_impl.h"
#include "mojo/public/cpp/bindings/binder_map.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "v8/include/v8-isolate.h"
#include "v8/include/v8-statistics.h"

namespace content {

namespace {

constexpr int kWaitForWorkersStatsTimeoutMS = 20;

class ResourceUsageReporterImpl : public content::mojom::ResourceUsageReporter {
 public:
  explicit ResourceUsageReporterImpl(base::WeakPtr<RenderThread> thread)
      : thread_(std::move(thread)) {}
  ResourceUsageReporterImpl(const ResourceUsageReporterImpl&) = delete;
  ~ResourceUsageReporterImpl() override = default;

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

 private:
  static void CollectOnWorkerThread(
      const scoped_refptr<base::TaskRunner>& master,
      base::WeakPtr<ResourceUsageReporterImpl> impl) {
    size_t total_bytes = 0;
    size_t used_bytes = 0;
    v8::Isolate* isolate = v8::Isolate::GetCurrent();
    if (isolate) {
      v8::HeapStatistics heap_stats;
      isolate->GetHeapStatistics(&heap_stats);
      total_bytes = heap_stats.total_heap_size();
      used_bytes = heap_stats.used_heap_size();
    }
    master->PostTask(FROM_HERE,
                     base::BindOnce(&ResourceUsageReporterImpl::ReceiveStats,
                                    impl, total_bytes, used_bytes));
  }

  void ReceiveStats(size_t total_bytes, size_t used_bytes) {
    usage_data_->v8_bytes_allocated += total_bytes;
    usage_data_->v8_bytes_used += used_bytes;
    workers_to_go_--;
    if (!workers_to_go_)
      SendResults();
  }

  void SendResults() {
    if (!callback_.is_null())
      std::move(callback_).Run(std::move(usage_data_));
    callback_.Reset();
    weak_factory_.InvalidateWeakPtrs();
    workers_to_go_ = 0;
  }

  void GetUsageData(GetUsageDataCallback callback) override {
    DCHECK(callback_.is_null());
    weak_factory_.InvalidateWeakPtrs();
    usage_data_ = mojom::ResourceUsageData::New();
    usage_data_->reports_v8_stats = true;
    callback_ = std::move(callback);

    // Since it is not safe to call any Blink or V8 functions until Blink has
    // been initialized (which also initializes V8), early out and send 0 back
    // for all resources.
    if (!thread_) {
      SendResults();
      return;
    }

    blink::WebCacheResourceTypeStats stats;
    blink::WebCache::GetResourceTypeStats(&stats);
    usage_data_->web_cache_stats = mojom::ResourceTypeStats::From(stats);

    v8::Isolate* isolate = v8::Isolate::GetCurrent();
    if (isolate) {
      v8::HeapStatistics heap_stats;
      isolate->GetHeapStatistics(&heap_stats);
      usage_data_->v8_bytes_allocated = heap_stats.total_heap_size();
      usage_data_->v8_bytes_used = heap_stats.used_heap_size();
    }
    base::RepeatingClosure collect =
        base::BindRepeating(&ResourceUsageReporterImpl::CollectOnWorkerThread,
                            base::SingleThreadTaskRunner::GetCurrentDefault(),
                            weak_factory_.GetWeakPtr());
    workers_to_go_ =
        RenderThread::Get()->PostTaskToAllWebWorkers(std::move(collect));
    if (workers_to_go_) {
      // The guard task to send out partial stats
      // in case some workers are not responsive.
      base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
          FROM_HERE,
          base::BindOnce(&ResourceUsageReporterImpl::SendResults,
                         weak_factory_.GetWeakPtr()),
          base::Milliseconds(kWaitForWorkersStatsTimeoutMS));
    } else {
      // No worker threads so just send out the main thread data right away.
      SendResults();
    }
  }

  const base::WeakPtr<RenderThread> thread_;
  mojom::ResourceUsageDataPtr usage_data_;
  GetUsageDataCallback callback_;
  int workers_to_go_ = 0;

  base::WeakPtrFactory<ResourceUsageReporterImpl> weak_factory_{this};
};

void CreateResourceUsageReporter(
    base::WeakPtr<RenderThreadImpl> render_thread,
    mojo::PendingReceiver<mojom::ResourceUsageReporter> receiver) {
  mojo::MakeSelfOwnedReceiver(
      std::make_unique<ResourceUsageReporterImpl>(std::move(render_thread)),
      std::move(receiver));
}

void CreateEmbeddedWorkerWithRenderMainThread(
    scoped_refptr<base::SingleThreadTaskRunner> initiator_task_runner,
    base::WeakPtr<RenderThreadImpl> render_thread,
    mojo::PendingReceiver<blink::mojom::EmbeddedWorkerInstanceClient>
        receiver) {
  TRACE_EVENT0("ServiceWorker", "CreateEmbeddedWorkerWithRenderMainThread");
  initiator_task_runner->PostTask(
      FROM_HERE, base::BindOnce(&EmbeddedWorkerInstanceClientImpl::Create,
                                initiator_task_runner,
                                render_thread->cors_exempt_header_list(),
                                std::move(receiver)));
}

void CreateEmbeddedWorker(
    scoped_refptr<base::SingleThreadTaskRunner> initiator_task_runner,
    mojo::PendingReceiver<blink::mojom::EmbeddedWorkerInstanceClient>
        receiver) {
  TRACE_EVENT0("ServiceWorker", "CreateEmbeddedWorker");
  // An empty fake list is passed to
  // `EmbeddedWorkerInstanceClientImpl::Create()`. That will be overridden by
  // the actual cors exempt header list in
  // `EmbeddedWorkerInstanceClientImpl::StartWorker()`.
  //
  // TODO(crbug.com/40753993): Remove this fake empty list once we confirmed
  // this approach is fine.
  const std::vector<std::string> fake_cors_exempt_header_list;
  initiator_task_runner->PostTask(
      FROM_HERE,
      base::BindOnce(&EmbeddedWorkerInstanceClientImpl::Create,
                     initiator_task_runner, fake_cors_exempt_header_list,
                     std::move(receiver)));
}
}  // namespace

void ExposeRendererInterfacesToBrowser(
    base::WeakPtr<RenderThreadImpl> render_thread,
    mojo::BinderMap* binders) {
  DCHECK(render_thread);

  binders->Add<blink::mojom::SharedWorkerFactory>(
      &SharedWorkerFactoryImpl::Create,
      base::SingleThreadTaskRunner::GetCurrentDefault());
  binders->Add<mojom::ResourceUsageReporter>(
      base::BindRepeating(&CreateResourceUsageReporter, render_thread),
      base::SingleThreadTaskRunner::GetCurrentDefault());
#if BUILDFLAG(IS_ANDROID)
  binders->Add<auction_worklet::mojom::AuctionWorkletService>(

      &auction_worklet::AuctionWorkletServiceImpl::CreateForRenderer,
      base::SingleThreadTaskRunner::GetCurrentDefault());
#endif

  auto task_runner_for_service_worker_startup =
      base::ThreadPool::CreateSingleThreadTaskRunner(
          {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
           base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
  // TODO(crbug.com/40753993): Remove the feature flag and
  // `CreateEmbeddedWorkerWithRenderMainThread()` once we confirmed this
  // approach is fine.
  //
  // The kServiceWorkerAvoidMainThreadForInitialization feature flag is the
  // experimental flag to avoid the additional thread hop over the main thread
  // for the ServiceWorker initialization. Currently it's on the main thread as
  // CreateEmbeddedWorker accesses `cors_exempt_header_list` from
  // `render_thread`. When this feature flag is enabled, binds on
  // `task_runner_for_service_worker_startup` instead of the main thread, so
  // startup isn't blocked on the main thread.
  if (base::FeatureList::IsEnabled(
          features::kServiceWorkerAvoidMainThreadForInitialization)) {
    binders->Add<blink::mojom::EmbeddedWorkerInstanceClient>(
        base::BindRepeating(&CreateEmbeddedWorker,
                            task_runner_for_service_worker_startup),
        task_runner_for_service_worker_startup);
  } else {
    binders->Add<blink::mojom::EmbeddedWorkerInstanceClient>(
        base::BindRepeating(&CreateEmbeddedWorkerWithRenderMainThread,
                            task_runner_for_service_worker_startup,
                            render_thread),
        base::SingleThreadTaskRunner::GetCurrentDefault());
  }

  GetContentClient()->renderer()->ExposeInterfacesToBrowser(binders);
}

}  // namespace content