mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			168 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright 2024 The Chromium Embedded Framework Authors. All rights reserved.
 | 
						|
// Use of this source code is governed by a BSD-style license that can be found
 | 
						|
// in the LICENSE file.
 | 
						|
 | 
						|
#include "cef/libcef/browser/hang_monitor.h"
 | 
						|
 | 
						|
#include "base/memory/raw_ptr.h"
 | 
						|
#include "build/build_config.h"
 | 
						|
#include "cef/include/cef_client.h"
 | 
						|
#include "cef/libcef/browser/browser_host_base.h"
 | 
						|
#include "chrome/browser/hang_monitor/hang_crash_dump.h"
 | 
						|
#include "content/public/browser/render_process_host.h"
 | 
						|
#include "content/public/browser/render_widget_host.h"
 | 
						|
#include "content/public/common/result_codes.h"
 | 
						|
 | 
						|
namespace hang_monitor {
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
// Based on HungRendererDialogView::ForceCrashHungRenderer.
 | 
						|
void ForceCrashHungRenderer(content::RenderWidgetHost* render_widget_host) {
 | 
						|
  content::RenderProcessHost* rph = render_widget_host->GetProcess();
 | 
						|
  if (rph) {
 | 
						|
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 | 
						|
    // A generic |CrashDumpHungChildProcess()| is not implemented for Linux.
 | 
						|
    // Instead we send an explicit IPC to crash on the renderer's IO thread.
 | 
						|
    rph->ForceCrash();
 | 
						|
#else
 | 
						|
    // Try to generate a crash report for the hung process.
 | 
						|
    CrashDumpHungChildProcess(rph->GetProcess().Handle());
 | 
						|
    rph->Shutdown(content::RESULT_CODE_HUNG);
 | 
						|
#endif
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class CefUnresponsiveProcessCallbackImpl
 | 
						|
    : public CefUnresponsiveProcessCallback {
 | 
						|
 public:
 | 
						|
  CefUnresponsiveProcessCallbackImpl(
 | 
						|
      content::RenderWidgetHost* render_widget_host,
 | 
						|
      base::RepeatingClosure hang_monitor_restarter)
 | 
						|
      : render_widget_host_(render_widget_host),
 | 
						|
        hang_monitor_restarter_(hang_monitor_restarter) {}
 | 
						|
 | 
						|
  CefUnresponsiveProcessCallbackImpl(
 | 
						|
      const CefUnresponsiveProcessCallbackImpl&) = delete;
 | 
						|
  CefUnresponsiveProcessCallbackImpl& operator=(
 | 
						|
      const CefUnresponsiveProcessCallbackImpl&) = delete;
 | 
						|
 | 
						|
  ~CefUnresponsiveProcessCallbackImpl() override {
 | 
						|
    // Do nothing on destruction.
 | 
						|
  }
 | 
						|
 | 
						|
  void Wait() override { ContinueNow(true); }
 | 
						|
 | 
						|
  void Terminate() override { ContinueNow(false); }
 | 
						|
 | 
						|
  void Detach() {
 | 
						|
    render_widget_host_ = nullptr;
 | 
						|
    hang_monitor_restarter_.Reset();
 | 
						|
  }
 | 
						|
 | 
						|
  bool IsDetached() const { return !render_widget_host_; }
 | 
						|
 | 
						|
 private:
 | 
						|
  void ContinueNow(bool wait) {
 | 
						|
    if (CEF_CURRENTLY_ON_UIT()) {
 | 
						|
      if (!IsDetached()) {
 | 
						|
        RunNow(render_widget_host_, hang_monitor_restarter_, wait);
 | 
						|
        Detach();
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      CEF_POST_TASK(
 | 
						|
          CEF_UIT,
 | 
						|
          base::BindOnce(&CefUnresponsiveProcessCallbackImpl::ContinueNow, this,
 | 
						|
                         wait));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  static void RunNow(content::RenderWidgetHost* render_widget_host,
 | 
						|
                     base::RepeatingClosure hang_monitor_restarter,
 | 
						|
                     bool wait) {
 | 
						|
    CEF_REQUIRE_UIT();
 | 
						|
    if (wait) {
 | 
						|
      hang_monitor_restarter.Run();
 | 
						|
    } else {
 | 
						|
      ForceCrashHungRenderer(render_widget_host);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  raw_ptr<content::RenderWidgetHost> render_widget_host_;
 | 
						|
  base::RepeatingClosure hang_monitor_restarter_;
 | 
						|
 | 
						|
  IMPLEMENT_REFCOUNTING(CefUnresponsiveProcessCallbackImpl);
 | 
						|
};
 | 
						|
 | 
						|
bool ResetRendererCallback(CefBrowserHostBase* browser) {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
  if (auto callback = browser->unresponsive_process_callback()) {
 | 
						|
    Detach(callback);
 | 
						|
    browser->set_unresponsive_process_callback(nullptr);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
CefRefPtr<CefRequestHandler> GetRequestHandler(CefBrowserHostBase* browser) {
 | 
						|
  if (auto client = browser->GetClient()) {
 | 
						|
    return client->GetRequestHandler();
 | 
						|
  }
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace
 | 
						|
 | 
						|
bool RendererUnresponsive(CefBrowserHostBase* browser,
 | 
						|
                          content::RenderWidgetHost* render_widget_host,
 | 
						|
                          base::RepeatingClosure hang_monitor_restarter) {
 | 
						|
  // There should be no callback currently.
 | 
						|
  DCHECK(!browser->unresponsive_process_callback());
 | 
						|
 | 
						|
  if (auto handler = GetRequestHandler(browser)) {
 | 
						|
    CefRefPtr<CefUnresponsiveProcessCallbackImpl> callbackImpl(
 | 
						|
        new CefUnresponsiveProcessCallbackImpl(render_widget_host,
 | 
						|
                                               hang_monitor_restarter));
 | 
						|
    if (!handler->OnRenderProcessUnresponsive(browser, callbackImpl.get())) {
 | 
						|
      if (!callbackImpl->IsDetached()) {
 | 
						|
        // Proceed with default handling.
 | 
						|
        callbackImpl->Detach();
 | 
						|
        return false;
 | 
						|
      } else {
 | 
						|
        LOG(ERROR) << "Should return true from OnRenderProcessUnresponsive "
 | 
						|
                      "when executing the callback";
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // Proceed with client handling. The callback may already be executed, but
 | 
						|
    // we still want to wait for RendererResponsive.
 | 
						|
    browser->set_unresponsive_process_callback(callbackImpl.get());
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  // Proceed with default handling.
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
bool RendererResponsive(CefBrowserHostBase* browser,
 | 
						|
                        content::RenderWidgetHost* render_widget_host) {
 | 
						|
  // |handled| will be true if the client handled OnRenderProcessUnresponsive.
 | 
						|
  bool handled = ResetRendererCallback(browser);
 | 
						|
 | 
						|
  // Always execute the client callback.
 | 
						|
  if (auto handler = GetRequestHandler(browser)) {
 | 
						|
    handler->OnRenderProcessResponsive(browser);
 | 
						|
  }
 | 
						|
 | 
						|
  return handled;
 | 
						|
}
 | 
						|
 | 
						|
void Detach(CefRefPtr<CefUnresponsiveProcessCallback> callback) {
 | 
						|
  CEF_REQUIRE_UIT();
 | 
						|
  auto* callback_impl =
 | 
						|
      static_cast<CefUnresponsiveProcessCallbackImpl*>(callback.get());
 | 
						|
  callback_impl->Detach();
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace hang_monitor
 |