diff --git a/gfx/src/nsRegion.h b/gfx/src/nsRegion.h index 92a005536e7e..023aa763a1f7 100644 --- a/gfx/src/nsRegion.h +++ b/gfx/src/nsRegion.h @@ -68,6 +68,10 @@ public: { return IsEqual(aRgn); } + bool operator!=(const nsRegion& aRgn) const + { + return !(*this == aRgn); + } friend std::ostream& operator<<(std::ostream& stream, const nsRegion& m); @@ -464,6 +468,10 @@ public: { return IsEqual(aRgn); } + bool operator!=(const nsIntRegion& aRgn) const + { + return !(*this == aRgn); + } friend std::ostream& operator<<(std::ostream& stream, const nsIntRegion& m) { return stream << m.mImpl; diff --git a/widget/cocoa/nsChildView.h b/widget/cocoa/nsChildView.h index 721f3cc4665b..bf57575f6968 100644 --- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -310,8 +310,6 @@ typedef NSInteger NSEventGestureAxis; - (void)handleMouseMoved:(NSEvent*)aEvent; -- (void)updateWindowDraggableStateOnMouseMove:(NSEvent*)theEvent; - - (void)sendMouseEnterOrExitEvent:(NSEvent*)aEvent enter:(BOOL)aEnter type:(mozilla::WidgetMouseEvent::exitType)aType; @@ -548,6 +546,9 @@ public: virtual void UpdateThemeGeometries(const nsTArray& aThemeGeometries); + virtual void UpdateWindowDraggingRegion(const nsIntRegion& aRegion) MOZ_OVERRIDE; + const nsIntRegion& GetDraggableRegion() { return mDraggableRegion; } + void HidePlugin(); void UpdatePluginPort(); @@ -679,6 +680,8 @@ protected: // uploaded to to mTitlebarImage. Main thread only. nsIntRegion mDirtyTitlebarRegion; + nsIntRegion mDraggableRegion; + // Cached value of [mView backingScaleFactor], to avoid sending two obj-c // messages (respondsToSelector, backingScaleFactor) every time we need to // use it. diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 1bd8777a3c44..4661ae19daca 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -121,6 +121,7 @@ extern "C" { typedef CFTypeRef CGSRegionObj; CGError CGSNewRegionWithRect(const CGRect *rect, CGSRegionObj *outRegion); + CGError CGSNewRegionWithRectList(const CGRect *rects, int rectCount, CGSRegionObj *outRegion); } // defined in nsMenuBarX.mm @@ -203,6 +204,7 @@ static uint32_t gNumberOfWidgetsNeedingEventThread = 0; - (APZCTreeManager*)apzctm; - (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent; +- (void)updateWindowDraggableState; @end @@ -221,6 +223,10 @@ static uint32_t gNumberOfWidgetsNeedingEventThread = 0; - (float)roundedCornerRadius; @end +@interface NSView(DraggableRegion) +- (CGSRegionObj)_regionForOpaqueDescendants:(NSRect)aRect forMove:(BOOL)aForMove; +@end + // Starting with 10.7 the bottom corners of all windows are rounded. // Unfortunately, the standard rounding that OS X applies to OpenGL views // does not use anti-aliasing and looks very crude. Since we want a smooth, @@ -2387,15 +2393,15 @@ nsChildView::UpdateTitlebarCGContext() CGContextSaveGState(ctx); - std::vector rects; + nsTArray rects; nsIntRegionRectIterator iter(dirtyTitlebarRegion); for (;;) { const nsIntRect* r = iter.Next(); if (!r) break; - rects.push_back(CGRectMake(r->x, r->y, r->width, r->height)); + rects.AppendElement(CGRectMake(r->x, r->y, r->width, r->height)); } - CGContextClipToRects(ctx, rects.data(), rects.size()); + CGContextClipToRects(ctx, rects.Elements(), rects.Length()); CGContextClearRect(ctx, CGRectMake(0, 0, texSize.width, texSize.height)); @@ -2748,6 +2754,15 @@ nsChildView::DoRemoteComposition(const nsIntRect& aRenderRect) [(ChildView*)mView postRender:mGLPresenter->GetNSOpenGLContext()]; } +void +nsChildView::UpdateWindowDraggingRegion(const nsIntRegion& aRegion) +{ + if (mDraggableRegion != aRegion) { + mDraggableRegion = aRegion; + [(ChildView*)mView updateWindowDraggableState]; + } +} + #ifdef ACCESSIBILITY already_AddRefed nsChildView::GetDocumentAccessible() @@ -3602,7 +3617,9 @@ NSEvent* gLastDragMouseDownEvent = nil; - (BOOL)mouseDownCanMoveWindow { - return [[self window] isMovableByWindowBackground]; + // Return YES so that _regionForOpaqueDescendants gets called, where the + // actual draggable region will be assembled. + return YES; } -(void)updateGLContext @@ -4885,24 +4902,60 @@ NSEvent* gLastDragMouseDownEvent = nil; mGeckoChild->DispatchEvent(&event, status); } -- (void)updateWindowDraggableStateOnMouseMove:(NSEvent*)theEvent +- (void)updateWindowDraggableState { - if (!theEvent || !mGeckoChild) { - return; + // Trigger update to the window server. + [[self window] setMovableByWindowBackground:NO]; + [[self window] setMovableByWindowBackground:YES]; +} + +// aRect is in view coordinates relative to this NSView. +- (CGRect)convertToFlippedWindowCoordinates:(NSRect)aRect +{ + // First, convert the rect to regular window coordinates... + NSRect inWindowCoords = [self convertRect:aRect toView:nil]; + // ... and then flip it again because window coordinates have their origin + // in the bottom left corner, and we need it to be in the top left corner. + inWindowCoords.origin.y = [[self window] frame].size.height - NSMaxY(inWindowCoords); + return NSRectToCGRect(inWindowCoords); +} + +static CGSRegionObj +NewCGSRegionFromRegion(const nsIntRegion& aRegion, + CGRect (^aRectConverter)(const nsIntRect&)) +{ + nsTArray rects; + nsIntRegionRectIterator iter(aRegion); + for (;;) { + const nsIntRect* r = iter.Next(); + if (!r) + break; + rects.AppendElement(aRectConverter(*r)); } - nsCocoaWindow* windowWidget = mGeckoChild->GetXULWindowWidget(); - if (!windowWidget) { - return; + CGSRegionObj region; + CGSNewRegionWithRectList(rects.Elements(), rects.Length(), ®ion); + return region; +} + +// This function is called with forMove:YES to calculate the draggable region +// of the window which will be submitted to the window server. Window dragging +// is handled on the window server without calling back into our process, so it +// also works while our app is unresponsive. +- (CGSRegionObj)_regionForOpaqueDescendants:(NSRect)aRect forMove:(BOOL)aForMove +{ + if (!aForMove || !mGeckoChild) { + return [super _regionForOpaqueDescendants:aRect forMove:aForMove]; } - // We assume later on that sending a hit test event won't cause widget destruction. - WidgetMouseEvent hitTestEvent(true, NS_MOUSE_MOZHITTEST, mGeckoChild, - WidgetMouseEvent::eReal); - [self convertCocoaMouseEvent:theEvent toGeckoEvent:&hitTestEvent]; - bool result = mGeckoChild->DispatchWindowEvent(hitTestEvent); + nsIntRect boundingRect = mGeckoChild->CocoaPointsToDevPixels(aRect); - [windowWidget->GetCocoaWindow() setMovableByWindowBackground:result]; + nsIntRegion opaqueRegion; + opaqueRegion.Sub(boundingRect, mGeckoChild->GetDraggableRegion()); + + return NewCGSRegionFromRegion(opaqueRegion, ^(const nsIntRect& r) { + return [self convertToFlippedWindowCoordinates:mGeckoChild->DevPixelsToCocoaPoints(r)]; + }); } - (void)handleMouseMoved:(NSEvent*)theEvent diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index a7931c94edb9..435da5c1bf69 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -3269,9 +3269,6 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton"; if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) { nsCocoaWindow *widget = [(WindowDelegate *)delegate geckoWidget]; if (widget) { - if (type == NSMouseMoved) { - [[self mainChildView] updateWindowDraggableStateOnMouseMove:anEvent]; - } if (gGeckoAppModalWindowList && (widget != gGeckoAppModalWindowList->window)) return; if (widget->HasModalDescendents())