Skip to content

Commit

Permalink
fix: prevent destroyed view references from causing crashes (#25609)
Browse files Browse the repository at this point in the history
  • Loading branch information
mlaurencin committed Sep 25, 2020
1 parent c32cb09 commit 858d74b
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 37 deletions.
19 changes: 12 additions & 7 deletions shell/browser/api/electron_api_top_level_window.cc
Expand Up @@ -741,8 +741,10 @@ void TopLevelWindow::AddBrowserView(v8::Local<v8::Value> value) {
gin::ConvertFromV8(isolate(), value, &browser_view)) {
auto get_that_view = browser_views_.find(browser_view->weak_map_id());
if (get_that_view == browser_views_.end()) {
window_->AddBrowserView(browser_view->view());
browser_view->web_contents()->SetOwnerWindow(window_.get());
if (browser_view->web_contents()) {
window_->AddBrowserView(browser_view->view());
browser_view->web_contents()->SetOwnerWindow(window_.get());
}
browser_views_[browser_view->weak_map_id()].Reset(isolate(), value);
}
}
Expand All @@ -754,9 +756,10 @@ void TopLevelWindow::RemoveBrowserView(v8::Local<v8::Value> value) {
gin::ConvertFromV8(isolate(), value, &browser_view)) {
auto get_that_view = browser_views_.find(browser_view->weak_map_id());
if (get_that_view != browser_views_.end()) {
window_->RemoveBrowserView(browser_view->view());
browser_view->web_contents()->SetOwnerWindow(nullptr);

if (browser_view->web_contents()) {
window_->RemoveBrowserView(browser_view->view());
browser_view->web_contents()->SetOwnerWindow(nullptr);
}
(*get_that_view).second.Reset(isolate(), value);
browser_views_.erase(get_that_view);
}
Expand Down Expand Up @@ -1054,8 +1057,10 @@ void TopLevelWindow::ResetBrowserViews() {
v8::Local<v8::Value>::New(isolate(), item.second),
&browser_view) &&
!browser_view.IsEmpty()) {
window_->RemoveBrowserView(browser_view->view());
browser_view->web_contents()->SetOwnerWindow(nullptr);
if (browser_view->web_contents()) {
window_->RemoveBrowserView(browser_view->view());
browser_view->web_contents()->SetOwnerWindow(nullptr);
}
}

item.second.Reset();
Expand Down
3 changes: 2 additions & 1 deletion shell/browser/api/electron_api_web_contents.cc
Expand Up @@ -698,7 +698,8 @@ WebContents::~WebContents() {
} else {
// Destroy WebContents asynchronously unless app is shutting down,
// 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
// destroy the webContents in the next tick. So we have to manually
// call it here to make sure "destroyed" event is emitted.
Expand Down
12 changes: 11 additions & 1 deletion shell/browser/native_browser_view.cc
Expand Up @@ -13,16 +13,26 @@ namespace electron {

NativeBrowserView::NativeBrowserView(
InspectableWebContents* inspectable_web_contents)
: inspectable_web_contents_(inspectable_web_contents) {}
: inspectable_web_contents_(inspectable_web_contents) {
Observe(inspectable_web_contents_->GetWebContents());
}

NativeBrowserView::~NativeBrowserView() = default;

InspectableWebContentsView* NativeBrowserView::GetInspectableWebContentsView() {
if (!inspectable_web_contents_)
return nullptr;
return inspectable_web_contents_->GetView();
}

content::WebContents* NativeBrowserView::GetWebContents() {
if (!inspectable_web_contents_)
return nullptr;
return inspectable_web_contents_->GetWebContents();
}

void NativeBrowserView::WebContentsDestroyed() {
inspectable_web_contents_ = nullptr;
}

} // namespace electron
7 changes: 5 additions & 2 deletions shell/browser/native_browser_view.h
Expand Up @@ -9,6 +9,7 @@

#include "base/macros.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "third_party/skia/include/core/SkColor.h"

namespace gfx {
Expand All @@ -27,9 +28,9 @@ enum AutoResizeFlags {
class InspectableWebContents;
class InspectableWebContentsView;

class NativeBrowserView {
class NativeBrowserView : public content::WebContentsObserver {
public:
virtual ~NativeBrowserView();
~NativeBrowserView() override;

static NativeBrowserView* Create(
InspectableWebContents* inspectable_web_contents);
Expand All @@ -52,6 +53,8 @@ class NativeBrowserView {

protected:
explicit NativeBrowserView(InspectableWebContents* inspectable_web_contents);
// content::WebContentsObserver:
void WebContentsDestroyed() override;

InspectableWebContents* inspectable_web_contents_;

Expand Down
37 changes: 25 additions & 12 deletions shell/browser/native_browser_view_mac.mm
Expand Up @@ -162,8 +162,10 @@ - (BOOL)mouseDownCanMoveWindow {
NativeBrowserViewMac::NativeBrowserViewMac(
InspectableWebContents* inspectable_web_contents)
: NativeBrowserView(inspectable_web_contents) {
auto* view =
GetInspectableWebContentsView()->GetNativeView().GetNativeNSView();
auto* iwc_view = GetInspectableWebContentsView();
if (!iwc_view)
return;
auto* view = iwc_view->GetNativeView().GetNativeNSView();
view.autoresizingMask = kDefaultAutoResizingMask;
}

Expand All @@ -186,14 +188,18 @@ - (BOOL)mouseDownCanMoveWindow {
NSViewMaxYMargin | NSViewMinYMargin | NSViewHeightSizable;
}

auto* view =
GetInspectableWebContentsView()->GetNativeView().GetNativeNSView();
auto* iwc_view = GetInspectableWebContentsView();
if (!iwc_view)
return;
auto* view = iwc_view->GetNativeView().GetNativeNSView();
view.autoresizingMask = autoresizing_mask;
}

void NativeBrowserViewMac::SetBounds(const gfx::Rect& bounds) {
auto* view =
GetInspectableWebContentsView()->GetNativeView().GetNativeNSView();
auto* iwc_view = GetInspectableWebContentsView();
if (!iwc_view)
return;
auto* view = iwc_view->GetNativeView().GetNativeNSView();
auto* superview = view.superview;
const auto superview_height = superview ? superview.frame.size.height : 0;
view.frame =
Expand All @@ -202,8 +208,10 @@ - (BOOL)mouseDownCanMoveWindow {
}

gfx::Rect NativeBrowserViewMac::GetBounds() {
NSView* view =
GetInspectableWebContentsView()->GetNativeView().GetNativeNSView();
auto* iwc_view = GetInspectableWebContentsView();
if (!iwc_view)
return gfx::Rect();
NSView* view = iwc_view->GetNativeView().GetNativeNSView();
const int superview_height =
(view.superview) ? view.superview.frame.size.height : 0;
return gfx::Rect(
Expand All @@ -213,17 +221,22 @@ - (BOOL)mouseDownCanMoveWindow {
}

void NativeBrowserViewMac::SetBackgroundColor(SkColor color) {
auto* view =
GetInspectableWebContentsView()->GetNativeView().GetNativeNSView();
auto* iwc_view = GetInspectableWebContentsView();
if (!iwc_view)
return;
auto* view = iwc_view->GetNativeView().GetNativeNSView();
view.wantsLayer = YES;
view.layer.backgroundColor = skia::CGColorCreateFromSkColor(color);
}

void NativeBrowserViewMac::UpdateDraggableRegions(
const std::vector<gfx::Rect>& drag_exclude_rects) {
auto* iwc_view = GetInspectableWebContentsView();
auto* web_contents = GetWebContents();
if (!(iwc_view && web_contents))
return;
NSView* web_view = GetWebContents()->GetNativeView().GetNativeNSView();
NSView* inspectable_view =
GetInspectableWebContentsView()->GetNativeView().GetNativeNSView();
NSView* inspectable_view = iwc_view->GetNativeView().GetNativeNSView();
NSView* window_content_view = inspectable_view.superview;
const auto window_content_view_height = NSHeight(window_content_view.bounds);

Expand Down
30 changes: 24 additions & 6 deletions shell/browser/native_browser_view_views.cc
Expand Up @@ -26,7 +26,10 @@ void NativeBrowserViewViews::SetAutoResizeProportions(
const gfx::Size& window_size) {
if ((auto_resize_flags_ & AutoResizeFlags::kAutoResizeHorizontal) &&
!auto_horizontal_proportion_set_) {
auto* view = GetInspectableWebContentsView()->GetView();
auto* iwc_view = GetInspectableWebContentsView();
if (iwc_view)
return;
auto* view = iwc_view->GetView();
auto view_bounds = view->bounds();
auto_horizontal_proportion_width_ =
static_cast<float>(window_size.width()) /
Expand All @@ -37,7 +40,10 @@ void NativeBrowserViewViews::SetAutoResizeProportions(
}
if ((auto_resize_flags_ & AutoResizeFlags::kAutoResizeVertical) &&
!auto_vertical_proportion_set_) {
auto* view = GetInspectableWebContentsView()->GetView();
auto* iwc_view = GetInspectableWebContentsView();
if (iwc_view)
return;
auto* view = iwc_view->GetView();
auto view_bounds = view->bounds();
auto_vertical_proportion_height_ =
static_cast<float>(window_size.height()) /
Expand All @@ -51,7 +57,10 @@ void NativeBrowserViewViews::SetAutoResizeProportions(
void NativeBrowserViewViews::AutoResize(const gfx::Rect& new_window,
int width_delta,
int height_delta) {
auto* view = GetInspectableWebContentsView()->GetView();
auto* iwc_view = GetInspectableWebContentsView();
if (iwc_view)
return;
auto* view = iwc_view->GetView();
const auto flags = GetAutoResizeFlags();
if (!(flags & kAutoResizeWidth)) {
width_delta = 0;
Expand Down Expand Up @@ -92,17 +101,26 @@ void NativeBrowserViewViews::ResetAutoResizeProportions() {
}

void NativeBrowserViewViews::SetBounds(const gfx::Rect& bounds) {
auto* view = GetInspectableWebContentsView()->GetView();
auto* iwc_view = GetInspectableWebContentsView();
if (!iwc_view)
return;
auto* view = iwc_view->GetView();
view->SetBoundsRect(bounds);
ResetAutoResizeProportions();
}

gfx::Rect NativeBrowserViewViews::GetBounds() {
return GetInspectableWebContentsView()->GetView()->bounds();
auto* iwc_view = GetInspectableWebContentsView();
if (!iwc_view)
return gfx::Rect();
return iwc_view->GetView()->bounds();
}

void NativeBrowserViewViews::SetBackgroundColor(SkColor color) {
auto* view = GetInspectableWebContentsView()->GetView();
auto* iwc_view = GetInspectableWebContentsView();
if (!iwc_view)
return;
auto* view = iwc_view->GetView();
view->SetBackground(views::CreateSolidBackground(color));
view->SchedulePaint();
}
Expand Down
14 changes: 10 additions & 4 deletions shell/browser/native_window_mac.mm
Expand Up @@ -1247,8 +1247,11 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) {
}

add_browser_view(view);
auto* native_view =
view->GetInspectableWebContentsView()->GetNativeView().GetNativeNSView();
auto* iwc_view = view->GetInspectableWebContentsView();
if (!iwc_view)
return;

auto* native_view = iwc_view->GetNativeView().GetNativeNSView();
[[window_ contentView] addSubview:native_view
positioned:NSWindowAbove
relativeTo:nil];
Expand All @@ -1266,8 +1269,11 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) {
return;
}

[view->GetInspectableWebContentsView()->GetNativeView().GetNativeNSView()
removeFromSuperview];
auto* iwc_view = view->GetInspectableWebContentsView();
if (!iwc_view)
return;

[iwc_view->GetNativeView().GetNativeNSView() removeFromSuperview];
remove_browser_view(view);

[CATransaction commit];
Expand Down
13 changes: 9 additions & 4 deletions shell/browser/native_window_views.cc
Expand Up @@ -1065,8 +1065,10 @@ void NativeWindowViews::AddBrowserView(NativeBrowserView* view) {

add_browser_view(view);

content_view()->AddChildView(
view->GetInspectableWebContentsView()->GetView());
auto* iwc_view = view->GetInspectableWebContentsView();
if (!iwc_view)
return;
content_view()->AddChildView(iwc_view->GetView());
}

void NativeWindowViews::RemoveBrowserView(NativeBrowserView* view) {
Expand All @@ -1077,8 +1079,11 @@ void NativeWindowViews::RemoveBrowserView(NativeBrowserView* view) {
return;
}

content_view()->RemoveChildView(
view->GetInspectableWebContentsView()->GetView());
auto* iwc_view = view->GetInspectableWebContentsView();
if (!iwc_view)
return;

content_view()->RemoveChildView(iwc_view->GetView());
remove_browser_view(view);
}

Expand Down
27 changes: 27 additions & 0 deletions spec-main/fixtures/crash-cases/api-browser-destroy/index.js
@@ -0,0 +1,27 @@
const { app, BrowserWindow, BrowserView } = require('electron');
const { expect } = require('chai');
const assert = require('assert');

function createWindow () {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
});
const view = new BrowserView();
mainWindow.addBrowserView(view);
view.webContents.destroy();

view.setBounds({ x: 0, y: 0, width: 0, height: 0 });
const bounds = view.getBounds();
expect(bounds).to.deep.equal({ x: 0, y: 0, width: 0, height: 0 });
view.setBackgroundColor('#56cc5b10');
}

app.on('ready', () => {
createWindow();
app.quit();
});

0 comments on commit 858d74b

Please sign in to comment.