Skip to content

fix(electron-ozone): NativeWindowViews leak on rohtmlwidget cycle OS-21158#140

Open
gostoshbs wants to merge 1 commit into
28-x-y-bsfrom
gostosh/OS-21158_native_window_content_view_release
Open

fix(electron-ozone): NativeWindowViews leak on rohtmlwidget cycle OS-21158#140
gostoshbs wants to merge 1 commit into
28-x-y-bsfrom
gostosh/OS-21158_native_window_content_view_release

Conversation

@gostoshbs
Copy link
Copy Markdown

Continuously creating and destroying Electron BrowserWindows in a loop causes the Electron main process's memory consumption to grow monotonically. Part of this is caused by
NativeWindowViews::SetContentView, which when replacing a content view detaches the child from the view hierarchy but does not delete it. Under the views ownership model, child views are implicitly owned by their parent, so the detached subtree, including the WebContentsView and associated widget plumbing, becomes unreachable and leaks. Switch to RemoveChildViewT, which transfers ownership back to the caller as a unique_ptr that is immediately dropped to destroy the subtree. The existing RemoveChildView path is retained for views with owned_by_client() set, since the framework must not delete those (see crbug/40115694).

Future upstream merges may address this leak differently. This change should be re-evaluated when Electron/Chromium is upgraded.

…21158

Continuously creating and destroying Electron BrowserWindows in a loop
causes the Electron main process's memory consumption to grow
monotonically. Part of this is caused by
NativeWindowViews::SetContentView, which when replacing a content
view detaches the child from the view hierarchy but does not delete
it. Under the views ownership model, child views are implicitly owned
by their parent, so the detached subtree, including the
WebContentsView and associated widget plumbing, becomes unreachable
and leaks. Switch to RemoveChildViewT, which transfers ownership back
to the caller as a unique_ptr that is immediately dropped to destroy
the subtree. The existing RemoveChildView path is retained for views
with owned_by_client() set, since the framework must not delete those
(see crbug/40115694).

Future upstream merges may address this leak differently. This change
should be re-evaluated when Electron/Chromium is upgraded.
root_view_.RemoveChildView(content_view());
auto* cv = content_view();
if (cv) {
set_content_view(nullptr);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this as set_content_view will always be called below and all it does is set content_view_?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This defends against use after free when RemoveChildViewT path triggers

auto* cv = content_view();
if (cv) {
set_content_view(nullptr);
focused_view_ = nullptr;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this as focused_view_ will always be set below?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a memory leak in the Views/Ozone windowing path where repeatedly replacing a NativeWindowViews content view could leave the old View subtree detached but not destroyed, causing main-process memory growth during BrowserWindow create/destroy cycles.

Changes:

  • Updates NativeWindowViews::SetContentView to remove the previous content view using RemoveChildViewT() when the view is parent-owned so the detached subtree is destroyed.
  • Preserves the legacy RemoveChildView() path for owned_by_client() views to avoid deleting client-owned views (per the referenced Chromium behavior).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +528 to +532
if (!cv->owned_by_client()) {
root_view_.RemoveChildViewT(cv);
} else {
root_view_.RemoveChildView(cv);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gostoshbs
We are almost at the tip of the tree on Electron and haven't been addressed upstream.
Can you please add a test case and send this upstream? That would avoid same issue coming back again or having merge issues.

Copy link
Copy Markdown

@caneraltinbasak caneraltinbasak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please format the patch with upstream requirements and send there as well as our fork? It would also be good to have a test case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants