| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // 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/bind.h" |
| #include "base/feature_list.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/task/post_task.h" |
| #include "base/task/thread_pool.h" |
| #include "base/task_runner.h" |
| #include "base/time/time.h" |
| #include "build/build_config.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_frame_impl.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 "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.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::ThreadTaskRunnerHandle::Get(), 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::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&ResourceUsageReporterImpl::SendResults, |
| weak_factory_.GetWeakPtr()), |
| base::TimeDelta::FromMilliseconds(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 CreateEmbeddedWorker( |
| scoped_refptr<base::SingleThreadTaskRunner> initiator_task_runner, |
| base::WeakPtr<RenderThreadImpl> render_thread, |
| mojo::PendingReceiver<blink::mojom::EmbeddedWorkerInstanceClient> |
| receiver) { |
| initiator_task_runner->PostTask( |
| FROM_HERE, |
| base::BindOnce(&EmbeddedWorkerInstanceClientImpl::CreateForRequest, |
| initiator_task_runner, |
| render_thread->cors_exempt_header_list(), |
| std::move(receiver))); |
| } |
| |
| } // namespace |
| |
| void ExposeRendererInterfacesToBrowser( |
| base::WeakPtr<RenderThreadImpl> render_thread, |
| mojo::BinderMap* binders) { |
| DCHECK(render_thread); |
| |
| binders->Add(base::BindRepeating(&SharedWorkerFactoryImpl::Create), |
| base::ThreadTaskRunnerHandle::Get()); |
| binders->Add(base::BindRepeating(&CreateResourceUsageReporter, render_thread), |
| base::ThreadTaskRunnerHandle::Get()); |
| |
| auto task_runner_for_service_worker_startup = |
| base::ThreadPool::CreateSingleThreadTaskRunner( |
| {base::MayBlock(), base::TaskPriority::USER_BLOCKING, |
| base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); |
| // TODO(crbug.com/1186912): Bind on `task_runner_for_service_worker_startup` |
| // instead of the main thread, so startup isn't blocked on the main thread. |
| // Currently it's on the main thread as CreateEmbeddedWorker accesses |
| // `cors_exempt_header_list` from `render_thread`. |
| binders->Add(base::BindRepeating(&CreateEmbeddedWorker, |
| task_runner_for_service_worker_startup, |
| render_thread), |
| base::ThreadTaskRunnerHandle::Get()); |
| |
| GetContentClient()->renderer()->ExposeInterfacesToBrowser(binders); |
| } |
| |
| } // namespace content |