| // Copyright 2012 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. |
| |
| // Represents the browser side of the browser <--> renderer communication |
| // channel. There will be one RenderProcessHost per renderer process. |
| |
| #include "content/browser/renderer_host/render_process_host_impl.h" |
| |
| #include <algorithm> |
| #include <limits> |
| #include <map> |
| #include <set> |
| #include <unordered_map> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/base_switches.h" |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/callback.h" |
| #include "base/clang_profiling_buildflags.h" |
| #include "base/command_line.h" |
| #include "base/containers/adapters.h" |
| #include "base/debug/alias.h" |
| #include "base/debug/crash_logging.h" |
| #include "base/debug/dump_without_crashing.h" |
| #include "base/feature_list.h" |
| #include "base/files/file.h" |
| #include "base/files/file_util.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/memory/memory_pressure_monitor.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/shared_memory_mapping.h" |
| #include "base/memory/writable_shared_memory_region.h" |
| #include "base/message_loop/message_pump_type.h" |
| #include "base/metrics/field_trial.h" |
| #include "base/metrics/histogram_base.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/metrics/persistent_histogram_allocator.h" |
| #include "base/metrics/persistent_memory_allocator.h" |
| #include "base/metrics/statistics_recorder.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/no_destructor.h" |
| #include "base/numerics/ranges.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/process/process_handle.h" |
| #include "base/rand_util.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/supports_user_data.h" |
| #include "base/synchronization/lock.h" |
| #include "base/system/sys_info.h" |
| #include "base/task/post_task.h" |
| #include "base/task/thread_pool.h" |
| #include "base/thread_annotations.h" |
| #include "base/threading/thread.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/default_tick_clock.h" |
| #include "base/time/time.h" |
| #include "base/token.h" |
| #include "base/trace_event/memory_allocator_dump.h" |
| #include "base/trace_event/memory_dump_manager.h" |
| #include "base/trace_event/memory_dump_provider.h" |
| #include "base/trace_event/trace_event.h" |
| #include "build/build_config.h" |
| #include "cc/base/switches.h" |
| #include "components/discardable_memory/public/mojom/discardable_shared_memory_manager.mojom.h" |
| #include "components/discardable_memory/service/discardable_shared_memory_manager.h" |
| #include "components/metrics/single_sample_metrics.h" |
| #include "components/tracing/common/tracing_switches.h" |
| #include "components/viz/common/switches.h" |
| #include "components/viz/host/gpu_client.h" |
| #include "content/browser/appcache/chrome_appcache_service.h" |
| #include "content/browser/bad_message.h" |
| #include "content/browser/blob_storage/blob_registry_wrapper.h" |
| #include "content/browser/broadcast_channel/broadcast_channel_provider.h" |
| #include "content/browser/browser_child_process_host_impl.h" |
| #include "content/browser/browser_main.h" |
| #include "content/browser/browser_main_loop.h" |
| #include "content/browser/cache_storage/cache_storage_context_impl.h" |
| #include "content/browser/child_process_security_policy_impl.h" |
| #include "content/browser/code_cache/generated_code_cache.h" |
| #include "content/browser/code_cache/generated_code_cache_context.h" |
| #include "content/browser/compositor/surface_utils.h" |
| #include "content/browser/dom_storage/dom_storage_context_wrapper.h" |
| #include "content/browser/field_trial_recorder.h" |
| #include "content/browser/field_trial_synchronizer.h" |
| #include "content/browser/file_system/file_system_manager_impl.h" |
| #include "content/browser/font_unique_name_lookup/font_unique_name_lookup_service.h" |
| #include "content/browser/frame_host/render_frame_message_filter.h" |
| #include "content/browser/gpu/browser_gpu_client_delegate.h" |
| #include "content/browser/gpu/compositor_util.h" |
| #include "content/browser/gpu/gpu_process_host.h" |
| #include "content/browser/gpu/shader_cache_factory.h" |
| #include "content/browser/histogram_controller.h" |
| #include "content/browser/locks/lock_manager.h" |
| #include "content/browser/media/capture/audio_mirroring_manager.h" |
| #include "content/browser/media/media_internals.h" |
| #include "content/browser/media/midi_host.h" |
| #include "content/browser/mime_registry_impl.h" |
| #include "content/browser/native_file_system/native_file_system_manager_impl.h" |
| #include "content/browser/native_io/native_io_context.h" |
| #include "content/browser/navigation_subresource_loader_params.h" |
| #include "content/browser/network_service_instance_impl.h" |
| #include "content/browser/notifications/platform_notification_context_impl.h" |
| #include "content/browser/payments/payment_manager.h" |
| #include "content/browser/permissions/permission_service_context.h" |
| #include "content/browser/permissions/permission_service_impl.h" |
| #include "content/browser/push_messaging/push_messaging_manager.h" |
| #include "content/browser/quota/quota_context.h" |
| #include "content/browser/renderer_host/agent_metrics_collector.h" |
| #include "content/browser/renderer_host/code_cache_host_impl.h" |
| #include "content/browser/renderer_host/embedded_frame_sink_provider_impl.h" |
| #include "content/browser/renderer_host/file_utilities_host_impl.h" |
| #include "content/browser/renderer_host/media/media_stream_manager.h" |
| #include "content/browser/renderer_host/media/media_stream_track_metrics_host.h" |
| #include "content/browser/renderer_host/media/peer_connection_tracker_host.h" |
| #include "content/browser/renderer_host/media/video_capture_host.h" |
| #include "content/browser/renderer_host/p2p/socket_dispatcher_host.h" |
| #include "content/browser/renderer_host/pepper/pepper_message_filter.h" |
| #include "content/browser/renderer_host/pepper/pepper_renderer_connection.h" |
| #include "content/browser/renderer_host/plugin_registry_impl.h" |
| #include "content/browser/renderer_host/render_message_filter.h" |
| #include "content/browser/renderer_host/render_widget_helper.h" |
| #include "content/browser/renderer_host/render_widget_host_impl.h" |
| #include "content/browser/renderer_host/web_database_host_impl.h" |
| #include "content/browser/resolve_proxy_helper.h" |
| #include "content/browser/service_worker/service_worker_context_wrapper.h" |
| #include "content/browser/site_instance_impl.h" |
| #include "content/browser/storage_partition_impl.h" |
| #include "content/browser/theme_helper.h" |
| #include "content/browser/tracing/background_tracing_manager_impl.h" |
| #include "content/browser/url_loader_factory_params_helper.h" |
| #include "content/browser/v8_snapshot_files.h" |
| #include "content/browser/websockets/websocket_connector_impl.h" |
| #include "content/browser/webui/web_ui_controller_factory_registry.h" |
| #include "content/common/child_process.mojom.h" |
| #include "content/common/child_process_host_impl.h" |
| #include "content/common/content_switches_internal.h" |
| #include "content/common/frame_messages.h" |
| #include "content/common/in_process_child_thread_params.h" |
| #include "content/common/resource_messages.h" |
| #include "content/common/service_worker/service_worker_utils.h" |
| #include "content/common/view_messages.h" |
| #include "content/common/widget_messages.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_or_resource_context.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/content_browser_client.h" |
| #include "content/public/browser/device_service.h" |
| #include "content/public/browser/network_service_instance.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/notification_types.h" |
| #include "content/public/browser/render_process_host_creation_observer.h" |
| #include "content/public/browser/render_process_host_factory.h" |
| #include "content/public/browser/render_process_host_observer.h" |
| #include "content/public/browser/render_widget_host.h" |
| #include "content/public/browser/render_widget_host_iterator.h" |
| #include "content/public/browser/resource_context.h" |
| #include "content/public/browser/resource_coordinator_service.h" |
| #include "content/public/browser/site_isolation_policy.h" |
| #include "content/public/browser/webrtc_log.h" |
| #include "content/public/common/child_process_host.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/content_constants.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/common/process_type.h" |
| #include "content/public/common/result_codes.h" |
| #include "content/public/common/sandboxed_process_launcher_delegate.h" |
| #include "content/public/common/service_names.mojom.h" |
| #include "content/public/common/url_constants.h" |
| #include "content/public/common/web_preferences.h" |
| #include "content/public/common/zygote/zygote_buildflags.h" |
| #include "device/gamepad/gamepad_haptics_manager.h" |
| #include "google_apis/gaia/gaia_switches.h" |
| #include "gpu/GLES2/gl2extchromium.h" |
| #include "gpu/command_buffer/client/gpu_switches.h" |
| #include "gpu/command_buffer/common/context_creation_attribs.h" |
| #include "gpu/command_buffer/service/gpu_switches.h" |
| #include "gpu/ipc/host/gpu_memory_buffer_support.h" |
| #include "ipc/ipc.mojom.h" |
| #include "ipc/ipc_channel.h" |
| #include "ipc/ipc_channel_mojo.h" |
| #include "ipc/ipc_logging.h" |
| #include "ipc/trace_ipc_message.h" |
| #include "media/audio/audio_manager.h" |
| #include "media/base/media_switches.h" |
| #include "media/capture/capture_switches.h" |
| #include "media/media_buildflags.h" |
| #include "media/mojo/services/video_decode_perf_history.h" |
| #include "media/webrtc/webrtc_switches.h" |
| #include "mojo/public/cpp/bindings/pending_receiver.h" |
| #include "mojo/public/cpp/bindings/receiver.h" |
| #include "mojo/public/cpp/bindings/scoped_message_error_crash_key.h" |
| #include "mojo/public/cpp/system/platform_handle.h" |
| #include "net/url_request/url_request_context_getter.h" |
| #include "sandbox/policy/switches.h" |
| #include "services/device/public/mojom/battery_monitor.mojom.h" |
| #include "services/device/public/mojom/power_monitor.mojom.h" |
| #include "services/device/public/mojom/screen_orientation.mojom.h" |
| #include "services/device/public/mojom/time_zone_monitor.mojom.h" |
| #include "services/metrics/public/cpp/ukm_recorder.h" |
| #include "services/metrics/public/mojom/ukm_interface.mojom.h" |
| #include "services/metrics/ukm_recorder_interface.h" |
| #include "services/network/public/cpp/network_switches.h" |
| #include "services/network/public/mojom/network_service.mojom.h" |
| #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h" |
| #include "services/service_manager/embedder/switches.h" |
| #include "services/service_manager/public/cpp/binder_registry.h" |
| #include "services/service_manager/public/cpp/interface_provider.h" |
| #include "storage/browser/database/database_tracker.h" |
| #include "storage/browser/file_system/sandbox_file_system_backend.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/blink/public/common/page/launching_process_state.h" |
| #include "third_party/blink/public/common/switches.h" |
| #include "third_party/blink/public/common/user_agent/user_agent_metadata.h" |
| #include "third_party/blink/public/mojom/disk_allocator.mojom.h" |
| #include "third_party/blink/public/public_buildflags.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/accessibility/accessibility_switches.h" |
| #include "ui/base/ui_base_switches.h" |
| #include "ui/display/display_switches.h" |
| #include "ui/gl/gl_switches.h" |
| #include "ui/native_theme/native_theme_features.h" |
| #include "url/origin.h" |
| #include "url/url_constants.h" |
| |
| #if defined(OS_ANDROID) |
| #include "content/public/browser/android/java_interfaces.h" |
| #include "ipc/ipc_sync_channel.h" |
| #include "media/audio/android/audio_manager_android.h" |
| #else |
| #include "content/browser/gpu/gpu_data_manager_impl.h" |
| #endif |
| |
| #if defined(OS_LINUX) |
| #include <sys/resource.h> |
| #include <sys/time.h> |
| |
| #include "components/services/font/public/mojom/font_service.mojom.h" // nogncheck |
| #include "content/browser/font_service.h" // nogncheck |
| #include "third_party/blink/public/mojom/memory_usage_monitor_linux.mojom.h" // nogncheck |
| #endif |
| |
| #if defined(OS_MACOSX) |
| #include "content/browser/child_process_task_port_provider_mac.h" |
| #include "content/browser/sandbox_support_mac_impl.h" |
| #include "content/common/sandbox_support_mac.mojom.h" |
| #endif |
| |
| #if defined(OS_WIN) |
| #include "base/win/scoped_com_initializer.h" |
| #include "base/win/windows_version.h" |
| #include "content/browser/renderer_host/dwrite_font_proxy_impl_win.h" |
| #include "content/public/common/font_cache_dispatcher_win.h" |
| #include "content/public/common/font_cache_win.mojom.h" |
| #include "sandbox/policy/win/sandbox_win.h" |
| #include "sandbox/win/src/sandbox_policy.h" |
| #include "ui/display/win/dpi.h" |
| #endif |
| |
| #if BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| #include "content/browser/media/key_system_support_impl.h" |
| #endif |
| |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| #include "content/browser/plugin_service_impl.h" |
| #include "ppapi/shared_impl/ppapi_switches.h" // nogncheck |
| #endif |
| |
| #if BUILDFLAG(ENABLE_REPORTING) |
| #include "content/browser/net/reporting_service_proxy.h" |
| #endif |
| |
| #if BUILDFLAG(USE_MINIKIN_HYPHENATION) |
| #include "content/browser/hyphenation/hyphenation_impl.h" |
| #endif |
| |
| #if defined(USE_OZONE) |
| #include "ui/ozone/public/ozone_switches.h" |
| #endif |
| |
| #if BUILDFLAG(USE_ZYGOTE_HANDLE) |
| #include "content/public/common/zygote/zygote_handle.h" // nogncheck |
| #endif |
| |
| #if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX) |
| #include "content/public/common/profiling_utils.h" |
| #endif |
| |
| namespace content { |
| |
| namespace { |
| |
| // Stores the maximum number of renderer processes the content module can |
| // create. Only applies if it is set to a non-zero value. |
| size_t g_max_renderer_count_override = 0; |
| |
| bool g_run_renderer_in_process = false; |
| |
| RendererMainThreadFactoryFunction g_renderer_main_thread_factory = nullptr; |
| |
| base::Thread* g_in_process_thread = nullptr; |
| |
| RenderProcessHostFactory* g_render_process_host_factory_ = nullptr; |
| const char kSiteProcessMapKeyName[] = "content_site_process_map"; |
| |
| RenderProcessHost::AnalyzeHungRendererFunction g_analyze_hung_renderer = |
| nullptr; |
| |
| void CacheShaderInfo(int32_t id, base::FilePath path) { |
| if (GetShaderCacheFactorySingleton()) |
| GetShaderCacheFactorySingleton()->SetCacheInfo(id, path); |
| } |
| |
| void RemoveShaderInfo(int32_t id) { |
| if (GetShaderCacheFactorySingleton()) |
| GetShaderCacheFactorySingleton()->RemoveCacheInfo(id); |
| } |
| |
| // Allow us to only run the trial in the first renderer. |
| bool has_done_stun_trials = false; |
| |
| // the global list of all renderer processes |
| base::IDMap<RenderProcessHost*>& GetAllHosts() { |
| static base::NoDestructor<base::IDMap<RenderProcessHost*>> s_all_hosts; |
| return *s_all_hosts; |
| } |
| |
| // Returns the global list of RenderProcessHostCreationObserver objects. |
| std::vector<RenderProcessHostCreationObserver*>& GetAllCreationObservers() { |
| static base::NoDestructor<std::vector<RenderProcessHostCreationObserver*>> |
| s_all_creation_observers; |
| return *s_all_creation_observers; |
| } |
| |
| // Map of site to process, to ensure we only have one RenderProcessHost per |
| // site in process-per-site mode. Each map is specific to a BrowserContext. |
| class SiteProcessMap : public base::SupportsUserData::Data { |
| public: |
| typedef std::unordered_map<std::string, RenderProcessHost*> SiteToProcessMap; |
| SiteProcessMap() {} |
| |
| void RegisterProcess(const std::string& site, RenderProcessHost* process) { |
| // There could already exist a site to process mapping due to races between |
| // two WebContents with blank SiteInstances. If that occurs, keeping the |
| // existing entry and not overwriting it is a predictable behavior that is |
| // safe. |
| auto i = map_.find(site); |
| if (i == map_.end()) |
| map_[site] = process; |
| } |
| |
| RenderProcessHost* FindProcess(const std::string& site) { |
| auto i = map_.find(site); |
| if (i != map_.end()) |
| return i->second; |
| return nullptr; |
| } |
| |
| void RemoveProcess(RenderProcessHost* host) { |
| // Find all instances of this process in the map, then separately remove |
| // them. |
| std::set<std::string> sites; |
| for (SiteToProcessMap::const_iterator i = map_.begin(); i != map_.end(); |
| ++i) { |
| if (i->second == host) |
| sites.insert(i->first); |
| } |
| for (auto i = sites.begin(); i != sites.end(); ++i) { |
| auto iter = map_.find(*i); |
| if (iter != map_.end()) { |
| DCHECK_EQ(iter->second, host); |
| map_.erase(iter); |
| } |
| } |
| } |
| |
| private: |
| SiteToProcessMap map_; |
| }; |
| |
| // Find the SiteProcessMap specific to the given context. |
| SiteProcessMap* GetSiteProcessMapForBrowserContext(BrowserContext* context) { |
| DCHECK(context); |
| SiteProcessMap* existing_map = static_cast<SiteProcessMap*>( |
| context->GetUserData(kSiteProcessMapKeyName)); |
| if (existing_map) |
| return existing_map; |
| |
| auto new_map = std::make_unique<SiteProcessMap>(); |
| auto* new_map_ptr = new_map.get(); |
| context->SetUserData(kSiteProcessMapKeyName, std::move(new_map)); |
| return new_map_ptr; |
| } |
| |
| // NOTE: changes to this class need to be reviewed by the security team. |
| class RendererSandboxedProcessLauncherDelegate |
| : public SandboxedProcessLauncherDelegate { |
| public: |
| RendererSandboxedProcessLauncherDelegate() |
| #if defined(OS_WIN) |
| : renderer_code_integrity_enabled_( |
| GetContentClient()->browser()->IsRendererCodeIntegrityEnabled()) |
| #endif |
| { |
| } |
| |
| ~RendererSandboxedProcessLauncherDelegate() override {} |
| |
| #if defined(OS_WIN) |
| bool PreSpawnTarget(sandbox::TargetPolicy* policy) override { |
| sandbox::policy::SandboxWin::AddBaseHandleClosePolicy(policy); |
| |
| const base::string16& sid = |
| GetContentClient()->browser()->GetAppContainerSidForSandboxType( |
| GetSandboxType()); |
| if (!sid.empty()) |
| sandbox::policy::SandboxWin::AddAppContainerPolicy(policy, sid.c_str()); |
| ContentBrowserClient::RendererSpawnFlags flags( |
| ContentBrowserClient::RendererSpawnFlags::NONE); |
| if (renderer_code_integrity_enabled_) |
| flags = ContentBrowserClient::RendererSpawnFlags::RENDERER_CODE_INTEGRITY; |
| return GetContentClient()->browser()->PreSpawnRenderer(policy, flags); |
| } |
| #endif // OS_WIN |
| |
| #if BUILDFLAG(USE_ZYGOTE_HANDLE) |
| ZygoteHandle GetZygote() override { |
| const base::CommandLine& browser_command_line = |
| *base::CommandLine::ForCurrentProcess(); |
| base::CommandLine::StringType renderer_prefix = |
| browser_command_line.GetSwitchValueNative(switches::kRendererCmdPrefix); |
| if (!renderer_prefix.empty()) |
| return nullptr; |
| return GetGenericZygote(); |
| } |
| #endif // BUILDFLAG(USE_ZYGOTE_HANDLE) |
| |
| sandbox::policy::SandboxType GetSandboxType() override { |
| return sandbox::policy::SandboxType::kRenderer; |
| } |
| |
| #if defined(OS_WIN) |
| private: |
| const bool renderer_code_integrity_enabled_; |
| #endif |
| }; |
| |
| const char kSessionStorageHolderKey[] = "kSessionStorageHolderKey"; |
| |
| class SessionStorageHolder : public base::SupportsUserData::Data { |
| public: |
| SessionStorageHolder() |
| : session_storage_namespaces_awaiting_close_( |
| new std::map<int, SessionStorageNamespaceMap>) {} |
| |
| ~SessionStorageHolder() override { |
| // Its important to delete the map on the IO thread to avoid deleting |
| // the underlying namespaces prior to processing ipcs referring to them. |
| GetIOThreadTaskRunner({})->DeleteSoon( |
| FROM_HERE, session_storage_namespaces_awaiting_close_.release()); |
| } |
| |
| void Hold(const SessionStorageNamespaceMap& sessions, int widget_route_id) { |
| (*session_storage_namespaces_awaiting_close_)[widget_route_id] = sessions; |
| } |
| |
| void Release(int old_route_id) { |
| session_storage_namespaces_awaiting_close_->erase(old_route_id); |
| } |
| |
| private: |
| std::unique_ptr<std::map<int, SessionStorageNamespaceMap>> |
| session_storage_namespaces_awaiting_close_; |
| DISALLOW_COPY_AND_ASSIGN(SessionStorageHolder); |
| }; |
| |
| // This class manages spare RenderProcessHosts. |
| // |
| // There is a singleton instance of this class which manages a single spare |
| // renderer (SpareRenderProcessHostManager::GetInstance(), below). This class |
| // encapsulates the implementation of |
| // RenderProcessHost::WarmupSpareRenderProcessHost() |
| // |
| // RenderProcessHostImpl should call |
| // SpareRenderProcessHostManager::MaybeTakeSpareRenderProcessHost when creating |
| // a new RPH. In this implementation, the spare renderer is bound to a |
| // BrowserContext and its default StoragePartition. If |
| // MaybeTakeSpareRenderProcessHost is called with a BrowserContext that does not |
| // match, the spare renderer is discarded. Only the default StoragePartition |
| // will be able to use a spare renderer. The spare renderer will also not be |
| // used as a guest renderer (is_for_guests_ == true). |
| // |
| // It is safe to call WarmupSpareRenderProcessHost multiple times, although if |
| // called in a context where the spare renderer is not likely to be used |
| // performance may suffer due to the unnecessary RPH creation. |
| class SpareRenderProcessHostManager : public RenderProcessHostObserver { |
| public: |
| SpareRenderProcessHostManager() {} |
| |
| static SpareRenderProcessHostManager& GetInstance() { |
| static base::NoDestructor<SpareRenderProcessHostManager> s_instance; |
| return *s_instance; |
| } |
| |
| void WarmupSpareRenderProcessHost(BrowserContext* browser_context) { |
| if (spare_render_process_host_ && |
| spare_render_process_host_->GetBrowserContext() == browser_context) { |
| DCHECK_EQ(BrowserContext::GetDefaultStoragePartition(browser_context), |
| spare_render_process_host_->GetStoragePartition()); |
| return; // Nothing to warm up. |
| } |
| |
| CleanupSpareRenderProcessHost(); |
| |
| // Don't create a spare renderer if we're using --single-process or if we've |
| // got too many processes. See also ShouldTryToUseExistingProcessHost in |
| // this file. |
| if (RenderProcessHost::run_renderer_in_process() || |
| GetAllHosts().size() >= |
| RenderProcessHostImpl::GetMaxRendererProcessCount()) |
| return; |
| |
| // Don't create a spare renderer when the system is under load. This is |
| // currently approximated by only looking at the memory pressure. See also |
| // https://crbug.com/852905. |
| auto* memory_monitor = base::MemoryPressureMonitor::Get(); |
| if (memory_monitor && |
| memory_monitor->GetCurrentPressureLevel() >= |
| base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE) |
| return; |
| |
| spare_render_process_host_ = RenderProcessHostImpl::CreateRenderProcessHost( |
| browser_context, nullptr /* storage_partition_impl */, |
| nullptr /* site_instance */); |
| spare_render_process_host_->AddObserver(this); |
| spare_render_process_host_->Init(); |
| |
| // The spare render process isn't ready, so wait and do the "spare render |
| // process changed" callback in RenderProcessReady(). |
| } |
| |
| RenderProcessHost* MaybeTakeSpareRenderProcessHost( |
| BrowserContext* browser_context, |
| SiteInstanceImpl* site_instance) { |
| // Give embedder a chance to disable using a spare RenderProcessHost for |
| // certain SiteInstances. Some navigations, such as to NTP or extensions, |
| // require passing command-line flags to the renderer process at process |
| // launch time, but this cannot be done for spare RenderProcessHosts, which |
| // are started before it is known which navigation might use them. So, a |
| // spare RenderProcessHost should not be used in such cases. |
| // |
| // Note that exempting NTP and extensions from using the spare process might |
| // also happen via HasProcess check below (which returns true for |
| // process-per-site SiteInstances if the given process-per-site process |
| // already exists). Despite this potential overlap, it is important to do |
| // both kinds of checks (to account for other non-ntp/extension |
| // process-per-site scenarios + to work correctly even if |
| // ShouldUseSpareRenderProcessHost starts covering non-process-per-site |
| // scenarios). |
| bool embedder_allows_spare_usage = |
| GetContentClient()->browser()->ShouldUseSpareRenderProcessHost( |
| browser_context, site_instance->GetSiteInfo().site_url()); |
| |
| // We shouldn't use the spare if: |
| // 1. The SiteInstance has already got an associated process. This is |
| // important to avoid taking and then immediately discarding the spare |
| // for process-per-site scenarios (which the HasProcess call below |
| // accounts for). Note that HasProcess will return false and allow using |
| // the spare if the given process-per-site process hasn't been launched. |
| // 2. The SiteInstance has opted out of using the spare process. |
| bool site_instance_allows_spare_usage = |
| !site_instance->HasProcess() && |
| site_instance->CanAssociateWithSpareProcess(); |
| |
| // Get the StoragePartition for |site_instance|. Note that this might be |
| // different than the default StoragePartition for |browser_context|. |
| StoragePartition* site_storage = |
| BrowserContext::GetStoragePartition(browser_context, site_instance); |
| |
| // Log UMA metrics. |
| using SpareProcessMaybeTakeAction = |
| RenderProcessHostImpl::SpareProcessMaybeTakeAction; |
| SpareProcessMaybeTakeAction action = |
| SpareProcessMaybeTakeAction::kNoSparePresent; |
| if (!spare_render_process_host_) |
| action = SpareProcessMaybeTakeAction::kNoSparePresent; |
| else if (browser_context != spare_render_process_host_->GetBrowserContext()) |
| action = SpareProcessMaybeTakeAction::kMismatchedBrowserContext; |
| else if (site_storage != spare_render_process_host_->GetStoragePartition()) |
| action = SpareProcessMaybeTakeAction::kMismatchedStoragePartition; |
| else if (!embedder_allows_spare_usage) |
| action = SpareProcessMaybeTakeAction::kRefusedByEmbedder; |
| else if (!site_instance_allows_spare_usage) |
| action = SpareProcessMaybeTakeAction::kRefusedBySiteInstance; |
| else |
| action = SpareProcessMaybeTakeAction::kSpareTaken; |
| UMA_HISTOGRAM_ENUMERATION( |
| "BrowserRenderProcessHost.SpareProcessMaybeTakeAction", action); |
| |
| // Decide whether to take or drop the spare process. |
| RenderProcessHost* returned_process = nullptr; |
| if (spare_render_process_host_ && |
| browser_context == spare_render_process_host_->GetBrowserContext() && |
| site_storage == spare_render_process_host_->GetStoragePartition() && |
| !site_instance->IsGuest() && embedder_allows_spare_usage && |
| site_instance_allows_spare_usage) { |
| CHECK(spare_render_process_host_->HostHasNotBeenUsed()); |
| |
| // If the spare process ends up getting killed, the spare manager should |
| // discard the spare RPH, so if one exists, it should always be live here. |
| CHECK(spare_render_process_host_->IsInitializedAndNotDead()); |
| |
| DCHECK_EQ(SpareProcessMaybeTakeAction::kSpareTaken, action); |
| returned_process = spare_render_process_host_; |
| ReleaseSpareRenderProcessHost(spare_render_process_host_); |
| } else if (!RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes()) { |
| // If the spare shouldn't be kept around, then discard it as soon as we |
| // find that the current spare was mismatched. |
| CleanupSpareRenderProcessHost(); |
| } else if (GetAllHosts().size() >= |
| RenderProcessHostImpl::GetMaxRendererProcessCount()) { |
| // Drop the spare if we are at a process limit and the spare wasn't taken. |
| // This helps avoid process reuse. |
| CleanupSpareRenderProcessHost(); |
| } |
| |
| return returned_process; |
| } |
| |
| // Prepares for future requests (with an assumption that a future navigation |
| // might require a new process for |browser_context|). |
| // |
| // Note that depending on the caller PrepareForFutureRequests can be called |
| // after the spare_render_process_host_ has either been 1) matched and taken |
| // or 2) mismatched and ignored or 3) matched and ignored. |
| void PrepareForFutureRequests(BrowserContext* browser_context) { |
| if (RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes()) { |
| // Always keep around a spare process for the most recently requested |
| // |browser_context|. |
| WarmupSpareRenderProcessHost(browser_context); |
| } else { |
| // Discard the ignored (probably non-matching) spare so as not to waste |
| // resources. |
| CleanupSpareRenderProcessHost(); |
| } |
| } |
| |
| // Gracefully remove and cleanup a spare RenderProcessHost if it exists. |
| void CleanupSpareRenderProcessHost() { |
| if (spare_render_process_host_) { |
| // Stop observing the process, to avoid getting notifications as a |
| // consequence of the Cleanup call below - such notification could call |
| // back into CleanupSpareRenderProcessHost leading to stack overflow. |
| spare_render_process_host_->RemoveObserver(this); |
| |
| // Make sure the RenderProcessHost object gets destroyed. |
| if (!spare_render_process_host_->IsKeepAliveRefCountDisabled()) |
| spare_render_process_host_->Cleanup(); |
| |
| // Drop reference to the RenderProcessHost object. |
| spare_render_process_host_ = nullptr; |
| spare_render_process_host_changed_callback_list_.Notify(nullptr); |
| } |
| } |
| |
| RenderProcessHost* spare_render_process_host() { |
| return spare_render_process_host_; |
| } |
| |
| std::unique_ptr<base::CallbackList<void(RenderProcessHost*)>::Subscription> |
| RegisterSpareRenderProcessHostChangedCallback( |
| const base::RepeatingCallback<void(RenderProcessHost*)>& cb) { |
| // Do an initial notification, as the subscriber will need to know what the |
| // current spare host is. |
| cb.Run(spare_render_process_host_); |
| return spare_render_process_host_changed_callback_list_.Add(cb); |
| } |
| |
| private: |
| // Release ownership of |host| as a possible spare renderer. Called when |
| // |host| has either been 1) claimed to be used in a navigation or 2) shutdown |
| // somewhere else. |
| void ReleaseSpareRenderProcessHost(RenderProcessHost* host) { |
| if (spare_render_process_host_ && spare_render_process_host_ == host) { |
| spare_render_process_host_->RemoveObserver(this); |
| spare_render_process_host_ = nullptr; |
| spare_render_process_host_changed_callback_list_.Notify(nullptr); |
| } |
| } |
| |
| void RenderProcessReady(RenderProcessHost* host) override { |
| if (host == spare_render_process_host_) { |
| spare_render_process_host_changed_callback_list_.Notify( |
| spare_render_process_host_); |
| } |
| } |
| |
| void RenderProcessExited(RenderProcessHost* host, |
| const ChildProcessTerminationInfo& info) override { |
| if (host == spare_render_process_host_) |
| CleanupSpareRenderProcessHost(); |
| } |
| |
| void RenderProcessHostDestroyed(RenderProcessHost* host) override { |
| ReleaseSpareRenderProcessHost(host); |
| } |
| |
| // The clients who want to know when the spare render process host has |
| // changed. |
| base::CallbackList<void(RenderProcessHost*)> |
| spare_render_process_host_changed_callback_list_; |
| |
| // This is a bare pointer, because RenderProcessHost manages the lifetime of |
| // all its instances; see GetAllHosts(). |
| RenderProcessHost* spare_render_process_host_ = nullptr; |
| |
| DISALLOW_COPY_AND_ASSIGN(SpareRenderProcessHostManager); |
| }; |
| |
| class RenderProcessHostIsReadyObserver : public RenderProcessHostObserver { |
| public: |
| RenderProcessHostIsReadyObserver(RenderProcessHost* render_process_host, |
| base::OnceClosure task) |
| : render_process_host_(render_process_host), task_(std::move(task)) { |
| render_process_host_->AddObserver(this); |
| if (render_process_host_->IsReady()) |
| PostTask(); |
| } |
| |
| ~RenderProcessHostIsReadyObserver() override { |
| render_process_host_->RemoveObserver(this); |
| } |
| |
| void RenderProcessReady(RenderProcessHost* host) override { PostTask(); } |
| |
| void RenderProcessHostDestroyed(RenderProcessHost* host) override { |
| delete this; |
| } |
| |
| private: |
| void PostTask() { |
| GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce(&RenderProcessHostIsReadyObserver::CallTask, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void CallTask() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (render_process_host_->IsReady()) |
| std::move(task_).Run(); |
| |
| delete this; |
| } |
| |
| RenderProcessHost* render_process_host_; |
| base::OnceClosure task_; |
| base::WeakPtrFactory<RenderProcessHostIsReadyObserver> weak_factory_{this}; |
| |
| DISALLOW_COPY_AND_ASSIGN(RenderProcessHostIsReadyObserver); |
| }; |
| |
| // The following class is used to track the sites each RenderProcessHost is |
| // hosting frames for and expecting navigations to. There are two of them per |
| // BrowserContext: one for frames and one for navigations. |
| // |
| // For each site, the SiteProcessCountTracker keeps a map of counts per |
| // RenderProcessHost, which represents the number of frames/navigations |
| // for this site that are associated with the RenderProcessHost. This allows to |
| // quickly lookup a list of RenderProcessHost that can be used by a particular |
| // SiteInstance. On the other hand, it does not allow to quickly lookup which |
| // sites are hosted by a RenderProcessHost. This class is meant to help reusing |
| // RenderProcessHosts among SiteInstances, not to perform security checks for a |
| // RenderProcessHost. |
| // |
| // TODO(alexmos): Currently, the tracking in this class and in |
| // UnmatchedServiceWorkerProcessTracker is associated with a BrowserContext, |
| // but it needs to also consider StoragePartitions, so that process reuse is |
| // allowed only within the same StoragePartition. For now, the tracking is |
| // done only for the default StoragePartition. See https://crbug.com/752667. |
| const void* const kCommittedSiteProcessCountTrackerKey = |
| "CommittedSiteProcessCountTrackerKey"; |
| const void* const kPendingSiteProcessCountTrackerKey = |
| "PendingSiteProcessCountTrackerKey"; |
| class SiteProcessCountTracker : public base::SupportsUserData::Data, |
| public RenderProcessHostObserver { |
| public: |
| SiteProcessCountTracker() {} |
| ~SiteProcessCountTracker() override { DCHECK(map_.empty()); } |
| |
| void IncrementSiteProcessCount(const SiteInfo& site_info, |
| int render_process_host_id) { |
| std::map<ProcessID, Count>& counts_per_process = map_[site_info.site_url()]; |
| ++counts_per_process[render_process_host_id]; |
| |
| #ifndef NDEBUG |
| // In debug builds, observe the RenderProcessHost destruction, to check |
| // that it is properly removed from the map. |
| RenderProcessHost* host = RenderProcessHost::FromID(render_process_host_id); |
| if (!HasProcess(host)) |
| host->AddObserver(this); |
| #endif |
| } |
| |
| void DecrementSiteProcessCount(const SiteInfo& site_info, |
| int render_process_host_id) { |
| auto result = map_.find(site_info.site_url()); |
| DCHECK(result != map_.end()); |
| std::map<ProcessID, Count>& counts_per_process = result->second; |
| |
| --counts_per_process[render_process_host_id]; |
| DCHECK_GE(counts_per_process[render_process_host_id], 0); |
| |
| if (counts_per_process[render_process_host_id] == 0) |
| counts_per_process.erase(render_process_host_id); |
| |
| if (counts_per_process.empty()) |
| map_.erase(site_info.site_url()); |
| } |
| |
| void FindRenderProcessesForSiteInstance( |
| SiteInstanceImpl* site_instance, |
| std::set<RenderProcessHost*>* foreground_processes, |
| std::set<RenderProcessHost*>* background_processes) { |
| auto result = map_.find(site_instance->GetSiteInfo().site_url()); |
| if (result == map_.end()) |
| return; |
| |
| std::map<ProcessID, Count>& counts_per_process = result->second; |
| for (auto iter : counts_per_process) { |
| RenderProcessHost* host = RenderProcessHost::FromID(iter.first); |
| if (!host) { |
| // TODO(clamy): This shouldn't happen but we are getting reports from |
| // the field that this is happening. We need to figure out why some |
| // RenderProcessHosts are not taken out of the map when they're |
| // destroyed. |
| NOTREACHED(); |
| continue; |
| } |
| |
| // It's possible that |host| has become unsuitable for hosting |
| // |site_instance|, for example if it was reused by a navigation to a |
| // different site, and |site_instance| requires a dedicated process. Do |
| // not allow such hosts to be reused. See https://crbug.com/780661. |
| if (!host->MayReuseHost() || |
| !RenderProcessHostImpl::IsSuitableHost( |
| host, site_instance->GetIsolationContext(), |
| site_instance->GetSiteInfo().site_url(), |
| site_instance->lock_url(), site_instance->IsGuest())) { |
| continue; |
| } |
| |
| if (host->VisibleClientCount()) |
| foreground_processes->insert(host); |
| else |
| background_processes->insert(host); |
| } |
| } |
| |
| // Check whether |host| is associated with at least one URL for which |
| // SiteInstance does not assign site URLs. This is used to disqualify |host| |
| // from being reused if it has pending navigations to such URLs. |
| bool ContainsNonReusableSiteForHost(RenderProcessHost* host) { |
| for (auto iter : map_) { |
| // If SiteInstance doesn't assign a site URL for the current entry (and it |
| // isn't about:blank, which is allowed anywhere), check whether |host| is |
| // on the list of processes the entry is associated with. |
| // |
| // TODO(alexmos): ShouldAssignSiteForURL() expects a full URL, whereas we |
| // only have a site URL here. For now, this mismatch is ok since |
| // ShouldAssignSiteForURL() only cares about schemes in practice, but |
| // this should be cleaned up. |
| if (!SiteInstanceImpl::ShouldAssignSiteForURL(iter.first) && |
| !iter.first.IsAboutBlank() && |
| base::Contains(iter.second, host->GetID())) |
| return true; |
| } |
| return false; |
| } |
| |
| private: |
| void RenderProcessHostDestroyed(RenderProcessHost* host) override { |
| #ifndef NDEBUG |
| host->RemoveObserver(this); |
| DCHECK(!HasProcess(host)); |
| #endif |
| } |
| |
| #ifndef NDEBUG |
| // Used in debug builds to ensure that RenderProcessHost don't persist in the |
| // map after they've been destroyed. |
| bool HasProcess(RenderProcessHost* process) { |
| for (auto iter : map_) { |
| std::map<ProcessID, Count>& counts_per_process = iter.second; |
| for (auto iter_process : counts_per_process) { |
| if (iter_process.first == process->GetID()) |
| return true; |
| } |
| } |
| return false; |
| } |
| #endif |
| |
| using ProcessID = int; |
| using Count = int; |
| // TODO(wjmaclean): Convert the key from GURL to a SiteInfo hashkey. |
| using CountPerProcessPerSiteMap = std::map<GURL, std::map<ProcessID, Count>>; |
| CountPerProcessPerSiteMap map_; |
| }; |
| |
| bool ShouldUseSiteProcessTracking(BrowserContext* browser_context, |
| StoragePartition* dest_partition) { |
| // TODO(alexmos): Sites should be tracked separately for each |
| // StoragePartition. For now, track them only in the default one. |
| StoragePartition* default_partition = |
| BrowserContext::GetDefaultStoragePartition(browser_context); |
| if (dest_partition != default_partition) |
| return false; |
| |
| return true; |
| } |
| |
| bool ShouldTrackProcessForSite(BrowserContext* browser_context, |
| RenderProcessHost* render_process_host, |
| const SiteInfo& site_info) { |
| if (site_info.site_url().is_empty()) |
| return false; |
| |
| return ShouldUseSiteProcessTracking( |
| browser_context, render_process_host->GetStoragePartition()); |
| } |
| |
| bool ShouldFindReusableProcessHostForSite(BrowserContext* browser_context, |
| const SiteInfo& site_info) { |
| if (site_info.site_url().is_empty()) |
| return false; |
| |
| return ShouldUseSiteProcessTracking( |
| browser_context, BrowserContext::GetStoragePartitionForSite( |
| browser_context, site_info.site_url())); |
| } |
| |
| const void* const kUnmatchedServiceWorkerProcessTrackerKey = |
| "UnmatchedServiceWorkerProcessTrackerKey"; |
| |
| // This class tracks 'unmatched' service worker processes. When a service worker |
| // is started after a navigation to the site, SiteProcessCountTracker that is |
| // implemented above is used to find the matching renderer process which is used |
| // for the navigation. But a service worker may be started before a navigation |
| // (ex: Push notification -> show the page of the notification). |
| // This class tracks processes with 'unmatched' service workers until the |
| // processes are reused for a navigation to a matching site. After a single |
| // matching navigation is put into the process, all service workers for that |
| // site in that process are considered 'matched.' |
| // |
| // TODO(alexmos): Currently, the tracking in this class and in |
| // SiteProcessCountTracker is associated with a BrowserContext, but it needs to |
| // also consider StoragePartitions, so that process reuse is allowed only |
| // within the same StoragePartition. For now, the tracking is done only for |
| // the default StoragePartition. See https://crbug.com/752667. |
| class UnmatchedServiceWorkerProcessTracker |
| : public base::SupportsUserData::Data, |
| public RenderProcessHostObserver { |
| public: |
| // Registers |render_process_host| as having an unmatched service worker for |
| // |site_instance|. |
| static void Register(RenderProcessHost* render_process_host, |
| SiteInstanceImpl* site_instance) { |
| BrowserContext* browser_context = site_instance->GetBrowserContext(); |
| DCHECK(!site_instance->GetSiteInfo().site_url().is_empty()); |
| if (!ShouldTrackProcessForSite(browser_context, render_process_host, |
| site_instance->GetSiteInfo())) |
| return; |
| |
| UnmatchedServiceWorkerProcessTracker* tracker = |
| static_cast<UnmatchedServiceWorkerProcessTracker*>( |
| browser_context->GetUserData( |
| kUnmatchedServiceWorkerProcessTrackerKey)); |
| if (!tracker) { |
| tracker = new UnmatchedServiceWorkerProcessTracker(); |
| browser_context->SetUserData(kUnmatchedServiceWorkerProcessTrackerKey, |
| base::WrapUnique(tracker)); |
| } |
| tracker->RegisterProcessForSite(render_process_host, site_instance); |
| } |
| |
| // Find a process with an unmatched service worker for |site_instance| and |
| // removes the process from the tracker if it exists. |
| static RenderProcessHost* MatchWithSite(SiteInstanceImpl* site_instance) { |
| BrowserContext* browser_context = site_instance->GetBrowserContext(); |
| if (!ShouldFindReusableProcessHostForSite(browser_context, |
| site_instance->GetSiteInfo())) |
| return nullptr; |
| |
| UnmatchedServiceWorkerProcessTracker* tracker = |
| static_cast<UnmatchedServiceWorkerProcessTracker*>( |
| browser_context->GetUserData( |
| kUnmatchedServiceWorkerProcessTrackerKey)); |
| if (!tracker) |
| return nullptr; |
| return tracker->TakeFreshestProcessForSite(site_instance); |
| } |
| |
| UnmatchedServiceWorkerProcessTracker() {} |
| |
| ~UnmatchedServiceWorkerProcessTracker() override { |
| DCHECK(site_process_set_.empty()); |
| } |
| |
| // Implementation of RenderProcessHostObserver. |
| void RenderProcessHostDestroyed(RenderProcessHost* host) override { |
| DCHECK(HasProcess(host)); |
| int process_id = host->GetID(); |
| for (auto it = site_process_set_.begin(); it != site_process_set_.end();) { |
| if (it->second == process_id) { |
| it = site_process_set_.erase(it); |
| } else { |
| ++it; |
| } |
| } |
| host->RemoveObserver(this); |
| } |
| |
| private: |
| using ProcessID = int; |
| // TODO(wjmaclean): Convert the pair to use SiteInfo hashkey instead of GURL. |
| using SiteProcessIDPair = std::pair<GURL, ProcessID>; |
| using SiteProcessIDPairSet = std::set<SiteProcessIDPair>; |
| |
| void RegisterProcessForSite(RenderProcessHost* host, |
| SiteInstanceImpl* site_instance) { |
| if (!HasProcess(host)) |
| host->AddObserver(this); |
| site_process_set_.insert(SiteProcessIDPair( |
| site_instance->GetSiteInfo().site_url(), host->GetID())); |
| } |
| |
| RenderProcessHost* TakeFreshestProcessForSite( |
| SiteInstanceImpl* site_instance) { |
| SiteProcessIDPair site_process_pair = |
| FindFreshestProcessForSite(site_instance); |
| |
| if (site_process_pair.first.is_empty()) |
| return nullptr; |
| |
| RenderProcessHost* host = |
| RenderProcessHost::FromID(site_process_pair.second); |
| |
| if (!host) |
| return nullptr; |
| |
| // It's possible that |host| is currently unsuitable for hosting |
| // |site_url|, for example if it was used for a ServiceWorker for a |
| // nonexistent extension URL. See https://crbug.com/782349 and |
| // https://crbug.com/780661. |
| GURL site_url(site_instance->GetSiteInfo().site_url()); |
| if (!host->MayReuseHost() || |
| !RenderProcessHostImpl::IsSuitableHost( |
| host, site_instance->GetIsolationContext(), site_url, |
| site_instance->lock_url(), site_instance->IsGuest())) |
| return nullptr; |
| |
| site_process_set_.erase(site_process_pair); |
| if (!HasProcess(host)) |
| host->RemoveObserver(this); |
| return host; |
| } |
| |
| SiteProcessIDPair FindFreshestProcessForSite( |
| SiteInstanceImpl* site_instance) const { |
| const auto reversed_site_process_set = base::Reversed(site_process_set_); |
| if (site_instance->IsDefaultSiteInstance()) { |
| // See if we can find an entry that maps to a site associated with the |
| // default SiteInstance. This allows the default SiteInstance to reuse a |
| // service worker process for any site that has been associated with it. |
| for (const auto& site_process_pair : reversed_site_process_set) { |
| if (site_instance->IsSiteInDefaultSiteInstance(site_process_pair.first)) |
| return site_process_pair; |
| } |
| } else { |
| const GURL site_url(site_instance->GetSiteInfo().site_url()); |
| for (const auto& site_process_pair : reversed_site_process_set) { |
| if (site_process_pair.first == site_url) |
| return site_process_pair; |
| } |
| } |
| return SiteProcessIDPair(); |
| } |
| |
| // Returns true if this tracker contains the process ID |host->GetID()|. |
| bool HasProcess(RenderProcessHost* host) const { |
| int process_id = host->GetID(); |
| for (const auto& site_process_id : site_process_set_) { |
| if (site_process_id.second == process_id) |
| return true; |
| } |
| return false; |
| } |
| |
| // Use std::set because duplicates don't need to be tracked separately (eg., |
| // service workers for the same site in the same process). It is sorted in the |
| // order of insertion. |
| SiteProcessIDPairSet site_process_set_; |
| }; |
| |
| void CopyFeatureSwitch(const base::CommandLine& src, |
| base::CommandLine* dest, |
| const char* switch_name) { |
| std::vector<std::string> features = FeaturesFromSwitch(src, switch_name); |
| if (!features.empty()) |
| dest->AppendSwitchASCII(switch_name, base::JoinString(features, ",")); |
| } |
| |
| RenderProcessHostImpl::DomStorageBinder& GetDomStorageBinder() { |
| static base::NoDestructor<RenderProcessHostImpl::DomStorageBinder> binder; |
| return *binder; |
| } |
| |
| RenderProcessHostImpl::BroadcastChannelProviderReceiverHandler& |
| GetBroadcastChannelProviderReceiverHandler() { |
| static base::NoDestructor< |
| RenderProcessHostImpl::BroadcastChannelProviderReceiverHandler> |
| instance; |
| return *instance; |
| } |
| |
| RenderProcessHostImpl::CodeCacheHostReceiverHandler& |
| GetCodeCacheHostReceiverHandler() { |
| static base::NoDestructor<RenderProcessHostImpl::CodeCacheHostReceiverHandler> |
| instance; |
| return *instance; |
| } |
| |
| // Keep track of plugin process IDs that require exceptions from CORB, |
| // request_initiator_site_lock checks (for particular origins), or both. |
| struct PluginExceptionsForNetworkService { |
| bool is_corb_disabled = false; |
| std::set<url::Origin> allowed_request_initiators; |
| }; |
| std::map<int, PluginExceptionsForNetworkService>& |
| GetPluginExceptionsForNetworkService() { |
| static base::NoDestructor<std::map<int, PluginExceptionsForNetworkService>> |
| s_data; |
| return *s_data; |
| } |
| |
| void OnNetworkServiceCrashRestorePluginExceptions() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| network::mojom::NetworkService* network_service = GetNetworkService(); |
| for (auto it : GetPluginExceptionsForNetworkService()) { |
| const int process_id = it.first; |
| const PluginExceptionsForNetworkService& exceptions = it.second; |
| |
| if (exceptions.is_corb_disabled) |
| network_service->AddCorbExceptionForPlugin(process_id); |
| |
| for (const url::Origin& origin : exceptions.allowed_request_initiators) |
| network_service->AddAllowedRequestInitiatorForPlugin(process_id, origin); |
| } |
| } |
| |
| void RemoveNetworkServicePluginExceptions(int process_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| GetPluginExceptionsForNetworkService().erase(process_id); |
| GetNetworkService()->RemoveSecurityExceptionsForPlugin(process_id); |
| } |
| |
| bool PrepareToAddNewPluginExceptions(int process_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| RenderProcessHost* process = RenderProcessHostImpl::FromID(process_id); |
| if (!process) |
| return false; // failure |
| |
| process->CleanupNetworkServicePluginExceptionsUponDestruction(); |
| |
| static base::NoDestructor< |
| std::unique_ptr<base::CallbackList<void()>::Subscription>> |
| s_crash_handler_subscription; |
| if (!*s_crash_handler_subscription) { |
| *s_crash_handler_subscription = RegisterNetworkServiceCrashHandler( |
| base::BindRepeating(&OnNetworkServiceCrashRestorePluginExceptions)); |
| } |
| |
| return true; // success |
| } |
| |
| void AddCorbExceptionForPluginOnUIThread(int process_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (!PrepareToAddNewPluginExceptions(process_id)) |
| return; |
| |
| GetPluginExceptionsForNetworkService()[process_id].is_corb_disabled = true; |
| GetNetworkService()->AddCorbExceptionForPlugin(process_id); |
| } |
| |
| void AddAllowedRequestInitiatorForPluginOnUIThread( |
| int process_id, |
| const url::Origin& allowed_request_initiator) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (!PrepareToAddNewPluginExceptions(process_id)) |
| return; |
| |
| GetPluginExceptionsForNetworkService()[process_id] |
| .allowed_request_initiators.insert(allowed_request_initiator); |
| GetNetworkService()->AddAllowedRequestInitiatorForPlugin( |
| process_id, allowed_request_initiator); |
| } |
| |
| #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS) |
| static constexpr size_t kUnknownPlatformProcessLimit = 0; |
| |
| // Returns the process limit from the system. Use |kUnknownPlatformProcessLimit| |
| // to indicate failure and std::numeric_limits<size_t>::max() to indicate |
| // unlimited. |
| size_t GetPlatformProcessLimit() { |
| #if defined(OS_LINUX) |
| struct rlimit limit; |
| if (getrlimit(RLIMIT_NPROC, &limit) != 0) |
| return kUnknownPlatformProcessLimit; |
| |
| if (limit.rlim_cur == RLIM_INFINITY) |
| return std::numeric_limits<size_t>::max(); |
| return base::saturated_cast<size_t>(limit.rlim_cur); |
| #else |
| // TODO(https://crbug.com/104689): Implement on other platforms. |
| return kUnknownPlatformProcessLimit; |
| #endif // defined(OS_LINUX) |
| } |
| #endif // !defined(OS_ANDROID) && !defined(OS_CHROMEOS) |
| |
| RenderProcessHost::BindHostReceiverInterceptor& |
| GetBindHostReceiverInterceptor() { |
| static base::NoDestructor<RenderProcessHost::BindHostReceiverInterceptor> |
| interceptor; |
| return *interceptor; |
| } |
| |
| RenderProcessHostImpl::BatteryMonitorBinder& GetBatteryMonitorBinderOverride() { |
| static base::NoDestructor<RenderProcessHostImpl::BatteryMonitorBinder> binder; |
| return *binder; |
| } |
| |
| void BindBatteryMonitor( |
| mojo::PendingReceiver<device::mojom::BatteryMonitor> receiver) { |
| const auto& binder = GetBatteryMonitorBinderOverride(); |
| if (binder) |
| binder.Run(std::move(receiver)); |
| else |
| GetDeviceService().BindBatteryMonitor(std::move(receiver)); |
| } |
| |
| RenderProcessHostImpl::CreateNetworkFactoryCallback& |
| GetCreateNetworkFactoryCallback() { |
| static base::NoDestructor<RenderProcessHostImpl::CreateNetworkFactoryCallback> |
| s_callback; |
| return *s_callback; |
| } |
| |
| RenderProcessHostImpl::BadMojoMessageCallbackForTesting& |
| GetBadMojoMessageCallbackForTesting() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| static base::NoDestructor< |
| RenderProcessHostImpl::BadMojoMessageCallbackForTesting> |
| s_callback; |
| return *s_callback; |
| } |
| |
| void InvokeBadMojoMessageCallbackForTesting(int render_process_id, |
| const std::string& error) { |
| if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce(&InvokeBadMojoMessageCallbackForTesting, |
| render_process_id, error)); |
| return; |
| } |
| |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| RenderProcessHostImpl::BadMojoMessageCallbackForTesting& callback = |
| GetBadMojoMessageCallbackForTesting(); |
| if (!callback.is_null()) |
| callback.Run(render_process_id, error); |
| } |
| |
| } // namespace |
| |
| // A RenderProcessHostImpl's IO thread implementation of the |
| // |mojom::ChildProcessHost| interface. This exists to allow the process host |
| // to bind incoming receivers on the IO-thread without a main-thread hop if |
| // necessary. Also owns the RPHI's |mojom::ChildProcess| remote. |
| class RenderProcessHostImpl::IOThreadHostImpl |
| : public mojom::ChildProcessHostBootstrap, |
| public mojom::ChildProcessHost { |
| public: |
| IOThreadHostImpl(int render_process_id, |
| base::WeakPtr<RenderProcessHostImpl> weak_host, |
| std::unique_ptr<service_manager::BinderRegistry> binders, |
| mojo::PendingReceiver<mojom::ChildProcessHostBootstrap> |
| bootstrap_receiver) |
| : render_process_id_(render_process_id), |
| weak_host_(std::move(weak_host)), |
| binders_(std::move(binders)), |
| bootstrap_receiver_(this, std::move(bootstrap_receiver)) {} |
| ~IOThreadHostImpl() override = default; |
| |
| private: |
| // mojom::ChildProcessHostBootstrap implementation: |
| void BindProcessHost( |
| mojo::PendingReceiver<mojom::ChildProcessHost> receiver) override { |
| receiver_.Bind(std::move(receiver)); |
| } |
| |
| // mojom::ChildProcessHost implementation: |
| void BindHostReceiver(mojo::GenericPendingReceiver receiver) override { |
| const auto& interceptor = GetBindHostReceiverInterceptor(); |
| if (interceptor) { |
| interceptor.Run(render_process_id_, &receiver); |
| if (!receiver) |
| return; |
| } |
| |
| #if defined(OS_LINUX) |
| if (auto font_receiver = receiver.As<font_service::mojom::FontService>()) { |
| ConnectToFontService(std::move(font_receiver)); |
| return; |
| } |
| #endif |
| |
| #if defined(OS_WIN) |
| if (auto r = receiver.As<mojom::FontCacheWin>()) { |
| FontCacheDispatcher::Create(std::move(r)); |
| return; |
| } |
| #endif |
| |
| #if defined(OS_MACOSX) |
| if (auto r = receiver.As<mojom::SandboxSupportMac>()) { |
| static base::NoDestructor<SandboxSupportMacImpl> sandbox_support; |
| sandbox_support->BindReceiver(std::move(r)); |
| return; |
| } |
| #endif |
| |
| if (auto r = receiver.As< |
| discardable_memory::mojom::DiscardableSharedMemoryManager>()) { |
| discardable_memory::DiscardableSharedMemoryManager::Get()->Bind( |
| std::move(r)); |
| return; |
| } |
| |
| if (auto r = receiver.As<ukm::mojom::UkmRecorderInterface>()) { |
| metrics::UkmRecorderInterface::Create(ukm::UkmRecorder::Get(), |
| std::move(r)); |
| return; |
| } |
| |
| std::string interface_name = *receiver.interface_name(); |
| mojo::ScopedMessagePipeHandle pipe = receiver.PassPipe(); |
| if (binders_->TryBindInterface(interface_name, &pipe)) |
| return; |
| |
| receiver = mojo::GenericPendingReceiver(interface_name, std::move(pipe)); |
| if (!receiver) |
| return; |
| |
| GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce(&IOThreadHostImpl::BindHostReceiverOnUIThread, |
| weak_host_, std::move(receiver))); |
| } |
| |
| static void BindHostReceiverOnUIThread( |
| base::WeakPtr<RenderProcessHostImpl> weak_host, |
| mojo::GenericPendingReceiver receiver) { |
| if (weak_host) |
| weak_host->OnBindHostReceiver(std::move(receiver)); |
| } |
| |
| const int render_process_id_; |
| const base::WeakPtr<RenderProcessHostImpl> weak_host_; |
| std::unique_ptr<service_manager::BinderRegistry> binders_; |
| mojo::Receiver<mojom::ChildProcessHostBootstrap> bootstrap_receiver_; |
| mojo::Receiver<mojom::ChildProcessHost> receiver_{this}; |
| |
| DISALLOW_COPY_AND_ASSIGN(IOThreadHostImpl); |
| }; |
| |
| // static |
| scoped_refptr<base::SingleThreadTaskRunner> |
| RenderProcessHostImpl::GetInProcessRendererThreadTaskRunnerForTesting() { |
| return g_in_process_thread->task_runner(); |
| } |
| |
| #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS) |
| // static |
| size_t RenderProcessHostImpl::GetPlatformMaxRendererProcessCount() { |
| // Set the limit to half of the system limit to leave room for other programs. |
| size_t limit = GetPlatformProcessLimit() / 2; |
| |
| // If the system limit is unavailable, use a fallback value instead. |
| if (limit == kUnknownPlatformProcessLimit) { |
| static constexpr size_t kMaxRendererProcessCount = 82; |
| limit = kMaxRendererProcessCount; |
| } |
| return limit; |
| } |
| #endif |
| |
| // static |
| size_t RenderProcessHost::GetMaxRendererProcessCount() { |
| if (g_max_renderer_count_override) |
| return g_max_renderer_count_override; |
| |
| #if defined(OS_ANDROID) |
| // On Android we don't maintain a limit of renderer process hosts - we are |
| // happy with keeping a lot of these, as long as the number of live renderer |
| // processes remains reasonable, and on Android the OS takes care of that. |
| return std::numeric_limits<size_t>::max(); |
| #elif defined(OS_CHROMEOS) |
| // On Chrome OS new renderer processes are very cheap and there's no OS |
| // driven constraint on the number of processes, and the effectiveness |
| // of the tab discarder is very poor when we have tabs sharing a |
| // renderer process. So, set a high limit, and based on UMA stats |
| // for CrOS the 99.9th percentile of Tabs.MaxTabsInADay is around 100. |
| return 100; |
| #else |
| // On other platforms, calculate the maximum number of renderer process hosts |
| // according to the amount of installed memory as reported by the OS, along |
| // with some hard-coded limits. The calculation assumes that the renderers |
| // will use up to half of the installed RAM and assumes that each WebContents |
| // uses |kEstimatedWebContentsMemoryUsage| MB. If this assumption changes, the |
| // ThirtyFourTabs test needs to be adjusted to match the expected number of |
| // processes. |
| // |
| // Using the above assumptions, with the given amounts of installed memory |
| // below on a 64-bit CPU, the maximum renderer count based on available RAM |
| // alone will be as follows: |
| // |
| // 128 MB -> 0 |
| // 512 MB -> 3 |
| // 1024 MB -> 6 |
| // 4096 MB -> 24 |
| // 16384 MB -> 96 |
| // |
| // Then the calculated value will be clamped by |kMinRendererProcessCount| and |
| // GetPlatformMaxRendererProcessCount(). |
| |
| static size_t max_count = 0; |
| if (!max_count) { |
| static constexpr size_t kEstimatedWebContentsMemoryUsage = |
| #if defined(ARCH_CPU_64_BITS) |
| 85; // In MB |
| #else |
| 60; // In MB |
| #endif |
| max_count = base::SysInfo::AmountOfPhysicalMemoryMB() / 2; |
| max_count /= kEstimatedWebContentsMemoryUsage; |
| |
| static constexpr size_t kMinRendererProcessCount = 3; |
| static const size_t kMaxRendererProcessCount = |
| RenderProcessHostImpl::GetPlatformMaxRendererProcessCount(); |
| DCHECK_LE(kMinRendererProcessCount, kMaxRendererProcessCount); |
| |
| max_count = base::ClampToRange(max_count, kMinRendererProcessCount, |
| kMaxRendererProcessCount); |
| } |
| return max_count; |
| #endif |
| } |
| |
| // static |
| void RenderProcessHost::SetMaxRendererProcessCount(size_t count) { |
| g_max_renderer_count_override = count; |
| if (GetAllHosts().size() > count) { |
| SpareRenderProcessHostManager::GetInstance() |
| .CleanupSpareRenderProcessHost(); |
| } |
| } |
| |
| // static |
| int RenderProcessHost::GetCurrentRenderProcessCountForTesting() { |
| RenderProcessHost::iterator it = RenderProcessHost::AllHostsIterator(); |
| int count = 0; |
| while (!it.IsAtEnd()) { |
| RenderProcessHost* host = it.GetCurrentValue(); |
| if (host->IsInitializedAndNotDead() && |
| host != RenderProcessHostImpl::GetSpareRenderProcessHostForTesting()) { |
| count++; |
| } |
| it.Advance(); |
| } |
| return count; |
| } |
| |
| // static |
| RenderProcessHost* RenderProcessHostImpl::CreateRenderProcessHost( |
| BrowserContext* browser_context, |
| StoragePartitionImpl* storage_partition_impl, |
| SiteInstanceImpl* site_instance) { |
| if (g_render_process_host_factory_) { |
| return g_render_process_host_factory_->CreateRenderProcessHost( |
| browser_context, site_instance); |
| } |
| |
| if (!storage_partition_impl) { |
| storage_partition_impl = static_cast<StoragePartitionImpl*>( |
| BrowserContext::GetStoragePartition(browser_context, site_instance)); |
| } |
| // If we've made a StoragePartition for guests (e.g., for the <webview> tag), |
| // stash the Site URL on it. This way, when we start a service worker inside |
| // this storage partition, we can create the appropriate SiteInstance for |
| // finding a process (e.g., we will try to start a worker from |
| // "https://example.com/sw.js" but need to use the guest site URL |
| // to get a process in the guest's StoragePartition.) |
| const bool is_for_guests_only = site_instance && site_instance->IsGuest(); |
| if (is_for_guests_only && |
| storage_partition_impl->site_for_guest_service_worker().is_empty()) { |
| storage_partition_impl->set_site_for_guest_service_worker( |
| site_instance->GetSiteInfo().site_url()); |
| } |
| |
| return new RenderProcessHostImpl(browser_context, storage_partition_impl, |
| is_for_guests_only); |
| } |
| |
| // static |
| const unsigned int RenderProcessHostImpl::kMaxFrameDepthForPriority = |
| std::numeric_limits<unsigned int>::max(); |
| |
| RenderProcessHostImpl::RenderProcessHostImpl( |
| BrowserContext* browser_context, |
| StoragePartitionImpl* storage_partition_impl, |
| bool is_for_guests_only) |
| : fast_shutdown_started_(false), |
| deleting_soon_(false), |
| #ifndef NDEBUG |
| is_self_deleted_(false), |
| #endif |
| pending_views_(0), |
| keep_alive_ref_count_(0), |
| is_keep_alive_ref_count_disabled_(false), |
| visible_clients_(0), |
| priority_(!blink::kLaunchingProcessIsBackgrounded, |
| false /* has_media_stream */, |
| false /* has_foreground_service_worker */, |
| false /* all_low_priority_frames */, |
| frame_depth_, |
| false /* intersects_viewport */, |
| true /* boost_for_pending_views */ |
| #if defined(OS_ANDROID) |
| , |
| ChildProcessImportance::NORMAL |
| #endif |
| ), |
| clock_(base::DefaultTickClock::GetInstance()), |
| id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()), |
| browser_context_(browser_context), |
| storage_partition_impl_(storage_partition_impl), |
| sudden_termination_allowed_(true), |
| is_blocked_(false), |
| is_for_guests_only_(is_for_guests_only), |
| is_unused_(true), |
| delayed_cleanup_needed_(false), |
| within_process_died_observer_(false), |
| channel_connected_(false), |
| sent_render_process_ready_(false), |
| push_messaging_manager_( |
| nullptr, |
| base::OnTaskRunnerDeleter(base::CreateSequencedTaskRunner( |
| {ServiceWorkerContext::GetCoreThreadId()}))), |
| instance_weak_factory_(base::in_place, this), |
| frame_sink_provider_(id_), |
| shutdown_exit_code_(-1) { |
| TRACE_EVENT2("shutdown", "RenderProcessHostImpl", "render_process_host", this, |
| "id", GetID()); |
| TRACE_EVENT_NESTABLE_ASYNC_BEGIN2("shutdown", "Browser.RenderProcessHostImpl", |
| this, "render_process_host", this, |
| "browser_context", browser_context_); |
| widget_helper_ = new RenderWidgetHelper(); |
| resolve_proxy_helper_ = new ResolveProxyHelper(GetID()); |
| |
| ChildProcessSecurityPolicyImpl::GetInstance()->Add(GetID(), browser_context); |
| |
| CHECK(!BrowserMainRunner::ExitedMainMessageLoop()); |
| RegisterHost(GetID(), this); |
| GetAllHosts().set_check_on_null_data(true); |
| // Initialize |child_process_activity_time_| to a reasonable value. |
| mark_child_process_activity_time(); |
| |
| if (!GetBrowserContext()->IsOffTheRecord() && |
| !base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kDisableGpuShaderDiskCache)) { |
| GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce(&CacheShaderInfo, GetID(), |
| storage_partition_impl_->GetPath())); |
| } |
| |
| // This instance of PushMessagingManager is only used from clients |
| // bound to service workers (i.e. PushProvider), since frame-bound |
| // clients will rely on BrowserInterfaceBroker instead. Therefore, |
| // pass an invalid frame ID here. |
| // |
| // Constructing the manager must occur after RegisterHost(), since |
| // PushMessagingManager::Core looks up |this| using the process id. |
| push_messaging_manager_.reset(new PushMessagingManager( |
| GetID(), |
| /* render_frame_id= */ ChildProcessHost::kInvalidUniqueID, |
| storage_partition_impl_->GetServiceWorkerContext())); |
| |
| InitializeChannelProxy(); |
| |
| const int id = GetID(); |
| const uint64_t tracing_id = |
| ChildProcessHostImpl::ChildProcessUniqueIdToTracingProcessId(id); |
| gpu_client_.reset( |
| new viz::GpuClient(std::make_unique<BrowserGpuClientDelegate>(), id, |
| tracing_id, GetIOThreadTaskRunner({}))); |
| } |
| |
| // static |
| void RenderProcessHostImpl::ShutDownInProcessRenderer() { |
| DCHECK(g_run_renderer_in_process); |
| |
| switch (GetAllHosts().size()) { |
| case 0: |
| return; |
| case 1: { |
| RenderProcessHostImpl* host = static_cast<RenderProcessHostImpl*>( |
| AllHostsIterator().GetCurrentValue()); |
| for (auto& observer : host->observers_) |
| observer.RenderProcessHostDestroyed(host); |
| #ifndef NDEBUG |
| host->is_self_deleted_ = true; |
| #endif |
| delete host; |
| return; |
| } |
| default: |
| NOTREACHED() << "There should be only one RenderProcessHost when running " |
| << "in-process."; |
| } |
| } |
| |
| void RenderProcessHostImpl::RegisterRendererMainThreadFactory( |
| RendererMainThreadFactoryFunction create) { |
| g_renderer_main_thread_factory = create; |
| } |
| |
| void RenderProcessHostImpl::SetDomStorageBinderForTesting( |
| DomStorageBinder binder) { |
| GetDomStorageBinder() = std::move(binder); |
| } |
| |
| // static |
| void RenderProcessHostImpl::SetBadMojoMessageCallbackForTesting( |
| BadMojoMessageCallbackForTesting callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| // No support for setting the global callback twice. |
| DCHECK_NE(callback.is_null(), |
| GetBadMojoMessageCallbackForTesting().is_null()); |
| |
| GetBadMojoMessageCallbackForTesting() = callback; |
| } |
| |
| void RenderProcessHostImpl:: |
| SetBroadcastChannelProviderReceiverHandlerForTesting( |
| BroadcastChannelProviderReceiverHandler handler) { |
| GetBroadcastChannelProviderReceiverHandler() = handler; |
| } |
| |
| void RenderProcessHostImpl::SetCodeCacheHostReceiverHandlerForTesting( |
| CodeCacheHostReceiverHandler handler) { |
| GetCodeCacheHostReceiverHandler() = handler; |
| } |
| |
| RenderProcessHostImpl::~RenderProcessHostImpl() { |
| TRACE_EVENT2("shutdown", "~RenderProcessHostImpl", "render_process_host", |
| this, "id", GetID()); |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| #ifndef NDEBUG |
| DCHECK(is_self_deleted_) |
| << "RenderProcessHostImpl is destroyed by something other than itself"; |
| #endif |
| |
| // Make sure to clean up the in-process renderer before the channel, otherwise |
| // it may still run and have its IPCs fail, causing asserts. |
| in_process_renderer_.reset(); |
| g_in_process_thread = nullptr; |
| |
| ChildProcessSecurityPolicyImpl::GetInstance()->Remove(GetID()); |
| |
| is_dead_ = true; |
| |
| UnregisterHost(GetID()); |
| |
| if (!base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kDisableGpuShaderDiskCache)) { |
| GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce(&RemoveShaderInfo, GetID())); |
| } |
| |
| if (cleanup_network_service_plugin_exceptions_upon_destruction_) |
| RemoveNetworkServicePluginExceptions(GetID()); |
| |
| // Do reporting here for the priority of the frames seen by the host. |
| FramePrioritiesSeen report = FramePrioritiesSeen::kNoFramesSeen; |
| if (normal_priority_frames_seen_ && low_priority_frames_seen_) { |
| report = FramePrioritiesSeen::kMixedPrioritiesSeen; |
| } else if (normal_priority_frames_seen_) { |
| report = FramePrioritiesSeen::kOnlyNormalPrioritiesSeen; |
| } else if (low_priority_frames_seen_) { |
| report = FramePrioritiesSeen::kOnlyLowPrioritiesSeen; |
| } |
| UMA_HISTOGRAM_ENUMERATION("BrowserRenderProcessHost.FramePrioritiesSeen", |
| report); |
| |
| // Report the histograms if the time is nonzero. Note that LONG_TIMES records |
| // times in exponential bins up to an hour, so it should sufficiently catch |
| // most cases. |
| if (!background_status_update_time_.is_null()) { |
| base::TimeTicks current_time = clock_->NowTicks(); |
| base::TimeDelta total_duration = current_time - init_time_; |
| |
| // Only record for durations greater than zero. |
| if (total_duration.InMicroseconds() > 0) { |
| if (is_backgrounded_) |
| background_duration_ += current_time - background_status_update_time_; |
| UMA_HISTOGRAM_LONG_TIMES("BrowserRenderProcessHost.TotalTime", |
| total_duration); |
| UMA_HISTOGRAM_LONG_TIMES("BrowserRenderProcessHost.BackgroundTime", |
| background_duration_); |
| } |
| } |
| TRACE_EVENT_NESTABLE_ASYNC_END2("shutdown", "Cleanup in progress", this, |
| "render_process_host", this, |
| "browser_context", browser_context_); |
| TRACE_EVENT_NESTABLE_ASYNC_END2("shutdown", "Browser.RenderProcessHostImpl", |
| this, "render_process_host", this, |
| "browser_context", browser_context_); |
| } |
| |
| bool RenderProcessHostImpl::Init() { |
| // calling Init() more than once does nothing, this makes it more convenient |
| // for the view host which may not be sure in some cases |
| if (IsInitializedAndNotDead()) |
| return true; |
| |
| base::CommandLine::StringType renderer_prefix; |
| // A command prefix is something prepended to the command line of the spawned |
| // process. |
| const base::CommandLine& browser_command_line = |
| *base::CommandLine::ForCurrentProcess(); |
| renderer_prefix = |
| browser_command_line.GetSwitchValueNative(switches::kRendererCmdPrefix); |
| |
| #if defined(OS_LINUX) |
| int flags = renderer_prefix.empty() ? ChildProcessHost::CHILD_ALLOW_SELF |
| : ChildProcessHost::CHILD_NORMAL; |
| #elif defined(OS_MACOSX) |
| int flags = ChildProcessHost::CHILD_RENDERER; |
| #else |
| int flags = ChildProcessHost::CHILD_NORMAL; |
| #endif |
| |
| // Find the renderer before creating the channel so if this fails early we |
| // return without creating the channel. |
| base::FilePath renderer_path = ChildProcessHost::GetChildPath(flags); |
| if (renderer_path.empty()) |
| return false; |
| |
| is_initialized_ = true; |
| is_dead_ = false; |
| sent_render_process_ready_ = false; |
| |
| if (gpu_client_) |
| gpu_client_->PreEstablishGpuChannel(); |
| |
| // We may reach Init() during process death notification (e.g. |
| // RenderProcessExited on some observer). In this case the Channel may be |
| // null, so we re-initialize it here. |
| if (!channel_) |
| InitializeChannelProxy(); |
| |
| // Unpause the Channel briefly. This will be paused again below if we launch a |
| // real child process. Note that messages may be sent in the short window |
| // between now and then (e.g. in response to RenderProcessWillLaunch) and we |
| // depend on those messages being sent right away. |
| // |
| // |channel_| must always be non-null here: either it was initialized in |
| // the constructor, or in the most recent call to ProcessDied(). |
| channel_->Unpause(false /* flush */); |
| |
| // Call the embedder first so that their IPC filters have priority. |
| GetContentClient()->browser()->RenderProcessWillLaunch(this); |
| |
| FieldTrialSynchronizer::UpdateRendererVariationsHeader(this); |
| |
| #if defined(OS_ANDROID) |
| // Initialize the java audio manager so that media session tests will pass. |
| // See internal b/29872494. |
| static_cast<media::AudioManagerAndroid*>(media::AudioManager::Get()) |
| ->InitializeIfNeeded(); |
| #endif // defined(OS_ANDROID) |
| |
| CreateMessageFilters(); |
| RegisterMojoInterfaces(); |
| |
| if (run_renderer_in_process()) { |
| DCHECK(g_renderer_main_thread_factory); |
| // Crank up a thread and run the initialization there. With the way that |
| // messages flow between the browser and renderer, this thread is required |
| // to prevent a deadlock in single-process mode. Since the primordial |
| // thread in the renderer process runs the WebKit code and can sometimes |
| // make blocking calls to the UI thread (i.e. this thread), they need to run |
| // on separate threads. |
| in_process_renderer_.reset(g_renderer_main_thread_factory( |
| InProcessChildThreadParams(GetIOThreadTaskRunner({}), |
| &mojo_invitation_), |
| base::checked_cast<int32_t>(id_))); |
| |
| base::Thread::Options options; |
| #if defined(OS_WIN) && !defined(OS_MACOSX) |
| // In-process plugins require this to be a UI message loop. |
| options.message_pump_type = base::MessagePumpType::UI; |
| #else |
| // We can't have multiple UI loops on Linux and Android, so we don't support |
| // in-process plugins. |
| options.message_pump_type = base::MessagePumpType::DEFAULT; |
| #endif |
| // As for execution sequence, this callback should have no any dependency |
| // on starting in-process-render-thread. |
| // So put it here to trigger ChannelMojo initialization earlier to enable |
| // in-process-render-thread using ChannelMojo there. |
| OnProcessLaunched(); // Fake a callback that the process is ready. |
| |
| in_process_renderer_->StartWithOptions(options); |
| |
| g_in_process_thread = in_process_renderer_.get(); |
| |
| // Make sure any queued messages on the channel are flushed in the case |
| // where we aren't launching a child process. |
| channel_->Flush(); |
| } else { |
| // Build command line for renderer. We call AppendRendererCommandLine() |
| // first so the process type argument will appear first. |
| std::unique_ptr<base::CommandLine> cmd_line = |
| std::make_unique<base::CommandLine>(renderer_path); |
| if (!renderer_prefix.empty()) |
| cmd_line->PrependWrapper(renderer_prefix); |
| AppendRendererCommandLine(cmd_line.get()); |
| |
| // Spawn the child process asynchronously to avoid blocking the UI thread. |
| // As long as there's no renderer prefix, we can use the zygote process |
| // at this stage. |
| child_process_launcher_ = std::make_unique<ChildProcessLauncher>( |
| std::make_unique<RendererSandboxedProcessLauncherDelegate>(), |
| std::move(cmd_line), GetID(), this, std::move(mojo_invitation_), |
| base::BindRepeating(&RenderProcessHostImpl::OnMojoError, id_), |
| GetV8SnapshotFilesToPreload()); |
| channel_->Pause(); |
| |
| // In single process mode, browser-side tracing and memory will cover the |
| // whole process including renderers. |
| BackgroundTracingManagerImpl::ActivateForProcess(GetID(), |
| child_process_.get()); |
| |
| fast_shutdown_started_ = false; |
| } |
| |
| init_time_ = clock_->NowTicks(); |
| background_status_update_time_ = init_time_; |
| return true; |
| } |
| |
| void RenderProcessHostImpl::EnableSendQueue() { |
| if (!channel_) |
| InitializeChannelProxy(); |
| } |
| |
| bool RenderProcessHostImpl::HasOnlyLowPriorityFrames() { |
| return (low_priority_frames_ > 0) && (total_frames_ == low_priority_frames_); |
| } |
| |
| void RenderProcessHostImpl::InitializeChannelProxy() { |
| scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = |
| GetIOThreadTaskRunner({}); |
| |
| // Establish a ChildProcess interface connection to the new renderer. This is |
| // connected as the primordial message pipe via a Mojo invitation to the |
| // process. |
| mojo_invitation_ = {}; |
| child_process_.reset(); |
| child_process_.Bind(mojo::PendingRemote<mojom::ChildProcess>( |
| mojo_invitation_.AttachMessagePipe(0), /*version=*/0)); |
| |
| // Bootstrap the IPC Channel. |
| mojo::PendingRemote<IPC::mojom::ChannelBootstrap> bootstrap; |
| child_process_->BootstrapLegacyIpc( |
| bootstrap.InitWithNewPipeAndPassReceiver()); |
| std::unique_ptr<IPC::ChannelFactory> channel_factory = |
| IPC::ChannelMojo::CreateServerFactory( |
| bootstrap.PassPipe(), io_task_runner, |
| base::ThreadTaskRunnerHandle::Get()); |
| |
| ResetChannelProxy(); |
| |
| // Do NOT expand ifdef or run time condition checks here! Synchronous |
| // IPCs from browser process are banned. It is only narrowly allowed |
| // for Android WebView to maintain backward compatibility. |
| // See crbug.com/526842 for details. |
| #if defined(OS_ANDROID) |
| if (GetContentClient()->UsingSynchronousCompositing()) { |
| // Android never performs a clean shutdown, so we pass nullptr for |
| // shudown_event to indicate that we never intend to signal a shutdown. |
| channel_ = |
| IPC::SyncChannel::Create(this, io_task_runner.get(), |
| base::ThreadTaskRunnerHandle::Get(), nullptr); |
| } |
| #endif // OS_ANDROID |
| if (!channel_) { |
| channel_ = std::make_unique<IPC::ChannelProxy>( |
| this, io_task_runner.get(), base::ThreadTaskRunnerHandle::Get()); |
| } |
| channel_->Init(std::move(channel_factory), true /* create_pipe_now */); |
| |
| // Note that Channel send is effectively paused and unpaused at various points |
| // during startup, and existing code relies on a fragile relative message |
| // ordering resulting from some early messages being queued until process |
| // launch while others are sent immediately. See https://goo.gl/REW75h for |
| // details. |
| // |
| // We acquire a few associated interface proxies here -- before the channel is |
| // paused -- to ensure that subsequent initialization messages on those |
| // interfaces behave properly. Specifically, this avoids the risk of an |
| // interface being requested while the Channel is paused, which could |
| // effectively and undesirably block the transmission of a subsequent message |
| // on that interface while the Channel is unpaused. |
| // |
| // See OnProcessLaunched() for some additional details of this somewhat |
| // surprising behavior. |
| remote_route_provider_.reset(); |
| channel_->GetRemoteAssociatedInterface(&remote_route_provider_); |
| renderer_interface_.reset(); |
| channel_->GetRemoteAssociatedInterface(&renderer_interface_); |
| |
| // We start the Channel in a paused state. It will be briefly unpaused again |
| // in Init() if applicable, before process launch is initiated. |
| channel_->Pause(); |
| } |
| |
| void RenderProcessHostImpl::ResetChannelProxy() { |
| if (!channel_) |
| return; |
| |
| channel_.reset(); |
| channel_connected_ = false; |
| } |
| |
| void RenderProcessHostImpl::CreateMessageFilters() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| MediaInternals* media_internals = MediaInternals::GetInstance(); |
| |
| scoped_refptr<RenderMessageFilter> render_message_filter = |
| base::MakeRefCounted<RenderMessageFilter>( |
| GetID(), GetBrowserContext(), widget_helper_.get(), media_internals); |
| AddFilter(render_message_filter.get()); |
| |
| render_frame_message_filter_ = new RenderFrameMessageFilter( |
| GetID(), |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| PluginServiceImpl::GetInstance(), |
| #else |
| nullptr, |
| #endif |
| GetBrowserContext(), storage_partition_impl_, widget_helper_.get()); |
| AddFilter(render_frame_message_filter_.get()); |
| |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| AddFilter(new PepperRendererConnection(GetID())); |
| #endif |
| |
| p2p_socket_dispatcher_host_ = |
| std::make_unique<P2PSocketDispatcherHost>(GetID()); |
| |
| scoped_refptr<ServiceWorkerContextWrapper> service_worker_context( |
| static_cast<ServiceWorkerContextWrapper*>( |
| storage_partition_impl_->GetServiceWorkerContext())); |
| } |
| |
| void RenderProcessHostImpl::BindCacheStorage( |
| const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy, |
| mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> |
| coep_reporter_remote, |
| const url::Origin& origin, |
| mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| storage_partition_impl_->GetCacheStorageContext()->AddReceiver( |
| cross_origin_embedder_policy, std::move(coep_reporter_remote), origin, |
| std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::BindIndexedDB( |
| const url::Origin& origin, |
| mojo::PendingReceiver<blink::mojom::IDBFactory> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (origin.opaque()) { |
| // Opaque origins aren't valid for IndexedDB access, so we won't bind |
| // |receiver| to |indexed_db_factory_|. Return early here which |
| // will cause |receiver| to be freed. When |receiver| is |
| // freed, we expect the pipe on the client will be closed. |
| return; |
| } |
| storage_partition_impl_->GetIndexedDBControl().BindIndexedDB( |
| origin, std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::ForceCrash() { |
| child_process_->CrashHungProcess(); |
| } |
| |
| void RenderProcessHostImpl::BindFileSystemManager( |
| const url::Origin& origin, |
| mojo::PendingReceiver<blink::mojom::FileSystemManager> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // Note, the base::Unretained() is safe because the target object has an IO |
| // thread deleter and the callback is also targeting the IO thread. |
| // TODO(https://crbug.com/873661): Pass origin to FileSystemManager. |
| GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&FileSystemManagerImpl::BindReceiver, |
| base::Unretained(file_system_manager_impl_.get()), |
| std::move(receiver))); |
| } |
| |
| void RenderProcessHostImpl::BindNativeFileSystemManager( |
| const url::Origin& origin, |
| mojo::PendingReceiver<blink::mojom::NativeFileSystemManager> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // This code path is only for workers, hence always pass in |
| // MSG_ROUTING_NONE as frame ID. Frames themselves go through |
| // RenderFrameHostImpl instead. |
| auto* storage_partition = |
| static_cast<StoragePartitionImpl*>(GetStoragePartition()); |
| auto* manager = storage_partition->GetNativeFileSystemManager(); |
| manager->BindReceiver( |
| NativeFileSystemManagerImpl::BindingContext( |
| origin, |
| // TODO(https://crbug.com/989323): Obtain and use a better |
| // URL for workers instead of the origin as source url. |
| // This URL will be used for SafeBrowsing checks and for |
| // the Quarantine Service. |
| origin.GetURL(), GetID()), |
| std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::BindNativeIOHost( |
| const url::Origin& origin, |
| mojo::PendingReceiver<blink::mojom::NativeIOHost> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| auto* storage_partition = |
| static_cast<StoragePartitionImpl*>(GetStoragePartition()); |
| storage_partition->GetNativeIOContext()->BindReceiver(origin, |
| std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::SetClockForTesting(base::TickClock* clock) { |
| clock_ = clock; |
| init_time_ = clock_->NowTicks(); |
| background_status_update_time_ = init_time_; |
| } |
| |
| void RenderProcessHostImpl::BindRestrictedCookieManagerForServiceWorker( |
| const url::Origin& origin, |
| mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| StoragePartitionImpl* storage_partition = |
| static_cast<StoragePartitionImpl*>(GetStoragePartition()); |
| storage_partition->CreateRestrictedCookieManager( |
| network::mojom::RestrictedCookieManagerRole::SCRIPT, origin, |
| net::SiteForCookies::FromOrigin(origin), origin, |
| true /* is_service_worker */, GetID(), MSG_ROUTING_NONE, |
| std::move(receiver), |
| storage_partition->CreateCookieAccessObserverForServiceWorker()); |
| } |
| |
| void RenderProcessHostImpl::BindVideoDecodePerfHistory( |
| mojo::PendingReceiver<media::mojom::VideoDecodePerfHistory> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| GetBrowserContext()->GetVideoDecodePerfHistory()->BindReceiver( |
| std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::BindQuotaManagerHost( |
| int render_frame_id, |
| const url::Origin& origin, |
| mojo::PendingReceiver<blink::mojom::QuotaManagerHost> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| auto* storage_partition = |
| static_cast<StoragePartitionImpl*>(GetStoragePartition()); |
| storage_partition->GetQuotaContext()->BindQuotaManagerHost( |
| GetID(), render_frame_id, origin, std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::CreateLockManager( |
| int render_frame_id, |
| const url::Origin& origin, |
| mojo::PendingReceiver<blink::mojom::LockManager> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| storage_partition_impl_->GetLockManager()->BindReceiver( |
| GetID(), render_frame_id, origin, std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::CreatePermissionService( |
| const url::Origin& origin, |
| mojo::PendingReceiver<blink::mojom::PermissionService> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (!permission_service_context_) |
| permission_service_context_.reset(new PermissionServiceContext(this)); |
| |
| permission_service_context_->CreateServiceForWorker(origin, |
| std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::CreatePaymentManagerForOrigin( |
| const url::Origin& origin, |
| mojo::PendingReceiver<payments::mojom::PaymentManager> receiver) { |
| static_cast<StoragePartitionImpl*>(GetStoragePartition()) |
| ->GetPaymentAppContext() |
| ->CreatePaymentManagerForOrigin(origin, std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::CreateNotificationService( |
| const url::Origin& origin, |
| mojo::PendingReceiver<blink::mojom::NotificationService> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| storage_partition_impl_->GetPlatformNotificationContext()->CreateService( |
| origin, std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::CreateWebSocketConnector( |
| const url::Origin& origin, |
| mojo::PendingReceiver<blink::mojom::WebSocketConnector> receiver) { |
| // TODO(jam): is it ok to not send extraHeaders for sockets created from |
| // shared and service workers? |
| // |
| // Shared Workers and service workers are not directly associated with a |
| // frame, so the concept of "top-level frame" does not exist. Can use |
| // (origin, origin, origin) for the IsolationInfo for requests because these |
| // workers can only be created when the site has cookie access. |
| mojo::MakeSelfOwnedReceiver( |
| std::make_unique<WebSocketConnectorImpl>( |
| GetID(), MSG_ROUTING_NONE, origin, |
| net::IsolationInfo::Create( |
| net::IsolationInfo::RedirectMode::kUpdateNothing, origin, origin, |
| net::SiteForCookies::FromOrigin(origin))), |
| std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::CancelProcessShutdownDelayForUnload() { |
| if (IsKeepAliveRefCountDisabled()) |
| return; |
| DecrementKeepAliveRefCount(); |
| } |
| |
| void RenderProcessHostImpl::DelayProcessShutdownForUnload( |
| const base::TimeDelta& timeout) { |
| // No need to delay shutdown if the process is already shutting down. |
| if (IsKeepAliveRefCountDisabled() || deleting_soon_ || fast_shutdown_started_) |
| return; |
| |
| IncrementKeepAliveRefCount(); |
| GetUIThreadTaskRunner({})->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce( |
| &RenderProcessHostImpl::CancelProcessShutdownDelayForUnload, |
| weak_factory_.GetWeakPtr()), |
| timeout); |
| } |
| |
| // static |
| void RenderProcessHostImpl::AddCorbExceptionForPlugin(int process_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&AddCorbExceptionForPluginOnUIThread, process_id)); |
| } |
| |
| // static |
| void RenderProcessHostImpl::AddAllowedRequestInitiatorForPlugin( |
| int process_id, |
| const url::Origin& allowed_request_initiator) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce(&AddAllowedRequestInitiatorForPluginOnUIThread, |
| process_id, allowed_request_initiator)); |
| } |
| |
| void RenderProcessHostImpl:: |
| CleanupNetworkServicePluginExceptionsUponDestruction() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| cleanup_network_service_plugin_exceptions_upon_destruction_ = true; |
| } |
| |
| #if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX) |
| void RenderProcessHostImpl::DumpProfilingData(base::OnceClosure callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| GetRendererInterface()->WriteClangProfilingProfile(std::move(callback)); |
| } |
| #endif |
| |
| PeerConnectionTrackerHost* |
| RenderProcessHostImpl::GetPeerConnectionTrackerHost() { |
| if (!peer_connection_tracker_host_) { |
| peer_connection_tracker_host_ = |
| std::make_unique<PeerConnectionTrackerHost>(this); |
| } |
| return peer_connection_tracker_host_.get(); |
| } |
| |
| // static |
| void RenderProcessHostImpl::OverrideBatteryMonitorBinderForTesting( |
| BatteryMonitorBinder binder) { |
| GetBatteryMonitorBinderOverride() = std::move(binder); |
| } |
| |
| void RenderProcessHostImpl::RegisterMojoInterfaces() { |
| auto registry = std::make_unique<service_manager::BinderRegistry>(); |
| |
| AddUIThreadInterface(registry.get(), |
| base::BindRepeating(&BindBatteryMonitor)); |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating( |
| [](mojo::PendingReceiver<device::mojom::TimeZoneMonitor> receiver) { |
| GetDeviceService().BindTimeZoneMonitor(std::move(receiver)); |
| })); |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating( |
| [](mojo::PendingReceiver<device::mojom::PowerMonitor> receiver) { |
| GetDeviceService().BindPowerMonitor(std::move(receiver)); |
| })); |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating( |
| [](mojo::PendingReceiver<device::mojom::ScreenOrientationListener> |
| receiver) { |
| GetDeviceService().BindScreenOrientationListener( |
| std::move(receiver)); |
| })); |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating( |
| &RenderProcessHostImpl::CreateEmbeddedFrameSinkProvider, |
| weak_factory_.GetWeakPtr())); |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating(&RenderProcessHostImpl::BindFrameSinkProvider, |
| weak_factory_.GetWeakPtr())); |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating(&RenderProcessHostImpl::BindCompositingModeReporter, |
| weak_factory_.GetWeakPtr())); |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating(&RenderProcessHostImpl::CreateOneShotSyncService, |
| weak_factory_.GetWeakPtr())); |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating(&RenderProcessHostImpl::CreatePeriodicSyncService, |
| weak_factory_.GetWeakPtr())); |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating(&RenderProcessHostImpl::CreateDomStorageProvider, |
| weak_factory_.GetWeakPtr())); |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating( |
| &RenderProcessHostImpl::CreateBroadcastChannelProvider, |
| weak_factory_.GetWeakPtr())); |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating(&RenderProcessHostImpl::BindWebDatabaseHostImpl, |
| weak_factory_.GetWeakPtr())); |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating( |
| [](base::WeakPtr<RenderProcessHostImpl> host, |
| mojo::PendingReceiver< |
| memory_instrumentation::mojom::CoordinatorConnector> |
| receiver) { |
| if (!host) |
| return; |
| host->coordinator_connector_receiver_.reset(); |
| host->coordinator_connector_receiver_.Bind(std::move(receiver)); |
| if (!host->GetProcess().IsValid()) { |
| // We only want to accept messages from this interface once we |
| // have a known PID. |
| host->coordinator_connector_receiver_.Pause(); |
| } |
| }, |
| weak_factory_.GetWeakPtr())); |
| |
| registry->AddInterface( |
| base::BindRepeating(&MimeRegistryImpl::Create), |
| base::ThreadPool::CreateSequencedTaskRunner( |
| {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN, |
| base::TaskPriority::USER_BLOCKING})); |
| #if BUILDFLAG(USE_MINIKIN_HYPHENATION) |
| registry->AddInterface( |
| base::BindRepeating(&hyphenation::HyphenationImpl::Create), |
| hyphenation::HyphenationImpl::GetTaskRunner()); |
| #endif |
| #if defined(OS_ANDROID) |
| if (base::FeatureList::IsEnabled(features::kFontSrcLocalMatching)) { |
| registry->AddInterface( |
| base::BindRepeating(&FontUniqueNameLookupService::Create), |
| FontUniqueNameLookupService::GetTaskRunner()); |
| } |
| #endif |
| |
| #if defined(OS_WIN) |
| registry->AddInterface( |
| base::BindRepeating(&DWriteFontProxyImpl::Create), |
| base::ThreadPool::CreateSequencedTaskRunner( |
| {base::TaskPriority::USER_BLOCKING, base::MayBlock()})); |
| #endif |
| |
| registry->AddInterface( |
| base::BindRepeating(&device::GamepadHapticsManager::Create)); |
| |
| if (ServiceWorkerContext::IsServiceWorkerOnUIEnabled()) { |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating(&RenderProcessHostImpl::BindPushMessagingManager, |
| weak_factory_.GetWeakPtr())); |
| } else { |
| // Note, the base::Unretained() is safe because the target object has an IO |
| // thread deleter and the callback is also targeting the IO thread. When |
| // the RPHI is destroyed it also triggers the destruction of the registry |
| // on the IO thread. |
| registry->AddInterface( |
| base::BindRepeating(&PushMessagingManager::AddPushMessagingReceiver, |
| base::Unretained(push_messaging_manager_.get()))); |
| } |
| |
| file_system_manager_impl_.reset(new FileSystemManagerImpl( |
| GetID(), storage_partition_impl_->GetFileSystemContext(), |
| ChromeBlobStorageContext::GetFor(GetBrowserContext()))); |
| // This interface is still exposed by the RenderProcessHost's registry so |
| // that it can be accessed by PepperFileSystemHost. Blink accesses this |
| // interface through RenderFrameHost/RendererInterfaceBinders. |
| // |
| // Note, the base::Unretained() is safe because the target object has an IO |
| // thread deleter and the callback is also targeting the IO thread. When |
| // the RPHI is destroyed it also triggers the destruction of the registry |
| // on the IO thread. |
| // |
| // TODO(https://crbug.com/873661): Make PepperFileSystemHost access this with |
| // the RenderFrameHost's registry, and remove this registration. |
| registry->AddInterface( |
| base::BindRepeating(&FileSystemManagerImpl::BindReceiver, |
| base::Unretained(file_system_manager_impl_.get()))); |
| |
| registry->AddInterface(base::BindRepeating( |
| &MidiHost::BindReceiver, GetID(), |
| base::Unretained(BrowserMainLoop::GetInstance()->midi_service()))); |
| |
| if (gpu_client_) { |
| // |gpu_client_| outlives the registry, because its destruction is posted to |
| // IO thread from the destructor of |this|. |
| registry->AddInterface(base::BindRepeating( |
| &viz::GpuClient::Add, base::Unretained(gpu_client_.get()))); |
| } |
| |
| MediaStreamManager* media_stream_manager = |
| BrowserMainLoop::GetInstance()->media_stream_manager(); |
| |
| registry->AddInterface(base::BindRepeating(&VideoCaptureHost::Create, GetID(), |
| media_stream_manager)); |
| |
| registry->AddInterface( |
| base::BindRepeating(&FileUtilitiesHostImpl::Create, GetID()), |
| base::ThreadPool::CreateSequencedTaskRunner( |
| {base::MayBlock(), base::TaskPriority::USER_VISIBLE})); |
| |
| // Note, the base::Unretained() is safe because the target object has an IO |
| // thread deleter and the callback is also targeting the IO thread. When |
| // the RPHI is destroyed it also triggers the destruction of the registry |
| // on the IO thread. |
| media_stream_track_metrics_host_.reset(new MediaStreamTrackMetricsHost()); |
| registry->AddInterface(base::BindRepeating( |
| &MediaStreamTrackMetricsHost::BindReceiver, |
| base::Unretained(media_stream_track_metrics_host_.get()))); |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating( |
| &RenderProcessHostImpl::CreateAgentMetricsCollectorHost, |
| weak_factory_.GetWeakPtr())); |
| |
| registry->AddInterface( |
| base::BindRepeating(&metrics::CreateSingleSampleMetricsProvider)); |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating(&RenderProcessHostImpl::BindPeerConnectionTrackerHost, |
| weak_factory_.GetWeakPtr())); |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating(&RenderProcessHostImpl::CreateCodeCacheHost, |
| weak_factory_.GetWeakPtr())); |
| |
| #if BUILDFLAG(ENABLE_REPORTING) |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating(&CreateReportingServiceProxy, GetID())); |
| #endif // BUILDFLAG(ENABLE_REPORTING) |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating(&RenderProcessHostImpl::BindP2PSocketManager, |
| weak_factory_.GetWeakPtr())); |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating(&RenderProcessHostImpl::CreateMediaLogRecordHost, |
| weak_factory_.GetWeakPtr())); |
| |
| #if BUILDFLAG(ENABLE_MDNS) |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating(&RenderProcessHostImpl::CreateMdnsResponder, |
| weak_factory_.GetWeakPtr())); |
| #endif // BUILDFLAG(ENABLE_MDNS) |
| |
| AddUIThreadInterface(registry.get(), |
| base::BindRepeating(&FieldTrialRecorder::Create)); |
| |
| associated_interfaces_ = |
| std::make_unique<blink::AssociatedInterfaceRegistry>(); |
| blink::AssociatedInterfaceRegistry* associated_registry = |
| associated_interfaces_.get(); |
| |
| // This base::Unretained() usage is safe since the associated_registry is |
| // owned by this RPHI. |
| associated_registry->AddInterface(base::BindRepeating( |
| &RenderProcessHostImpl::BindRouteProvider, base::Unretained(this))); |
| associated_registry->AddInterface(base::BindRepeating( |
| &RenderProcessHostImpl::CreateRendererHost, base::Unretained(this))); |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating( |
| &RenderProcessHostImpl::CreateURLLoaderFactoryForRendererProcess, |
| weak_factory_.GetWeakPtr())); |
| |
| registry->AddInterface( |
| base::BindRepeating(&BlobRegistryWrapper::Bind, |
| storage_partition_impl_->GetBlobRegistry(), GetID())); |
| |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| // Initialization can happen more than once (in the case of a child process |
| // crash), but we don't want to lose the plugin registry in this case. |
| if (!plugin_registry_) { |
| plugin_registry_ = std::make_unique<PluginRegistryImpl>(GetID()); |
| } |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating(&RenderProcessHostImpl::BindPluginRegistry, |
| weak_factory_.GetWeakPtr())); |
| #endif |
| |
| #if BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| registry->AddInterface(base::BindRepeating(&KeySystemSupportImpl::Create)); |
| #endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating(&RenderProcessHostImpl::BindVideoDecoderService, |
| weak_factory_.GetWeakPtr())); |
| |
| AddUIThreadInterface( |
| registry.get(), |
| base::BindRepeating(&RenderProcessHostImpl::BindAecDumpManager, |
| weak_factory_.GetWeakPtr())); |
| |
| // ---- Please do not register interfaces below this line ------ |
| // |
| // This call should be done after registering all interfaces above, so that |
| // embedder can override any interfaces. The fact that registry calls |
| // the last registration for the name allows us to easily override interfaces. |
| GetContentClient()->browser()->ExposeInterfacesToRenderer( |
| registry.get(), associated_interfaces_.get(), this); |
| |
| mojo::PendingRemote<mojom::ChildProcessHostBootstrap> bootstrap_remote; |
| io_thread_host_impl_.emplace( |
| GetIOThreadTaskRunner({}), GetID(), instance_weak_factory_->GetWeakPtr(), |
| std::move(registry), bootstrap_remote.InitWithNewPipeAndPassReceiver()); |
| child_process_->Initialize(std::move(bootstrap_remote)); |
| } |
| |
| void RenderProcessHostImpl::BindRouteProvider( |
| mojo::PendingAssociatedReceiver<mojom::RouteProvider> receiver) { |
| if (route_provider_receiver_.is_bound()) |
| return; |
| route_provider_receiver_.Bind(std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::GetRoute( |
| int32_t routing_id, |
| mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterfaceProvider> |
| receiver) { |
| DCHECK(receiver.is_valid()); |
| associated_interface_provider_receivers_.Add(this, std::move(receiver), |
| routing_id); |
| } |
| |
| void RenderProcessHostImpl::GetAssociatedInterface( |
| const std::string& name, |
| mojo::PendingAssociatedReceiver<blink::mojom::AssociatedInterface> |
| receiver) { |
| int32_t routing_id = |
| associated_interface_provider_receivers_.current_context(); |
| IPC::Listener* listener = listeners_.Lookup(routing_id); |
| if (listener) |
| listener->OnAssociatedInterfaceRequest(name, receiver.PassHandle()); |
| } |
| |
| void RenderProcessHostImpl::CreateEmbeddedFrameSinkProvider( |
| mojo::PendingReceiver<blink::mojom::EmbeddedFrameSinkProvider> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (!embedded_frame_sink_provider_) { |
| // The client id gets converted to a uint32_t in FrameSinkId. |
| uint32_t renderer_client_id = base::checked_cast<uint32_t>(id_); |
| embedded_frame_sink_provider_ = |
| std::make_unique<EmbeddedFrameSinkProviderImpl>( |
| GetHostFrameSinkManager(), renderer_client_id); |
| } |
| embedded_frame_sink_provider_->Add(std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::BindFrameSinkProvider( |
| mojo::PendingReceiver<mojom::FrameSinkProvider> receiver) { |
| frame_sink_provider_.Bind(std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::BindCompositingModeReporter( |
| mojo::PendingReceiver<viz::mojom::CompositingModeReporter> receiver) { |
| BrowserMainLoop::GetInstance()->GetCompositingModeReporter( |
| std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::CreateDomStorageProvider( |
| mojo::PendingReceiver<blink::mojom::DomStorageProvider> receiver) { |
| DCHECK(!dom_storage_provider_receiver_.is_bound()); |
| dom_storage_provider_receiver_.Bind(std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::CreateBroadcastChannelProvider( |
| mojo::PendingReceiver<blink::mojom::BroadcastChannelProvider> receiver) { |
| if (!GetBroadcastChannelProviderReceiverHandler().is_null()) { |
| GetBroadcastChannelProviderReceiverHandler().Run(this, std::move(receiver)); |
| return; |
| } |
| |
| storage_partition_impl_->GetBroadcastChannelProvider()->Connect( |
| ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(id_), |
| std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::CreateCodeCacheHost( |
| mojo::PendingReceiver<blink::mojom::CodeCacheHost> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| // There should be at most one CodeCacheHostImpl for any given |
| // RenderProcessHost. |
| DCHECK(!code_cache_host_impl_); |
| |
| // Create a new CodeCacheHostImpl and bind it to the given receiver. |
| code_cache_host_impl_ = std::make_unique<CodeCacheHostImpl>( |
| GetID(), storage_partition_impl_->GetCacheStorageContext(), |
| storage_partition_impl_->GetGeneratedCodeCacheContext(), |
| std::move(receiver)); |
| |
| // If there is a callback registered, then invoke it with the newly |
| // created CodeCacheHostImpl. |
| if (!GetCodeCacheHostReceiverHandler().is_null()) { |
| GetCodeCacheHostReceiverHandler().Run(this, code_cache_host_impl_.get()); |
| } |
| } |
| |
| void RenderProcessHostImpl::BindVideoDecoderService( |
| mojo::PendingReceiver<media::mojom::InterfaceFactory> receiver) { |
| if (!video_decoder_proxy_) |
| video_decoder_proxy_.reset(new VideoDecoderProxy()); |
| video_decoder_proxy_->Add(std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::BindWebDatabaseHostImpl( |
| mojo::PendingReceiver<blink::mojom::WebDatabaseHost> receiver) { |
| storage::DatabaseTracker* db_tracker = |
| storage_partition_impl_->GetDatabaseTracker(); |
| db_tracker->task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&WebDatabaseHostImpl::Create, GetID(), |
| base::WrapRefCounted(db_tracker), std::move(receiver))); |
| } |
| void RenderProcessHostImpl::BindAecDumpManager( |
| mojo::PendingReceiver<blink::mojom::AecDumpManager> receiver) { |
| aec_dump_manager_.AddReceiver(std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::CreateOneShotSyncService( |
| mojo::PendingReceiver<blink::mojom::OneShotBackgroundSyncService> |
| receiver) { |
| storage_partition_impl_->GetBackgroundSyncContext()->CreateOneShotSyncService( |
| std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::CreatePeriodicSyncService( |
| mojo::PendingReceiver<blink::mojom::PeriodicBackgroundSyncService> |
| receiver) { |
| storage_partition_impl_->GetBackgroundSyncContext() |
| ->CreatePeriodicSyncService(std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::BindPushMessagingManager( |
| mojo::PendingReceiver<blink::mojom::PushMessaging> receiver) { |
| push_messaging_manager_->AddPushMessagingReceiver(std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::BindP2PSocketManager( |
| mojo::PendingReceiver<network::mojom::P2PSocketManager> receiver) { |
| p2p_socket_dispatcher_host_->BindReceiver(std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::CreateMediaLogRecordHost( |
| mojo::PendingReceiver<content::mojom::MediaInternalLogRecords> receiver) { |
| content::MediaInternals::CreateMediaLogRecords(GetID(), std::move(receiver)); |
| } |
| |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| void RenderProcessHostImpl::BindPluginRegistry( |
| mojo::PendingReceiver<blink::mojom::PluginRegistry> receiver) { |
| plugin_registry_->Bind(std::move(receiver)); |
| } |
| #endif |
| |
| void RenderProcessHostImpl::BindDomStorage( |
| mojo::PendingReceiver<blink::mojom::DomStorage> receiver, |
| mojo::PendingRemote<blink::mojom::DomStorageClient> client) { |
| const DomStorageBinder& binder = GetDomStorageBinder(); |
| if (binder) { |
| binder.Run(this, std::move(receiver)); |
| return; |
| } |
| |
| dom_storage_receiver_ids_.insert(storage_partition_impl_->BindDomStorage( |
| id_, std::move(receiver), std::move(client))); |
| |
| // Renderers only use this interface to send a single BindDomStorage message, |
| // so we can tear down the receiver now. |
| dom_storage_provider_receiver_.reset(); |
| } |
| |
| void RenderProcessHostImpl::RegisterCoordinatorClient( |
| mojo::PendingReceiver<memory_instrumentation::mojom::Coordinator> receiver, |
| mojo::PendingRemote<memory_instrumentation::mojom::ClientProcess> |
| client_process) { |
| if (!GetProcess().IsValid()) { |
| // If the process dies before we get this message. we have no valid PID |
| // and there's nothing to register. |
| return; |
| } |
| |
| base::trace_event::MemoryDumpManager::GetInstance() |
| ->GetDumpThreadTaskRunner() |
| ->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| [](mojo::PendingReceiver< |
| memory_instrumentation::mojom::Coordinator> receiver, |
| mojo::PendingRemote< |
| memory_instrumentation::mojom::ClientProcess> |
| client_process, |
| base::ProcessId pid) { |
| GetMemoryInstrumentationCoordinatorController() |
| ->RegisterClientProcess( |
| std::move(receiver), std::move(client_process), |
| memory_instrumentation::mojom::ProcessType::RENDERER, |
| pid, |
| /*service_name=*/base::nullopt); |
| }, |
| std::move(receiver), std::move(client_process), |
| GetProcess().Pid())); |
| |
| coordinator_connector_receiver_.reset(); |
| } |
| |
| void RenderProcessHostImpl::CreateRendererHost( |
| mojo::PendingAssociatedReceiver<mojom::RendererHost> receiver) { |
| renderer_host_receiver_.Bind(std::move(receiver)); |
| } |
| |
| int RenderProcessHostImpl::GetNextRoutingID() { |
| return widget_helper_->GetNextRoutingID(); |
| } |
| |
| void RenderProcessHostImpl::BindReceiver( |
| mojo::GenericPendingReceiver receiver) { |
| child_process_->BindReceiver(std::move(receiver)); |
| } |
| |
| std::unique_ptr<base::PersistentMemoryAllocator> |
| RenderProcessHostImpl::TakeMetricsAllocator() { |
| return std::move(metrics_allocator_); |
| } |
| |
| const base::TimeTicks& |
| RenderProcessHostImpl::GetInitTimeForNavigationMetrics() { |
| return init_time_; |
| } |
| |
| bool RenderProcessHostImpl::IsProcessBackgrounded() { |
| return priority_.is_background(); |
| } |
| |
| void RenderProcessHostImpl::IncrementKeepAliveRefCount() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(!is_keep_alive_ref_count_disabled_); |
| ++keep_alive_ref_count_; |
| if (keep_alive_ref_count_ == 1) |
| GetRendererInterface()->SetSchedulerKeepActive(true); |
| } |
| |
| void RenderProcessHostImpl::DecrementKeepAliveRefCount() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(!is_keep_alive_ref_count_disabled_); |
| DCHECK_GT(keep_alive_ref_count_, 0U); |
| --keep_alive_ref_count_; |
| if (keep_alive_ref_count_ == 0) { |
| Cleanup(); |
| GetRendererInterface()->SetSchedulerKeepActive(false); |
| } |
| } |
| |
| void RenderProcessHostImpl::DisableKeepAliveRefCount() { |
| TRACE_EVENT2("shutdown", "RenderProcessHostImpl::DisableKeepAliveRefCount", |
| "browser_context", browser_context_, "render_process_host", |
| this); |
| |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (is_keep_alive_ref_count_disabled_) |
| return; |
| is_keep_alive_ref_count_disabled_ = true; |
| |
| keep_alive_ref_count_ = 0; |
| // Cleaning up will also remove this from the SpareRenderProcessHostManager. |
| // (in this case |keep_alive_ref_count_| would be 0 even before). |
| Cleanup(); |
| } |
| |
| bool RenderProcessHostImpl::IsKeepAliveRefCountDisabled() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| return is_keep_alive_ref_count_disabled_; |
| } |
| |
| void RenderProcessHostImpl::Resume() {} |
| |
| mojom::Renderer* RenderProcessHostImpl::GetRendererInterface() { |
| return renderer_interface_.get(); |
| } |
| |
| // static |
| void RenderProcessHostImpl::SetNetworkFactoryForTesting( |
| const CreateNetworkFactoryCallback& create_network_factory_callback) { |
| DCHECK(!BrowserThread::IsThreadInitialized(BrowserThread::UI) || |
| BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK(create_network_factory_callback.is_null() || |
| GetCreateNetworkFactoryCallback().is_null()) |
| << "It is not expected that this is called with non-null callback when " |
| << "another overriding callback is already set."; |
| GetCreateNetworkFactoryCallback() = create_network_factory_callback; |
| } |
| |
| void RenderProcessHostImpl::CreateURLLoaderFactoryForRendererProcess( |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| CreateURLLoaderFactory( |
| std::move(receiver), |
| URLLoaderFactoryParamsHelper::CreateForRendererProcess(this)); |
| } |
| |
| void RenderProcessHostImpl::CreateURLLoaderFactory( |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver, |
| network::mojom::URLLoaderFactoryParamsPtr params) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(params); |
| DCHECK_EQ(GetID(), static_cast<int>(params->process_id)); |
| |
| if (GetCreateNetworkFactoryCallback().is_null()) { |
| storage_partition_impl_->GetNetworkContext()->CreateURLLoaderFactory( |
| std::move(receiver), std::move(params)); |
| } else { |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> original_factory; |
| storage_partition_impl_->GetNetworkContext()->CreateURLLoaderFactory( |
| original_factory.InitWithNewPipeAndPassReceiver(), std::move(params)); |
| GetCreateNetworkFactoryCallback().Run(std::move(receiver), GetID(), |
| std::move(original_factory)); |
| } |
| } |
| |
| bool RenderProcessHostImpl::MayReuseHost() { |
| return GetContentClient()->browser()->MayReuseHost(this); |
| } |
| |
| bool RenderProcessHostImpl::IsUnused() { |
| return is_unused_; |
| } |
| |
| void RenderProcessHostImpl::SetIsUsed() { |
| is_unused_ = false; |
| } |
| |
| mojom::RouteProvider* RenderProcessHostImpl::GetRemoteRouteProvider() { |
| return remote_route_provider_.get(); |
| } |
| |
| void RenderProcessHostImpl::AddRoute(int32_t routing_id, |
| IPC::Listener* listener) { |
| TRACE_EVENT2("shutdown", "RenderProcessHostImpl::AddRoute", |
| "render_process_host", this, "routing_id", routing_id); |
| CHECK(!listeners_.Lookup(routing_id)) |
| << "Found Routing ID Conflict: " << routing_id; |
| listeners_.AddWithID(listener, routing_id); |
| } |
| |
| void RenderProcessHostImpl::RemoveRoute(int32_t routing_id) { |
| TRACE_EVENT2("shutdown", "RenderProcessHostImpl::RemoveRoute", |
| "render_process_host", this, "routing_id", routing_id); |
| DCHECK(listeners_.Lookup(routing_id) != nullptr); |
| listeners_.Remove(routing_id); |
| Cleanup(); |
| } |
| |
| void RenderProcessHostImpl::AddObserver(RenderProcessHostObserver* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void RenderProcessHostImpl::RemoveObserver( |
| RenderProcessHostObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| void RenderProcessHostImpl::ShutdownForBadMessage( |
| CrashReportMode crash_report_mode) { |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch(switches::kDisableKillAfterBadIPC)) |
| return; |
| |
| if (run_renderer_in_process()) { |
| // In single process mode it is better if we don't suicide but just |
| // crash. |
| CHECK(false); |
| } |
| |
| // We kill the renderer but don't include a NOTREACHED, because we want the |
| // browser to try to survive when it gets illegal messages from the |
| // renderer. |
| Shutdown(RESULT_CODE_KILLED_BAD_MESSAGE); |
| |
| if (crash_report_mode == CrashReportMode::GENERATE_CRASH_DUMP) { |
| // Set crash keys to understand renderer kills related to site isolation. |
| ChildProcessSecurityPolicyImpl::GetInstance()->LogKilledProcessOriginLock( |
| GetID()); |
| |
| std::string site_isolation_mode; |
| if (SiteIsolationPolicy::UseDedicatedProcessesForAllSites()) |
| site_isolation_mode += "spp "; |
| if (SiteIsolationPolicy::AreIsolatedOriginsEnabled()) |
| site_isolation_mode += "io "; |
| if (SiteIsolationPolicy::IsStrictOriginIsolationEnabled()) |
| site_isolation_mode += "soi "; |
| if (site_isolation_mode.empty()) |
| site_isolation_mode = "(none)"; |
| |
| static auto* isolation_mode_key = base::debug::AllocateCrashKeyString( |
| "site_isolation_mode", base::debug::CrashKeySize::Size32); |
| base::debug::SetCrashKeyString(isolation_mode_key, site_isolation_mode); |
| |
| // Report a crash, since none will be generated by the killed renderer. |
| base::debug::DumpWithoutCrashing(); |
| } |
| |
| // Log the renderer kill to the histogram tracking all kills. |
| BrowserChildProcessHostImpl::HistogramBadMessageTerminated( |
| PROCESS_TYPE_RENDERER); |
| } |
| |
| void RenderProcessHostImpl::UpdateClientPriority(PriorityClient* client) { |
| DCHECK(client); |
| DCHECK_EQ(1u, priority_clients_.count(client)); |
| UpdateProcessPriorityInputs(); |
| } |
| |
| void RenderProcessHostImpl::UpdateFrameWithPriority( |
| base::Optional<FramePriority> previous_priority, |
| base::Optional<FramePriority> new_priority) { |
| // Record the priority of the frames seens so we know for reporting what |
| // combination of normal and low priority frames have been seen. |
| if (new_priority) { |
| if (new_priority == FramePriority::kNormal) { |
| normal_priority_frames_seen_ = true; |
| } else { |
| low_priority_frames_seen_ = true; |
| } |
| } |
| |
| // If we're not using frame priorities, return after recording. |
| if (!base::FeatureList::IsEnabled( |
| features::kUseFramePriorityInRenderProcessHost)) { |
| return; |
| } |
| |
| const bool previous_all_low_priority_frames = HasOnlyLowPriorityFrames(); |
| total_frames_ = |
| total_frames_ - (previous_priority ? 1 : 0) + (new_priority ? 1 : 0); |
| low_priority_frames_ = |
| low_priority_frames_ - |
| (previous_priority && previous_priority == FramePriority::kLow ? 1 : 0) + |
| (new_priority && new_priority == FramePriority::kLow ? 1 : 0); |
| if (previous_all_low_priority_frames != HasOnlyLowPriorityFrames()) |
| UpdateProcessPriority(); |
| } |
| |
| int RenderProcessHostImpl::VisibleClientCount() { |
| return visible_clients_; |
| } |
| |
| unsigned int RenderProcessHostImpl::GetFrameDepth() { |
| return frame_depth_; |
| } |
| |
| bool RenderProcessHostImpl::GetIntersectsViewport() { |
| return intersects_viewport_; |
| } |
| |
| #if defined(OS_ANDROID) |
| ChildProcessImportance RenderProcessHostImpl::GetEffectiveImportance() { |
| return effective_importance_; |
| } |
| |
| void RenderProcessHostImpl::DumpProcessStack() { |
| if (child_process_launcher_) |
| child_process_launcher_->DumpProcessStack(); |
| } |
| #endif |
| |
| void RenderProcessHostImpl::OnMediaStreamAdded() { |
| ++media_stream_count_; |
| UpdateProcessPriority(); |
| } |
| |
| void RenderProcessHostImpl::OnMediaStreamRemoved() { |
| DCHECK_GT(media_stream_count_, 0); |
| --media_stream_count_; |
| UpdateProcessPriority(); |
| } |
| |
| void RenderProcessHostImpl::OnForegroundServiceWorkerAdded() { |
| foreground_service_worker_count_ += 1; |
| UpdateProcessPriority(); |
| } |
| |
| void RenderProcessHostImpl::OnForegroundServiceWorkerRemoved() { |
| DCHECK_GT(foreground_service_worker_count_, 0); |
| foreground_service_worker_count_ -= 1; |
| UpdateProcessPriority(); |
| } |
| |
| // static |
| void RenderProcessHostImpl::set_render_process_host_factory_for_testing( |
| RenderProcessHostFactory* rph_factory) { |
| g_render_process_host_factory_ = rph_factory; |
| } |
| |
| // static |
| RenderProcessHostFactory* |
| RenderProcessHostImpl::get_render_process_host_factory_for_testing() { |
| return g_render_process_host_factory_; |
| } |
| |
| // static |
| void RenderProcessHostImpl::AddFrameWithSite( |
| BrowserContext* browser_context, |
| RenderProcessHost* render_process_host, |
| const SiteInfo& site_info) { |
| if (!ShouldTrackProcessForSite(browser_context, render_process_host, |
| site_info)) |
| return; |
| |
| SiteProcessCountTracker* tracker = static_cast<SiteProcessCountTracker*>( |
| browser_context->GetUserData(kCommittedSiteProcessCountTrackerKey)); |
| if (!tracker) { |
| tracker = new SiteProcessCountTracker(); |
| browser_context->SetUserData(kCommittedSiteProcessCountTrackerKey, |
| base::WrapUnique(tracker)); |
| } |
| tracker->IncrementSiteProcessCount(site_info, render_process_host->GetID()); |
| } |
| |
| // static |
| void RenderProcessHostImpl::RemoveFrameWithSite( |
| BrowserContext* browser_context, |
| RenderProcessHost* render_process_host, |
| const SiteInfo& site_info) { |
| if (!ShouldTrackProcessForSite(browser_context, render_process_host, |
| site_info)) |
| return; |
| |
| SiteProcessCountTracker* tracker = static_cast<SiteProcessCountTracker*>( |
| browser_context->GetUserData(kCommittedSiteProcessCountTrackerKey)); |
| if (!tracker) { |
| tracker = new SiteProcessCountTracker(); |
| browser_context->SetUserData(kCommittedSiteProcessCountTrackerKey, |
| base::WrapUnique(tracker)); |
| } |
| tracker->DecrementSiteProcessCount(site_info, render_process_host->GetID()); |
| } |
| |
| // static |
| void RenderProcessHostImpl::AddExpectedNavigationToSite( |
| BrowserContext* browser_context, |
| RenderProcessHost* render_process_host, |
| const SiteInfo& site_info) { |
| if (!ShouldTrackProcessForSite(browser_context, render_process_host, |
| site_info)) |
| return; |
| |
| SiteProcessCountTracker* tracker = static_cast<SiteProcessCountTracker*>( |
| browser_context->GetUserData(kPendingSiteProcessCountTrackerKey)); |
| if (!tracker) { |
| tracker = new SiteProcessCountTracker(); |
| browser_context->SetUserData(kPendingSiteProcessCountTrackerKey, |
| base::WrapUnique(tracker)); |
| } |
| tracker->IncrementSiteProcessCount(site_info, render_process_host->GetID()); |
| } |
| |
| // static |
| void RenderProcessHostImpl::RemoveExpectedNavigationToSite( |
| BrowserContext* browser_context, |
| RenderProcessHost* render_process_host, |
| const SiteInfo& site_info) { |
| if (!ShouldTrackProcessForSite(browser_context, render_process_host, |
| site_info)) |
| return; |
| |
| SiteProcessCountTracker* tracker = static_cast<SiteProcessCountTracker*>( |
| browser_context->GetUserData(kPendingSiteProcessCountTrackerKey)); |
| if (!tracker) { |
| tracker = new SiteProcessCountTracker(); |
| browser_context->SetUserData(kPendingSiteProcessCountTrackerKey, |
| base::WrapUnique(tracker)); |
| } |
| tracker->DecrementSiteProcessCount(site_info, render_process_host->GetID()); |
| } |
| |
| // static |
| void RenderProcessHostImpl::NotifySpareManagerAboutRecentlyUsedBrowserContext( |
| BrowserContext* browser_context) { |
| SpareRenderProcessHostManager::GetInstance().PrepareForFutureRequests( |
| browser_context); |
| } |
| |
| // static |
| RenderProcessHost* RenderProcessHost::GetSpareRenderProcessHostForTesting() { |
| return SpareRenderProcessHostManager::GetInstance() |
| .spare_render_process_host(); |
| } |
| |
| // static |
| std::unique_ptr<base::CallbackList<void(RenderProcessHost*)>::Subscription> |
| RenderProcessHost::RegisterSpareRenderProcessHostChangedCallback( |
| const base::RepeatingCallback<void(RenderProcessHost*)>& cb) { |
| return SpareRenderProcessHostManager::GetInstance() |
| .RegisterSpareRenderProcessHostChangedCallback(cb); |
| } |
| |
| // static |
| void RenderProcessHostImpl::DiscardSpareRenderProcessHostForTesting() { |
| SpareRenderProcessHostManager::GetInstance().CleanupSpareRenderProcessHost(); |
| } |
| |
| // static |
| bool RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes() { |
| if (!SiteIsolationPolicy::UseDedicatedProcessesForAllSites()) |
| return false; |
| |
| if (!base::FeatureList::IsEnabled(features::kSpareRendererForSitePerProcess)) |
| return false; |
| |
| // Spare renderer actually hurts performance on low-memory devices. See |
| // https://crbug.com/843775 for more details. |
| // |
| // The comparison below is using 1077 rather than 1024 because 1) this helps |
| // ensure that devices with exactly 1GB of RAM won't get included because of |
| // inaccuracies or off-by-one errors and 2) this is the bucket boundary in |
| // Memory.Stats.Win.TotalPhys2. |
| if (base::SysInfo::AmountOfPhysicalMemoryMB() <= 1077) |
| return false; |
| |
| return true; |
| } |
| |
| bool RenderProcessHostImpl::HostHasNotBeenUsed() { |
| return IsUnused() && listeners_.IsEmpty() && keep_alive_ref_count_ == 0 && |
| pending_views_ == 0; |
| } |
| |
| void RenderProcessHostImpl::LockToOrigin( |
| const IsolationContext& isolation_context, |
| const GURL& lock_url) { |
| ChildProcessSecurityPolicyImpl::GetInstance()->LockToOrigin( |
| isolation_context, GetID(), lock_url); |
| |
| // Note that LockToOrigin is only called once per RenderProcessHostImpl |
| // (when committing a navigation into an empty renderer). Therefore, the |
| // call to NotifyRendererIfLockedToSite below is insufficient for setting up |
| // renderers respawned after crashing - this is handled by another call to |
| // NotifyRendererIfLockedToSite from OnProcessLaunched. |
| NotifyRendererIfLockedToSite(); |
| } |
| |
| bool RenderProcessHostImpl::IsLockedToOriginForTesting() { |
| GURL lock_url = |
| ChildProcessSecurityPolicyImpl::GetInstance()->GetOriginLock(GetID()); |
| return !lock_url.is_empty(); |
| } |
| |
| void RenderProcessHostImpl::NotifyRendererIfLockedToSite() { |
| GURL lock_url = |
| ChildProcessSecurityPolicyImpl::GetInstance()->GetOriginLock(GetID()); |
| if (!lock_url.is_valid()) |
| return; |
| |
| if (!SiteInstanceImpl::IsOriginLockASite(lock_url)) |
| return; |
| |
| GetRendererInterface()->SetIsLockedToSite(); |
| } |
| |
| bool RenderProcessHostImpl::IsForGuestsOnly() { |
| return is_for_guests_only_; |
| } |
| |
| StoragePartition* RenderProcessHostImpl::GetStoragePartition() { |
| return storage_partition_impl_; |
| } |
| |
| static void AppendCompositorCommandLineFlags(base::CommandLine* command_line) { |
| command_line->AppendSwitchASCII( |
| switches::kNumRasterThreads, |
| base::NumberToString(NumberOfRendererRasterThreads())); |
| |
| int msaa_sample_count = GpuRasterizationMSAASampleCount(); |
| if (msaa_sample_count >= 0) { |
| command_line->AppendSwitchASCII( |
| blink::switches::kGpuRasterizationMSAASampleCount, |
| base::NumberToString(msaa_sample_count)); |
| } |
| |
| if (IsZeroCopyUploadEnabled()) |
| command_line->AppendSwitch(blink::switches::kEnableZeroCopy); |
| if (!IsPartialRasterEnabled()) |
| command_line->AppendSwitch(blink::switches::kDisablePartialRaster); |
| |
| if (IsGpuMemoryBufferCompositorResourcesEnabled()) { |
| command_line->AppendSwitch( |
| blink::switches::kEnableGpuMemoryBufferCompositorResources); |
| } |
| |
| if (IsMainFrameBeforeActivationEnabled()) |
| command_line->AppendSwitch(cc::switches::kEnableMainFrameBeforeActivation); |
| } |
| |
| void RenderProcessHostImpl::AppendRendererCommandLine( |
| base::CommandLine* command_line) { |
| // Pass the process type first, so it shows first in process listings. |
| command_line->AppendSwitchASCII(switches::kProcessType, |
| switches::kRendererProcess); |
| |
| #if defined(OS_WIN) |
| command_line->AppendArg(switches::kPrefetchArgumentRenderer); |
| #endif // defined(OS_WIN) |
| |
| // Now send any options from our own command line we want to propagate. |
| const base::CommandLine& browser_command_line = |
| *base::CommandLine::ForCurrentProcess(); |
| PropagateBrowserCommandLineToRenderer(browser_command_line, command_line); |
| |
| // Pass on the browser locale. |
| const std::string locale = |
| GetContentClient()->browser()->GetApplicationLocale(); |
| command_line->AppendSwitchASCII(switches::kLang, locale); |
| |
| // A non-empty RendererCmdPrefix implies that Zygote is disabled. |
| if (!base::CommandLine::ForCurrentProcess() |
| ->GetSwitchValueNative(switches::kRendererCmdPrefix) |
| .empty()) { |
| command_line->AppendSwitch(switches::kNoZygote); |
| } |
| |
| GetContentClient()->browser()->AppendExtraCommandLineSwitches(command_line, |
| GetID()); |
| |
| #if defined(OS_WIN) |
| command_line->AppendSwitchASCII( |
| switches::kDeviceScaleFactor, |
| base::NumberToString(display::win::GetDPIScale())); |
| #endif |
| |
| AppendCompositorCommandLineFlags(command_line); |
| |
| command_line->AppendSwitchASCII(switches::kRendererClientId, |
| std::to_string(GetID())); |
| |
| if (SiteIsolationPolicy::UseDedicatedProcessesForAllSites()) { |
| // Disable V8 code mitigations if renderer processes are site-isolated. |
| command_line->AppendSwitch(switches::kNoV8UntrustedCodeMitigations); |
| } |
| } |
| |
| void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( |
| const base::CommandLine& browser_cmd, |
| base::CommandLine* renderer_cmd) { |
| // Propagate the following switches to the renderer command line (along |
| // with any associated values) if present in the browser command line. |
| static const char* const kSwitchNames[] = { |
| network::switches::kExplicitlyAllowedPorts, |
| service_manager::switches::kDisableInProcessStackTraces, |
| sandbox::policy::switches::kDisableSeccompFilterSandbox, |
| sandbox::policy::switches::kNoSandbox, |
| #if defined(OS_LINUX) && !defined(OS_CHROMEOS) |
| switches::kDisableDevShmUsage, |
| #endif |
| #if defined(OS_MACOSX) |
| // Allow this to be set when invoking the browser and relayed along. |
| sandbox::policy::switches::kEnableSandboxLogging, |
| #endif |
| switches::kAgcStartupMinVolume, |
| switches::kAllowLoopbackInPeerConnection, |
| switches::kAndroidFontsPath, |
| switches::kAudioBufferSize, |
| switches::kAutoplayPolicy, |
| switches::kBlinkSettings, |
| switches::kMojoCoreLibraryPath, |
| switches::kDisable2dCanvasImageChromium, |
| switches::kDisableYUVImageDecoding, |
| switches::kDisableAcceleratedVideoDecode, |
| switches::kDisableBackgroundTimerThrottling, |
| switches::kDisableBestEffortTasks, |
| switches::kDisableBreakpad, |
| switches::kDisablePreferCompositingToLCDText, |
| switches::kDisableDatabases, |
| switches::kDisableFileSystem, |
| switches::kDisableFrameRateLimit, |
| switches::kDisableGpuMemoryBufferVideoFrames, |
| switches::kDisableHistogramCustomizer, |
| switches::kDisableLCDText, |
| switches::kDisableLogging, |
| switches::kDisableBackgroundMediaSuspend, |
| switches::kDisableNotifications, |
| switches::kDisableOopRasterization, |
| switches::kEnableDeJelly, |
| switches::kDisableOriginTrialControlledBlinkFeatures, |
| switches::kDisablePepper3DImageChromium, |
| switches::kDisablePermissionsAPI, |
| switches::kDisablePresentationAPI, |
| switches::kDisableRTCSmoothnessAlgorithm, |
| switches::kDisableScrollToTextFragment, |
| switches::kDisableSharedWorkers, |
| switches::kDisableSkiaRuntimeOpts, |
| switches::kDisableSpeechAPI, |
| switches::kDisableThreadedCompositing, |
| switches::kDisableThreadedScrolling, |
| switches::kDisableTouchAdjustment, |
| switches::kDisableTouchDragDrop, |
| switches::kDisableV8IdleTasks, |
| switches::kDisableVideoCaptureUseGpuMemoryBuffer, |
| switches::kDisableWebGLImageChromium, |
| switches::kDomAutomationController, |
| switches::kEnableAccessibilityObjectModel, |
| switches::kEnableAutomation, |
| switches::kEnableExperimentalAccessibilityLanguageDetection, |
| switches::kEnableExperimentalAccessibilityLabelsDebugging, |
| switches::kEnableExperimentalWebPlatformFeatures, |
| switches::kEnableGPUClientLogging, |
| switches::kEnableGpuClientTracing, |
| switches::kEnableGpuMemoryBufferVideoFrames, |
| switches::kEnableGPUServiceLogging, |
| switches::kEnableLCDText, |
| switches::kEnableLogging, |
| switches::kEnableNetworkInformationDownlinkMax, |
| switches::kEnableOopRasterization, |
| switches::kEnablePluginPlaceholderTesting, |
| switches::kEnablePreciseMemoryInfo, |
| switches::kEnablePreferCompositingToLCDText, |
| switches::kEnableSkiaBenchmarking, |
| switches::kEnableThreadedCompositing, |
| switches::kEnableTouchDragDrop, |
| switches::kEnableUnsafeWebGPU, |
| switches::kEnableUseZoomForDSF, |
| switches::kEnableViewport, |
| switches::kEnableVtune, |
| switches::kEnableWebGL2ComputeContext, |
| switches::kEnableWebGLDraftExtensions, |
| switches::kEnableWebGLImageChromium, |
| switches::kFileUrlPathAlias, |
| switches::kForceDeviceScaleFactor, |
| switches::kForceDisplayColorProfile, |
| switches::kForceGpuMemAvailableMb, |
| switches::kForceHighContrast, |
| switches::kForceOverlayFullscreenVideo, |
| switches::kForceVideoOverlays, |
| switches::kFullMemoryCrashReport, |
| switches::kGaiaUrl, |
| switches::kIPCConnectionTimeout, |
| switches::kJavaScriptFlags, |
| switches::kLogBestEffortTasks, |
| switches::kLogFile, |
| switches::kLoggingLevel, |
| switches::kMaxActiveWebGLContexts, |
| switches::kMSEAudioBufferSizeLimitMb, |
| switches::kMSEVideoBufferSizeLimitMb, |
| switches::kNetworkQuietTimeout, |
| switches::kNoZygote, |
| switches::kOverridePluginPowerSaverForTesting, |
| switches::kPassiveListenersDefault, |
| switches::kPerfettoDisableInterning, |
| switches::kPpapiInProcess, |
| switches::kProfilingAtStart, |
| switches::kProfilingFile, |
| switches::kProfilingFlush, |
| switches::kRegisterPepperPlugins, |
| switches::kRemoteDebuggingPipe, |
| switches::kRemoteDebuggingPort, |
| switches::kRendererStartupDialog, |
| switches::kReportVp9AsAnUnsupportedMimeType, |
| switches::kStatsCollectionController, |
| switches::kSkiaFontCacheLimitMb, |
| switches::kSkiaResourceCacheLimitMb, |
| switches::kTestType, |
| switches::kTouchEventFeatureDetection, |
| switches::kTouchTextSelectionStrategy, |
| switches::kTraceToConsole, |
| switches::kUseFakeCodecForPeerConnection, |
| switches::kUseFakeUIForMediaStream, |
| switches::kUseMobileUserAgent, |
| switches::kV, |
| switches::kVideoCaptureUseGpuMemoryBuffer, |
| switches::kVideoThreads, |
| switches::kVideoUnderflowThresholdMs, |
| switches::kVModule, |
| switches::kWebglAntialiasingMode, |
| switches::kWebglMSAASampleCount, |
| // Please keep these in alphabetical order. |
| blink::switches::kAllowPreCommitInput, |
| blink::switches::kDefaultTileWidth, |
| blink::switches::kDefaultTileHeight, |
| blink::switches::kDisableImageAnimationResync, |
| blink::switches::kDisableLowResTiling, |
| blink::switches::kDisableRGBA4444Textures, |
| blink::switches::kEnableLowResTiling, |
| blink::switches::kEnableRGBA4444Textures, |
| blink::switches::kMinHeightForGpuRasterTile, |
| blink::switches::kMaxUntiledLayerWidth, |
| blink::switches::kMaxUntiledLayerHeight, |
| blink::switches::kShowLayoutShiftRegions, |
| blink::switches::kShowPaintRects, |
| // Please keep these in alphabetical order. Compositor switches here |
| // should also be added to |
| // chrome/browser/chromeos/login/chrome_restart_request.cc. |
| cc::switches::kCCScrollAnimationDurationForTesting, |
| cc::switches::kCheckDamageEarly, |
| cc::switches::kDisableCheckerImaging, |
| cc::switches::kDisableCompositedAntialiasing, |
| cc::switches::kDisableThreadedAnimation, |
| cc::switches::kEnableGpuBenchmarking, |
| cc::switches::kHighlightNonLCDTextLayers, |
| cc::switches::kShowCompositedLayerBorders, |
| cc::switches::kShowFPSCounter, |
| cc::switches::kShowLayerAnimationBounds, |
| cc::switches::kShowPropertyChangedRects, |
| cc::switches::kShowScreenSpaceRects, |
| cc::switches::kShowSurfaceDamageRects, |
| cc::switches::kSlowDownRasterScaleFactor, |
| cc::switches::kBrowserControlsHideThreshold, |
| cc::switches::kBrowserControlsShowThreshold, |
| switches::kRunAllCompositorStagesBeforeDraw, |
| |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| switches::kEnablePepperTesting, |
| #endif |
| switches::kDisableWebRtcHWDecoding, |
| switches::kDisableWebRtcHWEncoding, |
| switches::kEnableWebRtcSrtpAesGcm, |
| switches::kEnableWebRtcSrtpEncryptedHeaders, |
| switches::kEnableWebRtcStunOrigin, |
| switches::kEnforceWebRtcIPPermissionCheck, |
| switches::kWebRtcMaxCaptureFramerate, |
| switches::kEnableLowEndDeviceMode, |
| switches::kDisableLowEndDeviceMode, |
| switches::kDisallowNonExactResourceReuse, |
| #if defined(OS_ANDROID) |
| switches::kDisableMediaSessionAPI, |
| switches::kEnableReachedCodeProfiler, |
| switches::kReachedCodeSamplingIntervalUs, |
| switches::kRendererWaitForJavaDebugger, |
| #endif |
| #if defined(OS_WIN) |
| switches::kDisableHighResTimer, |
| switches::kEnableWin7WebRtcHWH264Decoding, |
| switches::kTrySupportedChannelLayouts, |
| #endif |
| #if defined(USE_OZONE) |
| switches::kOzonePlatform, |
| #endif |
| #if defined(ENABLE_IPC_FUZZER) |
| switches::kIpcDumpDirectory, |
| switches::kIpcFuzzerTestcase, |
| #endif |
| }; |
| renderer_cmd->CopySwitchesFrom(browser_cmd, kSwitchNames, |
| base::size(kSwitchNames)); |
| |
| BrowserChildProcessHostImpl::CopyFeatureAndFieldTrialFlags(renderer_cmd); |
| BrowserChildProcessHostImpl::CopyTraceStartupFlags(renderer_cmd); |
| |
| // Only run the Stun trials in the first renderer. |
| if (!has_done_stun_trials && |
| browser_cmd.HasSwitch(switches::kWebRtcStunProbeTrialParameter)) { |
| has_done_stun_trials = true; |
| renderer_cmd->AppendSwitchASCII( |
| switches::kWebRtcStunProbeTrialParameter, |
| browser_cmd.GetSwitchValueASCII( |
| switches::kWebRtcStunProbeTrialParameter)); |
| } |
| |
| // Disable databases in incognito mode. |
| if (GetBrowserContext()->IsOffTheRecord() && |
| !browser_cmd.HasSwitch(switches::kDisableDatabases)) { |
| renderer_cmd->AppendSwitch(switches::kDisableDatabases); |
| } |
| |
| #if defined(OS_ANDROID) |
| if (browser_cmd.HasSwitch(switches::kDisableGpuCompositing)) { |
| renderer_cmd->AppendSwitch(switches::kDisableGpuCompositing); |
| } |
| #elif !defined(OS_CHROMEOS) |
| // If gpu compositing is not being used, tell the renderer at startup. This |
| // is inherently racey, as it may change while the renderer is being |
| // launched, but the renderer will hear about the correct state eventually. |
| // This optimizes the common case to avoid wasted work. |
| if (GpuDataManagerImpl::GetInstance()->IsGpuCompositingDisabled()) |
| renderer_cmd->AppendSwitch(switches::kDisableGpuCompositing); |
| #endif // defined(OS_ANDROID) |
| |
| // Add kWaitForDebugger to let renderer process wait for a debugger. |
| if (browser_cmd.HasSwitch(switches::kWaitForDebuggerChildren)) { |
| // Look to pass-on the kWaitForDebugger flag. |
| std::string value = |
| browser_cmd.GetSwitchValueASCII(switches::kWaitForDebuggerChildren); |
| if (value.empty() || value == switches::kRendererProcess) { |
| renderer_cmd->AppendSwitch(switches::kWaitForDebugger); |
| } |
| } |
| |
| #if defined(OS_WIN) && !defined(OFFICIAL_BUILD) |
| // Needed because we can't show the dialog from the sandbox. Don't pass |
| // --no-sandbox in official builds because that would bypass the bad_flgs |
| // prompt. |
| if (renderer_cmd->HasSwitch(switches::kRendererStartupDialog) && |
| !renderer_cmd->HasSwitch(sandbox::policy::switches::kNoSandbox)) { |
| renderer_cmd->AppendSwitch(sandbox::policy::switches::kNoSandbox); |
| } |
| #endif |
| |
| CopyFeatureSwitch(browser_cmd, renderer_cmd, switches::kEnableBlinkFeatures); |
| CopyFeatureSwitch(browser_cmd, renderer_cmd, switches::kDisableBlinkFeatures); |
| } |
| |
| const base::Process& RenderProcessHostImpl::GetProcess() { |
| if (run_renderer_in_process()) { |
| // This is a sentinel object used for this process in single process mode. |
| static const base::NoDestructor<base::Process> self( |
| base::Process::Current()); |
| return *self; |
| } |
| |
| if (!child_process_launcher_.get() || child_process_launcher_->IsStarting()) { |
| // This is a sentinel for "no process". |
| static const base::NoDestructor<base::Process> null_process; |
| return *null_process; |
| } |
| |
| return child_process_launcher_->GetProcess(); |
| } |
| |
| bool RenderProcessHostImpl::IsReady() { |
| // The process launch result (that sets GetHandle()) and the channel |
| // connection (that sets channel_connected_) can happen in either order. |
| return GetProcess().Handle() && channel_connected_; |
| } |
| |
| bool RenderProcessHostImpl::Shutdown(int exit_code) { |
| if (run_renderer_in_process()) |
| return false; // Single process mode never shuts down the renderer. |
| |
| if (!child_process_launcher_.get()) |
| return false; |
| |
| shutdown_exit_code_ = exit_code; |
| return child_process_launcher_->Terminate(exit_code); |
| } |
| |
| bool RenderProcessHostImpl::FastShutdownIfPossible(size_t page_count, |
| bool skip_unload_handlers) { |
| // Do not shut down the process if there are active or pending views other |
| // than the ones we're shutting down. |
| if (page_count && page_count != (GetActiveViewCount() + pending_views_)) |
| return false; |
| |
| if (run_renderer_in_process()) |
| return false; // Single process mode never shuts down the renderer. |
| |
| if (!child_process_launcher_.get()) |
| return false; // Render process hasn't started or is probably crashed. |
| |
| // Test if there's an unload listener. |
| // NOTE: It's possible that an onunload listener may be installed |
| // while we're shutting down, so there's a small race here. Given that |
| // the window is small, it's unlikely that the web page has much |
| // state that will be lost by not calling its unload handlers properly. |
| if (!skip_unload_handlers && !SuddenTerminationAllowed()) |
| return false; |
| |
| if (keep_alive_ref_count_ != 0) { |
| if (keep_alive_start_time_.is_null()) |
| keep_alive_start_time_ = base::TimeTicks::Now(); |
| return false; |
| } |
| |
| // Set this before ProcessDied() so observers can tell if the render process |
| // died due to fast shutdown versus another cause. |
| fast_shutdown_started_ = true; |
| |
| ProcessDied(false /* already_dead */, nullptr); |
| return true; |
| } |
| |
| bool RenderProcessHostImpl::Send(IPC::Message* msg) { |
| TRACE_IPC_MESSAGE_SEND("renderer_host", "RenderProcessHostImpl::Send", msg); |
| |
| std::unique_ptr<IPC::Message> message(msg); |
| |
| // |channel_| is only null after Cleanup(), at which point we don't care |
| // about delivering any messages. |
| if (!channel_) |
| return false; |
| |
| #if !defined(OS_ANDROID) |
| DCHECK(!message->is_sync()); |
| #else |
| if (message->is_sync()) { |
| // If Init() hasn't been called yet since construction or the last |
| // ProcessDied() we avoid blocking on sync IPC. |
| if (!IsInitializedAndNotDead()) |
| return false; |
| |
| // Likewise if we've done Init(), but process launch has not yet |
| // completed, we avoid blocking on sync IPC. |
| if (child_process_launcher_.get() && child_process_launcher_->IsStarting()) |
| return false; |
| } |
| #endif |
| |
| // Allow tests to watch IPCs sent to the renderer. |
| if (ipc_send_watcher_for_testing_) |
| ipc_send_watcher_for_testing_.Run(*message); |
| |
| return channel_->Send(message.release()); |
| } |
| |
| bool RenderProcessHostImpl::OnMessageReceived(const IPC::Message& msg) { |
| // If we're about to be deleted, or have initiated the fast shutdown |
| // sequence, we ignore incoming messages. |
| |
| if (deleting_soon_ || fast_shutdown_started_) |
| return false; |
| |
| mark_child_process_activity_time(); |
| if (msg.routing_id() == MSG_ROUTING_CONTROL) { |
| // Dispatch control messages. |
| IPC_BEGIN_MESSAGE_MAP(RenderProcessHostImpl, msg) |
| IPC_MESSAGE_HANDLER(WidgetHostMsg_Close_ACK, OnCloseACK) |
| // Adding single handlers for your service here is fine, but once your |
| // service needs more than one handler, please extract them into a new |
| // message filter and add that filter to CreateMessageFilters(). |
| IPC_END_MESSAGE_MAP() |
| |
| return true; |
| } |
| |
| // Dispatch incoming messages to the appropriate IPC::Listener. |
| IPC::Listener* listener = listeners_.Lookup(msg.routing_id()); |
| if (!listener) { |
| if (msg.is_sync()) { |
| // The listener has gone away, so we must respond or else the caller |
| // will hang waiting for a reply. |
| IPC::Message* reply = IPC::SyncMessage::GenerateReply(&msg); |
| reply->set_reply_error(); |
| Send(reply); |
| } |
| return true; |
| } |
| return listener->OnMessageReceived(msg); |
| } |
| |
| void RenderProcessHostImpl::OnAssociatedInterfaceRequest( |
| const std::string& interface_name, |
| mojo::ScopedInterfaceEndpointHandle handle) { |
| if (associated_interfaces_ && |
| !associated_interfaces_->TryBindInterface(interface_name, &handle)) { |
| LOG(ERROR) << "Request for unknown Channel-associated interface: " |
| << interface_name; |
| } |
| } |
| |
| void RenderProcessHostImpl::OnChannelConnected(int32_t peer_pid) { |
| channel_connected_ = true; |
| |
| #if defined(OS_MACOSX) |
| ChildProcessTaskPortProvider::GetInstance()->OnChildProcessLaunched( |
| peer_pid, child_process_.get()); |
| #endif |
| |
| if (IsReady()) { |
| DCHECK(!sent_render_process_ready_); |
| sent_render_process_ready_ = true; |
| // Send RenderProcessReady only if we already received the process handle. |
| for (auto& observer : observers_) |
| observer.RenderProcessReady(this); |
| |
| #if defined(OS_LINUX) |
| // Provide /proc/{renderer pid}/status and statm files for |
| // MemoryUsageMonitor in blink. |
| ProvideStatusFileForRenderer(); |
| #endif |
| |
| ProvideSwapFileForRenderer(); |
| } |
| |
| #if BUILDFLAG(IPC_MESSAGE_LOG_ENABLED) |
| child_process_->SetIPCLoggingEnabled(IPC::Logging::GetInstance()->Enabled()); |
| #endif |
| |
| #if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX) |
| child_process_->SetProfilingFile(OpenProfilingFile()); |
| #endif |
| } |
| |
| void RenderProcessHostImpl::OnChannelError() { |
| ProcessDied(true /* already_dead */, nullptr); |
| } |
| |
| void RenderProcessHostImpl::OnBadMessageReceived(const IPC::Message& message) { |
| // Message de-serialization failed. We consider this a capital crime. Kill |
| // the renderer if we have one. |
| auto type = message.type(); |
| LOG(ERROR) << "bad message " << type << " terminating renderer."; |
| |
| // The ReceivedBadMessage call below will trigger a DumpWithoutCrashing. |
| // Alias enough information here so that we can determine what the bad |
| // message was. |
| base::debug::Alias(&type); |
| |
| bad_message::ReceivedBadMessage(this, |
| bad_message::RPH_DESERIALIZATION_FAILED); |
| } |
| |
| BrowserContext* RenderProcessHostImpl::GetBrowserContext() { |
| return browser_context_; |
| } |
| |
| bool RenderProcessHostImpl::InSameStoragePartition( |
| StoragePartition* partition) { |
| return storage_partition_impl_ == partition; |
| } |
| |
| int RenderProcessHostImpl::GetID() { |
| return id_; |
| } |
| |
| bool RenderProcessHostImpl::IsInitializedAndNotDead() { |
| return is_initialized_ && !is_dead_; |
| } |
| |
| void RenderProcessHostImpl::SetBlocked(bool blocked) { |
| if (blocked == is_blocked_) |
| return; |
| |
| is_blocked_ = blocked; |
| blocked_state_changed_callback_list_.Notify(blocked); |
| } |
| |
| bool RenderProcessHostImpl::IsBlocked() { |
| return is_blocked_; |
| } |
| |
| std::unique_ptr<base::CallbackList<void(bool)>::Subscription> |
| RenderProcessHostImpl::RegisterBlockStateChangedCallback( |
| const base::RepeatingCallback<void(bool)>& cb) { |
| return blocked_state_changed_callback_list_.Add(cb); |
| } |
| |
| void RenderProcessHostImpl::Cleanup() { |
| TRACE_EVENT1("shutdown", "RenderProcessHostImpl::Cleanup", |
| "render_process_host", this); |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // Keep the one renderer thread around forever in single process mode. |
| if (run_renderer_in_process()) |
| return; |
| |
| // If within_process_died_observer_ is true, one of our observers performed |
| // an action that caused us to die (e.g. http://crbug.com/339504). |
| // Therefore, delay the destruction until all of the observer callbacks have |
| // been made, and guarantee that the RenderProcessHostDestroyed observer |
| // callback is always the last callback fired. |
| if (within_process_died_observer_) { |
| TRACE_EVENT1( |
| "shutdown", |
| "RenderProcessHostImpl::Cleanup : within_process_died_observer", |
| "render_process_host", this); |
| delayed_cleanup_needed_ = true; |
| return; |
| } |
| delayed_cleanup_needed_ = false; |
| |
| // Records the time when the process starts kept alive by the ref count for |
| // UMA. |
| if (listeners_.IsEmpty() && keep_alive_ref_count_ > 0 && |
| keep_alive_start_time_.is_null()) { |
| keep_alive_start_time_ = base::TimeTicks::Now(); |
| } |
| |
| // Until there are no other owners of this object, we can't delete |
| // ourselves. |
| if (!listeners_.IsEmpty()) { |
| TRACE_EVENT2("shutdown", "RenderProcessHostImpl::Cleanup : Has listeners.", |
| "render_process_host", this, "listener_count", |
| listeners_.size()); |
| return; |
| } else if (keep_alive_ref_count_ != 0) { |
| TRACE_EVENT2("shutdown", |
| "RenderProcessHostImpl::Cleanup : Have keep_alive_ref.", |
| "render_process_host", this, "keep_alive_ref_count_", |
| keep_alive_ref_count_); |
| return; |
| } |
| |
| TRACE_EVENT1("shutdown", "RenderProcessHostImpl::Cleanup : Starting cleanup.", |
| "render_process_host", this); |
| TRACE_EVENT_NESTABLE_ASYNC_BEGIN2("shutdown", "Cleanup in progress", this, |
| "render_process_host", this, |
| "browser_context", browser_context_); |
| |
| if (is_initialized_) { |
| GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&WebRtcLog::ClearLogMessageCallback, GetID())); |
| } |
| |
| if (!keep_alive_start_time_.is_null()) { |
| UMA_HISTOGRAM_LONG_TIMES("BrowserRenderProcessHost.KeepAliveDuration", |
| base::TimeTicks::Now() - keep_alive_start_time_); |
| } |
| |
| // We cannot clean up twice; if this fails, there is an issue with our |
| // control flow. |
| DCHECK(!deleting_soon_); |
| |
| DCHECK_EQ(0, pending_views_); |
| |
| // If the process associated with this RenderProcessHost is still alive, |
| // notify all observers that the process has exited cleanly, even though it |
| // will be destroyed a bit later. Observers shouldn't rely on this process |
| // anymore. |
| if (IsInitializedAndNotDead()) { |
| // Populates Android-only fields and closes the underlying base::Process. |
| ChildProcessTerminationInfo info = |
| child_process_launcher_->GetChildTerminationInfo( |
| false /* already_dead */); |
| info.status = base::TERMINATION_STATUS_NORMAL_TERMINATION; |
| info.exit_code = 0; |
| PopulateTerminationInfoRendererFields(&info); |
| for (auto& observer : observers_) { |
| observer.RenderProcessExited(this, info); |
| } |
| } |
| for (auto& observer : observers_) |
| observer.RenderProcessHostDestroyed(this); |
| NotificationService::current()->Notify( |
| NOTIFICATION_RENDERER_PROCESS_TERMINATED, Source<RenderProcessHost>(this), |
| NotificationService::NoDetails()); |
| |
| #ifndef NDEBUG |
| is_self_deleted_ = true; |
| #endif |
| base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); |
| deleting_soon_ = true; |
| |
| if (render_frame_message_filter_) { |
| // RenderFrameMessageFilter is refcounted and can outlive the |
| // ResourceContext. If the BrowserContext is shutting down, after |
| // RenderProcessHostImpl cleanup a task will be posted to the IO thread |
| // that destroys the ResourceContext. Therefore the ClearResourceContext |
| // task must be posted now to ensure it gets ahead of the destruction of |
| // the ResourceContext in the IOThread sequence. |
| GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&RenderFrameMessageFilter::ClearResourceContext, |
| render_frame_message_filter_)); |
| } |
| |
| // Destroy all mojo bindings and IPC channels that can cause calls to this |
| // object, to avoid method invocations that trigger usages of profile. |
| ResetIPC(); |
| |
| // Its important to remove the kSessionStorageHolder after the channel |
| // has been reset to avoid deleting the underlying namespaces prior |
| // to processing ipcs referring to them. |
| DCHECK(!channel_); |
| RemoveUserData(kSessionStorageHolderKey); |
| |
| // Remove ourself from the list of renderer processes so that we can't be |
| // reused in between now and when the Delete task runs. |
| UnregisterHost(GetID()); |
| } |
| |
| void RenderProcessHostImpl::PopulateTerminationInfoRendererFields( |
| ChildProcessTerminationInfo* info) { |
| info->renderer_has_visible_clients = VisibleClientCount() > 0; |
| info->renderer_was_subframe = GetFrameDepth() > 0; |
| } |
| |
| void RenderProcessHostImpl::AddPendingView() { |
| const bool had_pending_views = pending_views_++; |
| if (!had_pending_views) |
| UpdateProcessPriority(); |
| } |
| |
| void RenderProcessHostImpl::RemovePendingView() { |
| DCHECK(pending_views_); |
| --pending_views_; |
| if (!pending_views_) |
| UpdateProcessPriority(); |
| } |
| |
| void RenderProcessHostImpl::AddPriorityClient(PriorityClient* priority_client) { |
| DCHECK(!base::Contains(priority_clients_, priority_client)); |
| priority_clients_.insert(priority_client); |
| UpdateProcessPriorityInputs(); |
| } |
| |
| void RenderProcessHostImpl::RemovePriorityClient( |
| PriorityClient* priority_client) { |
| DCHECK(base::Contains(priority_clients_, priority_client)); |
| priority_clients_.erase(priority_client); |
| UpdateProcessPriorityInputs(); |
| } |
| |
| void RenderProcessHostImpl::SetPriorityOverride(bool foregrounded) { |
| priority_override_ = foregrounded; |
| UpdateProcessPriority(); |
| } |
| |
| bool RenderProcessHostImpl::HasPriorityOverride() { |
| return priority_override_.has_value(); |
| } |
| |
| void RenderProcessHostImpl::ClearPriorityOverride() { |
| priority_override_.reset(); |
| UpdateProcessPriority(); |
| } |
| |
| void RenderProcessHostImpl::SetSuddenTerminationAllowed(bool enabled) { |
| sudden_termination_allowed_ = enabled; |
| } |
| |
| bool RenderProcessHostImpl::SuddenTerminationAllowed() { |
| return sudden_termination_allowed_; |
| } |
| |
| base::TimeDelta RenderProcessHostImpl::GetChildProcessIdleTime() { |
| return base::TimeTicks::Now() - child_process_activity_time_; |
| } |
| |
| void RenderProcessHostImpl::FilterURL(bool empty_allowed, GURL* url) { |
| FilterURL(this, empty_allowed, url); |
| } |
| |
| void RenderProcessHostImpl::EnableAudioDebugRecordings( |
| const base::FilePath& file_path) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| aec_dump_manager_.Start(file_path); |
| } |
| |
| void RenderProcessHostImpl::DisableAudioDebugRecordings() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| aec_dump_manager_.Stop(); |
| } |
| |
| RenderProcessHostImpl::WebRtcStopRtpDumpCallback |
| RenderProcessHostImpl::StartRtpDump(bool incoming, |
| bool outgoing, |
| WebRtcRtpPacketCallback packet_callback) { |
| p2p_socket_dispatcher_host_->StartRtpDump(incoming, outgoing, |
| std::move(packet_callback)); |
| |
| return base::BindOnce(&P2PSocketDispatcherHost::StopRtpDump, |
| p2p_socket_dispatcher_host_->GetWeakPtr()); |
| } |
| |
| void RenderProcessHostImpl::EnableWebRtcEventLogOutput(int lid, |
| int output_period_ms) { |
| GetPeerConnectionTrackerHost()->StartEventLog(lid, output_period_ms); |
| } |
| |
| void RenderProcessHostImpl::DisableWebRtcEventLogOutput(int lid) { |
| GetPeerConnectionTrackerHost()->StopEventLog(lid); |
| } |
| |
| IPC::ChannelProxy* RenderProcessHostImpl::GetChannel() { |
| return channel_.get(); |
| } |
| |
| void RenderProcessHostImpl::AddFilter(BrowserMessageFilter* filter) { |
| filter->RegisterAssociatedInterfaces(channel_.get()); |
| channel_->AddFilter(filter->GetFilter()); |
| } |
| |
| bool RenderProcessHostImpl::FastShutdownStarted() { |
| return fast_shutdown_started_; |
| } |
| |
| // static |
| void RenderProcessHostImpl::RegisterHost(int host_id, RenderProcessHost* host) { |
| TRACE_EVENT2("shutdown", "RenderProcessHostImpl::RegisterHost", |
| "render_process_host", host, "host_id", host_id); |
| GetAllHosts().AddWithID(host, host_id); |
| } |
| |
| // static |
| void RenderProcessHostImpl::UnregisterHost(int host_id) { |
| RenderProcessHost* host = GetAllHosts().Lookup(host_id); |
| TRACE_EVENT2("shutdown", "RenderProcessHostImpl::UnregisterHost", |
| "render_process_host", host, "host_id", host_id); |
| |
| if (!host) |
| return; |
| |
| GetAllHosts().Remove(host_id); |
| |
| // Look up the map of site to process for the given browser_context, |
| // in case we need to remove this process from it. It will be registered |
| // under any sites it rendered that use process-per-site mode. |
| SiteProcessMap* map = |
| GetSiteProcessMapForBrowserContext(host->GetBrowserContext()); |
| map->RemoveProcess(host); |
| } |
| |
| // static |
| void RenderProcessHostImpl::RegisterCreationObserver( |
| RenderProcessHostCreationObserver* observer) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| GetAllCreationObservers().push_back(observer); |
| } |
| |
| // static |
| void RenderProcessHostImpl::UnregisterCreationObserver( |
| RenderProcessHostCreationObserver* observer) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| auto iter = std::find(GetAllCreationObservers().begin(), |
| GetAllCreationObservers().end(), observer); |
| DCHECK(iter != GetAllCreationObservers().end()); |
| GetAllCreationObservers().erase(iter); |
| } |
| |
| // static |
| void RenderProcessHostImpl::FilterURL(RenderProcessHost* rph, |
| bool empty_allowed, |
| GURL* url) { |
| if (empty_allowed && url->is_empty()) |
| return; |
| |
| if (!url->is_valid()) { |
| // Have to use about:blank for the denied case, instead of an empty GURL. |
| // This is because the browser treats navigation to an empty GURL as a |
| // navigation to the home page. This is often a privileged page |
| // (chrome://newtab/) which is exactly what we don't want. |
| TRACE_EVENT1("navigation", "RenderProcessHost::FilterURL - invalid URL", |
| "process_id", rph->GetID()); |
| *url = GURL(kBlockedURL); |
| return; |
| } |
| |
| ChildProcessSecurityPolicyImpl* policy = |
| ChildProcessSecurityPolicyImpl::GetInstance(); |
| if (!policy->CanRequestURL(rph->GetID(), *url)) { |
| // If this renderer is not permitted to request this URL, we invalidate |
| // the URL. This prevents us from storing the blocked URL and becoming |
| // confused later. |
| TRACE_EVENT2("navigation", |
| "RenderProcessHost::FilterURL - failed CanRequestURL", |
| "process_id", rph->GetID(), "url", url->spec()); |
| VLOG(1) << "Blocked URL " << url->spec(); |
| *url = GURL(kBlockedURL); |
| } |
| } |
| |
| // static |
| bool RenderProcessHostImpl::IsSuitableHost( |
| RenderProcessHost* host, |
| const IsolationContext& isolation_context, |
| const GURL& site_url, |
| const GURL& lock_url, |
| const bool is_guest) { |
| BrowserContext* browser_context = |
| isolation_context.browser_or_resource_context().ToBrowserContext(); |
| DCHECK(browser_context); |
| if (run_renderer_in_process()) { |
| DCHECK_EQ(host->GetBrowserContext(), browser_context) |
| << " Single-process mode does not support multiple browser contexts."; |
| return true; |
| } |
| |
| if (host->GetBrowserContext() != browser_context) |
| return false; |
| |
| // Do not allow sharing of guest hosts. This is to prevent bugs where guest |
| // and non-guest storage gets mixed. In the future, we might consider |
| // enabling the sharing of guests, in this case this check should be removed |
| // and InSameStoragePartition should handle the possible sharing. |
| if (host->IsForGuestsOnly()) |
| return false; |
| |
| // Check whether the given host and the intended site_url will be using the |
| // same StoragePartition, since a RenderProcessHost can only support a |
| // single StoragePartition. This is relevant for packaged apps. |
| StoragePartition* dest_partition = |
| BrowserContext::GetStoragePartitionForSite(browser_context, site_url); |
| if (!host->InSameStoragePartition(dest_partition)) |
| return false; |
| |
| // Check WebUI bindings and origin locks. Note that |lock_url| may differ |
| // from |site_url| if an effective URL is used. |
| auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); |
| bool host_has_web_ui_bindings = policy->HasWebUIBindings(host->GetID()); |
| GURL process_lock = policy->GetOriginLock(host->GetID()); |
| if (host->HostHasNotBeenUsed()) { |
| // If the host hasn't been used, it won't have the expected WebUI bindings |
| // or origin locks just *yet* - skip the checks in this case. One example |
| // where this case can happen is when the spare RenderProcessHost gets |
| // used. |
| CHECK(!host_has_web_ui_bindings); |
| CHECK(process_lock.is_empty()); |
| } else { |
| // WebUI checks. |
| bool url_requires_web_ui_bindings = |
| WebUIControllerFactoryRegistry::GetInstance()->UseWebUIBindingsForURL( |
| browser_context, site_url); |
| if (host_has_web_ui_bindings != url_requires_web_ui_bindings) |
| return false; |
| |
| if (!process_lock.is_empty()) { |
| // If this process is locked to a site, it cannot be reused for a |
| // destination that doesn't require a dedicated process, even for the |
| // same site. This can happen with dynamic isolated origins (see |
| // https://crbug.com/950453). |
| if (!SiteInstanceImpl::ShouldLockToOrigin(isolation_context, site_url, |
| is_guest)) |
| return false; |
| |
| // If the destination requires a different process lock, this process |
| // cannot be used. |
| if (process_lock != lock_url) |
| return false; |
| } else { |
| if (!host->IsUnused() && SiteInstanceImpl::ShouldLockToOrigin( |
| isolation_context, site_url, is_guest)) { |
| // If this process has been used to host any other content, it cannot |
| // be reused if the destination site requires a dedicated process and |
| // should use a process locked to just that site. |
| return false; |
| } |
| } |
| } |
| |
| if (!GetContentClient()->browser()->IsSuitableHost(host, site_url)) |
| return false; |
| |
| // If this site_url is going to require a dedicated process, then check |
| // whether this process has a pending navigation to a URL for which |
| // SiteInstance does not assign site URLs. If this is the case, it is not |
| // safe to reuse this process for a navigation which itself assigns site |
| // URLs, since in that case the latter navigation could lock this process |
| // before the commit for the siteless URL arrives, resulting in a renderer |
| // kill. See https://crbug.com/970046. |
| if (SiteInstanceImpl::ShouldAssignSiteForURL(site_url) && |
| SiteInstanceImpl::DoesSiteRequireDedicatedProcess(isolation_context, |
| site_url)) { |
| SiteProcessCountTracker* pending_tracker = |
| static_cast<SiteProcessCountTracker*>( |
| browser_context->GetUserData(kPendingSiteProcessCountTrackerKey)); |
| if (pending_tracker && |
| pending_tracker->ContainsNonReusableSiteForHost(host)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // static |
| void RenderProcessHost::WarmupSpareRenderProcessHost( |
| content::BrowserContext* browser_context) { |
| SpareRenderProcessHostManager::GetInstance().WarmupSpareRenderProcessHost( |
| browser_context); |
| } |
| |
| // static |
| bool RenderProcessHost::run_renderer_in_process() { |
| return g_run_renderer_in_process; |
| } |
| |
| // static |
| void RenderProcessHost::SetRunRendererInProcess(bool value) { |
| g_run_renderer_in_process = value; |
| |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| if (value) { |
| if (!command_line->HasSwitch(switches::kLang)) { |
| // Modify the current process' command line to include the browser |
| // locale, as the renderer expects this flag to be set. |
| const std::string locale = |
| GetContentClient()->browser()->GetApplicationLocale(); |
| command_line->AppendSwitchASCII(switches::kLang, locale); |
| } |
| // TODO(piman): we should really send configuration through bools rather |
| // than by parsing strings, i.e. sending an IPC rather than command line |
| // args. crbug.com/314909 |
| AppendCompositorCommandLineFlags(command_line); |
| } |
| } |
| |
| // static |
| RenderProcessHost::iterator RenderProcessHost::AllHostsIterator() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| return iterator(&GetAllHosts()); |
| } |
| |
| // static |
| RenderProcessHost* RenderProcessHost::FromID(int render_process_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| return GetAllHosts().Lookup(render_process_id); |
| } |
| |
| // static |
| bool RenderProcessHost::ShouldTryToUseExistingProcessHost( |
| BrowserContext* browser_context, |
| const GURL& url) { |
| if (run_renderer_in_process()) |
| return true; |
| |
| // NOTE: Sometimes it's necessary to create more render processes than |
| // GetMaxRendererProcessCount(), for instance when we want to create |
| // a renderer process for a browser context that has no existing |
| // renderers. This is OK in moderation, since the |
| // GetMaxRendererProcessCount() is conservative. |
| if (GetAllHosts().size() >= GetMaxRendererProcessCount()) |
| return true; |
| |
| return GetContentClient()->browser()->ShouldTryToUseExistingProcessHost( |
| browser_context, url); |
| } |
| |
| // static |
| RenderProcessHost* RenderProcessHostImpl::GetExistingProcessHost( |
| SiteInstanceImpl* site_instance) { |
| // First figure out which existing renderers we can use. |
| std::vector<RenderProcessHost*> suitable_renderers; |
| suitable_renderers.reserve(GetAllHosts().size()); |
| |
| for (iterator iter(AllHostsIterator()); !iter.IsAtEnd(); iter.Advance()) { |
| if (iter.GetCurrentValue()->MayReuseHost() && |
| RenderProcessHostImpl::IsSuitableHost( |
| iter.GetCurrentValue(), site_instance->GetIsolationContext(), |
| site_instance->GetSiteInfo().site_url(), site_instance->lock_url(), |
| site_instance->IsGuest())) { |
| // The spare is always considered before process reuse. |
| DCHECK_NE(iter.GetCurrentValue(), |
| SpareRenderProcessHostManager::GetInstance() |
| .spare_render_process_host()); |
| |
| suitable_renderers.push_back(iter.GetCurrentValue()); |
| } |
| } |
| |
| // Now pick a random suitable renderer, if we have any. |
| if (!suitable_renderers.empty()) { |
| int suitable_count = static_cast<int>(suitable_renderers.size()); |
| int random_index = base::RandInt(0, suitable_count - 1); |
| return suitable_renderers[random_index]; |
| } |
| |
| return nullptr; |
| } |
| |
| // static |
| RenderProcessHost* RenderProcessHostImpl::GetUnusedProcessHostForServiceWorker( |
| SiteInstanceImpl* site_instance) { |
| DCHECK(site_instance->is_for_service_worker()); |
| if (site_instance->process_reuse_policy() != |
| SiteInstanceImpl::ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE) { |
| return nullptr; |
| } |
| |
| auto& spare_process_manager = SpareRenderProcessHostManager::GetInstance(); |
| iterator iter(AllHostsIterator()); |
| while (!iter.IsAtEnd()) { |
| auto* host = iter.GetCurrentValue(); |
| // This function tries to choose the process that will be chosen by a |
| // navigation that will use the service worker that is being started. |
| // Prefer to not take the spare process host, since if the navigation is |
| // out of the New Tab Page on Android, it will be using the existing NTP |
| // process instead of the spare process. If this function doesn't find a |
| // suitable process, the spare can still be chosen when |
| // MaybeTakeSpareRenderProcessHost() is called later. |
| bool is_spare = (host == spare_process_manager.spare_render_process_host()); |
| |
| if (!is_spare && iter.GetCurrentValue()->MayReuseHost() && |
| iter.GetCurrentValue()->IsUnused() && |
| RenderProcessHostImpl::IsSuitableHost( |
| iter.GetCurrentValue(), site_instance->GetIsolationContext(), |
| site_instance->GetSiteInfo().site_url(), site_instance->lock_url(), |
| site_instance->IsGuest())) { |
| return host; |
| } |
| iter.Advance(); |
| } |
| return nullptr; |
| } |
| |
| // static |
| bool RenderProcessHostImpl::ShouldUseProcessPerSite( |
| BrowserContext* browser_context, |
| const SiteInfo& site_info) { |
| // Returns true if we should use the process-per-site model. This will be |
| // the case if the --process-per-site switch is specified, or in |
| // process-per-site-instance for particular sites (e.g., NTP). Note that |
| // --single-process is handled in ShouldTryToUseExistingProcessHost. |
| const base::CommandLine& command_line = |
| *base::CommandLine::ForCurrentProcess(); |
| if (command_line.HasSwitch(switches::kProcessPerSite)) |
| return true; |
| |
| // Error pages should use process-per-site model, as it is useful to |
| // consolidate them to minimize resource usage and there is no security |
| // drawback to combining them all in the same process. |
| if (site_info.site_url().SchemeIs(kChromeErrorScheme)) |
| return true; |
| |
| // Otherwise let the content client decide, defaulting to false. |
| return GetContentClient()->browser()->ShouldUseProcessPerSite( |
| browser_context, site_info.site_url()); |
| } |
| |
| // static |
| RenderProcessHost* RenderProcessHostImpl::GetSoleProcessHostForURL( |
| const IsolationContext& isolation_context, |
| const GURL& url) { |
| SiteInfo site_info = |
| SiteInstanceImpl::ComputeSiteInfo(isolation_context, url); |
| GURL lock_url = |
| SiteInstanceImpl::DetermineProcessLockURL(isolation_context, url); |
| return GetSoleProcessHostForSite(isolation_context, site_info, lock_url, |
| /* is_guest */ false); |
| } |
| |
| // static |
| RenderProcessHost* RenderProcessHostImpl::GetSoleProcessHostForSite( |
| const IsolationContext& isolation_context, |
| const SiteInfo& site_info, |
| const GURL& lock_url, |
| const bool is_guest) { |
| // Look up the map of site to process for the given browser_context. |
| SiteProcessMap* map = GetSiteProcessMapForBrowserContext( |
| isolation_context.browser_or_resource_context().ToBrowserContext()); |
| |
| // See if we have an existing process with appropriate bindings for this |
| // site. If not, the caller should create a new process and register it. |
| // Note that IsSuitableHost expects a site URL rather than the full |url|. |
| const GURL& site_url = site_info.site_url(); |
| RenderProcessHost* host = map->FindProcess(site_url.possibly_invalid_spec()); |
| if (host && (!host->MayReuseHost() || |
| !IsSuitableHost(host, isolation_context, site_url, lock_url, |
| is_guest))) { |
| // The registered process does not have an appropriate set of bindings for |
| // the url. Remove it from the map so we can register a better one. |
| RecordAction( |
| base::UserMetricsAction("BindingsMismatch_GetProcessHostPerSite")); |
| map->RemoveProcess(host); |
| host = nullptr; |
| } |
| |
| return host; |
| } |
| |
| void RenderProcessHostImpl::RegisterSoleProcessHostForSite( |
| RenderProcessHost* process, |
| SiteInstanceImpl* site_instance) { |
| DCHECK(process); |
| DCHECK(site_instance); |
| |
| // Look up the map of site to process for site_instance's BrowserContext. |
| SiteProcessMap* map = |
| GetSiteProcessMapForBrowserContext(site_instance->GetBrowserContext()); |
| |
| // Only register valid, non-empty sites. Empty or invalid sites will not |
| // use process-per-site mode. We cannot check whether the process has |
| // appropriate bindings here, because the bindings have not yet been |
| // granted. |
| std::string site = |
| site_instance->GetSiteInfo().site_url().possibly_invalid_spec(); |
| if (!site.empty()) |
| map->RegisterProcess(site, process); |
| } |
| |
| // static |
| RenderProcessHost* RenderProcessHostImpl::GetProcessHostForSiteInstance( |
| SiteInstanceImpl* site_instance) { |
| const SiteInfo& site_info = site_instance->GetSiteInfo(); |
| SiteInstanceImpl::ProcessReusePolicy process_reuse_policy = |
| site_instance->process_reuse_policy(); |
| RenderProcessHost* render_process_host = nullptr; |
| |
| bool is_unmatched_service_worker = site_instance->is_for_service_worker(); |
| BrowserContext* browser_context = site_instance->GetBrowserContext(); |
| |
| // First, attempt to reuse an existing RenderProcessHost if necessary. |
| switch (process_reuse_policy) { |
| case SiteInstanceImpl::ProcessReusePolicy::PROCESS_PER_SITE: |
| render_process_host = GetSoleProcessHostForSite( |
| site_instance->GetIsolationContext(), site_info, |
| site_instance->lock_url(), site_instance->IsGuest()); |
| break; |
| case SiteInstanceImpl::ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE: |
| render_process_host = |
| FindReusableProcessHostForSiteInstance(site_instance); |
| UMA_HISTOGRAM_BOOLEAN( |
| "SiteIsolation.ReusePendingOrCommittedSite.CouldReuse", |
| render_process_host != nullptr); |
| if (render_process_host) |
| is_unmatched_service_worker = false; |
| break; |
| default: |
| break; |
| } |
| |
| // If not, attempt to reuse an existing process with an unmatched service |
| // worker for this site. Exclude cases where the policy is DEFAULT and the |
| // site instance is for a service worker. We use DEFAULT when we have failed |
| // to start the service worker before and want to use a new process. |
| if (!render_process_host && |
| !(process_reuse_policy == SiteInstanceImpl::ProcessReusePolicy::DEFAULT && |
| site_instance->is_for_service_worker())) { |
| render_process_host = |
| UnmatchedServiceWorkerProcessTracker::MatchWithSite(site_instance); |
| } |
| |
| // If a process hasn't been selected yet, check whether a default process |
| // has been assigned for this browsing instance and use that. This is used |
| // to group all SiteInstance that don't require a dedicated process into a |
| // single process. This will only be set if |
| // kProcessSharingWithStrictSiteInstances is enabled. |
| if (!render_process_host) { |
| render_process_host = site_instance->GetDefaultProcessIfUsable(); |
| DCHECK(!render_process_host || |
| base::FeatureList::IsEnabled( |
| features::kProcessSharingWithStrictSiteInstances)); |
| } |
| |
| // If a process hasn't been selected yet, and the site instance is for a |
| // service worker, try to use an unused process host. One might have been |
| // created for a navigation and this will let the navigation and the service |
| // worker share the same process. |
| if (base::FeatureList::IsEnabled( |
| features::kServiceWorkerPrefersUnusedProcess) && |
| !render_process_host && is_unmatched_service_worker) { |
| render_process_host = GetUnusedProcessHostForServiceWorker(site_instance); |
| } |
| |
| // See if the spare RenderProcessHost can be used. |
| auto& spare_process_manager = SpareRenderProcessHostManager::GetInstance(); |
| bool spare_was_taken = false; |
| if (!render_process_host) { |
| render_process_host = spare_process_manager.MaybeTakeSpareRenderProcessHost( |
| browser_context, site_instance); |
| spare_was_taken = (render_process_host != nullptr); |
| } |
| |
| // If not (or if none found), see if we should reuse an existing process. |
| if (!render_process_host && ShouldTryToUseExistingProcessHost( |
| browser_context, site_info.site_url())) { |
| render_process_host = GetExistingProcessHost(site_instance); |
| } |
| |
| // If we found a process to reuse, sanity check that it is suitable for |
| // hosting |site_url|. For example, if |site_url| requires a dedicated |
| // process, we should never pick a process used by, or locked to, a different |
| // site. |
| if (render_process_host && |
| !RenderProcessHostImpl::IsSuitableHost( |
| render_process_host, site_instance->GetIsolationContext(), |
| site_info.site_url(), site_instance->lock_url(), |
| site_instance->IsGuest())) { |
| base::debug::SetCrashKeyString(bad_message::GetRequestedSiteURLKey(), |
| site_info.GetDebugString()); |
| ChildProcessSecurityPolicyImpl::GetInstance()->LogKilledProcessOriginLock( |
| render_process_host->GetID()); |
| CHECK(false) << "Unsuitable process reused for site " << site_info; |
| } |
| |
| // Otherwise, create a new RenderProcessHost. |
| if (!render_process_host) { |
| // Pass a null StoragePartition. Tests with TestBrowserContext using a |
| // RenderProcessHostFactory may not instantiate a StoragePartition, and |
| // creating one here with GetStoragePartition() can run into cross-thread |
| // issues as TestBrowserContext initialization is done on the main thread. |
| render_process_host = |
| CreateRenderProcessHost(browser_context, nullptr, site_instance); |
| } |
| |
| // It is important to call PrepareForFutureRequests *after* potentially |
| // creating a process a few statements earlier - doing this avoids violating |
| // the process limit. |
| // |
| // We should not warm-up another spare if the spare was not taken, because |
| // in this case we might have created a new process - we want to avoid |
| // spawning two processes at the same time. In this case the call to |
| // PrepareForFutureRequests will be postponed until later (e.g. until the |
| // navigation commits or a cross-site redirect happens). |
| if (spare_was_taken) |
| spare_process_manager.PrepareForFutureRequests(browser_context); |
| |
| if (is_unmatched_service_worker) { |
| UnmatchedServiceWorkerProcessTracker::Register(render_process_host, |
| site_instance); |
| } |
| |
| // Make sure the chosen process is in the correct StoragePartition for the |
| // SiteInstance. |
| CHECK(render_process_host->InSameStoragePartition( |
| BrowserContext::GetStoragePartition(browser_context, site_instance, |
| false /* can_create */))); |
| |
| return render_process_host; |
| } |
| |
| void RenderProcessHostImpl::CreateSharedRendererHistogramAllocator() { |
| // Create a persistent memory segment for renderer histograms only if |
| // they're active in the browser. |
| if (!base::GlobalHistogramAllocator::Get()) { |
| if (is_initialized_) { |
| HistogramController::GetInstance()->SetHistogramMemory<RenderProcessHost>( |
| this, base::WritableSharedMemoryRegion()); |
| } |
| return; |
| } |
| |
| // Get handle to the renderer process. Stop if there is none. |
| base::ProcessHandle destination = GetProcess().Handle(); |
| if (destination == base::kNullProcessHandle) |
| return; |
| |
| // Create persistent/shared memory and allow histograms to be stored in |
| // it. Memory that is not actually used won't be physically mapped by the |
| // system. RendererMetrics usage, as reported in UMA, peaked around 0.7MiB |
| // as of 2016-12-20. |
| base::WritableSharedMemoryRegion shm_region = |
| base::WritableSharedMemoryRegion::Create(2 << 20); // 2 MiB |
| base::WritableSharedMemoryMapping shm_mapping = shm_region.Map(); |
| if (!shm_region.IsValid() || !shm_mapping.IsValid()) |
| return; |
| |
| // If a renderer crashes before completing startup and gets restarted, this |
| // method will get called a second time meaning that a metrics-allocator |
| // already exists. We have to recreate it here because previously used |
| // |shm_region| is gone. |
| metrics_allocator_ = |
| std::make_unique<base::WritableSharedPersistentMemoryAllocator>( |
| std::move(shm_mapping), GetID(), "RendererMetrics"); |
| |
| HistogramController::GetInstance()->SetHistogramMemory<RenderProcessHost>( |
| this, std::move(shm_region)); |
| } |
| |
| void RenderProcessHostImpl::ProcessDied( |
| bool already_dead, |
| ChildProcessTerminationInfo* known_info) { |
| // Our child process has died. If we didn't expect it, it's a crash. |
| // In any case, we need to let everyone know it's gone. |
| // The OnChannelError notification can fire multiple times due to nested |
| // sync calls to a renderer. If we don't have a valid channel here it means |
| // we already handled the error. |
| |
| // It should not be possible for us to be called re-entrantly. |
| DCHECK(!within_process_died_observer_); |
| |
| // It should not be possible for a process death notification to come in |
| // while we are dying. |
| DCHECK(!deleting_soon_); |
| |
| // child_process_launcher_ can be NULL in single process mode or if fast |
| // termination happened. |
| ChildProcessTerminationInfo info; |
| info.exit_code = 0; |
| if (known_info) { |
| info = *known_info; |
| } else if (child_process_launcher_.get()) { |
| info = child_process_launcher_->GetChildTerminationInfo(already_dead); |
| if (already_dead && info.status == base::TERMINATION_STATUS_STILL_RUNNING) { |
| // May be in case of IPC error, if it takes long time for renderer |
| // to exit. Child process will be killed in any case during |
| // child_process_launcher_.reset(). Make sure we will not broadcast |
| // RenderProcessExited with status TERMINATION_STATUS_STILL_RUNNING, |
| // since this will break WebContentsImpl logic. |
| info.status = base::TERMINATION_STATUS_PROCESS_CRASHED; |
| |
| // TODO(siggi): Remove this once https://crbug.com/806661 is resolved. |
| #if defined(OS_WIN) |
| if (info.exit_code == WAIT_TIMEOUT && g_analyze_hung_renderer) |
| g_analyze_hung_renderer(child_process_launcher_->GetProcess()); |
| #endif |
| } |
| } |
| PopulateTerminationInfoRendererFields(&info); |
| |
| child_process_launcher_.reset(); |
| is_dead_ = true; |
| // Make sure no IPCs or mojo calls from the old process get dispatched after |
| // it has died. |
| ResetIPC(); |
| |
| UpdateProcessPriority(); |
| |
| // RenderProcessExited relies on the exit code set during shutdown. |
| if (shutdown_exit_code_ != -1) |
| info.exit_code = shutdown_exit_code_; |
| |
| within_process_died_observer_ = true; |
| for (auto& observer : observers_) |
| observer.RenderProcessExited(this, info); |
| |
| NotificationService::current()->Notify( |
| NOTIFICATION_RENDERER_PROCESS_CLOSED, Source<RenderProcessHost>(this), |
| Details<ChildProcessTerminationInfo>(&info)); |
| within_process_died_observer_ = false; |
| |
| RemoveUserData(kSessionStorageHolderKey); |
| |
| // Initialize a new ChannelProxy in case this host is re-used for a new |
| // process. This ensures that new messages can be sent on the host ASAP |
| // (even before Init()) and they'll eventually reach the new process. |
| // |
| // Note that this may have already been called by one of the above observers |
| EnableSendQueue(); |
| |
| // It's possible that one of the calls out to the observers might have |
| // caused this object to be no longer needed. |
| if (delayed_cleanup_needed_) |
| Cleanup(); |
| |
| compositing_mode_reporter_.reset(); |
| |
| HistogramController::GetInstance()->NotifyChildDied<RenderProcessHost>(this); |
| // This object is not deleted at this point and might be reused later. |
| // TODO(darin): clean this up |
| } |
| |
| void RenderProcessHostImpl::ResetIPC() { |
| renderer_host_receiver_.reset(); |
| io_thread_host_impl_.reset(); |
| route_provider_receiver_.reset(); |
| associated_interface_provider_receivers_.Clear(); |
| associated_interfaces_.reset(); |
| coordinator_connector_receiver_.reset(); |
| tracing_registration_.reset(); |
| |
| // Destroy all embedded CompositorFrameSinks. |
| embedded_frame_sink_provider_.reset(); |
| |
| dom_storage_provider_receiver_.reset(); |
| for (auto receiver_id : dom_storage_receiver_ids_) |
| storage_partition_impl_->UnbindDomStorage(receiver_id); |
| |
| instance_weak_factory_.emplace(this); |
| |
| // If RenderProcessHostImpl is reused, the next renderer will send a new |
| // request for FrameSinkProvider so make sure frame_sink_provider_ is ready |
| // for that. |
| frame_sink_provider_.Unbind(); |
| |
| // If RenderProcessHostImpl is reused, the next renderer will send a new |
| // request for CodeCacheHost. Make sure that we clear the stale |
| // object so that we can clearly create the new CodeCacheHostImpl while |
| // asserting we don't have any duplicates. |
| code_cache_host_impl_.reset(); |
| |
| // It's important not to wait for the DeleteTask to delete the channel |
| // proxy. Kill it off now. That way, in case the profile is going away, the |
| // rest of the objects attached to this RenderProcessHost start going |
| // away first, since deleting the channel proxy will post a |
| // OnChannelClosed() to IPC::ChannelProxy::Context on the IO thread. |
| ResetChannelProxy(); |
| |
| // The PermissionServiceContext holds PermissionSubscriptions originating from |
| // service workers. These subscriptions observe the PermissionControllerImpl |
| // that is owned by the Profile corresponding to |this|. At this point, IPC |
| // are unbound so no new subscriptions can be made. Existing subscriptions |
| // need to be released here, as the Profile, and with it, the |
| // PermissionControllerImpl, can be destroyed anytime after |
| // RenderProcessHostImpl::Cleanup() returns. |
| permission_service_context_.reset(); |
| } |
| |
| size_t RenderProcessHost::GetActiveViewCount() { |
| size_t num_active_views = 0; |
| std::unique_ptr<RenderWidgetHostIterator> widgets( |
| RenderWidgetHost::GetRenderWidgetHosts()); |
| while (RenderWidgetHost* widget = widgets->GetNextHost()) { |
| // Count only RenderWidgetHosts in this process. |
| if (widget->GetProcess()->GetID() == GetID()) |
| num_active_views++; |
| } |
| return num_active_views; |
| } |
| |
| void RenderProcessHost::PostTaskWhenProcessIsReady(base::OnceClosure task) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(!task.is_null()); |
| new RenderProcessHostIsReadyObserver(this, std::move(task)); |
| } |
| |
| // static |
| void RenderProcessHost::SetHungRendererAnalysisFunction( |
| AnalyzeHungRendererFunction analyze_hung_renderer) { |
| g_analyze_hung_renderer = analyze_hung_renderer; |
| } |
| |
| void RenderProcessHostImpl::ReleaseOnCloseACK( |
| RenderProcessHost* host, |
| const SessionStorageNamespaceMap& sessions, |
| int widget_route_id) { |
| DCHECK(host); |
| if (sessions.empty()) |
| return; |
| SessionStorageHolder* holder = static_cast<SessionStorageHolder*>( |
| host->GetUserData(kSessionStorageHolderKey)); |
| if (!holder) { |
| holder = new SessionStorageHolder(); |
| host->SetUserData(kSessionStorageHolderKey, base::WrapUnique(holder)); |
| } |
| holder->Hold(sessions, widget_route_id); |
| } |
| |
| void RenderProcessHostImpl::SuddenTerminationChanged(bool enabled) { |
| SetSuddenTerminationAllowed(enabled); |
| } |
| |
| void RenderProcessHostImpl::RecordUserMetricsAction(const std::string& action) { |
| base::RecordComputedAction(action); |
| } |
| |
| void RenderProcessHostImpl::ResolveProxy( |
| const GURL& url, |
| mojom::RendererHost::ResolveProxyCallback callback) { |
| resolve_proxy_helper_->ResolveProxy(url, std::move(callback)); |
| } |
| |
| void RenderProcessHostImpl::UpdateProcessPriorityInputs() { |
| int32_t new_visible_widgets_count = 0; |
| unsigned int new_frame_depth = kMaxFrameDepthForPriority; |
| bool new_intersects_viewport = false; |
| #if defined(OS_ANDROID) |
| ChildProcessImportance new_effective_importance = |
| ChildProcessImportance::NORMAL; |
| #endif |
| for (auto* client : priority_clients_) { |
| Priority priority = client->GetPriority(); |
| |
| // Compute the lowest depth of widgets with highest visibility priority. |
| // See comment on |frame_depth_| for more details. |
| if (priority.is_hidden) { |
| if (!new_visible_widgets_count) { |
| new_frame_depth = std::min(new_frame_depth, priority.frame_depth); |
| new_intersects_viewport = |
| new_intersects_viewport || priority.intersects_viewport; |
| } |
| } else { |
| if (new_visible_widgets_count) { |
| new_frame_depth = std::min(new_frame_depth, priority.frame_depth); |
| new_intersects_viewport = |
| new_intersects_viewport || priority.intersects_viewport; |
| } else { |
| new_frame_depth = priority.frame_depth; |
| new_intersects_viewport = priority.intersects_viewport; |
| } |
| new_visible_widgets_count++; |
| } |
| |
| #if defined(OS_ANDROID) |
| new_effective_importance = |
| std::max(new_effective_importance, priority.importance); |
| #endif |
| } |
| |
| bool inputs_changed = new_visible_widgets_count != visible_clients_ || |
| frame_depth_ != new_frame_depth || |
| intersects_viewport_ != new_intersects_viewport; |
| visible_clients_ = new_visible_widgets_count; |
| frame_depth_ = new_frame_depth; |
| intersects_viewport_ = new_intersects_viewport; |
| #if defined(OS_ANDROID) |
| inputs_changed = |
| inputs_changed || new_effective_importance != effective_importance_; |
| effective_importance_ = new_effective_importance; |
| #endif |
| if (inputs_changed) |
| UpdateProcessPriority(); |
| } |
| |
| void RenderProcessHostImpl::UpdateProcessPriority() { |
| if (!run_renderer_in_process() && (!child_process_launcher_.get() || |
| child_process_launcher_->IsStarting())) { |
| // This path can be hit early (no-op) or on ProcessDied(). Reset |
| // |priority_| to defaults in case this RenderProcessHostImpl is re-used. |
| priority_.visible = !blink::kLaunchingProcessIsBackgrounded; |
| priority_.boost_for_pending_views = true; |
| return; |
| } |
| |
| if (!has_recorded_media_stream_frame_depth_metric_ && !visible_clients_ && |
| media_stream_count_) { |
| UMA_HISTOGRAM_EXACT_LINEAR( |
| "BrowserRenderProcessHost.InvisibleMediaStreamFrameDepth", frame_depth_, |
| 50); |
| has_recorded_media_stream_frame_depth_metric_ = true; |
| } |
| |
| ChildProcessLauncherPriority priority( |
| visible_clients_ > 0 || base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kDisableRendererBackgrounding), |
| media_stream_count_ > 0, foreground_service_worker_count_ > 0, |
| HasOnlyLowPriorityFrames(), frame_depth_, intersects_viewport_, |
| !!pending_views_ /* boost_for_pending_views */ |
| #if defined(OS_ANDROID) |
| , |
| GetEffectiveImportance() |
| #endif |
| ); |
| |
| // If a priority override has been specified, use it instead. |
| // TODO(chrisha): After experimentation, either integrate the experimental |
| // logic into this class, or rip out the existing logic entirely. |
| if (priority_override_.has_value()) { |
| bool foregrounded = priority_override_.value(); |
| priority = ChildProcessLauncherPriority( |
| foregrounded, /* is_visible */ |
| foregrounded, /* has_media_stream */ |
| foregrounded, /* has_foreground_service_worker */ |
| false, /* has_only_low_priority_frames */ |
| 0, /* frame_depth */ |
| foregrounded, /* intersects_viewport */ |
| false /* boost_for_pending_views */ |
| #if defined(OS_ANDROID) |
| , |
| foregrounded ? ChildProcessImportance::NORMAL |
| : ChildProcessImportance::MODERATE /* importance */ |
| #endif |
| ); |
| DCHECK_EQ(!foregrounded, priority.is_background()); |
| } |
| |
| if (priority_ == priority) |
| return; |
| const bool background_state_changed = |
| priority_.is_background() != priority.is_background(); |
| const bool visibility_state_changed = priority_.visible != priority.visible; |
| |
| TRACE_EVENT2("renderer_host", "RenderProcessHostImpl::UpdateProcessPriority", |
| "should_background", priority.is_background(), |
| "has_pending_views", priority.boost_for_pending_views); |
| priority_ = priority; |
| |
| // Control the background state from the browser process, otherwise the task |
| // telling the renderer to "unbackground" itself may be preempted by other |
| // tasks executing at lowered priority ahead of it or simply by not being |
| // swiftly scheduled by the OS per the low process priority |
| // (http://crbug.com/398103). |
| if (!run_renderer_in_process()) { |
| DCHECK(child_process_launcher_.get()); |
| DCHECK(!child_process_launcher_->IsStarting()); |
| // Make sure to keep the pid in the trace so we can tell which process is |
| // being modified. |
| TRACE_EVENT2( |
| "renderer_host", |
| "RenderProcessHostImpl::UpdateProcessPriority.SetProcessPriority", |
| "pid", child_process_launcher_->GetProcess().Pid(), |
| "priority_is_background", priority.is_background()); |
| child_process_launcher_->SetProcessPriority(priority_); |
| } |
| |
| // When switching in/out of the background, update the time spent in the |
| // background so the time spent backgrounded vs overall can be reported. |
| if (background_state_changed) { |
| is_backgrounded_ = priority_.is_background(); |
| // Don't update backgrounding metrics until the render process finishes |
| // initializing, at which point it will set |background_status_update_time_| |
| // to the current time. |
| if (!background_status_update_time_.is_null()) { |
| base::TimeTicks update_time = clock_->NowTicks(); |
| base::TimeDelta update_duration = |
| update_time - background_status_update_time_; |
| background_status_update_time_ = update_time; |
| if (!is_backgrounded_) |
| background_duration_ += update_duration; |
| } |
| } |
| |
| // Notify the child process of the change in state. |
| if ((background_state_changed) || visibility_state_changed) { |
| SendProcessStateToRenderer(); |
| } |
| } |
| |
| void RenderProcessHostImpl::SendProcessStateToRenderer() { |
| mojom::RenderProcessBackgroundState background_state = |
| priority_.is_background() |
| ? mojom::RenderProcessBackgroundState::kBackgrounded |
| : mojom::RenderProcessBackgroundState::kForegrounded; |
| mojom::RenderProcessVisibleState visible_state = |
| priority_.visible ? mojom::RenderProcessVisibleState::kVisible |
| : mojom::RenderProcessVisibleState::kHidden; |
| GetRendererInterface()->SetProcessState(background_state, visible_state); |
| } |
| |
| void RenderProcessHostImpl::OnProcessLaunched() { |
| // No point doing anything, since this object will be destructed soon. We |
| // especially don't want to send the RENDERER_PROCESS_CREATED notification, |
| // since some clients might expect a RENDERER_PROCESS_TERMINATED afterwards |
| // to properly cleanup. |
| if (deleting_soon_) |
| return; |
| |
| if (child_process_launcher_) { |
| DCHECK(child_process_launcher_->GetProcess().IsValid()); |
| // TODO(https://crbug.com/875933): This should be based on |
| // |priority_.is_background()|, see similar check below. |
| DCHECK_EQ(blink::kLaunchingProcessIsBackgrounded, !priority_.visible); |
| |
| // Unpause the channel now that the process is launched. We don't flush it |
| // yet to ensure that any initialization messages sent here (e.g., things |
| // done in response to NOTIFICATION_RENDER_PROCESS_CREATED; see below) |
| // preempt already queued messages. |
| channel_->Unpause(false /* flush */); |
| |
| if (coordinator_connector_receiver_.is_bound()) |
| coordinator_connector_receiver_.Resume(); |
| |
| // Not all platforms launch processes in the same backgrounded state. Make |
| // sure |priority_.visible| reflects this platform's initial process |
| // state. |
| #if defined(OS_MACOSX) |
| priority_.visible = |
| !child_process_launcher_->GetProcess().IsProcessBackgrounded( |
| ChildProcessTaskPortProvider::GetInstance()); |
| #elif defined(OS_ANDROID) |
| // Android child process priority works differently and cannot be queried |
| // directly from base::Process. |
| // TODO(https://crbug.com/875933): Fix initial priority on Android to |
| // reflect |priority_.is_background()|. |
| DCHECK_EQ(blink::kLaunchingProcessIsBackgrounded, !priority_.visible); |
| #else |
| priority_.visible = |
| !child_process_launcher_->GetProcess().IsProcessBackgrounded(); |
| #endif // defined(OS_MACOSX) |
| |
| // Only update the priority on startup if boosting is enabled (to avoid |
| // reintroducing https://crbug.com/560446#c13 while pending views only |
| // experimentally result in a boost). |
| if (priority_.boost_for_pending_views) |
| UpdateProcessPriority(); |
| |
| // Share histograms between the renderer and this process. |
| CreateSharedRendererHistogramAllocator(); |
| } |
| |
| // Pass bits of global renderer state to the renderer. |
| GetRendererInterface()->SetIsCrossOriginIsolated(cross_origin_isolated_); |
| GetRendererInterface()->SetUserAgent( |
| GetContentClient()->browser()->GetUserAgent()); |
| GetRendererInterface()->SetUserAgentMetadata( |
| GetContentClient()->browser()->GetUserAgentMetadata()); |
| GetRendererInterface()->SetCorsExemptHeaderList( |
| storage_partition_impl_->cors_exempt_header_list()); |
| NotifyRendererIfLockedToSite(); |
| |
| // Send the initial system color info to the renderer. |
| ThemeHelper::GetInstance()->SendSystemColorInfo(GetRendererInterface()); |
| |
| // NOTE: This needs to be before flushing queued messages, because |
| // ExtensionService uses this notification to initialize the renderer |
| // process with state that must be there before any JavaScript executes. |
| // |
| // The queued messages contain such things as "navigate". If this |
| // notification was after, we can end up executing JavaScript before the |
| // initialization happens. |
| NotificationService::current()->Notify(NOTIFICATION_RENDERER_PROCESS_CREATED, |
| Source<RenderProcessHost>(this), |
| NotificationService::NoDetails()); |
| for (auto* observer : GetAllCreationObservers()) |
| observer->OnRenderProcessHostCreated(this); |
| |
| if (child_process_launcher_) |
| channel_->Flush(); |
| |
| if (IsReady()) { |
| DCHECK(!sent_render_process_ready_); |
| sent_render_process_ready_ = true; |
| // Send RenderProcessReady only if the channel is already connected. |
| for (auto& observer : observers_) |
| observer.RenderProcessReady(this); |
| |
| #if defined(OS_LINUX) |
| // Provide /proc/{renderer pid}/status and statm files for |
| // MemoryUsageMonitor in blink. |
| ProvideStatusFileForRenderer(); |
| #endif |
| } |
| |
| aec_dump_manager_.set_pid(GetProcess().Pid()); |
| aec_dump_manager_.AutoStart(); |
| |
| tracing_registration_ = TracingServiceController::Get().RegisterClient( |
| GetProcess().Pid(), |
| base::BindRepeating(&RenderProcessHostImpl::BindTracedProcess, |
| instance_weak_factory_->GetWeakPtr())); |
| } |
| |
| void RenderProcessHostImpl::OnProcessLaunchFailed(int error_code) { |
| // If this object will be destructed soon, then observers have already been |
| // sent a RenderProcessHostDestroyed notification, and we must observe our |
| // contract that says that will be the last call. |
| if (deleting_soon_) |
| return; |
| |
| ChildProcessTerminationInfo info; |
| info.status = base::TERMINATION_STATUS_LAUNCH_FAILED; |
| info.exit_code = error_code; |
| PopulateTerminationInfoRendererFields(&info); |
| ProcessDied(true, &info); |
| } |
| |
| void RenderProcessHostImpl::OnCloseACK(int closed_widget_route_id) { |
| SessionStorageHolder* holder = |
| static_cast<SessionStorageHolder*>(GetUserData(kSessionStorageHolderKey)); |
| if (!holder) |
| return; |
| holder->Release(closed_widget_route_id); |
| } |
| |
| // static |
| RenderProcessHost* |
| RenderProcessHostImpl::FindReusableProcessHostForSiteInstance( |
| SiteInstanceImpl* site_instance) { |
| BrowserContext* browser_context = site_instance->GetBrowserContext(); |
| if (!ShouldFindReusableProcessHostForSite(browser_context, |
| site_instance->GetSiteInfo())) { |
| return nullptr; |
| } |
| |
| std::set<RenderProcessHost*> eligible_foreground_hosts; |
| std::set<RenderProcessHost*> eligible_background_hosts; |
| |
| // First, add the RenderProcessHosts expecting a navigation to |site_url| to |
| // the list of eligible RenderProcessHosts. |
| SiteProcessCountTracker* pending_tracker = |
| static_cast<SiteProcessCountTracker*>( |
| browser_context->GetUserData(kPendingSiteProcessCountTrackerKey)); |
| if (pending_tracker) { |
| pending_tracker->FindRenderProcessesForSiteInstance( |
| site_instance, &eligible_foreground_hosts, &eligible_background_hosts); |
| } |
| |
| if (eligible_foreground_hosts.empty()) { |
| // If needed, add the RenderProcessHosts hosting a frame for |site_url| to |
| // the list of eligible RenderProcessHosts. |
| SiteProcessCountTracker* committed_tracker = |
| static_cast<SiteProcessCountTracker*>( |
| browser_context->GetUserData(kCommittedSiteProcessCountTrackerKey)); |
| if (committed_tracker) { |
| committed_tracker->FindRenderProcessesForSiteInstance( |
| site_instance, &eligible_foreground_hosts, |
| &eligible_background_hosts); |
| } |
| } |
| |
| if (!eligible_foreground_hosts.empty()) { |
| int index = base::RandInt(0, eligible_foreground_hosts.size() - 1); |
| auto iterator = eligible_foreground_hosts.begin(); |
| for (int i = 0; i < index; ++i) |
| ++iterator; |
| return *iterator; |
| } |
| |
| if (!eligible_background_hosts.empty()) { |
| int index = base::RandInt(0, eligible_background_hosts.size() - 1); |
| auto iterator = eligible_background_hosts.begin(); |
| for (int i = 0; i < index; ++i) |
| ++iterator; |
| return *iterator; |
| } |
| |
| return nullptr; |
| } |
| |
| void RenderProcessHostImpl::CreateAgentMetricsCollectorHost( |
| mojo::PendingReceiver<blink::mojom::AgentMetricsCollectorHost> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (!agent_metrics_collector_host_) { |
| agent_metrics_collector_host_.reset( |
| new AgentMetricsCollectorHost(this->GetID(), std::move(receiver))); |
| AddObserver(agent_metrics_collector_host_.get()); |
| } |
| } |
| |
| void RenderProcessHostImpl::BindPeerConnectionTrackerHost( |
| mojo::PendingReceiver<blink::mojom::PeerConnectionTrackerHost> receiver) { |
| GetPeerConnectionTrackerHost()->BindReceiver(std::move(receiver)); |
| } |
| |
| #if BUILDFLAG(ENABLE_MDNS) |
| void RenderProcessHostImpl::CreateMdnsResponder( |
| mojo::PendingReceiver<network::mojom::MdnsResponder> receiver) { |
| GetStoragePartition()->GetNetworkContext()->CreateMdnsResponder( |
| std::move(receiver)); |
| } |
| #endif // BUILDFLAG(ENABLE_MDNS) |
| |
| // static |
| void RenderProcessHostImpl::OnMojoError(int render_process_id, |
| const std::string& error) { |
| LOG(ERROR) << "Terminating render process for bad Mojo message: " << error; |
| |
| InvokeBadMojoMessageCallbackForTesting(render_process_id, error); |
| |
| // The ReceivedBadMessage call below will trigger a DumpWithoutCrashing. |
| // Capture the error message in a crash key value. |
| mojo::debug::ScopedMessageErrorCrashKey error_key_value(error); |
| bad_message::ReceivedBadMessage(render_process_id, |
| bad_message::RPH_MOJO_PROCESS_ERROR); |
| } |
| |
| void RenderProcessHostImpl::GetBrowserHistogram( |
| const std::string& name, |
| BrowserHistogramCallback callback) { |
| // Security: Only allow access to browser histograms when running in the |
| // context of a test. |
| bool using_stats_collection_controller = |
| base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kStatsCollectionController); |
| if (!using_stats_collection_controller) { |
| std::move(callback).Run(std::string()); |
| return; |
| } |
| base::HistogramBase* histogram = |
| base::StatisticsRecorder::FindHistogram(name); |
| std::string histogram_json; |
| if (!histogram) { |
| histogram_json = "{}"; |
| } else { |
| histogram->WriteJSON(&histogram_json, base::JSON_VERBOSITY_LEVEL_FULL); |
| } |
| std::move(callback).Run(histogram_json); |
| } |
| |
| void RenderProcessHostImpl::BindTracedProcess( |
| mojo::PendingReceiver<tracing::mojom::TracedProcess> receiver) { |
| BindReceiver(std::move(receiver)); |
| } |
| |
| void RenderProcessHostImpl::OnBindHostReceiver( |
| mojo::GenericPendingReceiver receiver) { |
| GetContentClient()->browser()->BindHostReceiverForRenderer( |
| this, std::move(receiver)); |
| } |
| |
| // static |
| void RenderProcessHost::InterceptBindHostReceiverForTesting( |
| BindHostReceiverInterceptor callback) { |
| GetBindHostReceiverInterceptor() = std::move(callback); |
| } |
| |
| #if defined(OS_LINUX) |
| void RenderProcessHostImpl::ProvideStatusFileForRenderer() { |
| // We use ScopedAllowBlocking, because opening /proc/{pid}/status and |
| // /proc/{pid}/statm is not blocking call. |
| base::ScopedAllowBlocking allow_blocking; |
| base::FilePath proc_pid_dir = |
| base::FilePath("/proc").Append(base::NumberToString(GetProcess().Pid())); |
| |
| base::File status_file( |
| proc_pid_dir.Append("status"), |
| base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ); |
| base::File statm_file( |
| proc_pid_dir.Append("statm"), |
| base::File::Flags::FLAG_OPEN | base::File::Flags::FLAG_READ); |
| if (!status_file.IsValid() || !statm_file.IsValid()) |
| return; |
| |
| mojo::Remote<blink::mojom::MemoryUsageMonitorLinux> monitor; |
| BindReceiver(monitor.BindNewPipeAndPassReceiver()); |
| monitor->SetProcFiles(statm_file.Duplicate(), status_file.Duplicate()); |
| } |
| #endif |
| |
| void RenderProcessHostImpl::ProvideSwapFileForRenderer() { |
| if (!base::FeatureList::IsEnabled(blink::features::kParkableStringsToDisk)) |
| return; |
| |
| // In Incognito, nothing should be written to disk. Don't provide a file.. |
| if (GetBrowserContext()->IsOffTheRecord()) |
| return; |
| |
| mojo::Remote<blink::mojom::DiskAllocator> allocator; |
| BindReceiver(allocator.BindNewPipeAndPassReceiver()); |
| |
| // File creation done on a background thread. The renderer side will behave |
| // correctly even if the file is provided later or never. |
| base::ThreadPool::PostTaskAndReplyWithResult( |
| FROM_HERE, {base::MayBlock()}, base::BindOnce([]() { |
| base::FilePath path; |
| if (!base::CreateTemporaryFile(&path)) |
| return base::File(); |
| |
| int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_READ | |
| base::File::FLAG_WRITE | base::File::FLAG_DELETE_ON_CLOSE; |
| return base::File(base::FilePath(path), flags); |
| }), |
| base::BindOnce( |
| [](mojo::Remote<blink::mojom::DiskAllocator> allocator, |
| base::File file) { |
| // File creation failed in the background. In this case, don't |
| // provide a file, the renderer will not wait for one (see the |
| // incognito case above, the renderer deals with no file being |
| // provided). |
| if (file.IsValid()) |
| allocator->ProvideTemporaryFile(std::move(file)); |
| }, |
| std::move(allocator))); |
| } |
| |
| } // namespace content |