From 85526c7f217758c1172c13adcb323404abda850f Mon Sep 17 00:00:00 2001 From: "trop[bot]" Date: Tue, 10 Jul 2018 13:09:46 +0900 Subject: [PATCH] fix: fallback to FullSizeContentView for frameless window on mac (#13600) When using `views::Widget` to create window, Chromium will automatically use a `BridgedContentView` as content view, which however does not support draggable regions inside it. By fallback to `FullSizeContentView` we can work around this problem, with the price of losing the ability to use `views::View` APIs. Since we don't expect users to use the new `View` APIs in `BrowserWindow` anyway, it should not be a problem. This change does not affect users of `TopLevelWindow`, and for users of `BrowserWindow` there is nothing to lose. In the long term we should look into how to make draggable regions work with `BridgedContentView`. The related Chromium code is still being changed rapidly, we can wait until Chromium migrated its `NativeAppWindowCocoa` class to use `views::Widget`. --- atom/browser/api/atom_api_browser_window.cc | 5 + atom/browser/api/atom_api_browser_window.h | 4 + .../api/atom_api_browser_window_mac.mm | 12 ++ atom/browser/native_window_mac.h | 6 + atom/browser/native_window_mac.mm | 151 +++++++++++------- 5 files changed, 123 insertions(+), 55 deletions(-) diff --git a/atom/browser/api/atom_api_browser_window.cc b/atom/browser/api/atom_api_browser_window.cc index c640ea28241..0e71c040066 100644 --- a/atom/browser/api/atom_api_browser_window.cc +++ b/atom/browser/api/atom_api_browser_window.cc @@ -85,6 +85,11 @@ BrowserWindow::BrowserWindow(v8::Isolate* isolate, InitWith(isolate, wrapper); +#if defined(OS_MACOSX) + if (!window()->has_frame()) + OverrideNSWindowContentView(); +#endif + // Init window after everything has been setup. window()->InitFromOptions(options); } diff --git a/atom/browser/api/atom_api_browser_window.h b/atom/browser/api/atom_api_browser_window.h index 6bf6585e370..e07cd3987dd 100644 --- a/atom/browser/api/atom_api_browser_window.h +++ b/atom/browser/api/atom_api_browser_window.h @@ -80,6 +80,10 @@ class BrowserWindow : public TopLevelWindow, v8::Local GetWebContents(v8::Isolate* isolate); private: +#if defined(OS_MACOSX) + void OverrideNSWindowContentView(); +#endif + // Helpers. // Called when the window needs to update its draggable region. diff --git a/atom/browser/api/atom_api_browser_window_mac.mm b/atom/browser/api/atom_api_browser_window_mac.mm index ac64fb800f3..dfcbfafea7f 100644 --- a/atom/browser/api/atom_api_browser_window_mac.mm +++ b/atom/browser/api/atom_api_browser_window_mac.mm @@ -7,6 +7,7 @@ #import #include "atom/browser/native_browser_view.h" +#include "atom/browser/native_window_mac.h" #include "atom/common/draggable_region.h" #include "base/mac/scoped_nsobject.h" @@ -53,6 +54,17 @@ std::vector CalculateNonDraggableRegions( } // namespace +void BrowserWindow::OverrideNSWindowContentView() { + // Make NativeWindow use a NSView as content view. + static_cast(window())->OverrideNSWindowContentView(); + // Add webview to contentView. + NSView* webView = web_contents()->GetNativeView(); + NSView* contentView = [window()->GetNativeWindow() contentView]; + [webView setFrame:[contentView bounds]]; + [contentView addSubview:webView]; + [contentView viewDidMoveToWindow]; +} + void BrowserWindow::UpdateDraggableRegions( content::RenderFrameHost* rfh, const std::vector& regions) { diff --git a/atom/browser/native_window_mac.h b/atom/browser/native_window_mac.h index dd343c6e2a2..54891c5b35a 100644 --- a/atom/browser/native_window_mac.h +++ b/atom/browser/native_window_mac.h @@ -128,6 +128,9 @@ class NativeWindowMac : public NativeWindow { gfx::Rect ContentBoundsToWindowBounds(const gfx::Rect& bounds) const override; gfx::Rect WindowBoundsToContentBounds(const gfx::Rect& bounds) const override; + // Use a custom content view instead of Chromium's BridgedContentView. + void OverrideNSWindowContentView(); + // Set the attribute of NSWindow while work around a bug of zoom button. void SetStyleMask(bool on, NSUInteger flag); void SetCollectionBehavior(bool on, NSUInteger flag); @@ -152,6 +155,9 @@ class NativeWindowMac : public NativeWindow { views::View* GetContentsView() override; private: + // Add custom layers to the content view. + void AddContentViewLayers(); + void InternalSetParentWindow(NativeWindow* parent, bool attach); void ShowWindowButton(NSWindowButton button); diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index ba28d4f501b..2d8149668e5 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -32,6 +32,31 @@ #include "ui/views/cocoa/bridged_native_widget.h" #include "ui/views/widget/widget.h" +// This view always takes the size of its superview. It is intended to be used +// as a NSWindow's contentView. It is needed because NSWindow's implementation +// explicitly resizes the contentView at inopportune times. +@interface FullSizeContentView : NSView +@end + +@implementation FullSizeContentView + +// This method is directly called by NSWindow during a window resize on OSX +// 10.10.0, beta 2. We must override it to prevent the content view from +// shrinking. +- (void)setFrameSize:(NSSize)size { + if ([self superview]) + size = [[self superview] bounds].size; + [super setFrameSize:size]; +} + +// The contentView gets moved around during certain full-screen operations. +// This is less than ideal, and should eventually be removed. +- (void)viewDidMoveToSuperview { + [self setFrame:[[self superview] bounds]]; +} + +@end + // Custom Quit, Minimize and Full Screen button container for frameless // windows. @interface CustomWindowButtonView : NSView { @@ -443,61 +468,7 @@ NativeWindowMac::NativeWindowMac(const mate::Dictionary& options, // Default content view. SetContentView(new views::View()); - - // Make sure the bottom corner is rounded for non-modal windows: - // http://crbug.com/396264. But do not enable it on OS X 10.9 for transparent - // window, otherwise a semi-transparent frame would show. - if (!(transparent() && base::mac::IsOS10_9()) && !is_modal()) { - base::scoped_nsobject background_layer([[CALayer alloc] init]); - [background_layer - setAutoresizingMask:kCALayerWidthSizable | kCALayerHeightSizable]; - [[window_ contentView] setLayer:background_layer]; - [[window_ contentView] setWantsLayer:YES]; - } - - if (!has_frame()) { - // In OSX 10.10, adding subviews to the root view for the NSView hierarchy - // produces warnings. To eliminate the warnings, we resize the contentView - // to fill the window, and add subviews to that. - // http://crbug.com/380412 - if (!original_set_frame_size) { - Class cl = [[window_ contentView] class]; - original_set_frame_size = class_replaceMethod( - cl, @selector(setFrameSize:), (IMP)SetFrameSize, "v@:{_NSSize=ff}"); - original_view_did_move_to_superview = - class_replaceMethod(cl, @selector(viewDidMoveToSuperview), - (IMP)ViewDidMoveToSuperview, "v@:"); - [[window_ contentView] viewDidMoveToWindow]; - } - - // The fullscreen button should always be hidden for frameless window. - [[window_ standardWindowButton:NSWindowFullScreenButton] setHidden:YES]; - - if (title_bar_style_ == CUSTOM_BUTTONS_ON_HOVER) { - buttons_view_.reset( - [[CustomWindowButtonView alloc] initWithFrame:NSZeroRect]); - [[window_ contentView] addSubview:buttons_view_]; - } else { - if (title_bar_style_ != NORMAL) { - if (base::mac::IsOS10_9()) { - ShowWindowButton(NSWindowZoomButton); - ShowWindowButton(NSWindowMiniaturizeButton); - ShowWindowButton(NSWindowCloseButton); - } - return; - } - - // Hide the window buttons. - [[window_ standardWindowButton:NSWindowZoomButton] setHidden:YES]; - [[window_ standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES]; - [[window_ standardWindowButton:NSWindowCloseButton] setHidden:YES]; - } - - // Some third-party macOS utilities check the zoom button's enabled state to - // determine whether to show custom UI on hover, so we disable it here to - // prevent them from doing so in a frameless app window. - [[window_ standardWindowButton:NSWindowZoomButton] setEnabled:NO]; - } + AddContentViewLayers(); } NativeWindowMac::~NativeWindowMac() { @@ -1320,6 +1291,63 @@ views::View* NativeWindowMac::GetContentsView() { return root_view_.get(); } +void NativeWindowMac::AddContentViewLayers() { + // Make sure the bottom corner is rounded for non-modal windows: + // http://crbug.com/396264. But do not enable it on OS X 10.9 for transparent + // window, otherwise a semi-transparent frame would show. + if (!(transparent() && base::mac::IsOS10_9()) && !is_modal()) { + base::scoped_nsobject background_layer([[CALayer alloc] init]); + [background_layer + setAutoresizingMask:kCALayerWidthSizable | kCALayerHeightSizable]; + [[window_ contentView] setLayer:background_layer]; + [[window_ contentView] setWantsLayer:YES]; + } + + if (!has_frame()) { + // In OSX 10.10, adding subviews to the root view for the NSView hierarchy + // produces warnings. To eliminate the warnings, we resize the contentView + // to fill the window, and add subviews to that. + // http://crbug.com/380412 + if (!original_set_frame_size) { + Class cl = [[window_ contentView] class]; + original_set_frame_size = class_replaceMethod( + cl, @selector(setFrameSize:), (IMP)SetFrameSize, "v@:{_NSSize=ff}"); + original_view_did_move_to_superview = + class_replaceMethod(cl, @selector(viewDidMoveToSuperview), + (IMP)ViewDidMoveToSuperview, "v@:"); + [[window_ contentView] viewDidMoveToWindow]; + } + + // The fullscreen button should always be hidden for frameless window. + [[window_ standardWindowButton:NSWindowFullScreenButton] setHidden:YES]; + + if (title_bar_style_ == CUSTOM_BUTTONS_ON_HOVER) { + buttons_view_.reset( + [[CustomWindowButtonView alloc] initWithFrame:NSZeroRect]); + [[window_ contentView] addSubview:buttons_view_]; + } else { + if (title_bar_style_ != NORMAL) { + if (base::mac::IsOS10_9()) { + ShowWindowButton(NSWindowZoomButton); + ShowWindowButton(NSWindowMiniaturizeButton); + ShowWindowButton(NSWindowCloseButton); + } + return; + } + + // Hide the window buttons. + [[window_ standardWindowButton:NSWindowZoomButton] setHidden:YES]; + [[window_ standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES]; + [[window_ standardWindowButton:NSWindowCloseButton] setHidden:YES]; + } + + // Some third-party macOS utilities check the zoom button's enabled state to + // determine whether to show custom UI on hover, so we disable it here to + // prevent them from doing so in a frameless app window. + [[window_ standardWindowButton:NSWindowZoomButton] setEnabled:NO]; + } +} + void NativeWindowMac::InternalSetParentWindow(NativeWindow* parent, bool attach) { if (is_modal()) @@ -1350,6 +1378,19 @@ void NativeWindowMac::SetForwardMouseMessages(bool forward) { [window_ setAcceptsMouseMovedEvents:forward]; } +void NativeWindowMac::OverrideNSWindowContentView() { + // When using `views::Widget` to hold WebContents, Chromium would use + // `BridgedContentView` as content view, which does not support draggable + // regions. In order to make draggable regions work, we have to replace the + // content view with a simple NSView. + container_view_.reset([[FullSizeContentView alloc] init]); + [container_view_ + setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [container_view_ setFrame:[[[window_ contentView] superview] bounds]]; + [window_ setContentView:container_view_]; + AddContentViewLayers(); +} + void NativeWindowMac::SetStyleMask(bool on, NSUInteger flag) { // Changing the styleMask of a frameless windows causes it to change size so // we explicitly disable resizing while setting it.