diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index b6bcac3e84d22..d5bfa220ecbe7 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc @@ -2461,9 +2461,17 @@ void Browser::RegisterProtocolHandler( permissions::PermissionRequestManager* permission_request_manager = permissions::PermissionRequestManager::FromWebContents(web_contents); if (permission_request_manager) { + // ForSecurityDropFullscreen() reaches Widget::SetFullscreen which on + // Windows/macOS can pump a nested message loop and synchronously destroy + // |web_contents| (and thus |permission_request_manager| and + // |requesting_frame|). The |!blocker| check is insufficient: the + // last-iteration gap in ForSecurityDropFullscreen can still return a + // non-nullopt closure after |this| is freed. + base::WeakPtr weak_contents = + web_contents->GetWeakPtr(); auto blocker = web_contents->ForSecurityDropFullscreen( /*display_id=*/display::kInvalidDisplayId); - if (!blocker) { + if (!blocker || !weak_contents) { return; } diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index ae5eb43ebf700..d6b70f4fead7a 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -7562,6 +7562,12 @@ WebContentsImpl::ForSecurityDropFullscreen(int64_t display_id) { blocked_contents_list.push_back(opener); } + // The final iteration's ExitFullscreen() may have spun a nested message loop + // and destroyed |this|; honour the documented contract and return nullopt. + if (!weak_this) { + return std::nullopt; + } + return base::ScopedClosureRunner(base::BindOnce( [](std::vector> blocked_contents_list) { for (base::WeakPtr& web_contents :