Bug 1400057 - Assemble vibrancy views in a container view that is placed below the window's content view, as a child view of the frame view. r?spohl
This seems to avoid vibrancy glitches in the titlebar on 10.13.
These glitches seem to be caused by a combination of the following:
- We're overriding methods on NSWindow that cause the window's contentView to
be sized to the full window size, including the titlebar.
- The NSVisualEffectViews are descendants of the window's contentView.
- The NSVisualEffectViews intersect the titlebar.
- Our window does not use the "full size content view" window style mask.
MozReview-Commit-ID: 4Vd36KtGXNq
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -576,16 +576,17 @@ protected:
void MaybeDrawTitlebar(mozilla::layers::GLManager* aManager);
// Redraw the contents of mTitlebarCGContext on the main thread, as
// determined by mDirtyTitlebarRegion.
void UpdateTitlebarCGContext();
LayoutDeviceIntRect RectContainingTitlebarControls();
void UpdateVibrancy(const nsTArray<ThemeGeometry>& aThemeGeometries);
+ NSView* EnsureVibrancyContainerView();
mozilla::VibrancyManager& EnsureVibrancyManager();
nsIWidget* GetWidgetForListenerEvents();
struct SwipeInfo {
bool wantsSwipe;
uint32_t allowedDirections;
};
@@ -597,16 +598,17 @@ protected:
protected:
NSView<mozView>* mView; // my parallel cocoa view (ChildView or NativeScrollbarView), [STRONG]
RefPtr<mozilla::widget::TextInputHandler> mTextInputHandler;
InputContext mInputContext;
NSView<mozView>* mParentView;
nsIWidget* mParentWidget;
+ NSView* mVibrancyContainerView;
#ifdef ACCESSIBILITY
// weak ref to this childview's associated mozAccessible for speed reasons
// (we get queried for it *a lot* but don't want to own it)
nsWeakPtr mAccessible;
#endif
// Protects the view from being teared down while a composition is in
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -240,16 +240,35 @@ static NSMutableDictionary* sNativeKeyEv
- (CGSRegionObj)_regionForOpaqueDescendants:(NSRect)aRect forMove:(BOOL)aForMove;
- (CGSRegionObj)_regionForOpaqueDescendants:(NSRect)aRect forMove:(BOOL)aForMove forUnderTitlebar:(BOOL)aForUnderTitlebar;
@end
@interface NSWindow(NSWindowShouldZoomOnDoubleClick)
+ (BOOL)_shouldZoomOnDoubleClick; // present on 10.7 and above
@end
+// This method is on NSThemeFrame starting with 10.10, but since NSThemeFrame
+// is not a public class, we declare the method on NSView instead. We only have
+// this declaration in order to avoid compiler warnings.
+@interface NSView(PrivateAddKnownSubviewMethod)
+ - (void)_addKnownSubview:(NSView*)aView positioned:(NSWindowOrderingMode)place relativeTo:(NSView*)otherView;
+@end
+
+@interface VibrancyContainerView : NSView
+@end
+
+@implementation VibrancyContainerView
+
+- (BOOL)isFlipped
+{
+ return YES;
+}
+
+@end
+
// Starting with 10.7 the bottom corners of all windows are rounded.
// Unfortunately, the standard rounding that OS X applies to OpenGL views
// does not use anti-aliasing and looks very crude. Since we want a smooth,
// anti-aliased curve, we'll draw it ourselves.
// Additionally, we need to turn off the OS-supplied rounding because it
// eats into our corner's curve. We do that by overriding an NSSurface method.
@interface NSSurface @end
@@ -350,16 +369,17 @@ struct SwipeEventQueue {
} // namespace mozilla
#pragma mark -
nsChildView::nsChildView() : nsBaseWidget()
, mView(nullptr)
, mParentView(nullptr)
, mParentWidget(nullptr)
+, mVibrancyContainerView(nil)
, mViewTearDownLock("ChildViewTearDown")
, mEffectsLock("WidgetEffects")
, mShowsResizeIndicator(false)
, mHasRoundedBottomCorners(false)
, mIsCoveringTitlebar(false)
, mIsFullscreen(false)
, mIsOpaque(false)
, mTitlebarCGContext(nullptr)
@@ -397,16 +417,22 @@ nsChildView::~nsChildView()
if (mAPZC && gfxPrefs::AsyncPanZoomSeparateEventThread()) {
gNumberOfWidgetsNeedingEventThread--;
if (gNumberOfWidgetsNeedingEventThread == 0) {
[EventThreadRunner stop];
}
}
+ if (mVibrancyContainerView) {
+ [mVibrancyContainerView removeFromSuperview];
+ [mVibrancyContainerView release];
+ mVibrancyContainerView = nil;
+ }
+
// An nsChildView object that was in use can be destroyed without Destroy()
// ever being called on it. So we also need to do a quick, safe cleanup
// here (it's too late to just call Destroy(), which can cause crashes).
// It's particularly important to make sure widgetDestroyed is called on our
// mView -- this method NULLs mView's mGeckoChild, and NULL checks on
// mGeckoChild are used throughout the ChildView class to tell if it's safe
// to use a ChildView object.
[mView widgetDestroyed]; // Safe if mView is nil.
@@ -2694,22 +2720,52 @@ nsChildView::VibrancyFillColorForThemeGe
{
if (VibrancyManager::SystemSupportsVibrancy()) {
return EnsureVibrancyManager().VibrancyFillColorForType(
ThemeGeometryTypeToVibrancyType(aThemeGeometryType));
}
return [NSColor whiteColor];
}
+NSView*
+nsChildView::EnsureVibrancyContainerView()
+{
+ MOZ_ASSERT(mView, "Only call this once we have a view!");
+ if (!mVibrancyContainerView) {
+ // Create an NSView that wraps all the visual effect views that make up
+ // our vibrant region.
+ NSWindow* window = [mView window];
+ NSView* frameView = [[window contentView] superview];
+ mVibrancyContainerView = [[VibrancyContainerView alloc] initWithFrame:[[window contentView] bounds]];
+ [mVibrancyContainerView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+ // Insert the wrapper view as a subview of the window's frame view, as a
+ // sibling to the window's content view.
+ // This isn't great, but it's the only way we found to have glitch-free
+ // vibrancy extend into the titlebar on 10.13 without the use of
+ // NSWindowStyleMaskFullSizeContentView.
+ // Once we work out how to use NSWindowStyleMaskFullSizeContentView without
+ // having our OpenGL rendering glitch out, we should switch to using that,
+ // and insert this wrapper view as a subview of the window's content view.
+ if ([frameView respondsToSelector:@selector(_addKnownSubview:positioned:relativeTo:)]) {
+ // 10.10+ prints a warning when we call addSubview on the frame view, so we
+ // silence the warning by calling a private method instead.
+ [frameView _addKnownSubview:mVibrancyContainerView positioned:NSWindowBelow relativeTo:nil];
+ } else {
+ [frameView addSubview:mVibrancyContainerView positioned:NSWindowBelow relativeTo:nil];
+ }
+ }
+ return mVibrancyContainerView;
+}
+
mozilla::VibrancyManager&
nsChildView::EnsureVibrancyManager()
{
MOZ_ASSERT(mView, "Only call this once we have a view!");
if (!mVibrancyManager) {
- mVibrancyManager = MakeUnique<VibrancyManager>(*this, mView);
+ mVibrancyManager = MakeUnique<VibrancyManager>(*this, EnsureVibrancyContainerView());
}
return *mVibrancyManager;
}
nsChildView::SwipeInfo
nsChildView::SendMayStartSwipe(const mozilla::PanGestureInput& aSwipeStartEvent)
{
nsCOMPtr<nsIWidget> kungFuDeathGrip(this);