summaryrefslogtreecommitdiffstats
path: root/widget/cocoa
diff options
context:
space:
mode:
Diffstat (limited to 'widget/cocoa')
-rw-r--r--widget/cocoa/VibrancyManager.h23
-rw-r--r--widget/cocoa/VibrancyManager.mm74
-rw-r--r--widget/cocoa/mozView.h5
-rw-r--r--widget/cocoa/nsChildView.h30
-rw-r--r--widget/cocoa/nsChildView.mm599
-rw-r--r--widget/cocoa/nsCocoaFeatures.h1
-rw-r--r--widget/cocoa/nsCocoaFeatures.mm10
-rw-r--r--widget/cocoa/nsCocoaWindow.h5
-rw-r--r--widget/cocoa/nsCocoaWindow.mm119
-rw-r--r--widget/cocoa/nsNativeThemeCocoa.mm35
10 files changed, 292 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..868687fe1 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,34 @@ 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];
+ if (!NS_IsMainThread()) {
+ // In the presence of CoreAnimation, this method can sometimes be called on
+ // a non-main thread. Ignore those calls because Gecko can only react to
+ // them on the main thread.
+ return;
}
-}
-- (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
+ CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
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 +3600,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 +3632,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 +4636,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 +4755,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 +5299,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 +5932,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 +6120,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(&currentProcess);
- CFMachPortRef eventPort =
- CGEventTapCreateForPSN(&currentProcess,
- 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;