|  | // Copyright 2012 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <stddef.h> | 
|  | #include <stdlib.h> | 
|  |  | 
|  | #include <memory> | 
|  | #include <tuple> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/check.h" | 
|  | #include "base/command_line.h" | 
|  | #include "base/feature_list.h" | 
|  | #include "base/memory/raw_ptr.h" | 
|  | #include "base/message_loop/message_pump_type.h" | 
|  | #include "base/metrics/histogram_macros.h" | 
|  | #include "base/numerics/clamped_math.h" | 
|  | #include "base/process/process_metrics.h" | 
|  | #include "base/rand_util.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/system/sys_info.h" | 
|  | #include "base/task/single_thread_task_executor.h" | 
|  | #include "base/task/thread_pool/thread_pool_instance.h" | 
|  | #include "base/threading/platform_thread.h" | 
|  | #include "base/time/time.h" | 
|  | #include "base/timer/hi_res_timer_manager.h" | 
|  | #include "base/trace_event/trace_event.h" | 
|  | #include "build/build_config.h" | 
|  | #include "build/chromeos_buildflags.h" | 
|  | #include "components/viz/service/main/viz_main_impl.h" | 
|  | #include "content/child/child_process.h" | 
|  | #include "content/common/content_constants_internal.h" | 
|  | #include "content/common/content_switches_internal.h" | 
|  | #include "content/common/partition_alloc_support.h" | 
|  | #include "content/common/skia_utils.h" | 
|  | #include "content/gpu/gpu_child_thread.h" | 
|  | #include "content/public/common/content_client.h" | 
|  | #include "content/public/common/content_switches.h" | 
|  | #include "content/public/common/main_function_params.h" | 
|  | #include "content/public/common/result_codes.h" | 
|  | #include "content/public/gpu/content_gpu_client.h" | 
|  | #include "gpu/command_buffer/service/gpu_switches.h" | 
|  | #include "gpu/config/gpu_driver_bug_list.h" | 
|  | #include "gpu/config/gpu_finch_features.h" | 
|  | #include "gpu/config/gpu_info_collector.h" | 
|  | #include "gpu/config/gpu_preferences.h" | 
|  | #include "gpu/config/gpu_switches.h" | 
|  | #include "gpu/config/gpu_util.h" | 
|  | #include "gpu/ipc/common/gpu_memory_buffer_support.h" | 
|  | #include "gpu/ipc/service/gpu_config.h" | 
|  | #include "gpu/ipc/service/gpu_init.h" | 
|  | #include "gpu/ipc/service/gpu_watchdog_thread.h" | 
|  | #include "media/gpu/buildflags.h" | 
|  | #include "mojo/public/cpp/bindings/sync_call_restrictions.h" | 
|  | #include "services/tracing/public/cpp/trace_startup.h" | 
|  | #include "third_party/angle/src/gpu_info_util/SystemInfo.h" | 
|  | #include "ui/base/ui_base_features.h" | 
|  | #include "ui/events/platform/platform_event_source.h" | 
|  | #include "ui/gfx/switches.h" | 
|  | #include "ui/gl/gl_context.h" | 
|  | #include "ui/gl/gl_implementation.h" | 
|  | #include "ui/gl/gl_surface.h" | 
|  | #include "ui/gl/gl_switches.h" | 
|  | #include "ui/gl/gpu_switching_manager.h" | 
|  | #include "ui/gl/init/gl_factory.h" | 
|  |  | 
|  | #if BUILDFLAG(IS_WIN) | 
|  | #include <dwmapi.h> | 
|  | #include <windows.h> | 
|  | #endif | 
|  |  | 
|  | #if BUILDFLAG(IS_ANDROID) | 
|  | #include "base/trace_event/memory_dump_manager.h" | 
|  | #include "components/tracing/common/graphics_memory_dump_provider_android.h" | 
|  | #endif | 
|  |  | 
|  | #if BUILDFLAG(IS_WIN) | 
|  | #include "base/trace_event/trace_event_etw_export_win.h" | 
|  | #include "base/win/scoped_com_initializer.h" | 
|  | #include "base/win/windows_version.h" | 
|  | #include "media/gpu/windows/dxva_video_decode_accelerator_win.h" | 
|  | #include "media/gpu/windows/media_foundation_video_encode_accelerator_win.h" | 
|  | #include "sandbox/win/src/sandbox.h" | 
|  | #endif | 
|  |  | 
|  | #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) | 
|  | #include "content/gpu/gpu_sandbox_hook_linux.h" | 
|  | #include "sandbox/policy/linux/sandbox_linux.h" | 
|  | #include "sandbox/policy/sandbox_type.h" | 
|  | #endif | 
|  |  | 
|  | #if BUILDFLAG(IS_MAC) | 
|  | #include "base/message_loop/message_pump_mac.h" | 
|  | #include "components/metal_util/device_removal.h" | 
|  | #include "components/metal_util/test_shader.h" | 
|  | #include "media/gpu/mac/vt_video_decode_accelerator_mac.h" | 
|  | #include "sandbox/mac/seatbelt.h" | 
|  | #endif | 
|  |  | 
|  | #if BUILDFLAG(USE_VAAPI) | 
|  | #include "media/gpu/vaapi/vaapi_wrapper.h" | 
|  | #endif | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) | 
|  | bool StartSandboxLinux(gpu::GpuWatchdogThread*, | 
|  | const gpu::GPUInfo*, | 
|  | const gpu::GpuPreferences&); | 
|  | #elif BUILDFLAG(IS_WIN) | 
|  | bool StartSandboxWindows(const sandbox::SandboxInterfaceInfo*); | 
|  | #endif | 
|  |  | 
|  | class ContentSandboxHelper : public gpu::GpuSandboxHelper { | 
|  | public: | 
|  | ContentSandboxHelper() {} | 
|  |  | 
|  | ContentSandboxHelper(const ContentSandboxHelper&) = delete; | 
|  | ContentSandboxHelper& operator=(const ContentSandboxHelper&) = delete; | 
|  |  | 
|  | ~ContentSandboxHelper() override {} | 
|  |  | 
|  | #if BUILDFLAG(IS_WIN) | 
|  | void set_sandbox_info(const sandbox::SandboxInterfaceInfo* info) { | 
|  | sandbox_info_ = info; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | private: | 
|  | // SandboxHelper: | 
|  | void PreSandboxStartup(const gpu::GpuPreferences& gpu_prefs) override { | 
|  | // Warm up resources that don't need access to GPUInfo. | 
|  | { | 
|  | TRACE_EVENT0("gpu", "Warm up rand"); | 
|  | // Warm up the random subsystem, which needs to be done pre-sandbox on all | 
|  | // platforms. | 
|  | std::ignore = base::RandUint64(); | 
|  | } | 
|  |  | 
|  | #if BUILDFLAG(USE_VAAPI) | 
|  | #if BUILDFLAG(IS_CHROMEOS) | 
|  | media::VaapiWrapper::PreSandboxInitialization(); | 
|  | #else  // For Linux with VA-API support. | 
|  | if (!gpu_prefs.disable_accelerated_video_decode) | 
|  | media::VaapiWrapper::PreSandboxInitialization(); | 
|  | #endif | 
|  | #endif  // BUILDFLAG(USE_VAAPI) | 
|  | #if BUILDFLAG(IS_WIN) | 
|  | media::DXVAVideoDecodeAccelerator::PreSandboxInitialization(); | 
|  | media::MediaFoundationVideoEncodeAccelerator::PreSandboxInitialization(); | 
|  | #endif | 
|  |  | 
|  | #if BUILDFLAG(IS_MAC) | 
|  | { | 
|  | TRACE_EVENT0("gpu", "Initialize VideoToolbox"); | 
|  | media::InitializeVideoToolbox(); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | // On Linux, reading system memory doesn't work through the GPU sandbox. | 
|  | // This value is cached, so access it here to populate the cache. | 
|  | base::SysInfo::AmountOfPhysicalMemory(); | 
|  | } | 
|  |  | 
|  | bool EnsureSandboxInitialized(gpu::GpuWatchdogThread* watchdog_thread, | 
|  | const gpu::GPUInfo* gpu_info, | 
|  | const gpu::GpuPreferences& gpu_prefs) override { | 
|  | #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) | 
|  | return StartSandboxLinux(watchdog_thread, gpu_info, gpu_prefs); | 
|  | #elif BUILDFLAG(IS_WIN) | 
|  | return StartSandboxWindows(sandbox_info_); | 
|  | #elif BUILDFLAG(IS_MAC) | 
|  | return sandbox::Seatbelt::IsSandboxed(); | 
|  | #else | 
|  | return false; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #if BUILDFLAG(IS_WIN) | 
|  | raw_ptr<const sandbox::SandboxInterfaceInfo> sandbox_info_ = nullptr; | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Main function for starting the Gpu process. | 
|  | int GpuMain(MainFunctionParams parameters) { | 
|  | TRACE_EVENT0("gpu", "GpuMain"); | 
|  | base::trace_event::TraceLog::GetInstance()->set_process_name("GPU Process"); | 
|  | base::trace_event::TraceLog::GetInstance()->SetProcessSortIndex( | 
|  | kTraceEventGpuProcessSortIndex); | 
|  |  | 
|  | const base::CommandLine& command_line = *parameters.command_line; | 
|  |  | 
|  | gpu::GpuPreferences gpu_preferences; | 
|  | if (command_line.HasSwitch(switches::kGpuPreferences)) { | 
|  | std::string value = | 
|  | command_line.GetSwitchValueASCII(switches::kGpuPreferences); | 
|  | bool success = gpu_preferences.FromSwitchValue(value); | 
|  | CHECK(success); | 
|  | } | 
|  |  | 
|  | // Disallow sending sync IPCs from the GPU process, in particular CrGpuMain | 
|  | // and VizCompositorThreads. Incoming sync IPCs can be received out of order | 
|  | // when waiting on response to an outgoing sync IPC. Both viz and gpu | 
|  | // interfaces rely on receiving messages in order so this message reordering | 
|  | // would break things. | 
|  | mojo::SyncCallRestrictions::DisallowSyncCall(); | 
|  |  | 
|  | if (gpu_preferences.gpu_startup_dialog) | 
|  | WaitForDebugger("Gpu"); | 
|  |  | 
|  | base::TimeTicks start_time = base::TimeTicks::Now(); | 
|  |  | 
|  | #if BUILDFLAG(IS_WIN) | 
|  | base::trace_event::TraceEventETWExport::EnableETWExport(); | 
|  |  | 
|  | // Prevent Windows from displaying a modal dialog on failures like not being | 
|  | // able to load a DLL. | 
|  | SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | | 
|  | SEM_NOOPENFILEERRORBOX); | 
|  |  | 
|  | // COM is used by some Windows Media Foundation calls made on this thread and | 
|  | // must be MTA so we don't have to worry about pumping messages to handle | 
|  | // COM callbacks. | 
|  | base::win::ScopedCOMInitializer com_initializer( | 
|  | base::win::ScopedCOMInitializer::kMTA); | 
|  |  | 
|  | if (base::FeatureList::IsEnabled(features::kGpuProcessHighPriorityWin)) | 
|  | ::SetPriorityClass(::GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); | 
|  | #endif | 
|  |  | 
|  | // Installs a base::LogMessageHandlerFunction which ensures messages are sent | 
|  | // to the GpuProcessHost once the GpuServiceImpl has started. | 
|  | viz::GpuServiceImpl::InstallPreInitializeLogHandler(); | 
|  |  | 
|  | // We are experiencing what appear to be memory-stomp issues in the GPU | 
|  | // process. These issues seem to be impacting the task executor and listeners | 
|  | // registered to it. Create the task executor on the heap to guard against | 
|  | // this. | 
|  | // TODO(ericrk): Revisit this once we assess its impact on crbug.com/662802 | 
|  | // and crbug.com/609252. | 
|  | std::unique_ptr<base::SingleThreadTaskExecutor> main_thread_task_executor; | 
|  | std::unique_ptr<ui::PlatformEventSource> event_source; | 
|  | if (command_line.HasSwitch(switches::kHeadless)) { | 
|  | main_thread_task_executor = | 
|  | std::make_unique<base::SingleThreadTaskExecutor>( | 
|  | base::MessagePumpType::DEFAULT); | 
|  | } else { | 
|  | #if BUILDFLAG(IS_WIN) | 
|  | // The GpuMain thread should not be pumping Windows messages because no UI | 
|  | // is expected to run on this thread. | 
|  | main_thread_task_executor = | 
|  | std::make_unique<base::SingleThreadTaskExecutor>( | 
|  | base::MessagePumpType::DEFAULT); | 
|  | #elif defined(USE_OZONE) | 
|  | // The MessagePump type required depends on the Ozone platform selected at | 
|  | // runtime. | 
|  | if (!main_thread_task_executor) { | 
|  | main_thread_task_executor = | 
|  | std::make_unique<base::SingleThreadTaskExecutor>( | 
|  | gpu_preferences.message_pump_type); | 
|  | } | 
|  | #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) | 
|  | #error "Unsupported Linux platform." | 
|  | #elif BUILDFLAG(IS_MAC) | 
|  | // Cross-process CoreAnimation requires a CFRunLoop to function at all, and | 
|  | // requires a NSRunLoop to not starve under heavy load. See: | 
|  | // https://crbug.com/312462#c51 and https://crbug.com/783298 | 
|  | main_thread_task_executor = | 
|  | std::make_unique<base::SingleThreadTaskExecutor>( | 
|  | base::MessagePumpType::NS_RUNLOOP); | 
|  | // As part of the migration to DoWork(), this policy is required to keep | 
|  | // previous behavior and avoid regressions. | 
|  | // TODO(crbug.com/1041853): Consider updating the policy. | 
|  | main_thread_task_executor->SetWorkBatchSize(2); | 
|  | #else | 
|  | main_thread_task_executor = | 
|  | std::make_unique<base::SingleThreadTaskExecutor>( | 
|  | base::MessagePumpType::DEFAULT); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | base::PlatformThread::SetName("CrGpuMain"); | 
|  |  | 
|  | // Set thread priority before sandbox initialization. | 
|  | if (!features::IsGpuMainThreadForcedToNormalPriorityDrDc()) { | 
|  | base::PlatformThread::SetCurrentThreadType(base::ThreadType::kCompositing); | 
|  | } | 
|  |  | 
|  | auto gpu_init = std::make_unique<gpu::GpuInit>(); | 
|  | ContentSandboxHelper sandbox_helper; | 
|  | #if BUILDFLAG(IS_WIN) | 
|  | sandbox_helper.set_sandbox_info(parameters.sandbox_info); | 
|  | #endif | 
|  |  | 
|  | gpu_init->set_sandbox_helper(&sandbox_helper); | 
|  |  | 
|  | // Since GPU initialization calls into skia, it's important to initialize skia | 
|  | // before it. | 
|  | InitializeSkia(); | 
|  |  | 
|  | // The ThreadPool must have been created before invoking |gpu_init| as it | 
|  | // needs the ThreadPool (in angle::InitializePlatform()). Do not start it | 
|  | // until after the sandbox is initialized however to avoid creating threads | 
|  | // outside the sandbox. | 
|  | DCHECK(base::ThreadPoolInstance::Get()); | 
|  |  | 
|  | // Gpu initialization may fail for various reasons, in which case we will need | 
|  | // to tear down this process. However, we can not do so safely until the IPC | 
|  | // channel is set up, because the detection of early return of a child process | 
|  | // is implemented using an IPC channel error. If the IPC channel is not fully | 
|  | // set up between the browser and GPU process, and the GPU process crashes or | 
|  | // exits early, the browser process will never detect it.  For this reason we | 
|  | // defer tearing down the GPU process until receiving the initialization | 
|  | // message from the browser (through mojom::VizMain::CreateGpuService()). | 
|  | const bool init_success = gpu_init->InitializeAndStartSandbox( | 
|  | const_cast<base::CommandLine*>(&command_line), gpu_preferences); | 
|  | const bool dead_on_arrival = !init_success; | 
|  |  | 
|  | auto* client = GetContentClient()->gpu(); | 
|  | if (client) { | 
|  | client->PostSandboxInitialized(); | 
|  | } | 
|  |  | 
|  | GetContentClient()->SetGpuInfo(gpu_init->gpu_info()); | 
|  |  | 
|  | base::ThreadType io_thread_type = base::ThreadType::kCompositing; | 
|  | #if BUILDFLAG(IS_MAC) | 
|  | // Increase the thread priority to get more reliable values in performance | 
|  | // test of mac_os. | 
|  | if (command_line.HasSwitch(switches::kUseHighGPUThreadPriorityForPerfTests)) | 
|  | io_thread_type = base::ThreadType::kRealtimeAudio; | 
|  | #endif | 
|  | // ChildProcess will start the ThreadPoolInstance now that the sandbox is | 
|  | // initialized. | 
|  | ChildProcess gpu_process(io_thread_type); | 
|  | DCHECK(base::ThreadPoolInstance::Get()->WasStarted()); | 
|  |  | 
|  | if (client) { | 
|  | client->PostIOThreadCreated(gpu_process.io_task_runner()); | 
|  | } | 
|  |  | 
|  | base::RunLoop run_loop; | 
|  | GpuChildThread* child_thread = | 
|  | new GpuChildThread(run_loop.QuitClosure(), std::move(gpu_init)); | 
|  | child_thread->Init(start_time); | 
|  |  | 
|  | gpu_process.set_main_thread(child_thread); | 
|  |  | 
|  | #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_MAC) | 
|  | // Startup tracing is usually enabled earlier, but if we forked from a zygote, | 
|  | // we can only enable it after mojo IPC support is brought up initialized by | 
|  | // GpuChildThread, because the mojo broker has to create the tracing SMB on | 
|  | // our behalf due to the zygote sandbox. | 
|  | if (parameters.zygote_child) | 
|  | tracing::EnableStartupTracingIfNeeded(); | 
|  | #endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_MAC) | 
|  |  | 
|  | #if BUILDFLAG(IS_MAC) | 
|  | // A GPUEjectPolicy of 'wait' is set in the Info.plist of the browser | 
|  | // process, meaning it is "responsible" for making sure it and its | 
|  | // subordinate processes (i.e. the GPU process) drop references to the | 
|  | // external GPU. Despite this, the system still sends the device removal | 
|  | // notifications to the GPU process, so the GPU process handles its own | 
|  | // graceful shutdown without help from the browser process. | 
|  | // | 
|  | // Using the "SafeEjectGPU" tool, we can see that when the browser process | 
|  | // has a policy of 'wait', the GPU process gets the 'rwait' policy: "Eject | 
|  | // actions apply to the responsible process, who in turn deals with | 
|  | // subordinates to eliminate their ejecting eGPU references" [man 8 | 
|  | // SafeEjectGPU]. Empirically, the browser does not relaunch. Once the GPU | 
|  | // process exits, it appears that the browser process is no longer considered | 
|  | // to be using the GPU, so it "succeeds" the 'wait'. | 
|  | metal::RegisterGracefulExitOnDeviceRemoval(); | 
|  | #endif | 
|  |  | 
|  | #if BUILDFLAG(IS_ANDROID) | 
|  | base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( | 
|  | tracing::GraphicsMemoryDumpProvider::GetInstance(), "AndroidGraphics", | 
|  | nullptr); | 
|  | #endif | 
|  |  | 
|  | internal::PartitionAllocSupport::Get()->ReconfigureAfterTaskRunnerInit( | 
|  | switches::kGpuProcess); | 
|  |  | 
|  | base::HighResolutionTimerManager hi_res_timer_manager; | 
|  |  | 
|  | { | 
|  | TRACE_EVENT0("gpu", "Run Message Loop"); | 
|  | run_loop.Run(); | 
|  | } | 
|  |  | 
|  | return dead_on_arrival ? RESULT_CODE_GPU_DEAD_ON_ARRIVAL : 0; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) | 
|  | bool StartSandboxLinux(gpu::GpuWatchdogThread* watchdog_thread, | 
|  | const gpu::GPUInfo* gpu_info, | 
|  | const gpu::GpuPreferences& gpu_prefs) { | 
|  | TRACE_EVENT0("gpu,startup", "Initialize sandbox"); | 
|  |  | 
|  | if (watchdog_thread) { | 
|  | // SandboxLinux needs to be able to ensure that the thread | 
|  | // has really been stopped. | 
|  | sandbox::policy::SandboxLinux::GetInstance()->StopThread(watchdog_thread); | 
|  | } | 
|  |  | 
|  | // SandboxLinux::InitializeSandbox() must always be called | 
|  | // with only one thread. | 
|  | sandbox::policy::SandboxLinux::Options sandbox_options; | 
|  | if (gpu_info) { | 
|  | // We have to enable sandbox settings for all GPUs in the system | 
|  | // for Chrome to be able to access/use them. | 
|  | sandbox_options.use_amd_specific_policies = | 
|  | angle::IsAMD(gpu_info->active_gpu().vendor_id); | 
|  | sandbox_options.use_intel_specific_policies = | 
|  | angle::IsIntel(gpu_info->active_gpu().vendor_id); | 
|  | sandbox_options.use_nvidia_specific_policies = | 
|  | angle::IsNVIDIA(gpu_info->active_gpu().vendor_id); | 
|  | for (const auto& gpu : gpu_info->secondary_gpus) { | 
|  | if (angle::IsAMD(gpu.vendor_id)) | 
|  | sandbox_options.use_amd_specific_policies = true; | 
|  | else if (angle::IsIntel(gpu.vendor_id)) | 
|  | sandbox_options.use_intel_specific_policies = true; | 
|  | else if (angle::IsNVIDIA(gpu.vendor_id)) | 
|  | sandbox_options.use_nvidia_specific_policies = true; | 
|  | } | 
|  | } | 
|  | sandbox_options.accelerated_video_decode_enabled = | 
|  | !gpu_prefs.disable_accelerated_video_decode; | 
|  | sandbox_options.accelerated_video_encode_enabled = | 
|  | !gpu_prefs.disable_accelerated_video_encode; | 
|  |  | 
|  | #if BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(USE_VAAPI) | 
|  | // Increase the FD limit by 512 on VA-API Chrome OS devices in order to | 
|  | // avoid running out of FDs in cases where many decoders are running | 
|  | // concurrently. See b/215553848. | 
|  | // TODO(b/195769334): revisit the need for this once out-of-process video | 
|  | // decoding has been fully implemented. | 
|  | const auto current_max_fds = | 
|  | base::saturated_cast<unsigned int>(base::GetMaxFds()); | 
|  | constexpr unsigned int kMaxFDsDelta = 1u << 9; | 
|  | const auto new_max_fds = | 
|  | static_cast<int>(base::ClampAdd(current_max_fds, kMaxFDsDelta)); | 
|  | base::IncreaseFdLimitTo(base::checked_cast<unsigned int>(new_max_fds)); | 
|  | #endif | 
|  |  | 
|  | bool res = sandbox::policy::SandboxLinux::GetInstance()->InitializeSandbox( | 
|  | sandbox::policy::SandboxTypeFromCommandLine( | 
|  | *base::CommandLine::ForCurrentProcess()), | 
|  | base::BindOnce(GpuProcessPreSandboxHook), sandbox_options); | 
|  |  | 
|  | if (watchdog_thread) { | 
|  | base::Thread::Options thread_options; | 
|  | thread_options.timer_slack = base::TIMER_SLACK_MAXIMUM; | 
|  | watchdog_thread->StartWithOptions(std::move(thread_options)); | 
|  | } | 
|  |  | 
|  | return res; | 
|  | } | 
|  | #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) | 
|  |  | 
|  | #if BUILDFLAG(IS_WIN) | 
|  | bool StartSandboxWindows(const sandbox::SandboxInterfaceInfo* sandbox_info) { | 
|  | TRACE_EVENT0("gpu,startup", "Lower token"); | 
|  |  | 
|  | // For Windows, if the target_services interface is not zero, the process | 
|  | // is sandboxed and we must call LowerToken() before rendering untrusted | 
|  | // content. | 
|  | sandbox::TargetServices* target_services = sandbox_info->target_services; | 
|  | if (target_services) { | 
|  | target_services->LowerToken(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  | #endif  // BUILDFLAG(IS_WIN) | 
|  |  | 
|  | }  // namespace. | 
|  |  | 
|  | }  // namespace content |