Backed out 6 changesets (bug 1820168) for causing multiple failures CLOSED TREE

Backed out changeset 03bc416d3399 (bug 1820168)
Backed out changeset 8da35371cbdf (bug 1820168)
Backed out changeset ebde44bcb736 (bug 1820168)
Backed out changeset 7b01e0445133 (bug 1820168)
Backed out changeset b32a9cf041fc (bug 1820168)
Backed out changeset e29e5a3349f4 (bug 1820168)
This commit is contained in:
Noemi Erli 2023-03-14 19:53:58 +02:00
parent ee3cbc95c4
commit 51cdfe05ee
9 changed files with 877 additions and 327 deletions

View File

@ -1,37 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZDynamicCursor_h_
#define MOZDynamicCursor_h_
#import <Cocoa/Cocoa.h>
#include "nsIWidget.h"
// MOZDynamicCursor.sharedInstance is a singleton NSCursor object whose
// underlying cursor can be changed at runtime.
// It can be used in an NSView cursorRect so that the system will call
// -[NSCursor set] on it at the right moments, for example when the
// mouse moves into a window or when the cursor needs to be set after
// a drag operation or when a context menu closes.
@interface MOZDynamicCursor : NSCursor {
@private
NSMutableDictionary* mCursors;
NSCursor* mCurrentCursor;
nsCursor mCurrentCursorType;
}
// Set a cursor.
// Returns an error if the cursor isn't custom or we couldn't set
// it for some reason.
- (nsresult)setCustomCursor:(const nsIWidget::Cursor&)aCursor
widgetScaleFactor:(CGFloat)aWidgetScaleFactor;
// Sets non-custom cursors and can be used as a fallback if setting
// a custom cursor did not succeed.
- (void)setNonCustomCursor:(const nsIWidget::Cursor&)aCursor;
+ (MOZDynamicCursor*)sharedInstance;
@end
#endif // MOZDynamicCursor_h_

View File

@ -1,280 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "imgIContainer.h"
#include "nsCocoaUtils.h"
#include "MOZDynamicCursor.h"
#include "nsObjCExceptions.h"
#include "nsDirectoryServiceDefs.h"
#include "nsIFile.h"
#include <math.h>
static MOZDynamicCursor* gInstance;
static CGFloat sCurrentCursorScaleFactor = 0.0f;
static nsIWidget::Cursor sCurrentCursor;
static constexpr nsCursor kCustomCursor = eCursorCount;
@interface MOZDynamicCursor (PrivateMethods)
+ (NSCursor*)freshCursorWithType:(nsCursor)aCursor;
- (NSCursor*)cursorWithType:(nsCursor)aCursor;
// Set the cursor.
- (void)setCursor:(NSCursor*)aMacCursor;
@end
@interface NSCursor (CreateWithImageNamed)
+ (NSCursor*)cursorWithImageNamed:(NSString*)imageName hotSpot:(NSPoint)aPoint;
@end
@interface NSCursor (Undocumented)
// busyButClickableCursor is an undocumented NSCursor API, but has been in use since
// at least OS X 10.4 and through 10.9.
+ (NSCursor*)busyButClickableCursor;
@end
@implementation MOZDynamicCursor
+ (MOZDynamicCursor*)sharedInstance {
if (!gInstance) {
gInstance = [[MOZDynamicCursor alloc] init];
}
return gInstance;
}
+ (NSCursor*)freshCursorWithType:(enum nsCursor)aCursor {
switch (aCursor) {
case eCursor_standard:
return [NSCursor arrowCursor];
case eCursor_wait:
case eCursor_spinning:
return [NSCursor busyButClickableCursor];
case eCursor_select:
return [NSCursor IBeamCursor];
case eCursor_hyperlink:
return [NSCursor pointingHandCursor];
case eCursor_crosshair:
return [NSCursor crosshairCursor];
case eCursor_move:
return [NSCursor cursorWithImageNamed:@"move" hotSpot:NSMakePoint(12, 12)];
case eCursor_help:
return [NSCursor cursorWithImageNamed:@"help" hotSpot:NSMakePoint(12, 12)];
case eCursor_copy:
return [NSCursor dragCopyCursor];
case eCursor_alias:
return [NSCursor dragLinkCursor];
case eCursor_context_menu:
return [NSCursor contextualMenuCursor];
case eCursor_cell:
return [NSCursor cursorWithImageNamed:@"cell" hotSpot:NSMakePoint(12, 12)];
case eCursor_grab:
return [NSCursor openHandCursor];
case eCursor_grabbing:
return [NSCursor closedHandCursor];
case eCursor_zoom_in:
return [NSCursor cursorWithImageNamed:@"zoomIn" hotSpot:NSMakePoint(10, 10)];
case eCursor_zoom_out:
return [NSCursor cursorWithImageNamed:@"zoomOut" hotSpot:NSMakePoint(10, 10)];
case eCursor_vertical_text:
return [NSCursor cursorWithImageNamed:@"vtIBeam" hotSpot:NSMakePoint(12, 11)];
case eCursor_all_scroll:
return [NSCursor openHandCursor];
case eCursor_not_allowed:
case eCursor_no_drop:
return [NSCursor operationNotAllowedCursor];
// Resize Cursors:
// North
case eCursor_n_resize:
return [NSCursor resizeUpCursor];
// North East
case eCursor_ne_resize:
return [NSCursor cursorWithImageNamed:@"sizeNE" hotSpot:NSMakePoint(12, 11)];
// East
case eCursor_e_resize:
return [NSCursor resizeRightCursor];
// South East
case eCursor_se_resize:
return [NSCursor cursorWithImageNamed:@"sizeSE" hotSpot:NSMakePoint(12, 12)];
// South
case eCursor_s_resize:
return [NSCursor resizeDownCursor];
// South West
case eCursor_sw_resize:
return [NSCursor cursorWithImageNamed:@"sizeSW" hotSpot:NSMakePoint(10, 12)];
// West
case eCursor_w_resize:
return [NSCursor resizeLeftCursor];
// North West
case eCursor_nw_resize:
return [NSCursor cursorWithImageNamed:@"sizeNW" hotSpot:NSMakePoint(11, 11)];
// North & South
case eCursor_ns_resize:
return [NSCursor resizeUpDownCursor];
// East & West
case eCursor_ew_resize:
return [NSCursor resizeLeftRightCursor];
// North East & South West
case eCursor_nesw_resize:
return [NSCursor cursorWithImageNamed:@"sizeNESW" hotSpot:NSMakePoint(12, 12)];
// North West & South East
case eCursor_nwse_resize:
return [NSCursor cursorWithImageNamed:@"sizeNWSE" hotSpot:NSMakePoint(12, 12)];
// Column Resize
case eCursor_col_resize:
return [NSCursor cursorWithImageNamed:@"colResize" hotSpot:NSMakePoint(12, 12)];
// Row Resize
case eCursor_row_resize:
return [NSCursor cursorWithImageNamed:@"rowResize" hotSpot:NSMakePoint(12, 12)];
default:
return [NSCursor arrowCursor];
}
}
- (id)init {
if ((self = [super init])) {
mCursors = [[NSMutableDictionary alloc] initWithCapacity:25];
mCurrentCursor = [NSCursor arrowCursor];
mCurrentCursorType = eCursor_standard;
}
return self;
}
- (void)setNonCustomCursor:(const nsIWidget::Cursor&)aCursor {
[self setCursor:[self cursorWithType:aCursor.mDefaultCursor] type:aCursor.mDefaultCursor];
sCurrentCursor = aCursor;
}
- (void)setCursor:(NSCursor*)aMacCursor type:(nsCursor)aType {
if (mCurrentCursorType != aType) {
if (aType == eCursor_none) {
[NSCursor hide];
} else if (mCurrentCursorType == eCursor_none) {
[NSCursor unhide];
}
mCurrentCursorType = aType;
}
if (mCurrentCursor != aMacCursor) {
[mCurrentCursor release];
mCurrentCursor = [aMacCursor retain];
[mCurrentCursor set];
}
}
- (nsresult)setCustomCursor:(const nsIWidget::Cursor&)aCursor
widgetScaleFactor:(CGFloat)scaleFactor {
// As the user moves the mouse, this gets called repeatedly with the same aCursorImage
if (sCurrentCursor == aCursor && sCurrentCursorScaleFactor == scaleFactor && mCurrentCursor) {
return NS_OK;
}
sCurrentCursor = aCursor;
sCurrentCursorScaleFactor = scaleFactor;
if (!aCursor.IsCustom()) {
return NS_ERROR_FAILURE;
}
nsIntSize size = nsIWidget::CustomCursorSize(aCursor);
// prevent DoS attacks
if (size.width > 128 || size.height > 128) {
return NS_ERROR_FAILURE;
}
NSImage* cursorImage;
nsresult rv = nsCocoaUtils::CreateNSImageFromImageContainer(
aCursor.mContainer, imgIContainer::FRAME_FIRST, nullptr, nullptr, &cursorImage, scaleFactor);
if (NS_FAILED(rv) || !cursorImage) {
return NS_ERROR_FAILURE;
}
{
NSSize cocoaSize = NSMakeSize(size.width, size.height);
[cursorImage setSize:cocoaSize];
[[[cursorImage representations] objectAtIndex:0] setSize:cocoaSize];
}
// if the hotspot is nonsensical, make it 0,0
uint32_t hotspotX = aCursor.mHotspotX > (uint32_t(size.width) - 1) ? 0 : aCursor.mHotspotX;
uint32_t hotspotY = aCursor.mHotspotY > (uint32_t(size.height) - 1) ? 0 : aCursor.mHotspotY;
NSPoint hotSpot = ::NSMakePoint(hotspotX, hotspotY);
[self setCursor:[[NSCursor alloc] initWithImage:cursorImage hotSpot:hotSpot] type:kCustomCursor];
[cursorImage release];
return NS_OK;
}
- (NSCursor*)cursorWithType:(enum nsCursor)aCursor {
NSCursor* result = [mCursors objectForKey:[NSNumber numberWithInt:aCursor]];
if (!result) {
result = [MOZDynamicCursor freshCursorWithType:aCursor];
[mCursors setObject:result forKey:[NSNumber numberWithInt:aCursor]];
}
return result;
}
// This method gets called by ChildView's cursor rect (or rather its underlying
// NSTrackingArea) whenever the mouse enters it, for example after a dragging
// operation, after a menu closes, or when the mouse enters a window.
- (void)set {
[mCurrentCursor set];
}
- (void)dealloc {
[mCurrentCursor release];
[mCursors release];
sCurrentCursor = {};
[super dealloc];
}
@end
@implementation NSCursor (CreateWithImageName)
+ (NSCursor*)cursorWithImageNamed:(NSString*)imageName hotSpot:(NSPoint)aPoint {
nsCOMPtr<nsIFile> resDir;
nsAutoCString resPath;
NSString *pathToImage, *pathToHiDpiImage;
NSImage *cursorImage, *hiDpiCursorImage;
nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(resDir));
if (NS_FAILED(rv)) goto INIT_FAILURE;
resDir->AppendNative("res"_ns);
resDir->AppendNative("cursors"_ns);
rv = resDir->GetNativePath(resPath);
if (NS_FAILED(rv)) goto INIT_FAILURE;
pathToImage = [NSString stringWithUTF8String:(const char*)resPath.get()];
if (!pathToImage) goto INIT_FAILURE;
pathToImage = [pathToImage stringByAppendingPathComponent:imageName];
pathToHiDpiImage = [pathToImage stringByAppendingString:@"@2x"];
// Add same extension to both image paths.
pathToImage = [pathToImage stringByAppendingPathExtension:@"png"];
pathToHiDpiImage = [pathToHiDpiImage stringByAppendingPathExtension:@"png"];
cursorImage = [[[NSImage alloc] initWithContentsOfFile:pathToImage] autorelease];
if (!cursorImage) goto INIT_FAILURE;
// Note 1: There are a few different ways to get a hidpi image via
// initWithContentsOfFile. We let the OS handle this here: when the
// file basename ends in "@2x", it will be displayed at native resolution
// instead of being pixel-doubled. See bug 784909 comment 7 for alternates ways.
//
// Note 2: The OS is picky, and will ignore the hidpi representation
// unless it is exactly twice the size of the lowdpi image.
hiDpiCursorImage = [[[NSImage alloc] initWithContentsOfFile:pathToHiDpiImage] autorelease];
if (hiDpiCursorImage) {
NSImageRep* imageRep = [[hiDpiCursorImage representations] objectAtIndex:0];
[cursorImage addRepresentation:imageRep];
}
return [[[NSCursor alloc] initWithImage:cursorImage hotSpot:aPoint] autorelease];
INIT_FAILURE:
NS_WARNING("Problem getting path to cursor image file!");
[self release];
return nil;
}
@end

View File

@ -33,7 +33,6 @@ EXPORTS += [
UNIFIED_SOURCES += [
"AppearanceOverride.mm",
"GfxInfo.mm",
"MOZDynamicCursor.mm",
"MOZIconHelper.mm",
"MOZMenuOpeningCoordinator.mm",
"NativeKeyBindings.mm",
@ -45,9 +44,11 @@ UNIFIED_SOURCES += [
"nsCocoaUtils.mm",
"nsCocoaWindow.mm",
"nsColorPicker.mm",
"nsCursorManager.mm",
"nsDeviceContextSpecX.mm",
"nsFilePicker.mm",
"nsLookAndFeel.mm",
"nsMacCursor.mm",
"nsMacDockSupport.mm",
"nsMacFinderProgress.mm",
"nsMacSharingService.mm",

View File

@ -50,7 +50,7 @@
#include "nsDragService.h"
#include "nsClipboard.h"
#include "MOZDynamicCursor.h"
#include "nsCursorManager.h"
#include "nsWindowMap.h"
#include "nsCocoaFeatures.h"
#include "nsCocoaUtils.h"
@ -618,12 +618,12 @@ void nsChildView::SetCursor(const Cursor& aCursor) {
nsBaseWidget::SetCursor(aCursor);
if (NS_SUCCEEDED([MOZDynamicCursor.sharedInstance setCustomCursor:aCursor
widgetScaleFactor:BackingScaleFactor()])) {
if (NS_SUCCEEDED([[nsCursorManager sharedInstance] setCustomCursor:aCursor
widgetScaleFactor:BackingScaleFactor()])) {
return;
}
[MOZDynamicCursor.sharedInstance setNonCustomCursor:aCursor];
[[nsCursorManager sharedInstance] setNonCustomCursor:aCursor];
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
@ -4109,10 +4109,6 @@ static gfx::IntPoint GetIntegerDeltaForEvent(NSEvent* aEvent) {
NS_OBJC_END_TRY_BLOCK_RETURN(NSDragOperationNone);
}
- (void)resetCursorRects {
[self addCursorRect:self.bounds cursor:MOZDynamicCursor.sharedInstance];
}
// NSDraggingDestination
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
@ -4833,7 +4829,7 @@ void ChildViewMouseTracker::ReEvaluateMouseEnterState(NSEvent* aEvent, ChildView
[oldView sendMouseEnterOrExitEvent:aEvent enter:NO exitFrom:exitFrom];
// After the cursor exits the window set it to a visible regular arrow cursor.
if (exitFrom == WidgetMouseEvent::ePlatformTopLevel) {
[MOZDynamicCursor.sharedInstance setNonCustomCursor:nsIWidget::Cursor{eCursor_standard}];
[[nsCursorManager sharedInstance] setNonCustomCursor:nsIWidget::Cursor{eCursor_standard}];
}
[sLastMouseEventView sendMouseEnterOrExitEvent:aEvent enter:YES exitFrom:exitFrom];
}

View File

@ -552,6 +552,7 @@ nsresult nsCocoaWindow::CreateNativeWindow(const NSRect& aRect, BorderStyle aBor
[mWindow setCollectionBehavior:newBehavior];
[mWindow setContentMinSize:NSMakeSize(60, 60)];
[mWindow disableCursorRects];
// Make the window use CoreAnimation from the start, so that we don't
// switch from a non-CA window to a CA-window in the middle.

View File

@ -0,0 +1,60 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsCursorManager_h_
#define nsCursorManager_h_
#import <Foundation/Foundation.h>
#include "nsIWidget.h"
#include "nsMacCursor.h"
/*! @class nsCursorManager
@abstract Singleton service provides access to all cursors available in the application.
@discussion Use <code>nsCusorManager</code> to set the current cursor using
an XP <code>nsCusor</code> enum value.
<code>nsCursorManager</code> encapsulates the details of
setting different types of cursors, animating cursors and
cleaning up cursors when they are no longer in use.
*/
@interface nsCursorManager : NSObject {
@private
NSMutableDictionary* mCursors;
nsMacCursor* mCurrentMacCursor;
}
/*! @method setCursor:
@abstract Sets the current cursor.
@discussion Sets the current cursor to the cursor indicated by the XP
cursor given in the argument. Resources associated with the
previous cursor are cleaned up.
@param aCursor the cursor to use
*/
- (nsresult)setNonCustomCursor:(const nsIWidget::Cursor&)aCursor;
// As above, but returns an error if the cursor isn't custom or we couldn't set
// it for some reason.
- (nsresult)setCustomCursor:(const nsIWidget::Cursor&)aCursor
widgetScaleFactor:(CGFloat)aWidgetScaleFactor;
/*! @method sharedInstance
@abstract Get the Singleton instance of the cursor manager.
@discussion Use this method to obtain a reference to the cursor manager.
@result a reference to the cursor manager
*/
+ (nsCursorManager*)sharedInstance;
/*! @method dispose
@abstract Releases the shared instance of the cursor manager.
@discussion Use dispose to clean up the cursor manager and associated cursors.
*/
+ (void)dispose;
@end
@interface NSCursor (Undocumented)
// busyButClickableCursor is an undocumented NSCursor API, but has been in use since
// at least OS X 10.4 and through 10.9.
+ (NSCursor*)busyButClickableCursor;
@end
#endif // nsCursorManager_h_

View File

@ -0,0 +1,314 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "imgIContainer.h"
#include "nsCocoaUtils.h"
#include "nsCursorManager.h"
#include "nsObjCExceptions.h"
#include <math.h>
static nsCursorManager* gInstance;
static CGFloat sCurrentCursorScaleFactor = 0.0f;
static nsIWidget::Cursor sCurrentCursor;
static constexpr nsCursor kCustomCursor = eCursorCount;
/*! @category nsCursorManager(PrivateMethods)
Private methods for the cursor manager class.
*/
@interface nsCursorManager (PrivateMethods)
/*! @method getCursor:
@abstract Get a reference to the native Mac representation of a cursor.
@discussion Gets a reference to the Mac native implementation of a cursor.
If the cursor has been requested before, it is retreived from the cursor cache,
otherwise it is created and cached.
@param aCursor the cursor to get
@result the Mac native implementation of the cursor
*/
- (nsMacCursor*)getCursor:(nsCursor)aCursor;
/*! @method setMacCursor:
@abstract Set the current Mac native cursor
@discussion Sets the current cursor - this routine is what actually causes the cursor to change.
The argument is retained and the old cursor is released.
@param aMacCursor the cursor to set
@result NS_OK
*/
- (nsresult)setMacCursor:(nsMacCursor*)aMacCursor;
/*! @method createCursor:
@abstract Create a Mac native representation of a cursor.
@discussion Creates a version of the Mac native representation of this cursor
@param aCursor the cursor to create
@result the Mac native implementation of the cursor
*/
+ (nsMacCursor*)createCursor:(enum nsCursor)aCursor;
@end
@implementation nsCursorManager
+ (nsCursorManager*)sharedInstance {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
if (!gInstance) {
gInstance = [[nsCursorManager alloc] init];
}
return gInstance;
NS_OBJC_END_TRY_BLOCK_RETURN(nil);
}
+ (void)dispose {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
[gInstance release];
gInstance = nil;
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
+ (nsMacCursor*)createCursor:(enum nsCursor)aCursor {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
switch (aCursor) {
SEL cursorSelector;
case eCursor_standard:
return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor];
case eCursor_wait:
case eCursor_spinning: {
return [nsMacCursor cursorWithCursor:[NSCursor busyButClickableCursor] type:aCursor];
}
case eCursor_select:
return [nsMacCursor cursorWithCursor:[NSCursor IBeamCursor] type:aCursor];
case eCursor_hyperlink:
return [nsMacCursor cursorWithCursor:[NSCursor pointingHandCursor] type:aCursor];
case eCursor_crosshair:
return [nsMacCursor cursorWithCursor:[NSCursor crosshairCursor] type:aCursor];
case eCursor_move:
return [nsMacCursor cursorWithImageNamed:@"move" hotSpot:NSMakePoint(12, 12) type:aCursor];
case eCursor_help:
return [nsMacCursor cursorWithImageNamed:@"help" hotSpot:NSMakePoint(12, 12) type:aCursor];
case eCursor_copy:
cursorSelector = @selector(dragCopyCursor);
return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector]
? [NSCursor performSelector:cursorSelector]
: [NSCursor arrowCursor]
type:aCursor];
case eCursor_alias:
cursorSelector = @selector(dragLinkCursor);
return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector]
? [NSCursor performSelector:cursorSelector]
: [NSCursor arrowCursor]
type:aCursor];
case eCursor_context_menu:
cursorSelector = @selector(contextualMenuCursor);
return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector]
? [NSCursor performSelector:cursorSelector]
: [NSCursor arrowCursor]
type:aCursor];
case eCursor_cell:
return [nsMacCursor cursorWithImageNamed:@"cell" hotSpot:NSMakePoint(12, 12) type:aCursor];
case eCursor_grab:
return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor] type:aCursor];
case eCursor_grabbing:
return [nsMacCursor cursorWithCursor:[NSCursor closedHandCursor] type:aCursor];
case eCursor_zoom_in:
return [nsMacCursor cursorWithImageNamed:@"zoomIn" hotSpot:NSMakePoint(10, 10) type:aCursor];
case eCursor_zoom_out:
return [nsMacCursor cursorWithImageNamed:@"zoomOut" hotSpot:NSMakePoint(10, 10) type:aCursor];
case eCursor_vertical_text:
return [nsMacCursor cursorWithImageNamed:@"vtIBeam" hotSpot:NSMakePoint(12, 11) type:aCursor];
case eCursor_all_scroll:
return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor] type:aCursor];
case eCursor_not_allowed:
case eCursor_no_drop:
cursorSelector = @selector(operationNotAllowedCursor);
return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector]
? [NSCursor performSelector:cursorSelector]
: [NSCursor arrowCursor]
type:aCursor];
// Resize Cursors:
// North
case eCursor_n_resize:
return [nsMacCursor cursorWithCursor:[NSCursor resizeUpCursor] type:aCursor];
// North East
case eCursor_ne_resize:
return [nsMacCursor cursorWithImageNamed:@"sizeNE" hotSpot:NSMakePoint(12, 11) type:aCursor];
// East
case eCursor_e_resize:
return [nsMacCursor cursorWithCursor:[NSCursor resizeRightCursor] type:aCursor];
// South East
case eCursor_se_resize:
return [nsMacCursor cursorWithImageNamed:@"sizeSE" hotSpot:NSMakePoint(12, 12) type:aCursor];
// South
case eCursor_s_resize:
return [nsMacCursor cursorWithCursor:[NSCursor resizeDownCursor] type:aCursor];
// South West
case eCursor_sw_resize:
return [nsMacCursor cursorWithImageNamed:@"sizeSW" hotSpot:NSMakePoint(10, 12) type:aCursor];
// West
case eCursor_w_resize:
return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftCursor] type:aCursor];
// North West
case eCursor_nw_resize:
return [nsMacCursor cursorWithImageNamed:@"sizeNW" hotSpot:NSMakePoint(11, 11) type:aCursor];
// North & South
case eCursor_ns_resize:
return [nsMacCursor cursorWithCursor:[NSCursor resizeUpDownCursor] type:aCursor];
// East & West
case eCursor_ew_resize:
return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftRightCursor] type:aCursor];
// North East & South West
case eCursor_nesw_resize:
return [nsMacCursor cursorWithImageNamed:@"sizeNESW"
hotSpot:NSMakePoint(12, 12)
type:aCursor];
// North West & South East
case eCursor_nwse_resize:
return [nsMacCursor cursorWithImageNamed:@"sizeNWSE"
hotSpot:NSMakePoint(12, 12)
type:aCursor];
// Column Resize
case eCursor_col_resize:
return [nsMacCursor cursorWithImageNamed:@"colResize"
hotSpot:NSMakePoint(12, 12)
type:aCursor];
// Row Resize
case eCursor_row_resize:
return [nsMacCursor cursorWithImageNamed:@"rowResize"
hotSpot:NSMakePoint(12, 12)
type:aCursor];
default:
return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor];
}
NS_OBJC_END_TRY_BLOCK_RETURN(nil);
}
- (id)init {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
if ((self = [super init])) {
mCursors = [[NSMutableDictionary alloc] initWithCapacity:25];
}
return self;
NS_OBJC_END_TRY_BLOCK_RETURN(nil);
}
- (nsresult)setNonCustomCursor:(const nsIWidget::Cursor&)aCursor {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
[self setMacCursor:[self getCursor:aCursor.mDefaultCursor]];
sCurrentCursor = aCursor;
return NS_OK;
NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
}
- (nsresult)setMacCursor:(nsMacCursor*)aMacCursor {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
nsCursor oldType = [mCurrentMacCursor type];
nsCursor newType = [aMacCursor type];
if (oldType != newType) {
if (newType == eCursor_none) {
[NSCursor hide];
} else if (oldType == eCursor_none) {
[NSCursor unhide];
}
}
if (mCurrentMacCursor != aMacCursor || ![mCurrentMacCursor isSet]) {
[aMacCursor retain];
[mCurrentMacCursor unset];
[aMacCursor set];
[mCurrentMacCursor release];
mCurrentMacCursor = aMacCursor;
}
return NS_OK;
NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
}
- (nsresult)setCustomCursor:(const nsIWidget::Cursor&)aCursor
widgetScaleFactor:(CGFloat)scaleFactor {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
// As the user moves the mouse, this gets called repeatedly with the same aCursorImage
if (sCurrentCursor == aCursor && sCurrentCursorScaleFactor == scaleFactor && mCurrentMacCursor) {
// Native dragging can unset our cursor apparently (see bug 1739352).
if (MOZ_UNLIKELY(![mCurrentMacCursor isSet])) {
[mCurrentMacCursor set];
}
return NS_OK;
}
sCurrentCursor = aCursor;
sCurrentCursorScaleFactor = scaleFactor;
if (!aCursor.IsCustom()) {
return NS_ERROR_FAILURE;
}
nsIntSize size = nsIWidget::CustomCursorSize(aCursor);
// prevent DoS attacks
if (size.width > 128 || size.height > 128) {
return NS_ERROR_FAILURE;
}
NSImage* cursorImage;
nsresult rv = nsCocoaUtils::CreateNSImageFromImageContainer(
aCursor.mContainer, imgIContainer::FRAME_FIRST, nullptr, nullptr, &cursorImage, scaleFactor);
if (NS_FAILED(rv) || !cursorImage) {
return NS_ERROR_FAILURE;
}
{
NSSize cocoaSize = NSMakeSize(size.width, size.height);
[cursorImage setSize:cocoaSize];
[[[cursorImage representations] objectAtIndex:0] setSize:cocoaSize];
}
// if the hotspot is nonsensical, make it 0,0
uint32_t hotspotX = aCursor.mHotspotX > (uint32_t(size.width) - 1) ? 0 : aCursor.mHotspotX;
uint32_t hotspotY = aCursor.mHotspotY > (uint32_t(size.height) - 1) ? 0 : aCursor.mHotspotY;
NSPoint hotSpot = ::NSMakePoint(hotspotX, hotspotY);
[self setMacCursor:[nsMacCursor cursorWithCursor:[[NSCursor alloc] initWithImage:cursorImage
hotSpot:hotSpot]
type:kCustomCursor]];
[cursorImage release];
return NS_OK;
NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
}
- (nsMacCursor*)getCursor:(enum nsCursor)aCursor {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
nsMacCursor* result = [mCursors objectForKey:[NSNumber numberWithInt:aCursor]];
if (!result) {
result = [nsCursorManager createCursor:aCursor];
[mCursors setObject:result forKey:[NSNumber numberWithInt:aCursor]];
}
return result;
NS_OBJC_END_TRY_BLOCK_RETURN(nil);
}
- (void)dealloc {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
[mCurrentMacCursor unset];
[mCurrentMacCursor release];
[mCursors release];
sCurrentCursor = {};
[super dealloc];
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
@end

128
widget/cocoa/nsMacCursor.h Normal file
View File

@ -0,0 +1,128 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsMacCursor_h_
#define nsMacCursor_h_
#import <Cocoa/Cocoa.h>
#import "nsIWidget.h"
/*! @class nsMacCursor
@abstract Represents a native Mac cursor.
@discussion <code>nsMacCursor</code> provides a simple API for creating and
working with native Macintosh cursors. Cursors can be created
used without needing to be aware of the way different cursors
are implemented, in particular the details of managing an
animated cursor are hidden.
*/
@interface nsMacCursor : NSObject {
@private
NSTimer* mTimer;
@protected
nsCursor mType;
int mFrameCounter;
}
/*! @method cursorWithCursor:
@abstract Create a cursor by specifying a Cocoa <code>NSCursor</code>.
@discussion Creates a cursor representing the given Cocoa built-in cursor.
@param aCursor the <code>NSCursor</code> to use
@param aType the corresponding <code>nsCursor</code> constant
@result an autoreleased instance of <code>nsMacCursor</code>
representing the given <code>NSCursor</code>
*/
+ (nsMacCursor*)cursorWithCursor:(NSCursor*)aCursor type:(nsCursor)aType;
/*! @method cursorWithImageNamed:hotSpot:type:
@abstract Create a cursor by specifying the name of an image resource to
use for the cursor and a hotspot.
@discussion Creates a cursor by loading the named image using the
<code>+[NSImage imageNamed:]</code> method.
<p>The image must be compatible with any restrictions laid down
by <code>NSCursor</code>. These vary by operating system
version.</p>
<p>The hotspot precisely determines the point where the user
clicks when using the cursor.</p>
@param aCursor the name of the image to use for the cursor
@param aPoint the point within the cursor to use as the hotspot
@param aType the corresponding <code>nsCursor</code> constant
@result an autoreleased instance of <code>nsMacCursor</code> that uses the given image and
hotspot
*/
+ (nsMacCursor*)cursorWithImageNamed:(NSString*)aCursorImage
hotSpot:(NSPoint)aPoint
type:(nsCursor)aType;
/*! @method cursorWithFrames:type:
@abstract Create an animated cursor by specifying the frames to use for
the animation.
@discussion Creates a cursor that will animate by cycling through the given
frames. Each element of the array must be an instance of
<code>NSCursor</code>
@param aCursorFrames an array of <code>NSCursor</code>, representing
the frames of an animated cursor, in the order they should be
played.
@param aType the corresponding <code>nsCursor</code> constant
@result an autoreleased instance of <code>nsMacCursor</code> that will
animate the given cursor frames
*/
+ (nsMacCursor*)cursorWithFrames:(NSArray*)aCursorFrames type:(nsCursor)aType;
/*! @method cocoaCursorWithImageNamed:hotSpot:
@abstract Create a Cocoa NSCursor object with a Gecko image resource name
and a hotspot point.
@discussion Create a Cocoa NSCursor object with a Gecko image resource name
and a hotspot point.
@param imageName the name of the gecko image resource, "tiff"
extension is assumed, do not append.
@param aPoint the point within the cursor to use as the hotspot
@result an autoreleased instance of <code>nsMacCursor</code> that will
animate the given cursor frames
*/
+ (NSCursor*)cocoaCursorWithImageNamed:(NSString*)imageName hotSpot:(NSPoint)aPoint;
/*! @method isSet
@abstract Determines whether this cursor is currently active.
@discussion This can be helpful when the Cocoa NSCursor state can be
influenced without going through nsCursorManager.
@result whether the cursor is currently set
*/
- (BOOL)isSet;
/*! @method set
@abstract Set the cursor.
@discussion Makes this cursor the current cursor. If the cursor is
animated, the animation is started.
*/
- (void)set;
/*! @method unset
@abstract Unset the cursor. The cursor will return to the default
(usually the arrow cursor).
@discussion Unsets the cursor. If the cursor is animated, the animation is
stopped.
*/
- (void)unset;
/*! @method isAnimated
@abstract Tests whether this cursor is animated.
@discussion Use this method to determine whether a cursor is animated
@result YES if the cursor is animated (has more than one frame), NO if
it is a simple static cursor.
*/
- (BOOL)isAnimated;
/** @method cursorType
@abstract Get the cursor type for this cursor
@discussion This method returns the <code>nsCursor</code> constant that
corresponds to this cursor, which is equivalent to the CSS
name for the cursor.
@result The nsCursor constant corresponding to this cursor, or
nsCursor's 'eCursorCount' if the cursor is a custom cursor
loaded from a URI
*/
- (nsCursor)type;
@end
#endif // nsMacCursor_h_

367
widget/cocoa/nsMacCursor.mm Normal file
View File

@ -0,0 +1,367 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsMacCursor.h"
#include "nsObjCExceptions.h"
#include "nsDebug.h"
#include "nsDirectoryServiceDefs.h"
#include "nsCOMPtr.h"
#include "nsIFile.h"
#include "nsString.h"
/*! @category nsMacCursor (PrivateMethods)
@abstract Private methods internal to the nsMacCursor class.
@discussion <code>nsMacCursor</code> is effectively an abstract class. It does not define
complete behaviour in and of itself, the subclasses defined in this file provide the useful
implementations.
*/
@interface nsMacCursor (PrivateMethods)
/*! @method getNextCursorFrame
@abstract get the index of the next cursor frame to display.
@discussion Increments and returns the frame counter of an animated cursor.
@result The index of the next frame to display in the cursor animation
*/
- (int)getNextCursorFrame;
/*! @method numFrames
@abstract Query the number of frames in this cursor's animation.
@discussion Returns the number of frames in this cursor's animation. Static cursors return 1.
*/
- (int)numFrames;
/*! @method createTimer
@abstract Create a Timer to use to animate the cursor.
@discussion Creates an instance of <code>NSTimer</code> which is used to drive the cursor
animation. This method should only be called for cursors that are animated.
*/
- (void)createTimer;
/*! @method destroyTimer
@abstract Destroy any timer instance associated with this cursor.
@discussion Invalidates and releases any <code>NSTimer</code> instance associated with this
cursor.
*/
- (void)destroyTimer;
/*! @method destroyTimer
@abstract Destroy any timer instance associated with this cursor.
@discussion Invalidates and releases any <code>NSTimer</code> instance associated with this
cursor.
*/
/*! @method advanceAnimatedCursor:
@abstract Method called by animation timer to perform animation.
@discussion Called by an animated cursor's associated timer to advance the animation to the next
frame. Determines which frame should occur next and sets the cursor to that frame.
@param aTimer the timer causing the animation
*/
- (void)advanceAnimatedCursor:(NSTimer*)aTimer;
/*! @method setFrame:
@abstract Sets the current cursor, using an index to determine which frame in the animation to
display.
@discussion Sets the current cursor. The frame index determines which frame is shown if the
cursor is animated. Frames and numbered from <code>0</code> to <code>-[nsMacCursor numFrames] -
1</code>. A static cursor has a single frame, numbered 0.
@param aFrameIndex the index indicating which frame from the animation to display
*/
- (void)setFrame:(int)aFrameIndex;
@end
/*! @class nsCocoaCursor
@abstract Implementation of <code>nsMacCursor</code> that uses Cocoa <code>NSCursor</code>
instances.
@discussion Displays a static or animated cursor, using Cocoa <code>NSCursor</code> instances.
These can be either built-in <code>NSCursor</code> instances, or custom <code>NSCursor</code>s
created from images. When more than one <code>NSCursor</code> is provided, the cursor will use
these as animation frames.
*/
@interface nsCocoaCursor : nsMacCursor {
@private
NSArray* mFrames;
NSCursor* mLastSetCocoaCursor;
}
/*! @method initWithFrames:
@abstract Create an animated cursor by specifying the frames to use for the animation.
@discussion Creates a cursor that will animate by cycling through the given frames. Each element
of the array must be an instance of <code>NSCursor</code>
@param aCursorFrames an array of <code>NSCursor</code>, representing the frames of an
animated cursor, in the order they should be played.
@param aType the corresponding <code>nsCursor</code> constant
@result an instance of <code>nsCocoaCursor</code> that will animate the given cursor frames
*/
- (id)initWithFrames:(NSArray*)aCursorFrames type:(nsCursor)aType;
/*! @method initWithCursor:
@abstract Create a cursor by specifying a Cocoa <code>NSCursor</code>.
@discussion Creates a cursor representing the given Cocoa built-in cursor.
@param aCursor the <code>NSCursor</code> to use
@param aType the corresponding <code>nsCursor</code> constant
@result an instance of <code>nsCocoaCursor</code> representing the given
<code>NSCursor</code>
*/
- (id)initWithCursor:(NSCursor*)aCursor type:(nsCursor)aType;
/*! @method initWithImageNamed:hotSpot:
@abstract Create a cursor by specifying the name of an image resource to use for the cursor
and a hotspot.
@discussion Creates a cursor by loading the named image using the <code>+[NSImage
imageNamed:]</code> method. <p>The image must be compatible with any restrictions laid down by
<code>NSCursor</code>. These vary by operating system version.</p> <p>The hotspot precisely
determines the point where the user clicks when using the cursor.</p>
@param aCursor the name of the image to use for the cursor
@param aPoint the point within the cursor to use as the hotspot
@param aType the corresponding <code>nsCursor</code> constant
@result an instance of <code>nsCocoaCursor</code> that uses the given image and hotspot
*/
- (id)initWithImageNamed:(NSString*)aCursorImage hotSpot:(NSPoint)aPoint type:(nsCursor)aType;
@end
@implementation nsMacCursor
+ (nsMacCursor*)cursorWithCursor:(NSCursor*)aCursor type:(nsCursor)aType {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
return [[[nsCocoaCursor alloc] initWithCursor:aCursor type:aType] autorelease];
NS_OBJC_END_TRY_BLOCK_RETURN(nil);
}
+ (nsMacCursor*)cursorWithImageNamed:(NSString*)aCursorImage
hotSpot:(NSPoint)aPoint
type:(nsCursor)aType {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
return [[[nsCocoaCursor alloc] initWithImageNamed:aCursorImage hotSpot:aPoint
type:aType] autorelease];
NS_OBJC_END_TRY_BLOCK_RETURN(nil);
}
+ (nsMacCursor*)cursorWithFrames:(NSArray*)aCursorFrames type:(nsCursor)aType {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
return [[[nsCocoaCursor alloc] initWithFrames:aCursorFrames type:aType] autorelease];
NS_OBJC_END_TRY_BLOCK_RETURN(nil);
}
+ (NSCursor*)cocoaCursorWithImageNamed:(NSString*)imageName hotSpot:(NSPoint)aPoint {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
nsCOMPtr<nsIFile> resDir;
nsAutoCString resPath;
NSString *pathToImage, *pathToHiDpiImage;
NSImage *cursorImage, *hiDpiCursorImage;
nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(resDir));
if (NS_FAILED(rv)) goto INIT_FAILURE;
resDir->AppendNative("res"_ns);
resDir->AppendNative("cursors"_ns);
rv = resDir->GetNativePath(resPath);
if (NS_FAILED(rv)) goto INIT_FAILURE;
pathToImage = [NSString stringWithUTF8String:(const char*)resPath.get()];
if (!pathToImage) goto INIT_FAILURE;
pathToImage = [pathToImage stringByAppendingPathComponent:imageName];
pathToHiDpiImage = [pathToImage stringByAppendingString:@"@2x"];
// Add same extension to both image paths.
pathToImage = [pathToImage stringByAppendingPathExtension:@"png"];
pathToHiDpiImage = [pathToHiDpiImage stringByAppendingPathExtension:@"png"];
cursorImage = [[[NSImage alloc] initWithContentsOfFile:pathToImage] autorelease];
if (!cursorImage) goto INIT_FAILURE;
// Note 1: There are a few different ways to get a hidpi image via
// initWithContentsOfFile. We let the OS handle this here: when the
// file basename ends in "@2x", it will be displayed at native resolution
// instead of being pixel-doubled. See bug 784909 comment 7 for alternates ways.
//
// Note 2: The OS is picky, and will ignore the hidpi representation
// unless it is exactly twice the size of the lowdpi image.
hiDpiCursorImage = [[[NSImage alloc] initWithContentsOfFile:pathToHiDpiImage] autorelease];
if (hiDpiCursorImage) {
NSImageRep* imageRep = [[hiDpiCursorImage representations] objectAtIndex:0];
[cursorImage addRepresentation:imageRep];
}
return [[[NSCursor alloc] initWithImage:cursorImage hotSpot:aPoint] autorelease];
INIT_FAILURE:
NS_WARNING("Problem getting path to cursor image file!");
[self release];
return nil;
NS_OBJC_END_TRY_BLOCK_RETURN(nil);
}
- (BOOL)isSet {
// implemented by subclasses
return NO;
}
- (void)set {
if ([self isAnimated]) {
[self createTimer];
}
// if the cursor isn't animated or the timer creation fails for any reason...
if (!mTimer) {
[self setFrame:0];
}
}
- (void)unset {
[self destroyTimer];
}
- (BOOL)isAnimated {
return [self numFrames] > 1;
}
- (int)numFrames {
// subclasses need to override this to support animation
return 1;
}
- (int)getNextCursorFrame {
mFrameCounter = (mFrameCounter + 1) % [self numFrames];
return mFrameCounter;
}
- (void)createTimer {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
if (!mTimer) {
mTimer = [[NSTimer scheduledTimerWithTimeInterval:0.25
target:self
selector:@selector(advanceAnimatedCursor:)
userInfo:nil
repeats:YES] retain];
}
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
- (void)destroyTimer {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
if (mTimer) {
[mTimer invalidate];
[mTimer release];
mTimer = nil;
}
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
- (void)advanceAnimatedCursor:(NSTimer*)aTimer {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
if ([aTimer isValid]) {
[self setFrame:[self getNextCursorFrame]];
}
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
- (void)setFrame:(int)aFrameIndex {
// subclasses need to do something useful here
}
- (nsCursor)type {
return mType;
}
- (void)dealloc {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
[self destroyTimer];
[super dealloc];
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
@end
@implementation nsCocoaCursor
- (id)initWithFrames:(NSArray*)aCursorFrames type:(nsCursor)aType {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
self = [super init];
NSEnumerator* it = [aCursorFrames objectEnumerator];
NSObject* frame = nil;
while ((frame = [it nextObject])) {
NS_ASSERTION([frame isKindOfClass:[NSCursor class]],
"Invalid argument: All frames must be of type NSCursor");
}
mFrames = [aCursorFrames retain];
mFrameCounter = 0;
mType = aType;
return self;
NS_OBJC_END_TRY_BLOCK_RETURN(nil);
}
- (id)initWithCursor:(NSCursor*)aCursor type:(nsCursor)aType {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
NSArray* frame = [NSArray arrayWithObjects:aCursor, nil];
return [self initWithFrames:frame type:aType];
NS_OBJC_END_TRY_BLOCK_RETURN(nil);
}
- (id)initWithImageNamed:(NSString*)aCursorImage hotSpot:(NSPoint)aPoint type:(nsCursor)aType {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
return [self initWithCursor:[nsMacCursor cocoaCursorWithImageNamed:aCursorImage hotSpot:aPoint]
type:aType];
NS_OBJC_END_TRY_BLOCK_RETURN(nil);
}
- (BOOL)isSet {
return [NSCursor currentCursor] == mLastSetCocoaCursor;
}
- (void)setFrame:(int)aFrameIndex {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
NSCursor* newCursor = [mFrames objectAtIndex:aFrameIndex];
[newCursor set];
mLastSetCocoaCursor = newCursor;
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
- (int)numFrames {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
return [mFrames count];
NS_OBJC_END_TRY_BLOCK_RETURN(0);
}
- (NSString*)description {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
return [mFrames description];
NS_OBJC_END_TRY_BLOCK_RETURN(nil);
}
- (void)dealloc {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
[mFrames release];
[super dealloc];
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
@end