fix: prevent crash if BrowserView webContents was destroyed (#25112)

This commit is contained in:
Shelley Vohr 2020-08-25 20:04:13 -07:00 committed by GitHub
parent c97f8109c2
commit c8a0b2b71d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 41 additions and 32 deletions

View file

@ -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

View file

@ -745,7 +745,8 @@ 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());
browser_view->web_contents()->SetOwnerWindow(window_.get()); if (browser_view->web_contents())
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());
browser_view->web_contents()->SetOwnerWindow(nullptr); if (browser_view->web_contents())
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,7 +1056,8 @@ void BaseWindow::ResetBrowserViews() {
&browser_view) && &browser_view) &&
!browser_view.IsEmpty()) { !browser_view.IsEmpty()) {
window_->RemoveBrowserView(browser_view->view()); window_->RemoveBrowserView(browser_view->view());
browser_view->web_contents()->SetOwnerWindow(nullptr); if (browser_view->web_contents())
browser_view->web_contents()->SetOwnerWindow(nullptr);
} }
item.second.Reset(); item.second.Reset();

View file

@ -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.

View file

@ -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()
[[window_ contentView] addSubview:native_view ->GetNativeView()
positioned:NSWindowAbove .GetNativeNSView();
relativeTo:nil]; [[window_ contentView] addSubview:native_view
native_view.hidden = NO; positioned:NSWindowAbove
relativeTo:nil];
native_view.hidden = NO;
}
[CATransaction commit]; [CATransaction commit];
} }
@ -1294,8 +1297,9 @@ void NativeWindowMac::RemoveBrowserView(NativeBrowserView* view) {
return; return;
} }
[view->GetInspectableWebContentsView()->GetNativeView().GetNativeNSView() if (view->GetInspectableWebContentsView())
removeFromSuperview]; [view->GetInspectableWebContentsView()->GetNativeView().GetNativeNSView()
removeFromSuperview];
remove_browser_view(view); remove_browser_view(view);
[CATransaction commit]; [CATransaction commit];

View file

@ -1072,9 +1072,9 @@ 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());
} }
void NativeWindowViews::RemoveBrowserView(NativeBrowserView* view) { void NativeWindowViews::RemoveBrowserView(NativeBrowserView* view) {
@ -1085,8 +1085,9 @@ void NativeWindowViews::RemoveBrowserView(NativeBrowserView* view) {
return; return;
} }
content_view()->RemoveChildView( if (view->GetInspectableWebContentsView())
view->GetInspectableWebContentsView()->GetView()); content_view()->RemoveChildView(
view->GetInspectableWebContentsView()->GetView());
remove_browser_view(view); remove_browser_view(view);
} }

View file

@ -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', () => {
view = new BrowserView(); expect(() => {
w.addBrowserView(view); view = new BrowserView();
w.removeBrowserView(view); w.addBrowserView(view);
w.removeBrowserView(view); w.removeBrowserView(view);
w.removeBrowserView(view);
}).to.not.throw();
}); });
}); });