mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
Add notification for aborted popups (fixes #3776)
Pass a new |popup_id| parameter to OnBeforePopup and call a new OnBeforePopupAborted callback if the popup is aborted before OnAfterCreated is called for the popup browser. Add new CefBrowserHost::GetBrowserByIdentifier and GetOpenerIdentifier methods to assist with retrieval of associated browsers. In cefclient, clean up state when a popup is aborted and close any associated popup browsers when the opener browser is closed. This also works when running with `--use-default-popup`.
This commit is contained in:
@@ -330,7 +330,7 @@ CefWindowHandle AlloyBrowserHostImpl::GetWindowHandle() {
|
||||
}
|
||||
|
||||
CefWindowHandle AlloyBrowserHostImpl::GetOpenerWindowHandle() {
|
||||
return opener_;
|
||||
return opener_window_handle_;
|
||||
}
|
||||
|
||||
void AlloyBrowserHostImpl::Find(const CefString& searchText,
|
||||
@@ -1414,14 +1414,17 @@ AlloyBrowserHostImpl::AlloyBrowserHostImpl(
|
||||
browser_info,
|
||||
request_context),
|
||||
content::WebContentsObserver(web_contents),
|
||||
opener_(kNullWindowHandle),
|
||||
is_windowless_(platform_delegate_->IsWindowless()) {
|
||||
contents_delegate_.ObserveWebContents(web_contents);
|
||||
|
||||
if (opener.get() && !is_views_hosted_) {
|
||||
// GetOpenerWindowHandle() only returns a value for non-views-hosted
|
||||
// popup browsers.
|
||||
opener_ = opener->GetWindowHandle();
|
||||
if (opener.get()) {
|
||||
opener_id_ = opener->GetIdentifier();
|
||||
|
||||
if (!is_views_hosted_) {
|
||||
// GetOpenerWindowHandle() only returns a value for non-views-hosted
|
||||
// popup browsers.
|
||||
opener_window_handle_ = opener->GetWindowHandle();
|
||||
}
|
||||
}
|
||||
|
||||
// Associate the platform delegate with this browser.
|
||||
|
@@ -174,7 +174,8 @@ class AlloyBrowserHostImpl : public CefBrowserHostBase,
|
||||
DestructionState destruction_state() const { return destruction_state_; }
|
||||
|
||||
// content::WebContentsDelegate methods.
|
||||
void PrintCrossProcessSubframe(content::WebContents* web_contents,
|
||||
void PrintCrossProcessSubframe(
|
||||
content::WebContents* web_contents,
|
||||
const gfx::Rect& rect,
|
||||
int document_cookie,
|
||||
content::RenderFrameHost* subframe_host) const override;
|
||||
@@ -317,7 +318,7 @@ class AlloyBrowserHostImpl : public CefBrowserHostBase,
|
||||
void StartAudioCapturer();
|
||||
void OnRecentlyAudibleTimerFired();
|
||||
|
||||
CefWindowHandle opener_;
|
||||
CefWindowHandle opener_window_handle_ = kNullWindowHandle;
|
||||
const bool is_windowless_;
|
||||
CefWindowHandle host_window_handle_ = kNullWindowHandle;
|
||||
|
||||
|
@@ -480,6 +480,10 @@ void CefBrowserHostBase::SetFocus(bool focus) {
|
||||
}
|
||||
}
|
||||
|
||||
int CefBrowserHostBase::GetOpenerIdentifier() {
|
||||
return opener_id_;
|
||||
}
|
||||
|
||||
void CefBrowserHostBase::RunFileDialog(
|
||||
FileDialogMode mode,
|
||||
const CefString& title,
|
||||
@@ -1445,6 +1449,11 @@ bool CefBrowserHostBase::IsVisible() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
int CefBrowserHostBase::GetNextPopupId() {
|
||||
CEF_REQUIRE_UIT();
|
||||
return next_popup_id_++;
|
||||
}
|
||||
|
||||
bool CefBrowserHostBase::EnsureDevToolsProtocolManager() {
|
||||
CEF_REQUIRE_UIT();
|
||||
if (!contents_delegate_.web_contents()) {
|
||||
|
@@ -220,6 +220,7 @@ class CefBrowserHostBase : public CefBrowserHost,
|
||||
bool HasView() override;
|
||||
bool IsReadyToBeClosed() override;
|
||||
void SetFocus(bool focus) override;
|
||||
int GetOpenerIdentifier() override;
|
||||
void RunFileDialog(FileDialogMode mode,
|
||||
const CefString& title,
|
||||
const CefString& default_file_path,
|
||||
@@ -418,6 +419,10 @@ class CefBrowserHostBase : public CefBrowserHost,
|
||||
// Returns true if this browser is currently visible.
|
||||
virtual bool IsVisible() const;
|
||||
|
||||
// Returns the next popup ID for use with OnBeforePopup. Must be called on
|
||||
// the UI thread.
|
||||
int GetNextPopupId();
|
||||
|
||||
protected:
|
||||
bool EnsureDevToolsProtocolManager();
|
||||
void InitializeDevToolsRegistrationOnUIThread(
|
||||
@@ -439,6 +444,7 @@ class CefBrowserHostBase : public CefBrowserHost,
|
||||
scoped_refptr<CefBrowserInfo> browser_info_;
|
||||
CefRefPtr<CefRequestContextImpl> request_context_;
|
||||
const bool is_views_hosted_;
|
||||
int opener_id_ = 0;
|
||||
|
||||
// Only accessed on the UI thread.
|
||||
CefBrowserContentsDelegate contents_delegate_;
|
||||
@@ -473,6 +479,8 @@ class CefBrowserHostBase : public CefBrowserHost,
|
||||
|
||||
std::unique_ptr<CefMediaStreamRegistrar> media_stream_registrar_;
|
||||
|
||||
int next_popup_id_ = 1;
|
||||
|
||||
private:
|
||||
IMPLEMENT_REFCOUNTING(CefBrowserHostBase);
|
||||
};
|
||||
|
@@ -151,6 +151,21 @@ CefRefPtr<CefBrowser> CefBrowserHost::CreateBrowserSync(
|
||||
return CefBrowserHostBase::Create(create_params);
|
||||
}
|
||||
|
||||
// static
|
||||
CefRefPtr<CefBrowser> CefBrowserHost::GetBrowserByIdentifier(int browser_id) {
|
||||
// Verify that the context is in a valid state.
|
||||
if (!CONTEXT_STATE_VALID()) {
|
||||
DCHECK(false) << "context not valid";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (browser_id <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return CefBrowserHostBase::GetBrowserForBrowserId(browser_id).get();
|
||||
}
|
||||
|
||||
// static
|
||||
bool CefBrowserCreateParams::IsChromeStyle(const CefWindowInfo* window_info) {
|
||||
if (!window_info) {
|
||||
|
@@ -45,6 +45,14 @@ CefBrowserInfoManager* g_info_manager = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
CefBrowserInfoManager::PendingPopup::~PendingPopup() {
|
||||
CEF_REQUIRE_UIT();
|
||||
if (step != CREATION_COMPLETE && !aborted_callback.is_null()) {
|
||||
// Notify of pending popup abort.
|
||||
std::move(aborted_callback).Run();
|
||||
}
|
||||
}
|
||||
|
||||
CefBrowserInfoManager::CefBrowserInfoManager() {
|
||||
DCHECK(!g_info_manager);
|
||||
g_info_manager = this;
|
||||
@@ -167,14 +175,35 @@ bool CefBrowserInfoManager::CanCreateWindow(
|
||||
window_info.bounds.height = cef_features.height;
|
||||
}
|
||||
|
||||
const int popup_id = browser->GetNextPopupId();
|
||||
|
||||
allow = !handler->OnBeforePopup(
|
||||
browser.get(), opener_frame, pending_popup->target_url.spec(),
|
||||
pending_popup->target_frame_name,
|
||||
browser.get(), opener_frame, popup_id,
|
||||
pending_popup->target_url.spec(), pending_popup->target_frame_name,
|
||||
static_cast<cef_window_open_disposition_t>(disposition), user_gesture,
|
||||
cef_features, window_info, pending_popup->client,
|
||||
pending_popup->settings, pending_popup->extra_info,
|
||||
no_javascript_access);
|
||||
handled = true;
|
||||
|
||||
if (allow) {
|
||||
// The parent browser may be destroyed during popup creation, so don't
|
||||
// bind a direct reference.
|
||||
pending_popup->aborted_callback = base::BindOnce(
|
||||
[](int browser_id, int popup_id) {
|
||||
LOG(WARNING) << "Pending popup " << popup_id
|
||||
<< " aborted for browser " << browser_id;
|
||||
if (auto browser =
|
||||
CefBrowserHostBase::GetBrowserForBrowserId(browser_id)) {
|
||||
if (auto client = browser->GetClient()) {
|
||||
if (auto handler = client->GetLifeSpanHandler()) {
|
||||
handler->OnBeforePopupAborted(browser.get(), popup_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
browser->GetIdentifier(), popup_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,12 +246,41 @@ bool CefBrowserInfoManager::CanCreateWindow(
|
||||
// otherwise GetCustomWebContentsView will fail to retrieve the PopupInfo.
|
||||
opener->GetProcess()->FilterURL(false, &pending_popup->target_url);
|
||||
|
||||
pending_create_popup_ = pending_popup.get();
|
||||
|
||||
// Need to Push here because WebContentsCreated may be called before
|
||||
// CreateWindowResult.
|
||||
PushPendingPopup(std::move(pending_popup));
|
||||
}
|
||||
|
||||
return allow;
|
||||
}
|
||||
|
||||
void CefBrowserInfoManager::CreateWindowResult(content::RenderFrameHost* opener,
|
||||
bool success) {
|
||||
// This method is called during RenderFrameHostImpl::CreateNewWindow execution
|
||||
// (if CanCreateWindow returns true) with three possible states:
|
||||
// 1. Before WebContentsCreated with |success=false|. This is the normal
|
||||
// failure case where the pending popup will be canceled. For example, if a
|
||||
// file select dialog is active.
|
||||
// 2. After WebContentsCreated/AddWebContents with |success=true|. This is the
|
||||
// normal success case where OnAfterCreated has already been called.
|
||||
// 3. After WebContentsCreated/AddWebContents with |success=false|. This is
|
||||
// the failure case where a WebContents won't have an opener from the
|
||||
// renderer's perspective (for example, with JavaScript access disabled or
|
||||
// no-referrer links). The WebContents is still valid, will navigate
|
||||
// normally, and OnAfterCreated has already been called.
|
||||
if (!success && pending_create_popup_) {
|
||||
const auto* popup = pending_create_popup_.get();
|
||||
pending_create_popup_ = nullptr;
|
||||
|
||||
// Cancel the pending popup.
|
||||
std::erase_if(pending_popup_list_, [popup](const auto& popup_ptr) {
|
||||
return popup_ptr.get() == popup;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void CefBrowserInfoManager::GetCustomWebContentsView(
|
||||
const GURL& target_url,
|
||||
const content::GlobalRenderFrameHostId& opener_global_id,
|
||||
@@ -255,6 +313,8 @@ void CefBrowserInfoManager::WebContentsCreated(
|
||||
content::WebContents* new_contents) {
|
||||
CEF_REQUIRE_UIT();
|
||||
|
||||
pending_create_popup_ = nullptr;
|
||||
|
||||
// GET_CUSTOM_WEB_CONTENTS_VIEW is only used with Alloy style.
|
||||
auto pending_popup = PopPendingPopup(
|
||||
PendingPopup::GET_CUSTOM_WEB_CONTENTS_VIEW,
|
||||
@@ -272,6 +332,8 @@ void CefBrowserInfoManager::WebContentsCreated(
|
||||
pending_popup->step = PendingPopup::WEB_CONTENTS_CREATED;
|
||||
pending_popup->new_contents = new_contents;
|
||||
PushPendingPopup(std::move(pending_popup));
|
||||
} else {
|
||||
pending_popup->step = PendingPopup::CREATION_COMPLETE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,6 +348,7 @@ bool CefBrowserInfoManager::AddWebContents(content::WebContents* new_contents) {
|
||||
PendingPopup::WEB_CONTENTS_CREATED, new_contents);
|
||||
if (pending_popup) {
|
||||
DCHECK(!pending_popup->alloy_style);
|
||||
pending_popup->step = PendingPopup::CREATION_COMPLETE;
|
||||
return !pending_popup->use_default_browser_creation;
|
||||
}
|
||||
|
||||
@@ -589,17 +652,10 @@ void CefBrowserInfoManager::RenderProcessHostDestroyed(
|
||||
}
|
||||
|
||||
// Remove all pending popups that reference the destroyed host as the opener.
|
||||
{
|
||||
PendingPopupList::iterator it = pending_popup_list_.begin();
|
||||
while (it != pending_popup_list_.end()) {
|
||||
PendingPopup* popup = it->get();
|
||||
if (popup->opener_global_id.child_id == render_process_id) {
|
||||
it = pending_popup_list_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::erase_if(
|
||||
pending_popup_list_, [render_process_id](const auto& popup_ptr) {
|
||||
return popup_ptr->opener_global_id.child_id == render_process_id;
|
||||
});
|
||||
}
|
||||
|
||||
void CefBrowserInfoManager::PushPendingPopup(
|
||||
|
@@ -82,6 +82,10 @@ class CefBrowserInfoManager : public content::RenderProcessHostObserver {
|
||||
bool opener_suppressed,
|
||||
bool* no_javascript_access);
|
||||
|
||||
// Called from ContentBrowserClient::CreateWindowResult if CanCreateWindow
|
||||
// returns true. See comments on PendingPopup for more information.
|
||||
void CreateWindowResult(content::RenderFrameHost* opener, bool success);
|
||||
|
||||
// Called from WebContentsDelegate::GetCustomWebContentsView (Alloy style
|
||||
// only). See comments on PendingPopup for more information.
|
||||
void GetCustomWebContentsView(
|
||||
@@ -161,23 +165,33 @@ class CefBrowserInfoManager : public content::RenderProcessHostObserver {
|
||||
// RenderProcessHostObserver methods:
|
||||
void RenderProcessHostDestroyed(content::RenderProcessHost* host) override;
|
||||
|
||||
// Store state information about pending popups. Call order is:
|
||||
// Store state information about pending popups. The UIT callbacks occur
|
||||
// synchronously during RenderFrameHostImpl::CreateNewWindow execution. The
|
||||
// result of CreateNewWindow execution will be passed to CreateWindowResult
|
||||
// (may call OnBeforePopupAborted; see documentation in that method). Call
|
||||
// order for successful popup creation is is:
|
||||
// - CanCreateWindow (UIT):
|
||||
// Provides an opportunity to cancel the popup (calls OnBeforePopup) and
|
||||
// creates the new platform delegate for the popup. If the popup owner is
|
||||
// an extension guest view (PDF viewer) then the popup is canceled and
|
||||
// WebContentsDelegate::OpenURLFromTab is called via the
|
||||
// CefBrowserHostBase::MaybeAllowNavigation implementation.
|
||||
// And then the following calls may occur at the same time:
|
||||
// And then the following UIT and IOT calls may occur at the same time:
|
||||
// - GetCustomWebContentsView (UIT) (Alloy style only):
|
||||
// Creates the OSR views for windowless popups.
|
||||
// - WebContentsCreated (UIT):
|
||||
// Creates the CefBrowserHost representation for the popup.
|
||||
// Creates the CefBrowserHost representation for the popup (calls
|
||||
// OnAfterCreated).
|
||||
// - AddWebContents (UIT) (Chrome style only):
|
||||
// Creates the Browser or tab representation for the popup.
|
||||
// - CefBrowserManager::GetNewBrowserInfo (IOT)
|
||||
// Passes information about the popup to the renderer process.
|
||||
struct PendingPopup {
|
||||
~PendingPopup();
|
||||
|
||||
// Used to notify if popup creation is aborted.
|
||||
base::OnceClosure aborted_callback;
|
||||
|
||||
// Track the last method that modified this PendingPopup instance. There may
|
||||
// be multiple pending popups with the same identifiers and this allows us
|
||||
// to differentiate between them at different processing steps.
|
||||
@@ -185,6 +199,7 @@ class CefBrowserInfoManager : public content::RenderProcessHostObserver {
|
||||
CAN_CREATE_WINDOW,
|
||||
GET_CUSTOM_WEB_CONTENTS_VIEW,
|
||||
WEB_CONTENTS_CREATED,
|
||||
CREATION_COMPLETE,
|
||||
} step;
|
||||
|
||||
// True if this popup is Alloy style, otherwise Chrome style.
|
||||
@@ -286,6 +301,11 @@ class CefBrowserInfoManager : public content::RenderProcessHostObserver {
|
||||
using PendingPopupList = std::vector<std::unique_ptr<PendingPopup>>;
|
||||
PendingPopupList pending_popup_list_;
|
||||
|
||||
// Current popup pending creation during RenderFrameHostImpl::CreateNewWindow
|
||||
// execution (valid from CanCreateWindow returning true to WebContentsCreated
|
||||
// or CreateWindowResult being called). Only accessed on the UI thread.
|
||||
raw_ptr<PendingPopup> pending_create_popup_ = nullptr;
|
||||
|
||||
int next_timeout_id_ = 0;
|
||||
};
|
||||
|
||||
|
@@ -500,6 +500,8 @@ void ChromeBrowserHostImpl::Attach(content::WebContents* web_contents,
|
||||
DCHECK(web_contents);
|
||||
|
||||
if (opener) {
|
||||
opener_id_ = opener->GetIdentifier();
|
||||
|
||||
// Give the opener browser's platform delegate an opportunity to modify the
|
||||
// new browser's platform delegate.
|
||||
opener->platform_delegate()->PopupWebContentsCreated(
|
||||
|
@@ -369,6 +369,12 @@ bool ChromeContentBrowserClientCef::CanCreateWindow(
|
||||
user_gesture, opener_suppressed, no_javascript_access);
|
||||
}
|
||||
|
||||
void ChromeContentBrowserClientCef::CreateWindowResult(
|
||||
content::RenderFrameHost* opener,
|
||||
bool success) {
|
||||
CefBrowserInfoManager::GetInstance()->CreateWindowResult(opener, success);
|
||||
}
|
||||
|
||||
void ChromeContentBrowserClientCef::OverrideWebkitPrefs(
|
||||
content::WebContents* web_contents,
|
||||
blink::web_pref::WebPreferences* prefs) {
|
||||
|
@@ -63,6 +63,8 @@ class ChromeContentBrowserClientCef : public ChromeContentBrowserClient {
|
||||
bool user_gesture,
|
||||
bool opener_suppressed,
|
||||
bool* no_javascript_access) override;
|
||||
void CreateWindowResult(content::RenderFrameHost* opener,
|
||||
bool success) override;
|
||||
void OverrideWebkitPrefs(content::WebContents* web_contents,
|
||||
blink::web_pref::WebPreferences* prefs) override;
|
||||
void WillCreateURLLoaderFactory(
|
||||
|
Reference in New Issue
Block a user