Repository: electron/electron (language: cpp)

Issue:
[Bug]: EXCEPTION_ACCESS_VIOLATION crash on `BrowserWindow.destroy()`
### Preflight Checklist

- [X] I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/main/CONTRIBUTING.md) for this project.
- [X] I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/main/CODE_OF_CONDUCT.md) that this project adheres to.
- [X] I have searched the [issue tracker](https://www.github.com/electron/electron/issues) for a bug report that matches the one I want to file, without success.

### Electron Version

 \>=v31.0.0 (tested v31.0.0, v31.2.1, v32.0.0-aplha.10)

### What operating system(s) are you using?

Windows

### Operating System Version

Windows 10 22H2

### What arch are you using?

x64

### Last Known Working Electron version

v30.3.0 (and everything below it)

### Expected Behavior

```js
childWindow.once('close', () => {
    // do some cleanup etc
    childWindow.destroy();;
});
```

shouldn't crash Electron


### Actual Behavior

```js
function cleanup() {
	// do some cleanup etc
    childWindow.destroy();;
}
ipcMain.once(`done`, cleanup);
childWindow.once('close', cleanup);
```
if `done` ipc event is not called and the window is manually closed, the window is destroyed and a few moments later The main Electron process crash with the following trace:

<details><summary>Stacktrace Windows</summary>
<p>

```
Received fatal exception EXCEPTION_ACCESS_VIOLATION
v8::internal::compiler::CompilationDependencies::FieldTypeDependencyOffTheRecord [0x03CB9E08+3764424]
v8::HeapStatistics::external_memory [0x00A48A12+8338]
cppgc::internal::SameThreadEnabledCheckingPolicyBase::SameThreadEnabledCheckingPolicyBase [0x00B0DE72+28834]
uv_req_get_data [0x022485EE+4269966]
uv_req_get_data [0x02248AAA+4271178]
uv_stream_get_write_queue_size [0x0277DBD5+2462421]
uv_stream_get_write_queue_size [0x0277D383+2460291]
GetHandleVerifier [0x024558B5+1278869]
GetHandleVerifier [0x02455387+1277543]
AddClipboardFormatListener [0x76981B7B+75]
GetClassLongW [0x76977FCA+1962]
GetClassLongW [0x76977BCA+938]
CallNextHookEx [0x7697C09F+415]
KiUserCallbackDispatcher [0x77EC54ED+77]
PostMessageW [0x769760CC+156]
Ordinal132 [0x75A435C5+4885]
Ordinal45 [0x75A3FAE1+2577]
Ordinal45 [0x75A3F758+1672]
GetSystemMetricsForDpi [0x76976D27+487]
sqlite3_dbdata_init [0x068DC9A7+2609367]
uv_stream_get_write_queue_size [0x0277E07F+2463615]
uv_stream_get_write_queue_size [0x0277D383+2460291]
GetHandleVerifier [0x024558B5+1278869]
GetHandleVerifier [0x02455387+1277543]
AddClipboardFormatListener [0x76981B7B+75]
GetClassLongW [0x76977FCA+1962]
GetClassLongW [0x76977BCA+938]
CallNextHookEx [0x7697C09F+415]
KiUserCallbackDispatcher [0x77EC54ED+77]
PostMessageW [0x769760CC+156]
Ordinal132 [0x75A435C5+4885]
Ordinal45 [0x75A3FAE1+2577]
Ordinal45 [0x75A3F758+1672]
GetSystemMetricsForDpi [0x76976D27+487]
uv_stream_get_write_queue_size [0x0277D3B9+2460345]
GetHandleVerifier [0x024558B5+1278869]
GetHandleVerifier [0x02455387+1277543]
AddClipboardFormatListener [0x76981B7B+75]
GetClassLongW [0x76977FCA+1962]
DispatchMessageW [0x76976901+1265]
DispatchMessageW [0x76976420+16]
uv_os_getpid [0x01D81F00+6592]
uv_os_getpid [0x01D81B5B+5659]
v8::internal::compiler::CompilationDependencies::FieldTypeDependencyOffTheRecord [0x03C980DB+3625883]
uv_os_getpid [0x01D817FB+4795]
GetHandleVerifier [0x0232C67C+61788]
uv_os_getpid [0x01DA8F14+166356]
v8::internal::compiler::CompilationDependencies::TransitionDependencyOffTheRecord [0x0169DD19+2893273]
v8::internal::compiler::CompilationDependencies::TransitionDependencyOffTheRecord [0x0169F5FE+2899646]
v8::internal::compiler::CompilationDependencies::TransitionDependencyOffTheRecord [0x0169B4AF+2882927]
cppgc::Platform::GetForegroundTaskRunner [0x00B9DF37+3175]
cppgc::Platform::GetForegroundTaskRunner [0x00B9EFF0+7456]
cppgc::Platform::GetForegroundTaskRunner [0x00B9EEB7+7143]
cppgc::Platform::GetForegroundTaskRunner [0x00B9D88E+1470]
cppgc::Platform::GetForegroundTaskRunner [0x00B9DA11+1857]
v8::SourceLocation::SourceLocation [0x00960B7D+46669]
v8::SourceLocation::SourceLocation [0x00960721+45553]
GetNumaHighestNodeNumber [0x77682911+161]
GetNumaHighestNodeNumber [0x776828C6+86]
RtlUserFiberStart [0x77EC2DC7+23]

Electron exited with code 3221226525.
```

</p>
</details> 

### Testcase Gist URL

> To reproduce, click the menu item then manually close the window that appears

No dependencies:

https://gist.github.com/Araxeus/4e80b05c8bce61a5fe709eec1e969f2b

Original issue:

https://gist.github.com/Araxeus/6548ef4f3438d35d1713d5fd69c3b494

### Additional Information

* test repo: https://github.com/Aetherinox/electron-prompts
* related issue: https://github.com/Araxeus/custom-electron-prompt/issues/26


Discussion / hints:
<details><summary>Stacktrace on macOS</summary>
<p>

```
[80356:0722/110229.619327:FATAL:lock_impl_posix.cc(46)] Check failed: rv == 0 || rv == EBUSY. . Invalid argument. Hint: This is often related to a use-after-free.
0   Electron Framework                  0x000000011b902974 base::debug::CollectStackTrace(void const**, unsigned long) + 28
1   Electron Framework                  0x000000011b8f225c base::debug::StackTrace::StackTrace() + 80
2   Electron Framework                  0x000000011b821800 logging::LogMessage::Flush() + 152
3   Electron Framework                  0x000000011b8216f4 logging::LogMessage::~LogMessage() + 36
4   Electron Framework                  0x000000011b80a568 logging::(anonymous namespace)::DCheckLogMessage::~DCheckLogMessage() + 124
5   Electron Framework                  0x000000011b80a5b8 logging::(anonymous namespace)::DCheckLogMessage::~DCheckLogMessage() + 12
6   Electron Framework                  0x000000011b80a034 logging::CheckError::~CheckError() + 44
7   Electron Framework                  0x000000011b80a09c logging::CheckError::~CheckError() + 12
8   Electron Framework                  0x000000011b8f0594 base::internal::dcheck_trylock_result(int) + 176
9   Electron Framework                  0x000000011b8720c4 base::Lock::Acquire(base::subtle::LockTracking) + 44
10  Electron Framework                  0x000000011b85c204 base::SequenceCheckerImpl::CalledOnValidSequence(std::__Cr::unique_ptr<base::debug::StackTrace, std::__Cr::default_delete<base::debug::StackTrace>>*) const + 44
11  Electron Framework                  0x000000011b85be80 base::ScopedValidateSequenceChecker::ScopedValidateSequenceChecker(base::SequenceCheckerImpl const&) + 40
12  Electron Framework                  0x000000011661bdc0 electron::NativeWindow::NotifyWindowCloseButtonClicked() + 624
13  Electron Framework                  0x000000011672c2fc -[ElectronNSWindowDelegate windowShouldClose:] + 208
14  AppKit                              0x0000000194831d9c __19-[NSWindow __close]_block_invoke + 160
15  AppKit                              0x0000000194831ccc -[NSWindow __close] + 400
16  AppKit                              0x00000001947120e8 -[NSApplication(NSResponder) sendAction:to:from:] + 460
17  AppKit                              0x0000000194711eec -[NSControl sendAction:to:] + 72
18  AppKit                              0x0000000194711e30 __26-[NSCell _sendActionFrom:]_block_invoke + 100
19  AppKit                              0x0000000194711d58 -[NSCell _sendActionFrom:] + 204
20  AppKit                              0x0000000194711c7c -[NSButtonCell _sendActionFrom:] + 88
21  AppKit                              0x000000019470f268 NSControlTrackMouse + 1480
22  AppKit                              0x000000019470ec74 -[NSCell trackMouse:inRect:ofView:untilMouseUp:] + 144
23  AppKit                              0x000000019470eb2c -[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] + 488
24  AppKit                              0x000000019470e000 -[NSControl mouseDown:] + 448
25  AppKit                              0x000000019470cdcc -[NSWindow(NSEventRouting) _handleMouseDownEvent:isDelayedEvent:] + 3472
26  AppKit                              0x000000019469840c -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 288
27  AppKit                              0x0000000194698118 -[NSWindow(NSEventRouting) sendEvent:] + 284
28  Electron Framework                  0x000000011d53614c -[NativeWidgetMacNSWindow sendEvent:] + 604
29  AppKit                              0x0000000194d60828 -[NSApplication(NSEventRouting) sendEvent:] + 1604
30  Electron Framework                  0x000000011670bc6c -[AtomApplication sendEvent:] + 128
31  AppKit                              0x00000001949ae89c -[NSApplication _handleEvent:] + 60
32  AppKit                              0x000000019455f0c0 -[NSApplication run] + 512
33  Electron Framework                  0x000000011b910b9c base::MessagePumpNSApplication::DoRun(base::MessagePump::Delegate*) + 396
34  Electron Framework                  0x000000011b90eabc base::MessagePumpCFRunLoopBase::Run(base::MessagePump::Delegate*) + 112
35  Electron Framework                  0x000000011b8a8210 base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::Run(bool, base::TimeDelta) + 692
36  Electron Framework                  0x000000011b856af8 base::RunLoop::Run(base::Location const&) + 952
37  Electron Framework                  0x000000011a31f818 content::BrowserMainLoop::RunMainMessageLoop() + 180
38  Electron Framework                  0x000000011a321740 content::BrowserMainRunnerImpl::Run() + 48
39  Electron Framework                  0x000000011a31c278 content::BrowserMain(content::MainFunctionParams) + 164
40  Electron Framework                  0x00000001168c6e0c content::RunBrowserProcessMain(content::MainFunctionParams, content::ContentMainDelegate*) + 176
41  Electron Framework                  0x00000001168c93c0 content::ContentMainRunnerImpl::RunBrowser(content::MainFunctionParams, bool) + 1824
42  Electron Framework                  0x00000001168c8b78 content::ContentMainRunnerImpl::Run() + 952
43  Electron Framework                  0x00000001168c64a8 content::RunContentProcess(content::ContentMainParams, content::ContentMainRunner*) + 1200
44  Electron Framework                  0x00000001168c66f0 content::ContentMain(content::ContentMainParams) + 112
45  Electron Framework                  0x00000001164b2c98 ElectronMain + 320
46  dyld                                0x00000001908a60e0 start + 2360
Crash keys:
  "ever_had_universal_access_exemption" = "true"
  "amfi-status" = "rv=0 status=0x0 allow_everything=0"
  "platform" = "darwin"
  "process_type" = "browser"

Electron exited with signal SIGTRAP.
```

</p>
</details> 

Tracked to https://github.com/electron/electron/commit/e67ab9a93dadccecff30de50ab4555191c30b6c4
Closed by https://github.com/electron/electron/pull/43033
> Closed by https://github.com/electron/electron/pull/43033

It says the PR is for MacOS but this issue was reported on Windows?

Fix patch:
diff --git a/filenames.gni b/filenames.gni
index 99c80b915a02d..9a950d0e1955c 100644
--- a/filenames.gni
+++ b/filenames.gni
@@ -159,12 +159,8 @@ filenames = {
     "shell/browser/osr/osr_web_contents_view_mac.mm",
     "shell/browser/relauncher_mac.cc",
     "shell/browser/ui/certificate_trust_mac.mm",
-    "shell/browser/ui/cocoa/delayed_native_view_host.h",
-    "shell/browser/ui/cocoa/delayed_native_view_host.mm",
     "shell/browser/ui/cocoa/electron_bundle_mover.h",
     "shell/browser/ui/cocoa/electron_bundle_mover.mm",
-    "shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h",
-    "shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm",
     "shell/browser/ui/cocoa/electron_menu_controller.h",
     "shell/browser/ui/cocoa/electron_menu_controller.mm",
     "shell/browser/ui/cocoa/electron_native_widget_mac.h",
@@ -191,8 +187,6 @@ filenames = {
     "shell/browser/ui/cocoa/window_buttons_proxy.mm",
     "shell/browser/ui/drag_util_mac.mm",
     "shell/browser/ui/file_dialog_mac.mm",
-    "shell/browser/ui/inspectable_web_contents_view_mac.h",
-    "shell/browser/ui/inspectable_web_contents_view_mac.mm",
     "shell/browser/ui/message_box_mac.mm",
     "shell/browser/ui/tray_icon_cocoa.h",
     "shell/browser/ui/tray_icon_cocoa.mm",
@@ -224,8 +218,6 @@ filenames = {
     "shell/browser/ui/views/electron_views_delegate.h",
     "shell/browser/ui/views/frameless_view.cc",
     "shell/browser/ui/views/frameless_view.h",
-    "shell/browser/ui/views/inspectable_web_contents_view_views.cc",
-    "shell/browser/ui/views/inspectable_web_contents_view_views.h",
     "shell/browser/ui/views/menu_bar.cc",
     "shell/browser/ui/views/menu_bar.h",
     "shell/browser/ui/views/menu_delegate.cc",
diff --git a/patches/chromium/.patches b/patches/chromium/.patches
index dc05787535848..939c67537716a 100644
--- a/patches/chromium/.patches
+++ b/patches/chromium/.patches
@@ -133,6 +133,8 @@ osr_shared_texture_remove_keyed_mutex_on_win_dxgi.patch
 feat_allow_usage_of_sccontentsharingpicker_on_supported_platforms.patch
 chore_partial_revert_of.patch
 fix_software_compositing_infinite_loop.patch
+fix_add_method_which_disables_headless_mode_on_native_widget.patch
+fix_put_nsvisualeffectview_before_viewscompositorsuperview.patch
 refactor_unfilter_unresponsive_events.patch
 build_disable_thin_lto_mac.patch
 build_add_public_config_simdutf_config.patch
diff --git a/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch b/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch
new file mode 100644
index 0000000000000..95d22359148d2
--- /dev/null
+++ b/patches/chromium/fix_add_method_which_disables_headless_mode_on_native_widget.patch
@@ -0,0 +1,26 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Cezary Kulakowski <cezary@openfin.co>
+Date: Mon, 22 Jul 2024 16:23:13 +0200
+Subject: fix: add method which disables headless mode on native widget
+
+We need this method as we create window in headless mode and we
+switch it back to normal mode only after inital paint is done in
+order to get some events like WebContents.beginFrameSubscription.
+If we don't set `is_headless_` to false then some child windows
+e.g. autofill popups will be created in headless mode leading to
+ui problems (like dissapearing popup during typing in html's
+input list.
+
+diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h
+index 30d0ea6633cb0f7f9aab37951a38be9b0482a12a..734e91e6d50c8d3afd20b39167c6254e934e7c1e 100644
+--- a/ui/views/widget/widget.h
++++ b/ui/views/widget/widget.h
+@@ -1144,6 +1144,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
+   // True if widget was created in headless mode.
+   bool is_headless() const { return is_headless_; }
+ 
++  void DisableHeadlessMode() { is_headless_ = false; }
++
+   // True if the window size will follow the content preferred size.
+   bool is_autosized() const { return is_autosized_; }
+ 
diff --git a/patches/chromium/fix_put_nsvisualeffectview_before_viewscompositorsuperview.patch b/patches/chromium/fix_put_nsvisualeffectview_before_viewscompositorsuperview.patch
new file mode 100644
index 0000000000000..d7e0977944197
--- /dev/null
+++ b/patches/chromium/fix_put_nsvisualeffectview_before_viewscompositorsuperview.patch
@@ -0,0 +1,36 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Micha=C5=82=20Pichli=C5=84ski?=
+ <michal.pichlinski@openfin.co>
+Date: Tue, 29 Oct 2024 21:16:29 +0100
+Subject: fix: Put NSVisualEffectView before ViewsCompositorSuperview
+
+Upstreamed at https://chromium-review.googlesource.com/c/chromium/src/+/6030552
+
+Otherwise when using `vibrancy` in `BrowserWindow` NSVisualEffectView
+will hide content displayed by the compositor.
+
+diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
+index 07c3997e6565cf77362ee73959c4d21da4fefe96..3353a7847df90b58eec34ea4d6ff8fb19617f5cc 100644
+--- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
++++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
+@@ -223,8 +223,19 @@ NSComparisonResult SubviewSorter(__kindof NSView* lhs,
+                                  void* rank_as_void) {
+   DCHECK_NE(lhs, rhs);
+ 
+-  if ([lhs isKindOfClass:[ViewsCompositorSuperview class]])
++
++  // Put NSVisualEffectView before ViewsCompositorSuperview otherwise when using
++  // `vibrancy` in `BrowserWindow` NSVisualEffectView will hide content
++  // displayed by the compositor.
++  if ([lhs isKindOfClass:[NSVisualEffectView class]]) {
+     return NSOrderedAscending;
++  }
++  if ([lhs isKindOfClass:[ViewsCompositorSuperview class]]) {
++    if ([rhs isKindOfClass:[NSVisualEffectView class]]) {
++      return NSOrderedDescending;
++    }
++    return NSOrderedAscending;
++  }
+ 
+   const RankMap* rank = static_cast<const RankMap*>(rank_as_void);
+   auto left_rank = rank->find(lhs);
diff --git a/shell/browser/api/electron_api_web_contents_view.cc b/shell/browser/api/electron_api_web_contents_view.cc
index 644ea373a7287..f76209fbdf5be 100644
--- a/shell/browser/api/electron_api_web_contents_view.cc
+++ b/shell/browser/api/electron_api_web_contents_view.cc
@@ -27,29 +27,14 @@
 #include "ui/views/view_class_properties.h"
 #include "ui/views/widget/widget.h"
 
-#if BUILDFLAG(IS_MAC)
-#include "shell/browser/ui/cocoa/delayed_native_view_host.h"
-#endif
-
 namespace electron::api {
 
 WebContentsView::WebContentsView(v8::Isolate* isolate,
                                  gin::Handle<WebContents> web_contents)
-#if BUILDFLAG(IS_MAC)
-    : View(new DelayedNativeViewHost(web_contents->inspectable_web_contents()
-                                         ->GetView()
-                                         ->GetNativeView())),
-#else
-    : View(web_contents->inspectable_web_contents()->GetView()->GetView()),
-#endif
+    : View(web_contents->inspectable_web_contents()->GetView()),
       web_contents_(isolate, web_contents.ToV8()),
       api_web_contents_(web_contents.get()) {
-#if !BUILDFLAG(IS_MAC)
-  // On macOS the View is a newly-created |DelayedNativeViewHost| and it is our
-  // responsibility to delete it. On other platforms the View is created and
-  // managed by InspectableWebContents.
   set_delete_view(false);
-#endif
   view()->SetProperty(
       views::kFlexBehaviorKey,
       views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToMinimum,
diff --git a/shell/browser/native_window_mac.h b/shell/browser/native_window_mac.h
index d153dec7f87d5..c49cdc9635932 100644
--- a/shell/browser/native_window_mac.h
+++ b/shell/browser/native_window_mac.h
@@ -169,9 +169,6 @@ class NativeWindowMac : public NativeWindow,
   void NotifyWindowDidFailToEnterFullScreen();
   void NotifyWindowWillLeaveFullScreen();
 
-  // views::WidgetDelegate:
-  views::View* GetContentsView() override;
-
   // Cleanup observers when window is getting closed. Note that the destructor
   // can be called much later after window gets closed, so we should not do
   // cleanup in destructor.
@@ -223,6 +220,7 @@ class NativeWindowMac : public NativeWindow,
 
  protected:
   // views::WidgetDelegate:
+  views::View* GetContentsView() override;
   bool CanMaximize() const override;
   std::unique_ptr<views::NonClientFrameView> CreateNonClientFrameView(
       views::Widget* widget) override;
diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm
index dc483ee3fd3aa..2acea04280688 100644
--- a/shell/browser/native_window_mac.mm
+++ b/shell/browser/native_window_mac.mm
@@ -194,6 +194,8 @@ static bool FromV8(v8::Isolate* isolate,
   params.bounds = bounds;
   params.delegate = this;
   params.type = views::Widget::InitParams::TYPE_WINDOW;
+  // Allow painting before shown, to be later disabled in ElectronNSWindow.
+  params.headless_mode = true;
   params.native_widget =
       new ElectronNativeWidgetMac(this, windowType, styleMask, widget());
   widget()->Init(std::move(params));
@@ -1674,6 +1676,7 @@ static bool FromV8(v8::Isolate* isolate,
   DCHECK(!IsClosed());
   ui::NativeTheme::GetInstanceForNativeUi()->RemoveObserver(this);
   display::Screen::GetScreen()->RemoveObserver(this);
+  [window_ cleanup];
 }
 
 class NativeAppWindowFrameViewMac : public views::NativeFrameViewMac {
diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc
index 1f6c3957c45ec..d5118ec346a8f 100644
--- a/shell/browser/native_window_views.cc
+++ b/shell/browser/native_window_views.cc
@@ -27,6 +27,7 @@
 #include "content/public/browser/desktop_media_id.h"
 #include "content/public/common/color_parser.h"
 #include "shell/browser/api/electron_api_web_contents.h"
+#include "shell/browser/ui/inspectable_web_contents_view.h"
 #include "shell/browser/ui/views/root_view.h"
 #include "shell/browser/web_contents_preferences.h"
 #include "shell/browser/web_view_manager.h"
diff --git a/shell/browser/ui/cocoa/delayed_native_view_host.h b/shell/browser/ui/cocoa/delayed_native_view_host.h
deleted file mode 100644
index e7020dba60715..0000000000000
--- a/shell/browser/ui/cocoa/delayed_native_view_host.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (c) 2018 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#ifndef ELECTRON_SHELL_BROWSER_UI_COCOA_DELAYED_NATIVE_VIEW_HOST_H_
-#define ELECTRON_SHELL_BROWSER_UI_COCOA_DELAYED_NATIVE_VIEW_HOST_H_
-
-#include "ui/views/controls/native/native_view_host.h"
-
-namespace electron {
-
-// Automatically attach the native view after the NativeViewHost is attached to
-// a widget. (Attaching it directly would cause crash.)
-class DelayedNativeViewHost : public views::NativeViewHost {
- public:
-  explicit DelayedNativeViewHost(gfx::NativeView native_view);
-  ~DelayedNativeViewHost() override;
-
-  // disable copy
-  DelayedNativeViewHost(const DelayedNativeViewHost&) = delete;
-  DelayedNativeViewHost& operator=(const DelayedNativeViewHost&) = delete;
-
-  // views::View:
-  void ViewHierarchyChanged(
-      const views::ViewHierarchyChangedDetails& details) override;
-
- private:
-  gfx::NativeView native_view_;
-};
-
-}  // namespace electron
-
-#endif  // ELECTRON_SHELL_BROWSER_UI_COCOA_DELAYED_NATIVE_VIEW_HOST_H_
diff --git a/shell/browser/ui/cocoa/delayed_native_view_host.mm b/shell/browser/ui/cocoa/delayed_native_view_host.mm
deleted file mode 100644
index 38b0796555e1f..0000000000000
--- a/shell/browser/ui/cocoa/delayed_native_view_host.mm
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2018 GitHub, Inc.
-// Use of this source code is governed by the MIT license that can be
-// found in the LICENSE file.
-
-#include "shell/browser/ui/cocoa/delayed_native_view_host.h"
-
-namespace electron {
-
-DelayedNativeViewHost::DelayedNativeViewHost(gfx::NativeView native_view)
-    : native_view_(native_view) {}
-
-DelayedNativeViewHost::~DelayedNativeViewHost() = default;
-
-void DelayedNativeViewHost::ViewHierarchyChanged(
-    const views::ViewHierarchyChangedDetails& details) {
-  if (!details.is_add && native_view())
-    Detach();
-  NativeViewHost::ViewHierarchyChanged(details);
-  if (details.is_add && GetWidget() && !native_view())
-    Attach(native_view_);
-}
-
-}  // namespace electron
diff --git a/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h b/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h
deleted file mode 100644
index 4286312573aab..0000000000000
--- a/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE-CHROMIUM file.
-
-#ifndef ELECTRON_SHELL_BROWSER_UI_COCOA_ELECTRON_INSPECTABLE_WEB_CONTENTS_VIEW_H_
-#define ELECTRON_SHELL_BROWSER_UI_COCOA_ELECTRON_INSPECTABLE_WEB_CONTENTS_VIEW_H_
-
-#import <AppKit/AppKit.h>
-
-#include "base/apple/owned_objc.h"
-#include "base/memory/raw_ptr.h"
-#include "chrome/browser/devtools/devtools_contents_resizing_strategy.h"
-#include "ui/base/cocoa/base_view.h"
-
-namespace electron {
-class InspectableWebContentsViewMac;
-}
-
-using electron::InspectableWebContentsViewMac;
-
-@interface NSView (WebContentsView)
-- (void)setMouseDownCanMoveWindow:(BOOL)can_move;
-@end
-
-@interface ElectronInspectableWebContentsView : BaseView <NSWindowDelegate> {
- @private
-  raw_ptr<electron::InspectableWebContentsViewMac> inspectableWebContentsView_;
-
-  NSView* __strong fake_view_;
-  NSWindow* __strong devtools_window_;
-  BOOL devtools_visible_;
-  BOOL devtools_docked_;
-  BOOL devtools_is_first_responder_;
-  BOOL attached_to_window_;
-
-  DevToolsContentsResizingStrategy strategy_;
-}
-
-- (instancetype)initWithInspectableWebContentsViewMac:
-    (InspectableWebContentsViewMac*)view;
-- (void)notifyDevToolsFocused;
-- (void)setCornerRadii:(CGFloat)cornerRadius;
-- (void)setDevToolsVisible:(BOOL)visible activate:(BOOL)activate;
-- (BOOL)isDevToolsVisible;
-- (BOOL)isDevToolsFocused;
-- (void)setIsDocked:(BOOL)docked activate:(BOOL)activate;
-- (void)setContentsResizingStrategy:
-    (const DevToolsContentsResizingStrategy&)strategy;
-- (void)setTitle:(NSString*)title;
-- (NSString*)getTitle;
-
-@end
-
-#endif  // ELECTRON_SHELL_BROWSER_UI_COCOA_ELECTRON_INSPECTABLE_WEB_CONTENTS_VIEW_H_
diff --git a/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm b/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm
deleted file mode 100644
index d719fc164e476..0000000000000
--- a/shell/browser/ui/cocoa/electron_inspectable_web_contents_view.mm
+++ /dev/null
@@ -1,337 +0,0 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE-CHROMIUM file.
-
-#include "shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h"
-
-#include "content/public/browser/render_widget_host_view.h"
-#include "shell/browser/api/electron_api_web_contents.h"
-#include "shell/browser/ui/cocoa/event_dispatching_window.h"
-#include "shell/browser/ui/inspectable_web_contents.h"
-#include "shell/browser/ui/inspectable_web_contents_view_delegate.h"
-#include "shell/browser/ui/inspectable_web_contents_view_mac.h"
-#include "ui/base/cocoa/base_view.h"
-#include "ui/gfx/mac/scoped_cocoa_disable_screen_updates.h"
-
-@implementation ElectronInspectableWebContentsView
-
-- (instancetype)initWithInspectableWebContentsViewMac:
-    (InspectableWebContentsViewMac*)view {
-  self = [super init];
-  if (!self)
-    return nil;
-
-  inspectableWebContentsView_ = view;
-  devtools_visible_ = NO;
-  devtools_docked_ = NO;
-  devtools_is_first_responder_ = NO;
-  attached_to_window_ = NO;
-
-  if (inspectableWebContentsView_->inspectable_web_contents()->is_guest()) {
-    fake_view_ = [[NSView alloc] init];
-    [fake_view_ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
-    [self addSubview:fake_view_];
-  } else {
-    auto* contents = inspectableWebContentsView_->inspectable_web_contents()
-                         ->GetWebContents();
-    auto* contentsView = contents->GetNativeView().GetNativeNSView();
-    [contentsView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
-    [self addSubview:contentsView];
-  }
-
-  // See https://code.google.com/p/chromium/issues/detail?id=348490.
-  [self setWantsLayer:YES];
-
-  return self;
-}
-
-- (void)dealloc {
-  [[NSNotificationCenter defaultCenter] removeObserver:self];
-}
-
-- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize {
-  [self adjustSubviews];
-}
-
-- (void)viewDidMoveToWindow {
-  if (attached_to_window_ && !self.window) {
-    attached_to_window_ = NO;
-    [[NSNotificationCenter defaultCenter] removeObserver:self];
-  } else if (!attached_to_window_ && self.window) {
-    attached_to_window_ = YES;
-    [[NSNotificationCenter defaultCenter]
-        addObserver:self
-           selector:@selector(viewDidBecomeFirstResponder:)
-               name:kViewDidBecomeFirstResponder
-             object:nil];
-    [[NSNotificationCenter defaultCenter]
-        addObserver:self
-           selector:@selector(parentWindowBecameMain:)
-               name:NSWindowDidBecomeMainNotification
-             object:nil];
-  }
-}
-
-- (IBAction)showDevTools:(id)sender {
-  inspectableWebContentsView_->inspectable_web_contents()->ShowDevTools(true);
-}
-
-- (void)notifyDevToolsFocused {
-  if (inspectableWebContentsView_->GetDelegate())
-    inspectableWebContentsView_->GetDelegate()->DevToolsFocused();
-}
-
-- (void)setCornerRadii:(CGFloat)cornerRadius {
-  auto* inspectable_web_contents =
-      inspectableWebContentsView_->inspectable_web_contents();
-  DCHECK(inspectable_web_contents);
-  auto* webContents = inspectable_web_contents->GetWebContents();
-  if (!webContents)
-    return;
-  auto* webContentsView = webContents->GetNativeView().GetNativeNSView();
-  webContentsView.wantsLayer = YES;
-  webContentsView.layer.cornerRadius = cornerRadius;
-}
-
-- (void)notifyDevToolsResized {
-  // When devtools is opened, resizing devtools would not trigger
-  // UpdateDraggableRegions for WebContents, so we have to notify the window
-  // to do an update of draggable regions.
-  if (inspectableWebContentsView_->GetDelegate())
-    inspectableWebContentsView_->GetDelegate()->DevToolsResized();
-}
-
-- (void)setDevToolsVisible:(BOOL)visible activate:(BOOL)activate {
-  if (visible == devtools_visible_)
-    return;
-
-  auto* inspectable_web_contents =
-      inspectableWebContentsView_->inspectable_web_contents();
-  auto* devToolsWebContents =
-      inspectable_web_contents->GetDevToolsWebContents();
-  auto* devToolsView = devToolsWebContents->GetNativeView().GetNativeNSView();
-
-  devtools_visible_ = visible;
-  if (devtools_docked_) {
-    if (visible) {
-      // Place the devToolsView under contentsView, notice that we didn't set
-      // sizes for them until the setContentsResizingStrategy message.
-      [self addSubview:devToolsView positioned:NSWindowBelow relativeTo:nil];
-      [self adjustSubviews];
-
-      // Focus on web view.
-      devToolsWebContents->RestoreFocus();
-    } else {
-      gfx::ScopedCocoaDisableScreenUpdates disabler;
-      [devToolsView removeFromSuperview];
-      [self adjustSubviews];
-      [self notifyDevToolsResized];
-    }
-  } else {
-    if (visible) {
-      if (activate) {
-        [devtools_window_ makeKeyAndOrderFront:nil];
-      } else {
-        [devtools_window_ orderBack:nil];
-      }
-    } else {
-      [devtools_window_ setDelegate:nil];
-      [devtools_window_ close];
-      devtools_window_ = nil;
-    }
-  }
-}
-
-- (BOOL)isDevToolsVisible {
-  return devtools_visible_;
-}
-
-- (BOOL)isDevToolsFocused {
-  if (devtools_docked_) {
-    return [[self window] isKeyWindow] && devtools_is_first_responder_;
-  } else {
-    return [devtools_window_ isKeyWindow];
-  }
-}
-
-// TODO: remove NSWindowStyleMaskTexturedBackground.
-// https://github.com/electron/electron/issues/43125
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-
-- (void)setIsDocked:(BOOL)docked activate:(BOOL)activate {
-  // Revert to no-devtools state.
-  [self setDevToolsVisible:NO activate:NO];
-
-  // Switch to new state.
-  devtools_docked_ = docked;
-  auto* inspectable_web_contents =
-      inspectableWebContentsView_->inspectable_web_contents();
-  auto* devToolsWebContents =
-      inspectable_web_contents->GetDevToolsWebContents();
-  auto devToolsView = devToolsWebContents->GetNativeView().GetNativeNSView();
-  if (!docked) {
-    auto styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
-                     NSWindowStyleMaskMiniaturizable |
-                     NSWindowStyleMaskResizable |
-                     NSWindowStyleMaskTexturedBackground |
-                     NSWindowStyleMaskUnifiedTitleAndToolbar;
-    devtools_window_ = [[EventDispatchingWindow alloc]
-        initWithContentRect:NSMakeRect(0, 0, 800, 600)
-                  styleMask:styleMask
-                    backing:NSBackingStoreBuffered
-                      defer:YES];
-    [devtools_window_ setDelegate:self];
-    [devtools_window_ setFrameAutosaveName:@"electron.devtools"];
-    [devtools_window_ setTitle:@"Developer Tools"];
-    [devtools_window_ setReleasedWhenClosed:NO];
-    [devtools_window_ setAutorecalculatesContentBorderThickness:NO
-                                                        forEdge:NSMaxYEdge];
-    [devtools_window_ setContentBorderThickness:24 forEdge:NSMaxYEdge];
-
-    NSView* contentView = [devtools_window_ contentView];
-    devToolsView.frame = contentView.bounds;
-    devToolsView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
-
-    [contentView addSubview:devToolsView];
-    [devToolsView setMouseDownCanMoveWindow:NO];
-  } else {
-    [devToolsView setMouseDownCanMoveWindow:YES];
-  }
-  [self setDevToolsVisible:YES activate:activate];
-}
-
-// -Wdeprecated-declarations
-#pragma clang diagnostic pop
-
-- (void)setContentsResizingStrategy:
-    (const DevToolsContentsResizingStrategy&)strategy {
-  strategy_.CopyFrom(strategy);
-  [self adjustSubviews];
-}
-
-- (void)adjustSubviews {
-  if (![[self subviews] count])
-    return;
-
-  if (![self isDevToolsVisible] || devtools_window_) {
-    DCHECK_EQ(1u, [[self subviews] count]);
-    NSView* contents = [[self subviews] objectAtIndex:0];
-    [contents setFrame:[self bounds]];
-    return;
-  }
-
-  NSView* devToolsView = [[self subviews] objectAtIndex:0];
-  NSView* contentsView = [[self subviews] objectAtIndex:1];
-
-  DCHECK_EQ(2u, [[self subviews] count]);
-
-  gfx::Rect new_devtools_bounds;
-  gfx::Rect new_contents_bounds;
-  ApplyDevToolsContentsResizingStrategy(
-      strategy_, gfx::Size(NSSizeToCGSize([self bounds].size)),
-      &new_devtools_bounds, &new_contents_bounds);
-  [devToolsView setFrame:[self flipRectToNSRect:new_devtools_bounds]];
-  [contentsView setFrame:[self flipRectToNSRect:new_contents_bounds]];
-
-  // Move mask to the devtools area to exclude it from dragging.
-  NSRect cf = contentsView.frame;
-  NSRect sb = [self bounds];
-  NSRect devtools_frame;
-  if (cf.size.height < sb.size.height) {  // bottom docked
-    devtools_frame.origin.x = 0;
-    devtools_frame.origin.y = 0;
-    devtools_frame.size.width = sb.size.width;
-    devtools_frame.size.height = sb.size.height - cf.size.height;
-  } else {                // left or right docked
-    if (cf.origin.x > 0)  // left docked
-      devtools_frame.origin.x = 0;
-    else  // right docked.
-      devtools_frame.origin.x = cf.size.width;
-    devtools_frame.origin.y = 0;
-    devtools_frame.size.width = sb.size.width - cf.size.width;
-    devtools_frame.size.height = sb.size.height;
-  }
-
-  [self notifyDevToolsResized];
-}
-
-- (void)setTitle:(NSString*)title {
-  [devtools_window_ setTitle:title];
-}
-
-- (NSString*)getTitle {
-  return [devtools_window_ title];
-}
-
-- (void)viewDidBecomeFirstResponder:(NSNotification*)notification {
-  auto* inspectable_web_contents =
-      inspectableWebContentsView_->inspectable_web_contents();
-  DCHECK(inspectable_web_contents);
-  auto* webContents = inspectable_web_contents->GetWebContents();
-  if (!webContents)
-    return;
-  auto* webContentsView = webContents->GetNativeView().GetNativeNSView();
-
-  NSView* view = [notification object];
-  if ([[webContentsView subviews] containsObject:view]) {
-    devtools_is_first_responder_ = NO;
-    return;
-  }
-
-  auto* devToolsWebContents =
-      inspectable_web_contents->GetDevToolsWebContents();
-  if (!devToolsWebContents)
-    return;
-  auto devToolsView = devToolsWebContents->GetNativeView().GetNativeNSView();
-
-  if ([[devToolsView subviews] containsObject:view]) {
-    devtools_is_first_responder_ = YES;
-    [self notifyDevToolsFocused];
-  }
-}
-
-- (void)parentWindowBecameMain:(NSNotification*)notification {
-  NSWindow* parentWindow = [notification object];
-  if ([self window] == parentWindow && devtools_docked_ &&
-      devtools_is_first_responder_)
-    [self notifyDevToolsFocused];
-}
-
-#pragma mark - NSWindowDelegate
-
-- (void)windowWillClose:(NSNotification*)notification {
-  inspectableWebContentsView_->inspectable_web_contents()->CloseDevTools();
-}
-
-- (void)windowDidBecomeMain:(NSNotification*)notification {
-  content::WebContents* web_contents =
-      inspectableWebContentsView_->inspectable_web_contents()
-          ->GetDevToolsWebContents();
-  if (!web_contents)
-    return;
-
-  web_contents->RestoreFocus();
-
-  content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView();
-  if (rwhv)
-    rwhv->SetActive(true);
-
-  [self notifyDevToolsFocused];
-}
-
-- (void)windowDidResignMain:(NSNotification*)notification {
-  content::WebContents* web_contents =
-      inspectableWebContentsView_->inspectable_web_contents()
-          ->GetDevToolsWebContents();
-  if (!web_contents)
-    return;
-
-  web_contents->StoreFocus();
-
-  content::RenderWidgetHostView* rwhv = web_contents->GetRenderWidgetHostView();
-  if (rwhv)
-    rwhv->SetActive(false);
-}
-
-@end
diff --git a/shell/browser/ui/cocoa/electron_ns_window.h b/shell/browser/ui/cocoa/electron_ns_window.h
index 502592313c15d..63ea5355467d4 100644
--- a/shell/browser/ui/cocoa/electron_ns_window.h
+++ b/shell/browser/ui/cocoa/electron_ns_window.h
@@ -29,6 +29,8 @@ class ScopedDisableResize {
 
 }  // namespace electron
 
+class ElectronNativeWindowObserver;
+
 @interface ElectronNSWindow : NativeWidgetMacNSWindow {
  @private
   raw_ptr<electron::NativeWindowMac> shell_;
@@ -41,6 +43,7 @@ class ScopedDisableResize {
 @property(nonatomic, retain) NSImage* cornerMask;
 - (id)initWithShell:(electron::NativeWindowMac*)shell
           styleMask:(NSUInteger)styleMask;
+- (void)cleanup;
 - (electron::NativeWindowMac*)shell;
 - (id)accessibilityFocusedUIElement;
 - (NSRect)originalContentRectForFrameRect:(NSRect)frameRect;
diff --git a/shell/browser/ui/cocoa/electron_ns_window.mm b/shell/browser/ui/cocoa/electron_ns_window.mm
index 5c5f1512f4a3e..30780277d3a5c 100644
--- a/shell/browser/ui/cocoa/electron_ns_window.mm
+++ b/shell/browser/ui/cocoa/electron_ns_window.mm
@@ -8,8 +8,6 @@
 #include "electron/mas.h"
 #include "shell/browser/api/electron_api_web_contents.h"
 #include "shell/browser/native_window_mac.h"
-#include "shell/browser/ui/cocoa/delayed_native_view_host.h"
-#include "shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h"
 #include "shell/browser/ui/cocoa/electron_preview_item.h"
 #include "shell/browser/ui/cocoa/electron_touch_bar.h"
 #include "shell/browser/ui/cocoa/root_view_mac.h"
@@ -113,6 +111,7 @@ void SwizzleSwipeWithEvent(NSView* view, SEL swiz_selector) {
                            method_getImplementation(new_swipe_with_event));
 }
 #endif
+
 }  // namespace
 
 @implementation ElectronNSWindow
@@ -168,6 +167,10 @@ - (id)initWithShell:(electron::NativeWindowMac*)shell
   return self;
 }
 
+- (void)cleanup {
+  shell_ = nullptr;
+}
+
 - (electron::NativeWindowMac*)shell {
   return shell_;
 }
@@ -255,6 +258,17 @@ - (void)setFrame:(NSRect)windowFrame display:(BOOL)displayViews {
     [super setFrame:windowFrame display:displayViews];
 }
 
+- (void)orderWindow:(NSWindowOrderingMode)place relativeTo:(NSInteger)otherWin {
+  if (shell_) {
+    // We initialize the window in headless mode to allow painting before it is
+    // shown, but we don't want the headless behavior of allowing the window to
+    // be placed unconstrained.
+    self.isHeadless = false;
+    shell_->widget()->DisableHeadlessMode();
+  }
+  [super orderWindow:place relativeTo:otherWin];
+}
+
 - (id)accessibilityAttributeValue:(NSString*)attribute {
   if ([attribute isEqual:NSAccessibilityEnabledAttribute])
     return [NSNumber numberWithBool:YES];
diff --git a/shell/browser/ui/cocoa/electron_ns_window_delegate.mm b/shell/browser/ui/cocoa/electron_ns_window_delegate.mm
index d6af0ac6d9bb8..3f7c689bfc8b0 100644
--- a/shell/browser/ui/cocoa/electron_ns_window_delegate.mm
+++ b/shell/browser/ui/cocoa/electron_ns_window_delegate.mm
@@ -365,6 +365,19 @@ - (void)windowWillClose:(NSNotification*)notification {
       shell_->GetNativeWindow());
   auto* bridged_view = bridge_host->GetInProcessNSWindowBridge();
   bridged_view->OnWindowWillClose();
+
+  // Native widget and its compositor have been destroyed upon close. We need
+  // to detach contents view in order to prevent reusing its layer without
+  // compositor in the `WebContentsViewMac::CreateViewForWidget`, leading to
+  // `DCHECK` failure in `BrowserCompositorMac::SetParentUiLayer`.
+  auto* contents_view =
+      static_cast<views::WidgetDelegate*>(shell_)->GetContentsView();
+  if (contents_view) {
+    auto* parent = contents_view->parent();
+    if (parent) {
+      parent->RemoveChildView(contents_view);
+    }
+  }
 }
 
 - (BOOL)windowShouldClose:(id)window {
diff --git a/shell/browser/ui/inspectable_web_contents.cc b/shell/browser/ui/inspectable_web_contents.cc
index ea02168ef5596..75ebdd6441429 100644
--- a/shell/browser/ui/inspectable_web_contents.cc
+++ b/shell/browser/ui/inspectable_web_contents.cc
@@ -297,11 +297,6 @@ class InspectableWebContents::NetworkResourceLoader
   base::TimeDelta retry_delay_;
 };
 
-// Implemented separately on each platform.
-InspectableWebContentsView* CreateInspectableContentsView(
-    InspectableWebContents* inspectable_web_contents);
-
-// static
 // static
 void InspectableWebContents::RegisterPrefs(PrefRegistrySimple* registry) {
   registry->RegisterDictionaryPref(kDevToolsBoundsPref,
@@ -317,7 +312,7 @@ InspectableWebContents::InspectableWebContents(
     : pref_service_(pref_service),
       web_contents_(std::move(web_contents)),
       is_guest_(is_guest),
-      view_(CreateInspectableContentsView(this)) {
+      view_(new InspectableWebContentsView(this)) {
   const base::Value* bounds_dict =
       &pref_service_->GetValue(kDevToolsBoundsPref);
   if (bounds_dict->is_dict()) {
diff --git a/shell/browser/ui/inspectable_web_contents_view.cc b/shell/browser/ui/inspectable_web_contents_view.cc
index 946b5071bcc87..e61f008414df4 100644
--- a/shell/browser/ui/inspectable_web_contents_view.cc
+++ b/shell/browser/ui/inspectable_web_contents_view.cc
@@ -5,12 +5,250 @@
 
 #include "shell/browser/ui/inspectable_web_contents_view.h"
 
+#include <memory>
+#include <utility>
+
+#include "base/memory/raw_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "shell/browser/ui/drag_util.h"
+#include "shell/browser/ui/inspectable_web_contents.h"
+#include "shell/browser/ui/inspectable_web_contents_delegate.h"
+#include "shell/browser/ui/inspectable_web_contents_view_delegate.h"
+#include "ui/base/models/image_model.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/webview/webview.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+#include "ui/views/window/client_view.h"
+
 namespace electron {
 
+namespace {
+
+class DevToolsWindowDelegate : public views::ClientView,
+                               public views::WidgetDelegate {
+ public:
+  DevToolsWindowDelegate(InspectableWebContentsView* shell,
+                         views::View* view,
+                         views::Widget* widget)
+      : views::ClientView(widget, view),
+        shell_(shell),
+        view_(view),
+        widget_(widget) {
+    SetOwnedByWidget(true);
+    set_owned_by_client();
+
+    if (shell->GetDelegate())
+      icon_ = shell->GetDelegate()->GetDevToolsWindowIcon();
+  }
+  ~DevToolsWindowDelegate() override = default;
+
+  // disable copy
+  DevToolsWindowDelegate(const DevToolsWindowDelegate&) = delete;
+  DevToolsWindowDelegate& operator=(const DevToolsWindowDelegate&) = delete;
+
+  // views::WidgetDelegate:
+  views::View* GetInitiallyFocusedView() override { return view_; }
+  std::u16string GetWindowTitle() const override { return shell_->GetTitle(); }
+  ui::ImageModel GetWindowAppIcon() override { return GetWindowIcon(); }
+  ui::ImageModel GetWindowIcon() override { return icon_; }
+  views::Widget* GetWidget() override { return widget_; }
+  const views::Widget* GetWidget() const override { return widget_; }
+  views::View* GetContentsView() override { return view_; }
+  views::ClientView* CreateClientView(views::Widget* widget) override {
+    return this;
+  }
+
+  // views::ClientView:
+  views::CloseRequestResult OnWindowCloseRequested() override {
+    shell_->inspectable_web_contents()->CloseDevTools();
+    return views::CloseRequestResult::kCannotClose;
+  }
+
+ private:
+  raw_ptr<InspectableWebContentsView> shell_;
+  raw_ptr<views::View> view_;
+  raw_ptr<views::Widget> widget_;
+  ui::ImageModel icon_;
+};
+
+}  // namespace
+
 InspectableWebContentsView::InspectableWebContentsView(
     InspectableWebContents* inspectable_web_contents)
-    : inspectable_web_contents_(inspectable_web_contents) {}
+    : inspectable_web_contents_(inspectable_web_contents),
+      devtools_web_view_(new views::WebView(nullptr)),
+      title_(u"Developer Tools") {
+  if (!inspectable_web_contents_->is_guest() &&
+      inspectable_web_contents_->GetWebContents()->GetNativeView()) {
+    auto* contents_web_view = new views::WebView(nullptr);
+    contents_web_view->SetWebContents(
+        inspectable_web_contents_->GetWebContents());
+    contents_web_view_ = contents_web_view;
+  } else {
+    no_contents_view_ = new views::Label(u"No content under offscreen mode");
+  }
+
+  devtools_web_view_->SetVisible(false);
+  AddChildView(devtools_web_view_.get());
+  AddChildView(GetContentsView());
+}
+
+InspectableWebContentsView::~InspectableWebContentsView() {
+  if (devtools_window_)
+    inspectable_web_contents()->SaveDevToolsBounds(
+        devtools_window_->GetWindowBoundsInScreen());
+}
+
+void InspectableWebContentsView::SetCornerRadii(
+    const gfx::RoundedCornersF& corner_radii) {
+  // WebView won't exist for offscreen rendering.
+  if (contents_web_view_) {
+    contents_web_view_->holder()->SetCornerRadii(corner_radii);
+  }
+}
+
+void InspectableWebContentsView::ShowDevTools(bool activate) {
+  if (devtools_visible_)
+    return;
+
+  devtools_visible_ = true;
+  if (devtools_window_) {
+    devtools_window_web_view_->SetWebContents(
+        inspectable_web_contents_->GetDevToolsWebContents());
+    devtools_window_->SetBounds(inspectable_web_contents()->dev_tools_bounds());
+    if (activate) {
+      devtools_window_->Show();
+    } else {
+      devtools_window_->ShowInactive();
+    }
+
+    // Update draggable regions to account for the new dock position.
+    if (GetDelegate())
+      GetDelegate()->DevToolsResized();
+  } else {
+    devtools_web_view_->SetVisible(true);
+    devtools_web_view_->SetWebContents(
+        inspectable_web_contents_->GetDevToolsWebContents());
+    devtools_web_view_->RequestFocus();
+    DeprecatedLayoutImmediately();
+  }
+}
+
+void InspectableWebContentsView::CloseDevTools() {
+  if (!devtools_visible_)
+    return;
+
+  devtools_visible_ = false;
+  if (devtools_window_) {
+    auto save_bounds = devtools_window_->IsMinimized()
+                           ? devtools_window_->GetRestoredBounds()
+                           : devtools_window_->GetWindowBoundsInScreen();
+    inspectable_web_contents()->SaveDevToolsBounds(save_bounds);
+
+    devtools_window_.reset();
+    devtools_window_web_view_ = nullptr;
+    devtools_window_delegate_ = nullptr;
+  } else {
+    devtools_web_view_->SetVisible(false);
+    devtools_web_view_->SetWebContents(nullptr);
+    DeprecatedLayoutImmediately();
+  }
+}
+
+bool InspectableWebContentsView::IsDevToolsViewShowing() {
+  return devtools_visible_;
+}
+
+bool InspectableWebContentsView::IsDevToolsViewFocused() {
+  if (devtools_window_web_view_)
+    return devtools_window_web_view_->HasFocus();
+  else if (devtools_web_view_)
+    return devtools_web_view_->HasFocus();
+  else
+    return false;
+}
+
+void InspectableWebContentsView::SetIsDocked(bool docked, bool activate) {
+  CloseDevTools();
+
+  if (!docked) {
+    devtools_window_ = std::make_unique<views::Widget>();
+    devtools_window_web_view_ = new views::WebView(nullptr);
+    devtools_window_delegate_ = new DevToolsWindowDelegate(
+        this, devtools_window_web_view_, devtools_window_.get());
+
+    views::Widget::InitParams params(
+        views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET,
+        views::Widget::InitParams::TYPE_WINDOW);
+    params.delegate = devtools_window_delegate_;
+    params.bounds = inspectable_web_contents()->dev_tools_bounds();
+
+#if BUILDFLAG(IS_LINUX)
+    params.wm_role_name = "devtools";
+    if (GetDelegate())
+      GetDelegate()->GetDevToolsWindowWMClass(&params.wm_class_name,
+                                              &params.wm_class_class);
+#endif
+
+    devtools_window_->Init(std::move(params));
+    devtools_window_->UpdateWindowIcon();
+    devtools_window_->widget_delegate()->SetHasWindowSizeControls(true);
+  }
+
+  ShowDevTools(activate);
+}
+
+void InspectableWebContentsView::SetContentsResizingStrategy(
+    const DevToolsContentsResizingStrategy& strategy) {
+  strategy_.CopyFrom(strategy);
+  DeprecatedLayoutImmediately();
+}
+
+void InspectableWebContentsView::SetTitle(const std::u16string& title) {
+  if (devtools_window_) {
+    title_ = title;
+    devtools_window_->UpdateWindowTitle();
+  }
+}
+
+const std::u16string InspectableWebContentsView::GetTitle() {
+  return title_;
+}
+
+void InspectableWebContentsView::Layout(PassKey) {
+  if (!devtools_web_view_->GetVisible()) {
+    GetContentsView()->SetBoundsRect(GetContentsBounds());
+    // Propagate layout call to all children, for example browser views.
+    LayoutSuperclass<View>(this);
+    return;
+  }
+
+  gfx::Size container_size(width(), height());
+  gfx::Rect new_devtools_bounds;
+  gfx::Rect new_contents_bounds;
+  ApplyDevToolsContentsResizingStrategy(
+      strategy_, container_size, &new_devtools_bounds, &new_contents_bounds);
+
+  // DevTools cares about the specific position, so we have to compensate RTL
+  // layout here.
+  new_devtools_bounds.set_x(GetMirroredXForRect(new_devtools_bounds));
+  new_contents_bounds.set_x(GetMirroredXForRect(new_contents_bounds));
+
+  devtools_web_view_->SetBoundsRect(new_devtools_bounds);
+  GetContentsView()->SetBoundsRect(new_contents_bounds);
+
+  // Propagate layout call to all children, for example browser views.
+  LayoutSuperclass<View>(this);
+
+  if (GetDelegate())
+    GetDelegate()->DevToolsResized();
+}
+
+views::View* InspectableWebContentsView::GetContentsView() const {
+  DCHECK(contents_web_view_ || no_contents_view_);
 
-InspectableWebContentsView::~InspectableWebContentsView() = default;
+  return contents_web_view_ ? contents_web_view_ : no_contents_view_;
+}
 
 }  // namespace electron
diff --git a/shell/browser/ui/inspectable_web_contents_view.h b/shell/browser/ui/inspectable_web_contents_view.h
index a9c95d477e00d..282d3bf805a97 100644
--- a/shell/browser/ui/inspectable_web_contents_view.h
+++ b/shell/browser/ui/inspectable_web_contents_view.h
@@ -9,13 +9,9 @@
 #include <string>
 
 #include "base/memory/raw_ptr.h"
-#include "build/build_config.h"
-
-#if defined(TOOLKIT_VIEWS) && !BUILDFLAG(IS_MAC)
-#include "ui/views/view.h"
-#else
+#include "chrome/browser/devtools/devtools_contents_resizing_strategy.h"
 #include "ui/gfx/native_widget_types.h"
-#endif
+#include "ui/views/view.h"
 
 class DevToolsContentsResizingStrategy;
 
@@ -23,23 +19,22 @@ namespace gfx {
 class RoundedCornersF;
 }  // namespace gfx
 
-#if defined(TOOLKIT_VIEWS)
 namespace views {
-class View;
 class WebView;
+class Widget;
+class WidgetDelegate;
 }  // namespace views
-#endif
 
 namespace electron {
 
 class InspectableWebContents;
 class InspectableWebContentsViewDelegate;
 
-class InspectableWebContentsView {
+class InspectableWebContentsView : public views::View {
  public:
   explicit InspectableWebContentsView(
       InspectableWebContents* inspectable_web_contents);
-  virtual ~InspectableWebContentsView();
+  ~InspectableWebContentsView() override;
 
   InspectableWebContents* inspectable_web_contents() {
     return inspectable_web_contents_;
@@ -51,32 +46,40 @@ class InspectableWebContentsView {
   }
   InspectableWebContentsViewDelegate* GetDelegate() const { return delegate_; }
 
-#if defined(TOOLKIT_VIEWS) && !BUILDFLAG(IS_MAC)
-  // Returns the container control, which has devtools view attached.
-  virtual views::View* GetView() = 0;
-#else
-  virtual gfx::NativeView GetNativeView() const = 0;
-#endif
-
-  virtual void ShowDevTools(bool activate) = 0;
-  virtual void SetCornerRadii(const gfx::RoundedCornersF& corner_radii) = 0;
-  // Hide the DevTools view.
-  virtual void CloseDevTools() = 0;
-  virtual bool IsDevToolsViewShowing() = 0;
-  virtual bool IsDevToolsViewFocused() = 0;
-  virtual void SetIsDocked(bool docked, bool activate) = 0;
-  virtual void SetContentsResizingStrategy(
-      const DevToolsContentsResizingStrategy& strategy) = 0;
-  virtual void SetTitle(const std::u16string& title) = 0;
-  virtual const std::u16string GetTitle() = 0;
-
- protected:
+  void SetCornerRadii(const gfx::RoundedCornersF& corner_radii);
+
+  void ShowDevTools(bool activate);
+  void CloseDevTools();
+  bool IsDevToolsViewShowing();
+  bool IsDevToolsViewFocused();
+  void SetIsDocked(bool docked, bool activate);
+  void SetContentsResizingStrategy(
+      const DevToolsContentsResizingStrategy& strategy);
+  void SetTitle(const std::u16string& title);
+  const std::u16string GetTitle();
+
+  // views::View:
+  void Layout(PassKey) override;
+
+ private:
+  views::View* GetContentsView() const;
+
   // Owns us.
   raw_ptr<InspectableWebContents> inspectable_web_contents_;
 
- private:
   raw_ptr<InspectableWebContentsViewDelegate> delegate_ =
       nullptr;  // weak references.
+
+  std::unique_ptr<views::Widget> devtools_window_;
+  raw_ptr<views::WebView> devtools_window_web_view_ = nullptr;
+  raw_ptr<views::WebView> contents_web_view_ = nullptr;
+  raw_ptr<views::View> no_contents_view_ = nullptr;
+  raw_ptr<views::WebView> devtools_web_view_ = nullptr;
+
+  DevToolsContentsResizingStrategy strategy_;
+  bool devtools_visible_ = false;
+  raw_ptr<views::WidgetDelegate> devtools_window_delegate_ = nullptr;
+  std::u16string title_;
 };
 
 }  // namespace electron
diff --git a/shell/browser/ui/inspectable_web_contents_view_mac.h b/shell/browser/ui/inspectable_web_contents_view_mac.h
deleted file mode 100644
index 88b4078614f42..0000000000000
--- a/shell/browser/ui/inspectable_web_contents_view_mac.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Copyright (c) 2013 Adam Roben <adam@roben.org>. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE-CHROMIUM file.
-
-#ifndef ELECTRON_SHELL_BROWSER_UI_INSPECTABLE_WEB_CONTENTS_VIEW_MAC_H_
-#define ELECTRON_SHELL_BROWSER_UI_INSPECTABLE_WEB_CONTENTS_VIEW_MAC_H_
-
-#include "shell/browser/ui/inspectable_web_contents_view.h"
-
-@class ElectronInspectableWebContentsView;
-
-namespace electron {
-
-class InspectableWebContentsViewMac : public InspectableWebContentsView {
- public:
-  explicit InspectableWebContentsViewMac(
-      InspectableWebContents* inspectable_web_contents);
-  InspectableWebContentsViewMac(const InspectableWebContentsViewMac&) = delete;
-  InspectableWebContentsViewMac& operator=(
-      const InspectableWebContentsViewMac&) = delete;
-  ~InspectableWebContentsViewMac() override;
-
-  gfx::NativeView GetNativeView() const override;
-  void SetCornerRadii(const gfx::RoundedCornersF& corner_radii) override;
-  void ShowDevTools(bool activate) override;
-  void CloseDevTools() override;
-  bool IsDevToolsViewShowing() override;
-  bool IsDevToolsViewFocused() override;
-  void SetIsDocked(bool docked, bool activate) override;
-  void SetContentsResizingStrategy(
-      const DevToolsContentsResizingStrategy& strategy) override;
-  void SetTitle(const std::u16string& title) override;
-  const std::u16string GetTitle() override;
-
- private:
-  ElectronInspectableWebContentsView* __strong view_;
-};
-
-}  // namespace electron
-
-#endif  // ELECTRON_SHELL_BROWSER_UI_INSPECTABLE_WEB_CONTENTS_VIEW_MAC_H_
diff --git a/shell/browser/ui/inspectable_web_contents_view_mac.mm b/shell/browser/ui/inspectable_web_contents_view_mac.mm
deleted file mode 100644
index a3789e09a8fb3..0000000000000
--- a/shell/browser/ui/inspectable_web_contents_view_mac.mm
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Copyright (c) 2013 Adam Roben <adam@roben.org>. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE-CHROMIUM file.
-
-#include "shell/browser/ui/inspectable_web_contents_view_mac.h"
-
-#include "base/strings/sys_string_conversions.h"
-#import "shell/browser/ui/cocoa/electron_inspectable_web_contents_view.h"
-#include "shell/browser/ui/inspectable_web_contents.h"
-#include "shell/browser/ui/inspectable_web_contents_view_delegate.h"
-#include "ui/gfx/geometry/rounded_corners_f.h"
-
-namespace electron {
-
-InspectableWebContentsView* CreateInspectableContentsView(
-    InspectableWebContents* inspectable_web_contents) {
-  return new InspectableWebContentsViewMac(inspectable_web_contents);
-}
-
-InspectableWebContentsViewMac::InspectableWebContentsViewMac(
-    InspectableWebContents* inspectable_web_contents)
-    : InspectableWebContentsView(inspectable_web_contents),
-      view_([[ElectronInspectableWebContentsView alloc]
-          initWithInspectableWebContentsViewMac:this]) {}
-
-InspectableWebContentsViewMac::~InspectableWebContentsViewMac() {
-  [[NSNotificationCenter defaultCenter] removeObserver:view_];
-  CloseDevTools();
-}
-
-gfx::NativeView InspectableWebContentsViewMac::GetNativeView() const {
-  return view_;
-}
-
-void InspectableWebContentsViewMac::SetCornerRadii(
-    const gfx::RoundedCornersF& corner_radii) {
-  // We can assume all four values are identical.
-  [view_ setCornerRadii:corner_radii.upper_left()];
-}
-
-void InspectableWebContentsViewMac::ShowDevTools(bool activate) {
-  [view_ setDevToolsVisible:YES activate:activate];
-}
-
-void InspectableWebContentsViewMac::CloseDevTools() {
-  [view_ setDevToolsVisible:NO activate:NO];
-}
-
-bool InspectableWebContentsViewMac::IsDevToolsViewShowing() {
-  return [view_ isDevToolsVisible];
-}
-
-bool InspectableWebContentsViewMac::IsDevToolsViewFocused() {
-  return [view_ isDevToolsFocused];
-}
-
-void InspectableWebContentsViewMac::SetIsDocked(bool docked, bool activate) {
-  [view_ setIsDocked:docked activate:activate];
-}
-
-void InspectableWebContentsViewMac::SetContentsResizingStrategy(
-    const DevToolsContentsResizingStrategy& strategy) {
-  [view_ setContentsResizingStrategy:strategy];
-}
-
-void InspectableWebContentsViewMac::SetTitle(const std::u16string& title) {
-  [view_ setTitle:base::SysUTF16ToNSString(title)];
-}
-
-const std::u16string InspectableWebContentsViewMac::GetTitle() {
-  return base::SysNSStringToUTF16([view_ getTitle]);
-}
-
-}  // namespace electron
diff --git a/shell/browser/ui/views/frameless_view.cc b/shell/browser/ui/views/frameless_view.cc
index c963d1731093a..dcfed5ef695f0 100644
--- a/shell/browser/ui/views/frameless_view.cc
+++ b/shell/browser/ui/views/frameless_view.cc
@@ -5,6 +5,8 @@
 #include "shell/browser/ui/views/frameless_view.h"
 
 #include "shell/browser/native_window_views.h"
+#include "shell/browser/ui/inspectable_web_contents_view.h"
+#include "ui/aura/window.h"
 #include "ui/base/hit_test.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/views/widget/widget.h"
diff --git a/shell/browser/ui/views/inspectable_web_contents_view_views.cc b/shell/browser/ui/views/inspectable_web_contents_view_views.cc
deleted file mode 100644
index 61a5b013ecb12..0000000000000
--- a/shell/browser/ui/views/inspectable_web_contents_view_views.cc
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE-CHROMIUM file.
-
-#include "shell/browser/ui/views/inspectable_web_contents_view_views.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/memory/raw_ptr.h"
-#include "shell/browser/ui/drag_util.h"
-#include "shell/browser/ui/inspectable_web_contents.h"
-#include "shell/browser/ui/inspectable_web_contents_delegate.h"
-#include "shell/browser/ui/inspectable_web_contents_view_delegate.h"
-#include "ui/base/models/image_model.h"
-#include "ui/gfx/geometry/rounded_corners_f.h"
-#include "ui/views/controls/label.h"
-#include "ui/views/controls/webview/webview.h"
-#include "ui/views/widget/widget.h"
-#include "ui/views/widget/widget_delegate.h"
-#include "ui/views/window/client_view.h"
-
-namespace electron {
-
-namespace {
-
-class DevToolsWindowDelegate : public views::ClientView,
-                               public views::WidgetDelegate {
- public:
-  DevToolsWindowDelegate(InspectableWebContentsViewViews* shell,
-                         views::View* view,
-                         views::Widget* widget)
-      : views::ClientView(widget, view),
-        shell_(shell),
-        view_(view),
-        widget_(widget) {
-    SetOwnedByWidget(true);
-    set_owned_by_client();
-
-    if (shell->GetDelegate())
-      icon_ = shell->GetDelegate()->GetDevToolsWindowIcon();
-  }
-  ~DevToolsWindowDelegate() override = default;
-
-  // disable copy
-  DevToolsWindowDelegate(const DevToolsWindowDelegate&) = delete;
-  DevToolsWindowDelegate& operator=(const DevToolsWindowDelegate&) = delete;
-
-  // views::WidgetDelegate:
-  views::View* GetInitiallyFocusedView() override { return view_; }
-  std::u16string GetWindowTitle() const override { return shell_->GetTitle(); }
-  ui::ImageModel GetWindowAppIcon() override { return GetWindowIcon(); }
-  ui::ImageModel GetWindowIcon() override { return icon_; }
-  views::Widget* GetWidget() override { return widget_; }
-  const views::Widget* GetWidget() const override { return widget_; }
-  views::View* GetContentsView() override { return view_; }
-  views::ClientView* CreateClientView(views::Widget* widget) override {
-    return this;
-  }
-
-  // views::ClientView:
-  views::CloseRequestResult OnWindowCloseRequested() override {
-    shell_->inspectable_web_contents()->CloseDevTools();
-    return views::CloseRequestResult::kCannotClose;
-  }
-
- private:
-  raw_ptr<InspectableWebContentsViewViews> shell_;
-  raw_ptr<views::View> view_;
-  raw_ptr<views::Widget> widget_;
-  ui::ImageModel icon_;
-};
-
-}  // namespace
-
-InspectableWebContentsView* CreateInspectableContentsView(
-    InspectableWebContents* inspectable_web_contents) {
-  return new InspectableWebContentsViewViews(inspectable_web_contents);
-}
-
-InspectableWebContentsViewViews::InspectableWebContentsViewViews(
-    InspectableWebContents* inspectable_web_contents)
-    : InspectableWebContentsView(inspectable_web_contents),
-      devtools_web_view_(new views::WebView(nullptr)),
-      title_(u"Developer Tools") {
-  if (!inspectable_web_contents_->is_guest() &&
-      inspectable_web_contents_->GetWebContents()->GetNativeView()) {
-    auto* contents_web_view = new views::WebView(nullptr);
-    contents_web_view->SetWebContents(
-        inspectable_web_contents_->GetWebContents());
-    contents_view_ = contents_web_view_ = contents_web_view;
-  } else {
-    contents_view_ = new views::Label(u"No content under offscreen mode");
-  }
-
-  devtools_web_view_->SetVisible(false);
-  AddChildView(devtools_web_view_.get());
-  AddChildView(contents_view_.get());
-}
-
-InspectableWebContentsViewViews::~InspectableWebContentsViewViews() {
-  if (devtools_window_)
-    inspectable_web_contents()->SaveDevToolsBounds(
-        devtools_window_->GetWindowBoundsInScreen());
-}
-
-views::View* InspectableWebContentsViewViews::GetView() {
-  return this;
-}
-
-void InspectableWebContentsViewViews::SetCornerRadii(
-    const gfx::RoundedCornersF& corner_radii) {
-  // WebView won't exist for offscreen rendering.
-  if (contents_web_view_) {
-    contents_web_view_->holder()->SetCornerRadii(
-        gfx::RoundedCornersF(corner_radii));
-  }
-}
-
-void InspectableWebContentsViewViews::ShowDevTools(bool activate) {
-  if (devtools_visible_)
-    return;
-
-  devtools_visible_ = true;
-  if (devtools_window_) {
-    devtools_window_web_view_->SetWebContents(
-        inspectable_web_contents_->GetDevToolsWebContents());
-    devtools_window_->SetBounds(inspectable_web_contents()->dev_tools_bounds());
-    if (activate) {
-      devtools_window_->Show();
-    } else {
-      devtools_window_->ShowInactive();
-    }
-
-    // Update draggable regions to account for the new dock position.
-    if (GetDelegate())
-      GetDelegate()->DevToolsResized();
-  } else {
-    devtools_web_view_->SetVisible(true);
-    devtools_web_view_->SetWebContents(
-        inspectable_web_contents_->GetDevToolsWebContents());
-    devtools_web_view_->RequestFocus();
-    DeprecatedLayoutImmediately();
-  }
-}
-
-void InspectableWebContentsViewViews::CloseDevTools() {
-  if (!devtools_visible_)
-    return;
-
-  devtools_visible_ = false;
-  if (devtools_window_) {
-    auto save_bounds = devtools_window_->IsMinimized()
-                           ? devtools_window_->GetRestoredBounds()
-                           : devtools_window_->GetWindowBoundsInScreen();
-    inspectable_web_contents()->SaveDevToolsBounds(save_bounds);
-
-    devtools_window_.reset();
-    devtools_window_web_view_ = nullptr;
-    devtools_window_delegate_ = nullptr;
-  } else {
-    devtools_web_view_->SetVisible(false);
-    devtools_web_view_->SetWebContents(nullptr);
-    DeprecatedLayoutImmediately();
-  }
-}
-
-bool InspectableWebContentsViewViews::IsDevToolsViewShowing() {
-  return devtools_visible_;
-}
-
-bool InspectableWebContentsViewViews::IsDevToolsViewFocused() {
-  if (devtools_window_web_view_)
-    return devtools_window_web_view_->HasFocus();
-  else if (devtools_web_view_)
-    return devtools_web_view_->HasFocus();
-  else
-    return false;
-}
-
-void InspectableWebContentsViewViews::SetIsDocked(bool docked, bool activate) {
-  CloseDevTools();
-
-  if (!docked) {
-    devtools_window_ = std::make_unique<views::Widget>();
-    devtools_window_web_view_ = new views::WebView(nullptr);
-    devtools_window_delegate_ = new DevToolsWindowDelegate(
-        this, devtools_window_web_view_, devtools_window_.get());
-
-    views::Widget::InitParams params{
-        views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET};
-    params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
-    params.delegate = devtools_window_delegate_;
-    params.bounds = inspectable_web_contents()->dev_tools_bounds();
-
-#if BUILDFLAG(IS_LINUX)
-    params.wm_role_name = "devtools";
-    if (GetDelegate())
-      GetDelegate()->GetDevToolsWindowWMClass(&params.wm_class_name,
-                                              &params.wm_class_class);
-#endif
-
-    devtools_window_->Init(std::move(params));
-    devtools_window_->UpdateWindowIcon();
-    devtools_window_->widget_delegate()->SetHasWindowSizeControls(true);
-  }
-
-  ShowDevTools(activate);
-}
-
-void InspectableWebContentsViewViews::SetContentsResizingStrategy(
-    const DevToolsContentsResizingStrategy& strategy) {
-  strategy_.CopyFrom(strategy);
-  DeprecatedLayoutImmediately();
-}
-
-void InspectableWebContentsViewViews::SetTitle(const std::u16string& title) {
-  if (devtools_window_) {
-    title_ = title;
-    devtools_window_->UpdateWindowTitle();
-  }
-}
-
-const std::u16string InspectableWebContentsViewViews::GetTitle() {
-  return title_;
-}
-
-void InspectableWebContentsViewViews::Layout(PassKey) {
-  if (!devtools_web_view_->GetVisible()) {
-    contents_view_->SetBoundsRect(GetContentsBounds());
-    // Propagate layout call to all children, for example browser views.
-    LayoutSuperclass<View>(this);
-    return;
-  }
-
-  gfx::Size container_size(width(), height());
-  gfx::Rect new_devtools_bounds;
-  gfx::Rect new_contents_bounds;
-  ApplyDevToolsContentsResizingStrategy(
-      strategy_, container_size, &new_devtools_bounds, &new_contents_bounds);
-
-  // DevTools cares about the specific position, so we have to compensate RTL
-  // layout here.
-  new_devtools_bounds.set_x(GetMirroredXForRect(new_devtools_bounds));
-  new_contents_bounds.set_x(GetMirroredXForRect(new_contents_bounds));
-
-  devtools_web_view_->SetBoundsRect(new_devtools_bounds);
-  contents_view_->SetBoundsRect(new_contents_bounds);
-
-  // Propagate layout call to all children, for example browser views.
-  LayoutSuperclass<View>(this);
-
-  if (GetDelegate())
-    GetDelegate()->DevToolsResized();
-}
-
-}  // namespace electron
diff --git a/shell/browser/ui/views/inspectable_web_contents_view_views.h b/shell/browser/ui/views/inspectable_web_contents_view_views.h
deleted file mode 100644
index 36554a497d6f2..0000000000000
--- a/shell/browser/ui/views/inspectable_web_contents_view_views.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE-CHROMIUM file.
-
-#ifndef ELECTRON_SHELL_BROWSER_UI_VIEWS_INSPECTABLE_WEB_CONTENTS_VIEW_VIEWS_H_
-#define ELECTRON_SHELL_BROWSER_UI_VIEWS_INSPECTABLE_WEB_CONTENTS_VIEW_VIEWS_H_
-
-#include <memory>
-
-#include "base/compiler_specific.h"
-#include "base/memory/raw_ptr.h"
-#include "chrome/browser/devtools/devtools_contents_resizing_strategy.h"
-#include "shell/browser/ui/inspectable_web_contents_view.h"
-#include "third_party/skia/include/core/SkRegion.h"
-#include "ui/views/view.h"
-
-namespace views {
-class WebView;
-class Widget;
-class WidgetDelegate;
-}  // namespace views
-
-namespace electron {
-
-class InspectableWebContentsViewViews : public InspectableWebContentsView,
-                                        public views::View {
- public:
-  explicit InspectableWebContentsViewViews(
-      InspectableWebContents* inspectable_web_contents);
-  ~InspectableWebContentsViewViews() override;
-
-  // InspectableWebContentsView:
-  views::View* GetView() override;
-  void ShowDevTools(bool activate) override;
-  void SetCornerRadii(const gfx::RoundedCornersF& corner_radii) override;
-  void CloseDevTools() override;
-  bool IsDevToolsViewShowing() override;
-  bool IsDevToolsViewFocused() override;
-  void SetIsDocked(bool docked, bool activate) override;
-  void SetContentsResizingStrategy(
-      const DevToolsContentsResizingStrategy& strategy) override;
-  void SetTitle(const std::u16string& title) override;
-  const std::u16string GetTitle() override;
-
-  // views::View:
-  void Layout(PassKey) override;
-
- private:
-  std::unique_ptr<views::Widget> devtools_window_;
-  raw_ptr<views::WebView> devtools_window_web_view_ = nullptr;
-  raw_ptr<views::WebView> contents_web_view_ = nullptr;
-  raw_ptr<views::View> contents_view_ = nullptr;
-  raw_ptr<views::WebView> devtools_web_view_ = nullptr;
-
-  DevToolsContentsResizingStrategy strategy_;
-  bool devtools_visible_ = false;
-  raw_ptr<views::WidgetDelegate> devtools_window_delegate_ = nullptr;
-  std::u16string title_;
-};
-
-}  // namespace electron
-
-#endif  // ELECTRON_SHELL_BROWSER_UI_VIEWS_INSPECTABLE_WEB_CONTENTS_VIEW_VIEWS_H_


Test patch: