diff options
author | Brian Smith <brian@dbsoft.org> | 2020-11-16 07:23:44 -0600 |
---|---|---|
committer | Moonchild <moonchild@palemoon.org> | 2020-11-19 22:44:22 +0000 |
commit | 4f0f4aa8794ef12260d9d973b40ad3b942b69b72 (patch) | |
tree | c17d559d242f6b7c4a045a5845b78e9d81d841f1 | |
parent | 53ca366d2b34f237e42f8108d7a71586b5285e44 (diff) | |
download | UXP-4f0f4aa8794ef12260d9d973b40ad3b942b69b72.tar UXP-4f0f4aa8794ef12260d9d973b40ad3b942b69b72.tar.gz UXP-4f0f4aa8794ef12260d9d973b40ad3b942b69b72.tar.lz UXP-4f0f4aa8794ef12260d9d973b40ad3b942b69b72.tar.xz UXP-4f0f4aa8794ef12260d9d973b40ad3b942b69b72.zip |
Issue #1667 - Part 2: Add MacOS 11.0 Big Sur widget compatibility
This involves refactoring the vibrancy and OpenGL/Pixel rendering changes contained
in the following Mozilla meta bugs: 1496823 and 1491445
Also add Big Sur to the features tests and update popup menu look and feel based
on Mozilla bug 1656301.
-rw-r--r-- | widget/cocoa/VibrancyManager.h | 23 | ||||
-rw-r--r-- | widget/cocoa/VibrancyManager.mm | 74 | ||||
-rw-r--r-- | widget/cocoa/mozView.h | 5 | ||||
-rw-r--r-- | widget/cocoa/nsChildView.h | 30 | ||||
-rw-r--r-- | widget/cocoa/nsChildView.mm | 593 | ||||
-rw-r--r-- | widget/cocoa/nsCocoaFeatures.h | 1 | ||||
-rw-r--r-- | widget/cocoa/nsCocoaFeatures.mm | 10 | ||||
-rw-r--r-- | widget/cocoa/nsCocoaWindow.h | 5 | ||||
-rw-r--r-- | widget/cocoa/nsCocoaWindow.mm | 119 | ||||
-rw-r--r-- | widget/cocoa/nsNativeThemeCocoa.mm | 35 |
10 files changed, 286 insertions, 609 deletions
diff --git a/widget/cocoa/VibrancyManager.h b/widget/cocoa/VibrancyManager.h index 7a7ea3af1..cae57a269 100644 --- a/widget/cocoa/VibrancyManager.h +++ b/widget/cocoa/VibrancyManager.h @@ -78,14 +78,6 @@ public: bool HasVibrantRegions() { return !mVibrantRegions.IsEmpty(); } /** - * Clear the vibrant areas that we know about. - * The clearing happens in the current NSGraphicsContext. If you call this - * from within an -[NSView drawRect:] implementation, the currrent - * NSGraphicsContext is already correctly set to the window drawing context. - */ - void ClearVibrantAreas() const; - - /** * Return the fill color that should be drawn on top of the cleared window * parts. Usually this would be drawn by -[NSVisualEffectView drawRect:]. * The returned color is opaque if the system-wide "Reduce transparency" @@ -106,10 +98,19 @@ public: */ static bool SystemSupportsVibrancy(); -protected: - void ClearVibrantRegion(const LayoutDeviceIntRegion& aVibrantRegion) const; - NSView* CreateEffectView(VibrancyType aType); + /** + * Create an NSVisualEffectView for the specified vibrancy type. The return + * value is not autoreleased. We return an object of type NSView* because we + * compile with an SDK that does not contain a definition for + * NSVisualEffectView. + * @param aIsContainer Whether this NSView will have child views. This value + * affects hit testing: Container views will pass through + * hit testing requests to their children, and leaf views + * will be transparent to hit testing. + */ + static NSView* CreateEffectView(VibrancyType aType, BOOL aIsContainer = NO); +protected: const nsChildView& mCoordinateConverter; NSView* mContainerView; nsClassHashtable<nsUint32HashKey, ViewRegion> mVibrantRegions; diff --git a/widget/cocoa/VibrancyManager.mm b/widget/cocoa/VibrancyManager.mm index b6176de2b..93a774254 100644 --- a/widget/cocoa/VibrancyManager.mm +++ b/widget/cocoa/VibrancyManager.mm @@ -24,24 +24,6 @@ VibrancyManager::UpdateVibrantRegion(VibrancyType aType, }); } -void -VibrancyManager::ClearVibrantAreas() const -{ - for (auto iter = mVibrantRegions.ConstIter(); !iter.Done(); iter.Next()) { - ClearVibrantRegion(iter.UserData()->Region()); - } -} - -void -VibrancyManager::ClearVibrantRegion(const LayoutDeviceIntRegion& aVibrantRegion) const -{ - [[NSColor clearColor] set]; - - for (auto iter = aVibrantRegion.RectIter(); !iter.Done(); iter.Next()) { - NSRectFill(mCoordinateConverter.DevPixelsToCocoaPoints(iter.Get())); - } -} - @interface NSView(CurrentFillColor) - (NSColor*)_currentFillColor; @end @@ -65,9 +47,8 @@ VibrancyManager::VibrancyFillColorForType(VibrancyType aType) NSView* view = mVibrantRegions.LookupOrAdd(uint32_t(aType))->GetAnyView(); if (view && [view respondsToSelector:@selector(_currentFillColor)]) { - // -[NSVisualEffectView _currentFillColor] is the color that our view - // would draw during its drawRect implementation, if we hadn't - // disabled that. + // -[NSVisualEffectView _currentFillColor] is the color that the view + // draws in its drawRect implementation. return AdjustedColor([view _currentFillColor], aType); } return [NSColor whiteColor]; @@ -88,19 +69,6 @@ VibrancyManager::VibrancyFontSmoothingBackgroundColorForType(VibrancyType aType) return [NSColor clearColor]; } -static void -DrawRectNothing(id self, SEL _cmd, NSRect aRect) -{ - // The super implementation would clear the background. - // That's fine for views that are placed below their content, but our - // setup is different: Our drawn content is drawn to mContainerView, which - // sits below this EffectView. So we must not clear the background here, - // because we'd erase that drawn content. - // Of course the regular content drawing still needs to clear the background - // behind vibrant areas. This is taken care of by having nsNativeThemeCocoa - // return true from NeedToClearBackgroundBehindWidget for vibrant widgets. -} - static NSView* HitTestNil(id self, SEL _cmd, NSPoint aPoint) { @@ -116,21 +84,20 @@ AllowsVibrancyYes(id self, SEL _cmd) } static Class -CreateEffectViewClass(BOOL aForegroundVibrancy) +CreateEffectViewClass(BOOL aForegroundVibrancy, BOOL aIsContainer) { - // Create a class called EffectView that inherits from NSVisualEffectView - // and overrides the methods -[NSVisualEffectView drawRect:] and - // -[NSView hitTest:]. + // Create a class that inherits from NSVisualEffectView and overrides the + // methods -[NSView hitTest:] and -[NSVisualEffectView allowsVibrancy]. Class NSVisualEffectViewClass = NSClassFromString(@"NSVisualEffectView"); const char* className = aForegroundVibrancy ? "EffectViewWithForegroundVibrancy" : "EffectViewWithoutForegroundVibrancy"; Class EffectViewClass = objc_allocateClassPair(NSVisualEffectViewClass, className, 0); - class_addMethod(EffectViewClass, @selector(drawRect:), (IMP)DrawRectNothing, - "v@:{CGRect={CGPoint=dd}{CGSize=dd}}"); - class_addMethod(EffectViewClass, @selector(hitTest:), (IMP)HitTestNil, - "@@:{CGPoint=dd}"); + if (!aIsContainer) { + class_addMethod(EffectViewClass, @selector(hitTest:), (IMP)HitTestNil, + "@@:{CGPoint=dd}"); + } if (aForegroundVibrancy) { - // Also override the -[NSView allowsVibrancy] method to return YES. + // Override the -[NSView allowsVibrancy] method to return YES. class_addMethod(EffectViewClass, @selector(allowsVibrancy), (IMP)AllowsVibrancyYes, "I@:"); } return EffectViewClass; @@ -217,13 +184,20 @@ enum { @end NSView* -VibrancyManager::CreateEffectView(VibrancyType aType) -{ - static Class EffectViewClassWithoutForegroundVibrancy = CreateEffectViewClass(NO); - static Class EffectViewClassWithForegroundVibrancy = CreateEffectViewClass(YES); - - Class EffectViewClass = HasVibrantForeground(aType) - ? EffectViewClassWithForegroundVibrancy : EffectViewClassWithoutForegroundVibrancy; +VibrancyManager::CreateEffectView(VibrancyType aType, BOOL aIsContainer) +{ + static Class EffectViewWithoutForegroundVibrancy = CreateEffectViewClass(NO, NO); + static Class EffectViewWithForegroundVibrancy = CreateEffectViewClass(YES, NO); + static Class EffectViewContainer = CreateEffectViewClass(NO, YES); + + // Pick the right NSVisualEffectView subclass for the desired vibrancy mode. + // For "container" views, never use foreground vibrancy, because returning + // YES from allowsVibrancy forces on foreground vibrancy for all descendant + // views which can have unintended effects. + Class EffectViewClass = aIsContainer + ? EffectViewContainer + : (HasVibrantForeground(aType) ? EffectViewWithForegroundVibrancy + : EffectViewWithoutForegroundVibrancy); NSView* effectView = [[EffectViewClass alloc] initWithFrame:NSZeroRect]; [effectView performSelector:@selector(setAppearance:) withObject:AppearanceForVibrancyType(aType)]; diff --git a/widget/cocoa/mozView.h b/widget/cocoa/mozView.h index 9e94e3ab4..bd054893b 100644 --- a/widget/cocoa/mozView.h +++ b/widget/cocoa/mozView.h @@ -34,11 +34,6 @@ class TextInputHandler; // return a context menu for this view - (NSMenu*)contextMenu; - // Allows callers to do a delayed invalidate (e.g., if an invalidate - // happens during drawing) -- (void)setNeedsPendingDisplay; -- (void)setNeedsPendingDisplayInRect:(NSRect)invalidRect; - // called when our corresponding Gecko view goes away - (void)widgetDestroyed; diff --git a/widget/cocoa/nsChildView.h b/widget/cocoa/nsChildView.h index f6fb44633..2817c8d41 100644 --- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -56,6 +56,8 @@ class WidgetRenderingContext; } // namespace widget } // namespace mozilla +@class PixelHostingView; + @interface NSEvent (Undocumented) // Return Cocoa event's corresponding Carbon event. Not initialized (on @@ -139,11 +141,6 @@ class WidgetRenderingContext; // when acceptsFirstMouse: is called, we store the event here (strong) NSEvent* mClickThroughMouseDownEvent; - // rects that were invalidated during a draw, so have pending drawing - NSMutableArray* mPendingDirtyRects; - BOOL mPendingFullDisplay; - BOOL mPendingDisplay; - // WheelStart/Stop events should always come in pairs. This BOOL records the // last received event so that, when we receive one of the events, we make sure // to send its pair event first, in case we didn't yet for any reason. @@ -185,8 +182,6 @@ class WidgetRenderingContext; float mCumulativeMagnification; float mCumulativeRotation; - BOOL mWaitingForPaint; - #ifdef __LP64__ // Support for fluid swipe tracking. BOOL* mCancelSwipeAnimation; @@ -198,6 +193,15 @@ class WidgetRenderingContext; // The mask image that's used when painting into the titlebar using basic // CGContext painting (i.e. non-accelerated). CGImageRef mTopLeftCornerMask; + + // Subviews of self, which act as container views for vibrancy views and + // non-draggable views. + NSView* mVibrancyViewsContainer; // [STRONG] + NSView* mNonDraggableViewsContainer; // [STRONG] + + // The view that does our drawing. This is a subview of self so that it can + // be ordered on top of mVibrancyViewsContainer. + PixelHostingView* mPixelHostingView; } // class initialization @@ -226,6 +230,10 @@ class WidgetRenderingContext; - (bool)preRender:(NSOpenGLContext *)aGLContext; - (void)postRender:(NSOpenGLContext *)aGLContext; +- (NSView*)vibrancyViewsContainer; +- (NSView*)nonDraggableViewsContainer; +- (NSView*)pixelHostingView; + - (BOOL)isCoveringTitlebar; - (void)viewWillStartLiveResize; @@ -252,7 +260,6 @@ class WidgetRenderingContext; - (void)endGestureWithEvent:(NSEvent *)anEvent; - (void)scrollWheel:(NSEvent *)anEvent; -- (void)handleAsyncScrollEvent:(CGEventRef)cgEvent ofType:(CGEventType)type; - (void)setUsingOMTCompositor:(BOOL)aUseOMTC; @@ -507,8 +514,6 @@ public: void CleanupRemoteDrawing() override; bool InitCompositor(mozilla::layers::Compositor* aCompositor) override; - IAPZCTreeManager* APZCTM() { return mAPZC ; } - NS_IMETHOD StartPluginIME(const mozilla::WidgetKeyboardEvent& aKeyboardEvent, int32_t aPanelX, int32_t aPanelY, nsString& aCommitted) override; @@ -529,9 +534,6 @@ protected: void ReportMoveEvent(); void ReportSizeEvent(); - // override to create different kinds of child views. Autoreleases, so - // caller must retain. - virtual NSView* CreateCocoaView(NSRect inFrame); void TearDownView(); virtual already_AddRefed<nsIWidget> @@ -574,7 +576,7 @@ protected: protected: - NSView<mozView>* mView; // my parallel cocoa view (ChildView or NativeScrollbarView), [STRONG] + ChildView<mozView>* mView; // my parallel cocoa view, [STRONG] RefPtr<mozilla::widget::TextInputHandler> mTextInputHandler; InputContext mInputContext; diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 1a257d899..95547a13e 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -146,7 +146,10 @@ bool gUserCancelledDrag = false; uint32_t nsChildView::sLastInputEventCount = 0; -static uint32_t gNumberOfWidgetsNeedingEventThread = 0; +// The view that will do our drawing or host our NSOpenGLContext or Core Animation layer. +@interface PixelHostingView : NSView { +} +@end @interface ChildView(Private) @@ -163,14 +166,9 @@ static uint32_t gNumberOfWidgetsNeedingEventThread = 0; - (BOOL)isRectObscuredBySubview:(NSRect)inRect; -- (void)processPendingRedraws; - -- (void)drawRect:(NSRect)aRect inContext:(CGContextRef)aContext; - (LayoutDeviceIntRegion)nativeDirtyRegionWithBoundingRect:(NSRect)aRect; -- (BOOL)isUsingMainThreadOpenGL; - (BOOL)isUsingOpenGL; - (void)drawUsingOpenGL; -- (void)drawUsingOpenGLCallback; - (BOOL)hasRoundedBottomCorners; - (CGFloat)cornerRadius; @@ -195,7 +193,6 @@ static uint32_t gNumberOfWidgetsNeedingEventThread = 0; - (LayoutDeviceIntPoint)convertWindowCoordinates:(NSPoint)aPoint; - (LayoutDeviceIntPoint)convertWindowCoordinatesRoundDown:(NSPoint)aPoint; -- (IAPZCTreeManager*)apzctm; - (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent; - (void)updateWindowDraggableState; @@ -204,26 +201,10 @@ static uint32_t gNumberOfWidgetsNeedingEventThread = 0; @end -@interface EventThreadRunner : NSObject -{ - NSThread* mThread; -} -- (id)init; - -+ (void)start; -+ (void)stop; - -@end - @interface NSView(NSThemeFrameCornerRadius) - (float)roundedCornerRadius; @end -@interface NSView(DraggableRegion) -- (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 @@ -380,13 +361,6 @@ nsChildView::~nsChildView() DestroyCompositor(); - if (mAPZC && gfxPrefs::AsyncPanZoomSeparateEventThread()) { - gNumberOfWidgetsNeedingEventThread--; - if (gNumberOfWidgetsNeedingEventThread == 0) { - [EventThreadRunner stop]; - } - } - // 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). @@ -455,7 +429,8 @@ nsChildView::Create(nsIWidget* aParent, // that NS_NATIVE_WIDGET is the NSView. CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mParentView); NSRect r = nsCocoaUtils::DevPixelsToCocoaPoints(mBounds, scaleFactor); - mView = [(NSView<mozView>*)CreateCocoaView(r) retain]; + mView = [[[[ChildView alloc] initWithFrame:r geckoChild:this] autorelease] retain]; + if (!mView) { return NS_ERROR_FAILURE; } @@ -488,17 +463,6 @@ nsChildView::Create(nsIWidget* aParent, NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } -// Creates the appropriate child view. Override to create something other than -// our |ChildView| object. Autoreleases, so caller must retain. -NSView* -nsChildView::CreateCocoaView(NSRect inFrame) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - - return [[[ChildView alloc] initWithFrame:inFrame geckoChild:this] autorelease]; - - NS_OBJC_END_TRY_ABORT_BLOCK_NIL; -} void nsChildView::TearDownView() { @@ -982,8 +946,9 @@ NS_IMETHODIMP nsChildView::Resize(double aWidth, double aHeight, bool aRepaint) [mView setFrame:DevPixelsToCocoaPoints(mBounds)]; }); - if (mVisible && aRepaint) - [mView setNeedsDisplay:YES]; + if (mVisible && aRepaint) { + [[mView pixelHostingView] setNeedsDisplay:YES]; + } NotifyRollupGeometryChange(); ReportSizeEvent(); @@ -1021,8 +986,9 @@ NS_IMETHODIMP nsChildView::Resize(double aX, double aY, [mView setFrame:DevPixelsToCocoaPoints(mBounds)]; }); - if (mVisible && aRepaint) - [mView setNeedsDisplay:YES]; + if (mVisible && aRepaint) { + [[mView pixelHostingView] setNeedsDisplay:YES]; + } NotifyRollupGeometryChange(); if (isMoving) { @@ -1341,14 +1307,7 @@ NS_IMETHODIMP nsChildView::Invalidate(const LayoutDeviceIntRect& aRect) NS_ASSERTION(GetLayerManager()->GetBackendType() != LayersBackend::LAYERS_CLIENT, "Shouldn't need to invalidate with accelerated OMTC layers!"); - if ([NSView focusView]) { - // if a view is focussed (i.e. being drawn), then postpone the invalidate so that we - // don't lose it. - [mView setNeedsPendingDisplayInRect:DevPixelsToCocoaPoints(aRect)]; - } - else { - [mView setNeedsDisplayInRect:DevPixelsToCocoaPoints(aRect)]; - } + [[mView pixelHostingView] setNeedsDisplayInRect:DevPixelsToCocoaPoints(aRect)]; return NS_OK; @@ -1920,13 +1879,6 @@ void nsChildView::ConfigureAPZCTreeManager() { nsBaseWidget::ConfigureAPZCTreeManager(); - - if (gfxPrefs::AsyncPanZoomSeparateEventThread()) { - if (gNumberOfWidgetsNeedingEventThread == 0) { - [EventThreadRunner start]; - } - gNumberOfWidgetsNeedingEventThread++; - } } void @@ -2557,14 +2509,6 @@ nsChildView::UpdateVibrancy(const nsTArray<ThemeGeometry>& aThemeGeometries) vm.UpdateVibrantRegion(VibrancyType::DARK, vibrantDarkRegion); } -void -nsChildView::ClearVibrantAreas() -{ - if (VibrancyManager::SystemSupportsVibrancy()) { - EnsureVibrancyManager().ClearVibrantAreas(); - } -} - static VibrancyType ThemeGeometryTypeToVibrancyType(nsITheme::ThemeGeometryType aThemeGeometryType) { @@ -2617,7 +2561,7 @@ nsChildView::EnsureVibrancyManager() { MOZ_ASSERT(mView, "Only call this once we have a view!"); if (!mVibrancyManager) { - mVibrancyManager = MakeUnique<VibrancyManager>(*this, mView); + mVibrancyManager = MakeUnique<VibrancyManager>(*this, [mView vibrancyViewsContainer]); } return *mVibrancyManager; } @@ -2787,9 +2731,10 @@ nsChildView::UpdateWindowDraggingRegion(const LayoutDeviceIntRegion& aRegion) // Suppress calls to setNeedsDisplay during NSView geometry changes. ManipulateViewWithoutNeedingDisplay(mView, ^() { - changed = mNonDraggableRegion.UpdateRegion(nonDraggable, *this, mView, ^() { - return [[NonDraggableView alloc] initWithFrame:NSZeroRect]; - }); + changed = mNonDraggableRegion.UpdateRegion( + nonDraggable, *this, [mView nonDraggableViewsContainer], ^() { + return [[NonDraggableView alloc] initWithFrame:NSZeroRect]; + }); }); if (changed) { @@ -3160,6 +3105,31 @@ private: #pragma mark - +// ViewRegionContainerView is a view class for certain subviews of ChildView +// which contain the NSViews created for ViewRegions (see ViewRegion.h). +// It doesn't do anything interesting, it only acts as a container so that it's +// easier for ChildView to control the z order of its children. +@interface ViewRegionContainerView : NSView { +} +@end + +@implementation ViewRegionContainerView + +- (NSView*)hitTest:(NSPoint)aPoint { + return nil; // Be transparent to mouse events. +} + +- (BOOL)isFlipped { + return [[self superview] isFlipped]; +} + +- (BOOL)mouseDownCanMoveWindow { + return [[self superview] mouseDownCanMoveWindow]; +} + +@end +; + @implementation ChildView // globalDragPboard is non-null during native drag sessions that did not originate @@ -3215,7 +3185,6 @@ NSEvent* gLastDragMouseDownEvent = nil; if ((self = [super initWithFrame:inFrame])) { mGeckoChild = inChild; - mPendingDisplay = NO; mBlockedLastMouseDown = NO; mExpectingWheelStop = NO; @@ -3236,6 +3205,20 @@ NSEvent* gLastDragMouseDownEvent = nil; mCancelSwipeAnimation = nil; #endif + mNonDraggableViewsContainer = [[ViewRegionContainerView alloc] initWithFrame:[self bounds]]; + mVibrancyViewsContainer = [[ViewRegionContainerView alloc] initWithFrame:[self bounds]]; + + [mNonDraggableViewsContainer setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [mVibrancyViewsContainer setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + [self addSubview:mNonDraggableViewsContainer]; + [self addSubview:mVibrancyViewsContainer]; + + mPixelHostingView = [[PixelHostingView alloc] initWithFrame:[self bounds]]; + [mPixelHostingView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + [self addSubview:mPixelHostingView]; + mTopLeftCornerMask = NULL; } @@ -3250,10 +3233,9 @@ NSEvent* gLastDragMouseDownEvent = nil; selector:@selector(systemMetricsChanged) name:NSSystemColorsDidChangeNotification object:nil]; - // TODO: replace the string with the constant once we build with the 10.7 SDK [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scrollbarSystemMetricChanged) - name:@"NSPreferredScrollerStyleDidChangeNotification" + name:NSPreferredScrollerStyleDidChangeNotification object:nil]; [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(systemMetricsChanged) @@ -3263,7 +3245,7 @@ NSEvent* gLastDragMouseDownEvent = nil; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_surfaceNeedsUpdate:) name:NSViewGlobalFrameDidChangeNotification - object:self]; + object:mPixelHostingView]; return self; @@ -3348,12 +3330,23 @@ NSEvent* gLastDragMouseDownEvent = nil; NS_OBJC_END_TRY_ABORT_BLOCK; } +- (NSView*)vibrancyViewsContainer { + return mVibrancyViewsContainer; +} + +- (NSView*)nonDraggableViewsContainer { + return mNonDraggableViewsContainer; +} + +- (NSView*)pixelHostingView { + return mPixelHostingView; +} + - (void)dealloc { NS_OBJC_BEGIN_TRY_ABORT_BLOCK; [mGLContext release]; - [mPendingDirtyRects release]; [mLastMouseDownEvent release]; [mLastKeyDownEvent release]; [mClickThroughMouseDownEvent release]; @@ -3362,6 +3355,12 @@ NSEvent* gLastDragMouseDownEvent = nil; [[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSDistributedNotificationCenter defaultCenter] removeObserver:self]; + [mVibrancyViewsContainer removeFromSuperview]; + [mVibrancyViewsContainer release]; + [mNonDraggableViewsContainer removeFromSuperview]; + [mNonDraggableViewsContainer release]; + [mPixelHostingView removeFromSuperview]; + [mPixelHostingView release]; [super dealloc]; @@ -3408,82 +3407,6 @@ NSEvent* gLastDragMouseDownEvent = nil; } } -- (void)setNeedsPendingDisplay -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - mPendingFullDisplay = YES; - if (!mPendingDisplay) { - [self performSelector:@selector(processPendingRedraws) withObject:nil afterDelay:0]; - mPendingDisplay = YES; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)setNeedsPendingDisplayInRect:(NSRect)invalidRect -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (!mPendingDirtyRects) - mPendingDirtyRects = [[NSMutableArray alloc] initWithCapacity:1]; - [mPendingDirtyRects addObject:[NSValue valueWithRect:invalidRect]]; - if (!mPendingDisplay) { - [self performSelector:@selector(processPendingRedraws) withObject:nil afterDelay:0]; - mPendingDisplay = YES; - } - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -// Clears the queue of any pending invalides -- (void)processPendingRedraws -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - if (mPendingFullDisplay) { - [self setNeedsDisplay:YES]; - } - else if (mPendingDirtyRects) { - unsigned int count = [mPendingDirtyRects count]; - for (unsigned int i = 0; i < count; ++i) { - [self setNeedsDisplayInRect:[[mPendingDirtyRects objectAtIndex:i] rectValue]]; - } - } - mPendingFullDisplay = NO; - mPendingDisplay = NO; - [mPendingDirtyRects release]; - mPendingDirtyRects = nil; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - -- (void)setNeedsDisplayInRect:(NSRect)aRect -{ - if (![self isUsingOpenGL]) { - [super setNeedsDisplayInRect:aRect]; - return; - } - - if ([[self window] isVisible] && [self isUsingMainThreadOpenGL]) { - // Draw without calling drawRect. This prevent us from - // needing to access the normal window buffer surface unnecessarily, so we - // waste less time synchronizing the two surfaces. (These synchronizations - // show up in a profiler as CGSDeviceLock / _CGSLockWindow / - // _CGSSynchronizeWindowBackingStore.) It also means that Cocoa doesn't - // have any potentially expensive invalid rect management for us. - if (!mWaitingForPaint) { - mWaitingForPaint = YES; - // Use NSRunLoopCommonModes instead of the default NSDefaultRunLoopMode - // so that the timer also fires while a native menu is open. - [self performSelector:@selector(drawUsingOpenGLCallback) - withObject:nil - afterDelay:0 - inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; - } - } -} - - (NSString*)description { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; @@ -3537,25 +3460,6 @@ NSEvent* gLastDragMouseDownEvent = nil; return YES; } -- (void)scrollRect:(NSRect)aRect by:(NSSize)offset -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // Update any pending dirty rects to reflect the new scroll position - if (mPendingDirtyRects) { - unsigned int count = [mPendingDirtyRects count]; - for (unsigned int i = 0; i < count; ++i) { - NSRect oldRect = [[mPendingDirtyRects objectAtIndex:i] rectValue]; - NSRect newRect = NSOffsetRect(oldRect, offset.width, offset.height); - [mPendingDirtyRects replaceObjectAtIndex:i - withObject:[NSValue valueWithRect:newRect]]; - } - } - [super scrollRect:aRect by:offset]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} - - (BOOL)mouseDownCanMoveWindow { // Return YES so that parts of this view can be draggable. The non-draggable @@ -3567,7 +3471,7 @@ NSEvent* gLastDragMouseDownEvent = nil; -(void)updateGLContext { - [mGLContext setView:self]; + [mGLContext setView:mPixelHostingView]; [mGLContext update]; } @@ -3580,11 +3484,6 @@ NSEvent* gLastDragMouseDownEvent = nil; } } -- (BOOL)wantsBestResolutionOpenGLSurface -{ - return nsCocoaUtils::HiDPIEnabled() ? YES : NO; -} - - (void)viewDidChangeBackingProperties { [super viewDidChangeBackingProperties]; @@ -3646,7 +3545,7 @@ NSEvent* gLastDragMouseDownEvent = nil; LayoutDeviceIntRect boundingRect = mGeckoChild->CocoaPointsToDevPixels(aRect); const NSRect *rects; NSInteger count; - [self getRectsBeingDrawn:&rects count:&count]; + [mPixelHostingView getRectsBeingDrawn:&rects count:&count]; if (count > MAX_RECTS_IN_REGION) { return boundingRect; @@ -3662,53 +3561,28 @@ NSEvent* gLastDragMouseDownEvent = nil; // The display system has told us that a portion of our view is dirty. Tell // gecko to paint it -- (void)drawRect:(NSRect)aRect +// This method is called from mPixelHostingView's drawRect handler. +- (void)doDrawRect:(NSRect)aRect { CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; - [self drawRect:aRect inContext:cgContext]; - - // If we're a transparent window and our contents have changed, we need - // to make sure the shadow is updated to the new contents. - if ([[self window] isKindOfClass:[BaseWindow class]]) { - [(BaseWindow*)[self window] deferredInvalidateShadow]; - } -} -- (void)drawRect:(NSRect)aRect inContext:(CGContextRef)aContext -{ if (!mGeckoChild || !mGeckoChild->IsVisible()) return; -#ifdef DEBUG_UPDATE - LayoutDeviceIntRect geckoBounds = mGeckoChild->GetBounds(); - - fprintf (stderr, "---- Update[%p][%p] [%f %f %f %f] cgc: %p\n gecko bounds: [%d %d %d %d]\n", - self, mGeckoChild, - aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height, aContext, - geckoBounds.x, geckoBounds.y, geckoBounds.width, geckoBounds.height); - - CGAffineTransform xform = CGContextGetCTM(aContext); - fprintf (stderr, " xform in: [%f %f %f %f %f %f]\n", xform.a, xform.b, xform.c, xform.d, xform.tx, xform.ty); -#endif - if ([self isUsingOpenGL]) { - // For Gecko-initiated repaints in OpenGL mode, drawUsingOpenGL is - // directly called from a delayed perform callback - without going through - // drawRect. - // Paints that come through here are triggered by something that Cocoa - // controls, for example by window resizing or window focus changes. - // Since this view is usually declared as opaque, the window's pixel // buffer may now contain garbage which we need to prevent from reaching // the screen. The only place where garbage can show is in the window // corners and the vibrant regions of the window - the rest of the window // is covered by opaque content in our OpenGL surface. // So we need to clear the pixel buffer contents in these areas. - mGeckoChild->ClearVibrantAreas(); [self clearCorners]; - // Do GL composition and return. - [self drawUsingOpenGL]; + // Force a sync OMTC composite into the OpenGL context and return. + LayoutDeviceIntRect geckoBounds = mGeckoChild->GetBounds(); + LayoutDeviceIntRegion region(geckoBounds); + + mGeckoChild->PaintWindow(region); return; } @@ -3720,54 +3594,31 @@ NSEvent* gLastDragMouseDownEvent = nil; // multiple dev pixels. But Gecko expects its supplied context to be scaled // to device pixels, so we need to reverse the scaling. double scale = mGeckoChild->BackingScaleFactor(); - CGContextSaveGState(aContext); - CGContextScaleCTM(aContext, 1.0 / scale, 1.0 / scale); + CGContextSaveGState(cgContext); + CGContextScaleCTM(cgContext, 1.0 / scale, 1.0 / scale); NSSize viewSize = [self bounds].size; gfx::IntSize backingSize = gfx::IntSize::Truncate(viewSize.width * scale, viewSize.height * scale); LayoutDeviceIntRegion region = [self nativeDirtyRegionWithBoundingRect:aRect]; - bool painted = mGeckoChild->PaintWindowInContext(aContext, region, backingSize); + bool painted = mGeckoChild->PaintWindowInContext(cgContext, region, backingSize); // Undo the scale transform so that from now on the context is in // CocoaPoints again. - CGContextRestoreGState(aContext); + CGContextRestoreGState(cgContext); - if (!painted && [self isOpaque]) { + if (!painted && [mPixelHostingView isOpaque]) { // Gecko refused to draw, but we've claimed to be opaque, so we have to // draw something--fill with white. - CGContextSetRGBFillColor(aContext, 1, 1, 1, 1); - CGContextFillRect(aContext, NSRectToCGRect(aRect)); + CGContextSetRGBFillColor(cgContext, 1, 1, 1, 1); + CGContextFillRect(cgContext, NSRectToCGRect(aRect)); } if ([self isCoveringTitlebar]) { [self drawTitleString]; [self drawTitlebarHighlight]; - [self maskTopCornersInContext:aContext]; + [self maskTopCornersInContext:cgContext]; } - -#ifdef DEBUG_UPDATE - fprintf (stderr, "---- update done ----\n"); - -#if 0 - CGContextSetRGBStrokeColor (aContext, - ((((unsigned long)self) & 0xff)) / 255.0, - ((((unsigned long)self) & 0xff00) >> 8) / 255.0, - ((((unsigned long)self) & 0xff0000) >> 16) / 255.0, - 0.5); -#endif - CGContextSetRGBStrokeColor(aContext, 1, 0, 0, 0.8); - CGContextSetLineWidth(aContext, 4.0); - CGContextStrokeRect(aContext, NSRectToCGRect(aRect)); -#endif -} - -- (BOOL)isUsingMainThreadOpenGL -{ - if (!mGeckoChild || ![self window]) - return NO; - - return mGeckoChild->GetLayerManager(nullptr)->GetBackendType() == mozilla::layers::LayersBackend::LAYERS_OPENGL; } - (BOOL)isUsingOpenGL @@ -3775,33 +3626,9 @@ NSEvent* gLastDragMouseDownEvent = nil; if (!mGeckoChild || ![self window]) return NO; - return mGLContext || mUsingOMTCompositor || [self isUsingMainThreadOpenGL]; -} - -- (void)drawUsingOpenGL -{ - PROFILER_LABEL("ChildView", "drawUsingOpenGL", - js::ProfileEntry::Category::GRAPHICS); - - if (![self isUsingOpenGL] || !mGeckoChild->IsVisible()) - return; - - mWaitingForPaint = NO; - - LayoutDeviceIntRect geckoBounds = mGeckoChild->GetBounds(); - LayoutDeviceIntRegion region(geckoBounds); - - mGeckoChild->PaintWindow(region); + return mGLContext || mUsingOMTCompositor; } -// Called asynchronously after setNeedsDisplay in order to avoid entering the -// normal drawing machinery. -- (void)drawUsingOpenGLCallback -{ - if (mWaitingForPaint) { - [self drawUsingOpenGL]; - } -} - (BOOL)hasRoundedBottomCorners { @@ -4803,12 +4630,6 @@ GetIntegerDeltaForEvent(NSEvent* aEvent) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - if (gfxPrefs::AsyncPanZoomSeparateEventThread() && [self apzctm]) { - // Disable main-thread scrolling completely when using APZ with the - // separate event thread. This is bug 1013412. - return; - } - nsAutoRetainCocoaObject kungFuDeathGrip(self); ChildViewMouseTracker::MouseScrolled(theEvent); @@ -4928,105 +4749,6 @@ GetIntegerDeltaForEvent(NSEvent* aEvent) NS_OBJC_END_TRY_ABORT_BLOCK; } -- (void)handleAsyncScrollEvent:(CGEventRef)cgEvent ofType:(CGEventType)type -{ - IAPZCTreeManager* apzctm = [self apzctm]; - if (!apzctm) { - return; - } - - CGPoint loc = CGEventGetLocation(cgEvent); - loc.y = nsCocoaUtils::FlippedScreenY(loc.y); - NSPoint locationInWindow = - nsCocoaUtils::ConvertPointFromScreen([self window], NSPointFromCGPoint(loc)); - ScreenIntPoint location = ViewAs<ScreenPixel>( - [self convertWindowCoordinates:locationInWindow], - PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent); - - static NSTimeInterval sStartTime = [NSDate timeIntervalSinceReferenceDate]; - static TimeStamp sStartTimeStamp = TimeStamp::Now(); - - if (type == kCGEventScrollWheel) { - NSEvent* event = [NSEvent eventWithCGEvent:cgEvent]; - NSEventPhase phase = nsCocoaUtils::EventPhase(event); - NSEventPhase momentumPhase = nsCocoaUtils::EventMomentumPhase(event); - CGFloat pixelDeltaX = 0, pixelDeltaY = 0; - nsCocoaUtils::GetScrollingDeltas(event, &pixelDeltaX, &pixelDeltaY); - uint32_t eventTime = ([event timestamp] - sStartTime) * 1000; - TimeStamp eventTimeStamp = sStartTimeStamp + - TimeDuration::FromSeconds([event timestamp] - sStartTime); - NSPoint locationInWindowMoved = NSMakePoint( - locationInWindow.x + pixelDeltaX, - locationInWindow.y - pixelDeltaY); - ScreenIntPoint locationMoved = ViewAs<ScreenPixel>( - [self convertWindowCoordinates:locationInWindowMoved], - PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent); - ScreenPoint delta = ScreenPoint(locationMoved - location); - ScrollableLayerGuid guid; - - // MayBegin and Cancelled are dispatched when the fingers start or stop - // touching the touchpad before any scrolling has occurred. These events - // can be used to control scrollbar visibility or interrupt scroll - // animations. They are only dispatched on 10.8 or later, and only by - // relatively modern devices. - if (phase == NSEventPhaseMayBegin) { - PanGestureInput panInput(PanGestureInput::PANGESTURE_MAYSTART, eventTime, - eventTimeStamp, location, ScreenPoint(0, 0), 0); - apzctm->ReceiveInputEvent(panInput, &guid, nullptr); - return; - } - if (phase == NSEventPhaseCancelled) { - PanGestureInput panInput(PanGestureInput::PANGESTURE_CANCELLED, eventTime, - eventTimeStamp, location, ScreenPoint(0, 0), 0); - apzctm->ReceiveInputEvent(panInput, &guid, nullptr); - return; - } - - // Legacy scroll events are dispatched by devices that do not have a - // concept of a scroll gesture, for example by USB mice with - // traditional mouse wheels. - // For these kinds of scrolls, we want to surround every single scroll - // event with a PANGESTURE_START and a PANGESTURE_END event. The APZC - // needs to know that the real scroll gesture can end abruptly after any - // one of these events. - bool isLegacyScroll = (phase == NSEventPhaseNone && - momentumPhase == NSEventPhaseNone && delta != ScreenPoint(0, 0)); - - if (phase == NSEventPhaseBegan || isLegacyScroll) { - PanGestureInput panInput(PanGestureInput::PANGESTURE_START, eventTime, - eventTimeStamp, location, ScreenPoint(0, 0), 0); - apzctm->ReceiveInputEvent(panInput, &guid, nullptr); - } - if (momentumPhase == NSEventPhaseNone && delta != ScreenPoint(0, 0)) { - PanGestureInput panInput(PanGestureInput::PANGESTURE_PAN, eventTime, - eventTimeStamp, location, delta, 0); - apzctm->ReceiveInputEvent(panInput, &guid, nullptr); - } - if (phase == NSEventPhaseEnded || isLegacyScroll) { - PanGestureInput panInput(PanGestureInput::PANGESTURE_END, eventTime, - eventTimeStamp, location, ScreenPoint(0, 0), 0); - apzctm->ReceiveInputEvent(panInput, &guid, nullptr); - } - - // Any device that can dispatch momentum events supports all three momentum phases. - if (momentumPhase == NSEventPhaseBegan) { - PanGestureInput panInput(PanGestureInput::PANGESTURE_MOMENTUMSTART, eventTime, - eventTimeStamp, location, ScreenPoint(0, 0), 0); - apzctm->ReceiveInputEvent(panInput, &guid, nullptr); - } - if (momentumPhase == NSEventPhaseChanged && delta != ScreenPoint(0, 0)) { - PanGestureInput panInput(PanGestureInput::PANGESTURE_MOMENTUMPAN, eventTime, - eventTimeStamp, location, delta, 0); - apzctm->ReceiveInputEvent(panInput, &guid, nullptr); - } - if (momentumPhase == NSEventPhaseEnded) { - PanGestureInput panInput(PanGestureInput::PANGESTURE_MOMENTUMEND, eventTime, - eventTimeStamp, location, ScreenPoint(0, 0), 0); - apzctm->ReceiveInputEvent(panInput, &guid, nullptr); - } - } -} - -(NSMenu*)menuForEvent:(NSEvent*)theEvent { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; @@ -5571,11 +5293,6 @@ GetIntegerDeltaForEvent(NSEvent* aEvent) return mGeckoChild->CocoaPointsToDevPixelsRoundDown(localPoint); } -- (IAPZCTreeManager*)apzctm -{ - return mGeckoChild ? mGeckoChild->APZCTM() : nullptr; -} - // This is a utility function used by NSView drag event methods // to send events. It contains all of the logic needed for Gecko // dragging to work. Returns the appropriate cocoa drag operation code. @@ -6209,6 +5926,26 @@ nsChildView::GetSelectionAsPlaintext(nsAString& aResult) @end +@implementation PixelHostingView + +- (BOOL)isFlipped { + return YES; +} + +- (NSView*)hitTest:(NSPoint)aPoint { + return nil; +} + +- (void)drawRect:(NSRect)aRect { + [(ChildView*)[self superview] doDrawRect:aRect]; +} + +- (BOOL)wantsBestResolutionOpenGLSurface { + return nsCocoaUtils::HiDPIEnabled() ? YES : NO; +} + +@end + #pragma mark - void @@ -6377,106 +6114,6 @@ ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent, #pragma mark - -@interface EventThreadRunner(Private) -- (void)runEventThread; -- (void)shutdownAndReleaseCalledOnEventThread; -- (void)shutdownAndReleaseCalledOnAnyThread; -- (void)handleEvent:(CGEventRef)cgEvent type:(CGEventType)type; -@end - -static EventThreadRunner* sEventThreadRunner = nil; - -@implementation EventThreadRunner - -+ (void)start -{ - sEventThreadRunner = [[EventThreadRunner alloc] init]; -} - -+ (void)stop -{ - if (sEventThreadRunner) { - [sEventThreadRunner shutdownAndReleaseCalledOnAnyThread]; - sEventThreadRunner = nil; - } -} - -- (id)init -{ - if ((self = [super init])) { - mThread = nil; - [NSThread detachNewThreadSelector:@selector(runEventThread) - toTarget:self - withObject:nil]; - } - return self; -} - -static CGEventRef -HandleEvent(CGEventTapProxy aProxy, CGEventType aType, - CGEventRef aEvent, void* aClosure) -{ - [(EventThreadRunner*)aClosure handleEvent:aEvent type:aType]; - return aEvent; -} - -- (void)runEventThread -{ - char aLocal; - profiler_register_thread("APZC Event Thread", &aLocal); - PR_SetCurrentThreadName("APZC Event Thread"); - - mThread = [NSThread currentThread]; - ProcessSerialNumber currentProcess; - GetCurrentProcess(¤tProcess); - CFMachPortRef eventPort = - CGEventTapCreateForPSN(¤tProcess, - kCGHeadInsertEventTap, - kCGEventTapOptionListenOnly, - CGEventMaskBit(kCGEventScrollWheel), - HandleEvent, - self); - CFRunLoopSourceRef eventPortSource = - CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, eventPort, 0); - CFRunLoopAddSource(CFRunLoopGetCurrent(), eventPortSource, kCFRunLoopCommonModes); - CFRunLoopRun(); - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), eventPortSource, kCFRunLoopCommonModes); - CFRelease(eventPortSource); - CFRelease(eventPort); - [self release]; -} - -- (void)shutdownAndReleaseCalledOnEventThread -{ - CFRunLoopStop(CFRunLoopGetCurrent()); -} - -- (void)shutdownAndReleaseCalledOnAnyThread -{ - [self performSelector:@selector(shutdownAndReleaseCalledOnEventThread) onThread:mThread withObject:nil waitUntilDone:NO]; -} - -static const CGEventField kCGWindowNumberField = (const CGEventField) 51; - -// Called on scroll thread -- (void)handleEvent:(CGEventRef)cgEvent type:(CGEventType)type -{ - if (type != kCGEventScrollWheel) { - return; - } - - int windowNumber = CGEventGetIntegerValueField(cgEvent, kCGWindowNumberField); - NSWindow* window = [NSApp windowWithWindowNumber:windowNumber]; - if (!window || ![window isKindOfClass:[BaseWindow class]]) { - return; - } - - ChildView* childView = [(BaseWindow*)window mainChildView]; - [childView handleAsyncScrollEvent:cgEvent ofType:type]; -} - -@end - @interface NSView (MethodSwizzling) - (BOOL)nsChildView_NSView_mouseDownCanMoveWindow; @end diff --git a/widget/cocoa/nsCocoaFeatures.h b/widget/cocoa/nsCocoaFeatures.h index 984dae80e..7ebbe759f 100644 --- a/widget/cocoa/nsCocoaFeatures.h +++ b/widget/cocoa/nsCocoaFeatures.h @@ -24,6 +24,7 @@ public: static bool OnHighSierraOrLater(); static bool OnMojaveOrLater(); static bool OnCatalinaOrLater(); + static bool OnBigSurOrLater(); static bool IsAtLeastVersion(int32_t aMajor, int32_t aMinor, int32_t aBugFix=0); diff --git a/widget/cocoa/nsCocoaFeatures.mm b/widget/cocoa/nsCocoaFeatures.mm index 065260837..0b22c51bd 100644 --- a/widget/cocoa/nsCocoaFeatures.mm +++ b/widget/cocoa/nsCocoaFeatures.mm @@ -22,6 +22,8 @@ #define MAC_OS_X_VERSION_10_13_HEX 0x000010D0 #define MAC_OS_X_VERSION_10_14_HEX 0x000010E0 #define MAC_OS_X_VERSION_10_15_HEX 0x000010F0 +#define MAC_OS_X_VERSION_10_16_HEX 0x000A1000 +#define MAC_OS_X_VERSION_11_0_HEX 0x000B0000 #include "nsCocoaFeatures.h" #include "nsCocoaUtils.h" @@ -189,6 +191,14 @@ nsCocoaFeatures::OnCatalinaOrLater() } /* static */ bool +nsCocoaFeatures::OnBigSurOrLater() { + // Account for the version being 10.16 (which occurs when the + // application is linked with an older SDK) or 11.0 on Big Sur. + return ((OSXVersion() >= MAC_OS_X_VERSION_10_16_HEX) || + (OSXVersion() >= MAC_OS_X_VERSION_11_0_HEX)); +} + +/* static */ bool nsCocoaFeatures::IsAtLeastVersion(int32_t aMajor, int32_t aMinor, int32_t aBugFix) { return OSXVersion() >= GetVersion(aMajor, aMinor, aBugFix); diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h index 6338f474d..1913696b8 100644 --- a/widget/cocoa/nsCocoaWindow.h +++ b/widget/cocoa/nsCocoaWindow.h @@ -359,7 +359,8 @@ protected: nsresult CreateNativeWindow(const NSRect &aRect, nsBorderStyle aBorderStyle, bool aRectIsFrameRect); - nsresult CreatePopupContentView(const LayoutDeviceIntRect &aRect); + nsresult CreatePopupContentView(const LayoutDeviceIntRect &aRect, + nsWidgetInitData* aInitData); void DestroyNativeWindow(); void AdjustWindowShadow(); void SetWindowBackgroundBlur(); @@ -416,6 +417,8 @@ protected: bool mInReportMoveEvent; // true if in a call to ReportMoveEvent(). bool mInResize; // true if in a call to DoResize(). + bool mAlwaysOnTop; + int32_t mNumModalDescendents; InputContext mInputContext; }; diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index b6d94ea94..b141e80b0 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -35,6 +35,7 @@ #include "nsIWidgetListener.h" #include "nsIPresShell.h" #include "nsScreenCocoa.h" +#include "VibrancyManager.h" #include "gfxPlatform.h" #include "qcms.h" @@ -128,6 +129,7 @@ nsCocoaWindow::nsCocoaWindow() , mIsAnimationSuppressed(false) , mInReportMoveEvent(false) , mInResize(false) +, mAlwaysOnTop(false) , mNumModalDescendents(0) { if ([NSWindow respondsToSelector:@selector(setAllowsAutomaticWindowTabbing:)]) { @@ -302,12 +304,14 @@ nsCocoaWindow::Create(nsIWidget* aParent, if (mWindowType == eWindowType_popup) { if (aInitData->mMouseTransparent) { [mWindow setIgnoresMouseEvents:YES]; + } else { + [mWindow setIgnoresMouseEvents:NO]; } // now we can convert newBounds to device pixels for the window we created, // as the child view expects a rect expressed in the dev pix of its parent LayoutDeviceIntRect devRect = RoundedToInt(newBounds * GetDesktopToDeviceScale()); - return CreatePopupContentView(devRect); + return CreatePopupContentView(devRect, aInitData); } mIsAnimationSuppressed = aInitData->mIsAnimationSuppressed; @@ -456,6 +460,11 @@ nsresult nsCocoaWindow::CreateNativeWindow(const NSRect &aRect, mWindow = [[windowClass alloc] initWithContentRect:contentRect styleMask:features backing:NSBackingStoreBuffered defer:YES]; + // Make sure that window titles don't leak to disk in private browsing mode + // due to macOS' resume feature. + [mWindow setRestorable:NO]; + [mWindow disableSnapshotRestoration]; + // setup our notification delegate. Note that setDelegate: does NOT retain. mDelegate = [[WindowDelegate alloc] initWithGeckoWindow:this]; [mWindow setDelegate:mDelegate]; @@ -474,7 +483,6 @@ nsresult nsCocoaWindow::CreateNativeWindow(const NSRect &aRect, if (mWindowType == eWindowType_popup) { SetPopupWindowLevel(); - [mWindow setHasShadow:YES]; [mWindow setBackgroundColor:[NSColor clearColor]]; [mWindow setOpaque:NO]; } else { @@ -483,6 +491,13 @@ nsresult nsCocoaWindow::CreateNativeWindow(const NSRect &aRect, [mWindow setOpaque:YES]; } + NSWindowCollectionBehavior newBehavior = [mWindow collectionBehavior]; + if (mAlwaysOnTop) { + [mWindow setLevel:NSFloatingWindowLevel]; + newBehavior |= NSWindowCollectionBehaviorCanJoinAllSpaces; + } + [mWindow setCollectionBehavior:newBehavior]; + [mWindow setContentMinSize:NSMakeSize(60, 60)]; [mWindow disableCursorRects]; @@ -499,7 +514,8 @@ nsresult nsCocoaWindow::CreateNativeWindow(const NSRect &aRect, } NS_IMETHODIMP -nsCocoaWindow::CreatePopupContentView(const LayoutDeviceIntRect &aRect) +nsCocoaWindow::CreatePopupContentView(const LayoutDeviceIntRect &aRect, + nsWidgetInitData* aInitData) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; @@ -512,13 +528,16 @@ nsCocoaWindow::CreatePopupContentView(const LayoutDeviceIntRect &aRect) nsIWidget* thisAsWidget = static_cast<nsIWidget*>(this); nsresult rv = mPopupContentView->Create(thisAsWidget, nullptr, aRect, - nullptr); + aInitData); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - ChildView* newContentView = (ChildView*)mPopupContentView->GetNativeData(NS_NATIVE_WIDGET); - [mWindow setContentView:newContentView]; + NSView* contentView = [mWindow contentView]; + ChildView* childView = (ChildView*)mPopupContentView->GetNativeData(NS_NATIVE_WIDGET); + [childView setFrame:NSZeroRect]; + [childView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [contentView addSubview:childView]; return NS_OK; @@ -757,6 +776,13 @@ NS_IMETHODIMP nsCocoaWindow::Show(bool bState) (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW) : nil; if (bState && !mBounds.IsEmpty()) { + // Don't try to show a popup when the parent isn't visible or is minimized. + if (mWindowType == eWindowType_popup && nativeParentWindow) { + if (![nativeParentWindow isVisible] || [nativeParentWindow isMiniaturized]) { + return NS_ERROR_FAILURE; + } + } + if (mPopupContentView) { // Ensure our content view is visible. We never need to hide it. mPopupContentView->Show(true); @@ -2790,12 +2816,6 @@ static NSMutableSet *gSwizzledFrameViewClasses = nil; - (void)_addKnownSubview:(NSView*)aView positioned:(NSWindowOrderingMode)place relativeTo:(NSView*)otherView; @end -// Available on 10.10 -@interface NSWindow(PrivateCornerMaskMethod) - - (id)_cornerMask; - - (void)_cornerMaskChanged; -@end - #if !defined(MAC_OS_X_VERSION_10_10) || \ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10 @@ -2816,6 +2836,10 @@ static NSMutableSet *gSwizzledFrameViewClasses = nil; #endif +@interface NSView(NSVisualEffectViewSetMaskImage) +- (void)setMaskImage:(NSImage*)image; +@end + @interface BaseWindow(Private) - (void)removeTrackingArea; - (void)cursorUpdated:(NSEvent*)aEvent; @@ -2825,25 +2849,6 @@ static NSMutableSet *gSwizzledFrameViewClasses = nil; @implementation BaseWindow -- (id)_cornerMask -{ - if (!mUseMenuStyle) { - return [super _cornerMask]; - } - - CGFloat radius = 4.0f; - NSEdgeInsets insets = { 5, 5, 5, 5 }; - NSSize maskSize = { 12, 12 }; - NSImage* maskImage = [NSImage imageWithSize:maskSize flipped:YES drawingHandler:^BOOL(NSRect dstRect) { - NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:dstRect xRadius:radius yRadius:radius]; - [[NSColor colorWithDeviceWhite:1.0 alpha:1.0] set]; - [path fill]; - return YES; - }]; - [maskImage setCapInsets:insets]; - return maskImage; -} - // The frame of a window is implemented using undocumented NSView subclasses. // We offset the window buttons by overriding the methods _closeButtonOrigin // and _fullScreenButtonOrigin on these frame view classes. The class which is @@ -2916,14 +2921,54 @@ static NSMutableSet *gSwizzledFrameViewClasses = nil; return self; } +// Returns an autoreleased NSImage. +static NSImage* +GetMenuMaskImage() +{ + CGFloat radius = 4.0f; + NSEdgeInsets insets = { 5, 5, 5, 5 }; + NSSize maskSize = { 12, 12 }; + NSImage* maskImage = [NSImage imageWithSize:maskSize flipped:YES drawingHandler:^BOOL(NSRect dstRect) { + NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:dstRect xRadius:radius yRadius:radius]; + [[NSColor colorWithDeviceWhite:1.0 alpha:1.0] set]; + [path fill]; + return YES; + }]; + [maskImage setCapInsets:insets]; + return maskImage; +} + +- (void)swapOutChildViewWrapper:(NSView*)aNewWrapper +{ + [aNewWrapper setFrame:[[self contentView] frame]]; + NSView* childView = [[self mainChildView] retain]; + [childView removeFromSuperview]; + [aNewWrapper addSubview:childView]; + [childView release]; + [super setContentView:aNewWrapper]; +} + - (void)setUseMenuStyle:(BOOL)aValue { - if (aValue != mUseMenuStyle) { - mUseMenuStyle = aValue; - if ([self respondsToSelector:@selector(_cornerMaskChanged)]) { - [self _cornerMaskChanged]; + if (!VibrancyManager::SystemSupportsVibrancy()) { + return; + } + + if (aValue && !mUseMenuStyle) { + // Turn on rounded corner masking. + NSView* effectView = VibrancyManager::CreateEffectView(VibrancyType::MENU, YES); + if ([effectView respondsToSelector:@selector(setMaskImage:)]) { + [effectView setMaskImage:GetMenuMaskImage()]; } + [self swapOutChildViewWrapper:effectView]; + [effectView release]; + } else if (mUseMenuStyle && !aValue) { + // Turn off rounded corner masking. + NSView* wrapper = [[NSView alloc] initWithFrame:NSZeroRect]; + [self swapOutChildViewWrapper:wrapper]; + [wrapper release]; } + mUseMenuStyle = aValue; } - (void)setBeingShown:(BOOL)aValue @@ -3081,10 +3126,6 @@ static const NSString* kStateCollectionBehavior = @"collectionBehavior"; - (ChildView*)mainChildView { NSView *contentView = [self contentView]; - // A PopupWindow's contentView is a ChildView object. - if ([contentView isKindOfClass:[ChildView class]]) { - return (ChildView*)contentView; - } NSView* lastView = [[contentView subviews] lastObject]; if ([lastView isKindOfClass:[ChildView class]]) { return (ChildView*)lastView; diff --git a/widget/cocoa/nsNativeThemeCocoa.mm b/widget/cocoa/nsNativeThemeCocoa.mm index fc4f7f2e9..f93e40f7b 100644 --- a/widget/cocoa/nsNativeThemeCocoa.mm +++ b/widget/cocoa/nsNativeThemeCocoa.mm @@ -443,8 +443,8 @@ static ChildView* ChildViewForFrame(nsIFrame* aFrame) if (!widget) return nil; - NSView* view = (NSView*)widget->GetNativeData(NS_NATIVE_WIDGET); - return [view isKindOfClass:[ChildView class]] ? (ChildView*)view : nil; + NSWindow* window = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW); + return [window isKindOfClass:[BaseWindow class]] ? [(BaseWindow*)window mainChildView] : nil; } static NSWindow* NativeWindowForFrame(nsIFrame* aFrame, @@ -2437,17 +2437,30 @@ nsNativeThemeCocoa::DrawWidgetBackground(nsRenderingContext* aContext, break; case NS_THEME_MENUSEPARATOR: { - ThemeMenuState menuState; - if (IsDisabled(aFrame, eventState)) { - menuState = kThemeMenuDisabled; + // Workaround for visual artifacts issues with + // HIThemeDrawMenuSeparator on macOS Big Sur. + if (nsCocoaFeatures::OnBigSurOrLater()) { + CGRect separatorRect = macRect; + separatorRect.size.height = 1; + separatorRect.size.width -= 42; + separatorRect.origin.x += 21; + // Use a gray color similar to the native separator + CGContextSetRGBFillColor(cgContext, 0.816, 0.816, 0.816, 1.0); + CGContextFillRect(cgContext, separatorRect); } - else { - menuState = CheckBooleanAttr(aFrame, nsGkAtoms::menuactive) ? - kThemeMenuSelected : kThemeMenuActive; + else + { + ThemeMenuState menuState; + if (IsDisabled(aFrame, eventState)) { + menuState = kThemeMenuDisabled; + } + else { + menuState = CheckBooleanAttr(aFrame, nsGkAtoms::menuactive) ? + kThemeMenuSelected : kThemeMenuActive; + } + HIThemeMenuItemDrawInfo midi = { 0, kThemeMenuItemPlain, menuState }; + HIThemeDrawMenuSeparator(&macRect, &macRect, &midi, cgContext, HITHEME_ORIENTATION); } - - HIThemeMenuItemDrawInfo midi = { 0, kThemeMenuItemPlain, menuState }; - HIThemeDrawMenuSeparator(&macRect, &macRect, &midi, cgContext, HITHEME_ORIENTATION); } break; |