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 draft
authorMarkus Stange <mstange@themasta.com>
Fri, 29 Sep 2017 15:56:00 -0400
changeset 672851 6427fcf25dc9418940d276f2246ae32e034fec79
parent 672850 ac1719be7ff52aa99b9b1087711397f6101b7abd
child 733939 d83dbea6954000a591656e24a5c0594e54f41314
push id82392
push userbmo:mstange@themasta.com
push dateFri, 29 Sep 2017 20:17:23 +0000
reviewersspohl
bugs1400057
milestone58.0a1
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
widget/cocoa/nsChildView.h
widget/cocoa/nsChildView.mm
--- 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);