diff --git a/shell/browser/api/electron_api_view.cc b/shell/browser/api/electron_api_view.cc index b01a08be88c..c65f80b164f 100644 --- a/shell/browser/api/electron_api_view.cc +++ b/shell/browser/api/electron_api_view.cc @@ -124,6 +124,15 @@ struct Converter { } }; +template <> +struct Converter { + static bool FromV8(v8::Isolate* isolate, + v8::Local val, + electron::api::View* out) { + return gin::ConvertFromV8(isolate, val, &out); + } +}; + template <> struct Converter { static v8::Local ToV8(v8::Isolate* isolate, @@ -257,11 +266,13 @@ void View::RemoveChildView(gin::Handle child) { #if BUILDFLAG(IS_MAC) ScopedCAActionDisabler disable_animations; #endif + // Remove from child_views first so that OnChildViewRemoved doesn't try to + // remove it again + child_views_.erase(it); // It's possible for the child's view to be invalid here // if the child's webContents was closed or destroyed. if (child->view()) view_->RemoveChildView(child->view()); - child_views_.erase(it); } } @@ -388,6 +399,19 @@ void View::OnViewIsDeleting(views::View* observed_view) { view_ = nullptr; } +void View::OnChildViewRemoved(views::View* observed_view, views::View* child) { + v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); + auto it = std::ranges::find_if( + child_views_, [&](const v8::Global& child_view) { + View current_view; + gin::ConvertFromV8(isolate, child_view.Get(isolate), ¤t_view); + return current_view.view()->GetID() == child->GetID(); + }); + if (it != child_views_.end()) { + child_views_.erase(it); + } +} + // static gin_helper::WrappableBase* View::New(gin::Arguments* args) { View* view = new View(); diff --git a/shell/browser/api/electron_api_view.h b/shell/browser/api/electron_api_view.h index 6eeb1652190..1b0a3f815a6 100644 --- a/shell/browser/api/electron_api_view.h +++ b/shell/browser/api/electron_api_view.h @@ -47,6 +47,8 @@ class View : public gin_helper::EventEmitter, // views::ViewObserver void OnViewBoundsChanged(views::View* observed_view) override; void OnViewIsDeleting(views::View* observed_view) override; + void OnChildViewRemoved(views::View* observed_view, + views::View* child) override; views::View* view() const { return view_; } std::optional border_radius() const { return border_radius_; } diff --git a/spec/fixtures/crash-cases/webview-move-between-windows/index.js b/spec/fixtures/crash-cases/webview-move-between-windows/index.js new file mode 100644 index 00000000000..de9053ec65c --- /dev/null +++ b/spec/fixtures/crash-cases/webview-move-between-windows/index.js @@ -0,0 +1,31 @@ +const { app, BrowserWindow, WebContentsView } = require('electron'); + +function createWindow () { + // Create the browser window. + const mainWindow = new BrowserWindow(); + const secondaryWindow = new BrowserWindow(); + + const contentsView = new WebContentsView(); + mainWindow.contentView.addChildView(contentsView); + mainWindow.webContents.setDevToolsWebContents(contentsView.webContents); + mainWindow.openDevTools(); + + contentsView.setBounds({ + x: 400, + y: 0, + width: 400, + height: 600 + }); + + setTimeout(() => { + secondaryWindow.contentView.addChildView(contentsView); + setTimeout(() => { + mainWindow.contentView.addChildView(contentsView); + app.quit(); + }, 1000); + }, 1000); +} + +app.whenReady().then(() => { + createWindow(); +});