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.
This commit is contained in:
Brian Smith 2020-11-16 07:23:44 -06:00 committed by roytam1
commit de885df033
10 changed files with 286 additions and 609 deletions

View file

@ -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];
return mGLContext || mUsingOMTCompositor;
}
- (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);
}
// 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(&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