fix: prevent crash if BrowserView webContents was destroyed (#25112)
This commit is contained in:
parent
c97f8109c2
commit
c8a0b2b71d
6 changed files with 41 additions and 32 deletions
|
@ -40,16 +40,6 @@ A [`WebContents`](web-contents.md) object owned by this view.
|
||||||
|
|
||||||
Objects created with `new BrowserView` have the following instance methods:
|
Objects created with `new BrowserView` have the following instance methods:
|
||||||
|
|
||||||
#### `view.destroy()`
|
|
||||||
|
|
||||||
Force closing the view, the `unload` and `beforeunload` events won't be emitted
|
|
||||||
for the web page. After you're done with a view, call this function in order to
|
|
||||||
free memory and other resources as soon as possible.
|
|
||||||
|
|
||||||
#### `view.isDestroyed()`
|
|
||||||
|
|
||||||
Returns `Boolean` - Whether the view is destroyed.
|
|
||||||
|
|
||||||
#### `view.setAutoResize(options)` _Experimental_
|
#### `view.setAutoResize(options)` _Experimental_
|
||||||
|
|
||||||
* `options` Object
|
* `options` Object
|
||||||
|
|
|
@ -745,6 +745,7 @@ void BaseWindow::AddBrowserView(v8::Local<v8::Value> value) {
|
||||||
auto get_that_view = browser_views_.find(browser_view->ID());
|
auto get_that_view = browser_views_.find(browser_view->ID());
|
||||||
if (get_that_view == browser_views_.end()) {
|
if (get_that_view == browser_views_.end()) {
|
||||||
window_->AddBrowserView(browser_view->view());
|
window_->AddBrowserView(browser_view->view());
|
||||||
|
if (browser_view->web_contents())
|
||||||
browser_view->web_contents()->SetOwnerWindow(window_.get());
|
browser_view->web_contents()->SetOwnerWindow(window_.get());
|
||||||
browser_views_[browser_view->ID()].Reset(isolate(), value);
|
browser_views_[browser_view->ID()].Reset(isolate(), value);
|
||||||
}
|
}
|
||||||
|
@ -758,8 +759,8 @@ void BaseWindow::RemoveBrowserView(v8::Local<v8::Value> value) {
|
||||||
auto get_that_view = browser_views_.find(browser_view->ID());
|
auto get_that_view = browser_views_.find(browser_view->ID());
|
||||||
if (get_that_view != browser_views_.end()) {
|
if (get_that_view != browser_views_.end()) {
|
||||||
window_->RemoveBrowserView(browser_view->view());
|
window_->RemoveBrowserView(browser_view->view());
|
||||||
|
if (browser_view->web_contents())
|
||||||
browser_view->web_contents()->SetOwnerWindow(nullptr);
|
browser_view->web_contents()->SetOwnerWindow(nullptr);
|
||||||
|
|
||||||
(*get_that_view).second.Reset(isolate(), value);
|
(*get_that_view).second.Reset(isolate(), value);
|
||||||
browser_views_.erase(get_that_view);
|
browser_views_.erase(get_that_view);
|
||||||
}
|
}
|
||||||
|
@ -1055,6 +1056,7 @@ void BaseWindow::ResetBrowserViews() {
|
||||||
&browser_view) &&
|
&browser_view) &&
|
||||||
!browser_view.IsEmpty()) {
|
!browser_view.IsEmpty()) {
|
||||||
window_->RemoveBrowserView(browser_view->view());
|
window_->RemoveBrowserView(browser_view->view());
|
||||||
|
if (browser_view->web_contents())
|
||||||
browser_view->web_contents()->SetOwnerWindow(nullptr);
|
browser_view->web_contents()->SetOwnerWindow(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -658,7 +658,8 @@ WebContents::~WebContents() {
|
||||||
} else {
|
} else {
|
||||||
// Destroy WebContents asynchronously unless app is shutting down,
|
// Destroy WebContents asynchronously unless app is shutting down,
|
||||||
// because destroy() might be called inside WebContents's event handler.
|
// because destroy() might be called inside WebContents's event handler.
|
||||||
DestroyWebContents(!IsGuest() /* async */);
|
bool is_browser_view = type_ == Type::BROWSER_VIEW;
|
||||||
|
DestroyWebContents(!(IsGuest() || is_browser_view) /* async */);
|
||||||
// The WebContentsDestroyed will not be called automatically because we
|
// The WebContentsDestroyed will not be called automatically because we
|
||||||
// destroy the webContents in the next tick. So we have to manually
|
// destroy the webContents in the next tick. So we have to manually
|
||||||
// call it here to make sure "destroyed" event is emitted.
|
// call it here to make sure "destroyed" event is emitted.
|
||||||
|
|
|
@ -1275,12 +1275,15 @@ void NativeWindowMac::AddBrowserView(NativeBrowserView* view) {
|
||||||
}
|
}
|
||||||
|
|
||||||
add_browser_view(view);
|
add_browser_view(view);
|
||||||
auto* native_view =
|
if (view->GetInspectableWebContentsView()) {
|
||||||
view->GetInspectableWebContentsView()->GetNativeView().GetNativeNSView();
|
auto* native_view = view->GetInspectableWebContentsView()
|
||||||
|
->GetNativeView()
|
||||||
|
.GetNativeNSView();
|
||||||
[[window_ contentView] addSubview:native_view
|
[[window_ contentView] addSubview:native_view
|
||||||
positioned:NSWindowAbove
|
positioned:NSWindowAbove
|
||||||
relativeTo:nil];
|
relativeTo:nil];
|
||||||
native_view.hidden = NO;
|
native_view.hidden = NO;
|
||||||
|
}
|
||||||
|
|
||||||
[CATransaction commit];
|
[CATransaction commit];
|
||||||
}
|
}
|
||||||
|
@ -1294,6 +1297,7 @@ void NativeWindowMac::RemoveBrowserView(NativeBrowserView* view) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (view->GetInspectableWebContentsView())
|
||||||
[view->GetInspectableWebContentsView()->GetNativeView().GetNativeNSView()
|
[view->GetInspectableWebContentsView()->GetNativeView().GetNativeNSView()
|
||||||
removeFromSuperview];
|
removeFromSuperview];
|
||||||
remove_browser_view(view);
|
remove_browser_view(view);
|
||||||
|
|
|
@ -1072,7 +1072,7 @@ void NativeWindowViews::AddBrowserView(NativeBrowserView* view) {
|
||||||
}
|
}
|
||||||
|
|
||||||
add_browser_view(view);
|
add_browser_view(view);
|
||||||
|
if (view->GetInspectableWebContentsView())
|
||||||
content_view()->AddChildView(
|
content_view()->AddChildView(
|
||||||
view->GetInspectableWebContentsView()->GetView());
|
view->GetInspectableWebContentsView()->GetView());
|
||||||
}
|
}
|
||||||
|
@ -1085,6 +1085,7 @@ void NativeWindowViews::RemoveBrowserView(NativeBrowserView* view) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (view->GetInspectableWebContentsView())
|
||||||
content_view()->RemoveChildView(
|
content_view()->RemoveChildView(
|
||||||
view->GetInspectableWebContentsView()->GetView());
|
view->GetInspectableWebContentsView()->GetView());
|
||||||
remove_browser_view(view);
|
remove_browser_view(view);
|
||||||
|
|
|
@ -134,20 +134,31 @@ describe('BrowserView module', () => {
|
||||||
w.addBrowserView(view2);
|
w.addBrowserView(view2);
|
||||||
defer(() => w.removeBrowserView(view2));
|
defer(() => w.removeBrowserView(view2));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not throw if called multiple times with same view', () => {
|
it('does not throw if called multiple times with same view', () => {
|
||||||
view = new BrowserView();
|
view = new BrowserView();
|
||||||
w.addBrowserView(view);
|
w.addBrowserView(view);
|
||||||
w.addBrowserView(view);
|
w.addBrowserView(view);
|
||||||
w.addBrowserView(view);
|
w.addBrowserView(view);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not crash if the BrowserView webContents are destroyed prior to window removal', () => {
|
||||||
|
expect(() => {
|
||||||
|
const view1 = new BrowserView();
|
||||||
|
(view1.webContents as any).destroy();
|
||||||
|
w.addBrowserView(view1);
|
||||||
|
}).to.not.throw();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('BrowserWindow.removeBrowserView()', () => {
|
describe('BrowserWindow.removeBrowserView()', () => {
|
||||||
it('does not throw if called multiple times with same view', () => {
|
it('does not throw if called multiple times with same view', () => {
|
||||||
|
expect(() => {
|
||||||
view = new BrowserView();
|
view = new BrowserView();
|
||||||
w.addBrowserView(view);
|
w.addBrowserView(view);
|
||||||
w.removeBrowserView(view);
|
w.removeBrowserView(view);
|
||||||
w.removeBrowserView(view);
|
w.removeBrowserView(view);
|
||||||
|
}).to.not.throw();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue