widget/cocoa/nsCocoaUtils.mm

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "gfxImageSurface.h"
     7 #include "gfxPlatform.h"
     8 #include "gfxUtils.h"
     9 #include "nsCocoaUtils.h"
    10 #include "nsChildView.h"
    11 #include "nsMenuBarX.h"
    12 #include "nsCocoaWindow.h"
    13 #include "nsCOMPtr.h"
    14 #include "nsIInterfaceRequestorUtils.h"
    15 #include "nsIAppShellService.h"
    16 #include "nsIXULWindow.h"
    17 #include "nsIBaseWindow.h"
    18 #include "nsIServiceManager.h"
    19 #include "nsMenuUtilsX.h"
    20 #include "nsToolkit.h"
    21 #include "nsCRT.h"
    22 #include "mozilla/gfx/2D.h"
    23 #include "mozilla/MiscEvents.h"
    24 #include "mozilla/Preferences.h"
    25 #include "mozilla/TextEvents.h"
    27 using namespace mozilla;
    28 using namespace mozilla::widget;
    30 using mozilla::gfx::BackendType;
    31 using mozilla::gfx::DataSourceSurface;
    32 using mozilla::gfx::DrawTarget;
    33 using mozilla::gfx::Factory;
    34 using mozilla::gfx::IntPoint;
    35 using mozilla::gfx::IntRect;
    36 using mozilla::gfx::IntSize;
    37 using mozilla::gfx::SurfaceFormat;
    38 using mozilla::gfx::SourceSurface;
    40 static float
    41 MenuBarScreenHeight()
    42 {
    43   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
    45   NSArray* allScreens = [NSScreen screens];
    46   if ([allScreens count]) {
    47     return [[allScreens objectAtIndex:0] frame].size.height;
    48   }
    50   return 0.0;
    52   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0.0);
    53 }
    55 float
    56 nsCocoaUtils::FlippedScreenY(float y)
    57 {
    58   return MenuBarScreenHeight() - y;
    59 }
    61 NSRect nsCocoaUtils::GeckoRectToCocoaRect(const nsIntRect &geckoRect)
    62 {
    63   // We only need to change the Y coordinate by starting with the primary screen
    64   // height and subtracting the gecko Y coordinate of the bottom of the rect.
    65   return NSMakeRect(geckoRect.x,
    66                     MenuBarScreenHeight() - geckoRect.YMost(),
    67                     geckoRect.width,
    68                     geckoRect.height);
    69 }
    71 NSRect nsCocoaUtils::GeckoRectToCocoaRectDevPix(const nsIntRect &aGeckoRect,
    72                                                 CGFloat aBackingScale)
    73 {
    74   return NSMakeRect(aGeckoRect.x / aBackingScale,
    75                     MenuBarScreenHeight() - aGeckoRect.YMost() / aBackingScale,
    76                     aGeckoRect.width / aBackingScale,
    77                     aGeckoRect.height / aBackingScale);
    78 }
    80 nsIntRect nsCocoaUtils::CocoaRectToGeckoRect(const NSRect &cocoaRect)
    81 {
    82   // We only need to change the Y coordinate by starting with the primary screen
    83   // height and subtracting both the cocoa y origin and the height of the
    84   // cocoa rect.
    85   nsIntRect rect;
    86   rect.x = NSToIntRound(cocoaRect.origin.x);
    87   rect.y = NSToIntRound(FlippedScreenY(cocoaRect.origin.y + cocoaRect.size.height));
    88   rect.width = NSToIntRound(cocoaRect.origin.x + cocoaRect.size.width) - rect.x;
    89   rect.height = NSToIntRound(FlippedScreenY(cocoaRect.origin.y)) - rect.y;
    90   return rect;
    91 }
    93 nsIntRect nsCocoaUtils::CocoaRectToGeckoRectDevPix(const NSRect &aCocoaRect,
    94                                                    CGFloat aBackingScale)
    95 {
    96   nsIntRect rect;
    97   rect.x = NSToIntRound(aCocoaRect.origin.x * aBackingScale);
    98   rect.y = NSToIntRound(FlippedScreenY(aCocoaRect.origin.y + aCocoaRect.size.height) * aBackingScale);
    99   rect.width = NSToIntRound((aCocoaRect.origin.x + aCocoaRect.size.width) * aBackingScale) - rect.x;
   100   rect.height = NSToIntRound(FlippedScreenY(aCocoaRect.origin.y) * aBackingScale) - rect.y;
   101   return rect;
   102 }
   104 NSPoint nsCocoaUtils::ScreenLocationForEvent(NSEvent* anEvent)
   105 {
   106   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
   108   // Don't trust mouse locations of mouse move events, see bug 443178.
   109   if (!anEvent || [anEvent type] == NSMouseMoved)
   110     return [NSEvent mouseLocation];
   112   // Pin momentum scroll events to the location of the last user-controlled
   113   // scroll event.
   114   if (IsMomentumScrollEvent(anEvent))
   115     return ChildViewMouseTracker::sLastScrollEventScreenLocation;
   117   return [[anEvent window] convertBaseToScreen:[anEvent locationInWindow]];
   119   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakePoint(0.0, 0.0));
   120 }
   122 BOOL nsCocoaUtils::IsEventOverWindow(NSEvent* anEvent, NSWindow* aWindow)
   123 {
   124   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
   126   return NSPointInRect(ScreenLocationForEvent(anEvent), [aWindow frame]);
   128   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
   129 }
   131 NSPoint nsCocoaUtils::EventLocationForWindow(NSEvent* anEvent, NSWindow* aWindow)
   132 {
   133   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
   135   return [aWindow convertScreenToBase:ScreenLocationForEvent(anEvent)];
   137   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakePoint(0.0, 0.0));
   138 }
   140 BOOL nsCocoaUtils::IsMomentumScrollEvent(NSEvent* aEvent)
   141 {
   142   if ([aEvent type] != NSScrollWheel)
   143     return NO;
   145   if ([aEvent respondsToSelector:@selector(momentumPhase)])
   146     return ([aEvent momentumPhase] & NSEventPhaseChanged) != 0;
   148   if ([aEvent respondsToSelector:@selector(_scrollPhase)])
   149     return [aEvent _scrollPhase] != 0;
   151   return NO;
   152 }
   154 void nsCocoaUtils::HideOSChromeOnScreen(bool aShouldHide, NSScreen* aScreen)
   155 {
   156   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   158   // Keep track of how many hiding requests have been made, so that they can
   159   // be nested.
   160   static int sMenuBarHiddenCount = 0, sDockHiddenCount = 0;
   162   // Always hide the Dock, since it's not necessarily on the primary screen.
   163   sDockHiddenCount += aShouldHide ? 1 : -1;
   164   NS_ASSERTION(sMenuBarHiddenCount >= 0, "Unbalanced HideMenuAndDockForWindow calls");
   166   // Only hide the menu bar if the window is on the same screen.
   167   // The menu bar is always on the first screen in the screen list.
   168   if (aScreen == [[NSScreen screens] objectAtIndex:0]) {
   169     sMenuBarHiddenCount += aShouldHide ? 1 : -1;
   170     NS_ASSERTION(sDockHiddenCount >= 0, "Unbalanced HideMenuAndDockForWindow calls");
   171   }
   173   // TODO This should be upgraded to use [NSApplication setPresentationOptions:]
   174   // when support for 10.5 is dropped.
   175   if (sMenuBarHiddenCount > 0) {
   176     ::SetSystemUIMode(kUIModeAllHidden, 0);
   177   } else if (sDockHiddenCount > 0) {
   178     ::SetSystemUIMode(kUIModeContentHidden, 0);
   179   } else {
   180     ::SetSystemUIMode(kUIModeNormal, 0);
   181   }
   183   NS_OBJC_END_TRY_ABORT_BLOCK;
   184 }
   187 #define NS_APPSHELLSERVICE_CONTRACTID "@mozilla.org/appshell/appShellService;1"
   188 nsIWidget* nsCocoaUtils::GetHiddenWindowWidget()
   189 {
   190   nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
   191   if (!appShell) {
   192     NS_WARNING("Couldn't get AppShellService in order to get hidden window ref");
   193     return nullptr;
   194   }
   196   nsCOMPtr<nsIXULWindow> hiddenWindow;
   197   appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
   198   if (!hiddenWindow) {
   199     // Don't warn, this happens during shutdown, bug 358607.
   200     return nullptr;
   201   }
   203   nsCOMPtr<nsIBaseWindow> baseHiddenWindow;
   204   baseHiddenWindow = do_GetInterface(hiddenWindow);
   205   if (!baseHiddenWindow) {
   206     NS_WARNING("Couldn't get nsIBaseWindow from hidden window (nsIXULWindow)");
   207     return nullptr;
   208   }
   210   nsCOMPtr<nsIWidget> hiddenWindowWidget;
   211   if (NS_FAILED(baseHiddenWindow->GetMainWidget(getter_AddRefs(hiddenWindowWidget)))) {
   212     NS_WARNING("Couldn't get nsIWidget from hidden window (nsIBaseWindow)");
   213     return nullptr;
   214   }
   216   return hiddenWindowWidget;
   217 }
   219 void nsCocoaUtils::PrepareForNativeAppModalDialog()
   220 {
   221   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   223   // Don't do anything if this is embedding. We'll assume that if there is no hidden
   224   // window we shouldn't do anything, and that should cover the embedding case.
   225   nsMenuBarX* hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
   226   if (!hiddenWindowMenuBar)
   227     return;
   229   // First put up the hidden window menu bar so that app menu event handling is correct.
   230   hiddenWindowMenuBar->Paint();
   232   NSMenu* mainMenu = [NSApp mainMenu];
   233   NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
   235   // Create new menu bar for use with modal dialog
   236   NSMenu* newMenuBar = [[NSMenu alloc] initWithTitle:@""];
   238   // Swap in our app menu. Note that the event target is whatever window is up when
   239   // the app modal dialog goes up.
   240   NSMenuItem* firstMenuItem = [[mainMenu itemAtIndex:0] retain];
   241   [mainMenu removeItemAtIndex:0];
   242   [newMenuBar insertItem:firstMenuItem atIndex:0];
   243   [firstMenuItem release];
   245   // Add standard edit menu
   246   [newMenuBar addItem:nsMenuUtilsX::GetStandardEditMenuItem()];
   248   // Show the new menu bar
   249   [NSApp setMainMenu:newMenuBar];
   250   [newMenuBar release];
   252   NS_OBJC_END_TRY_ABORT_BLOCK;
   253 }
   255 void nsCocoaUtils::CleanUpAfterNativeAppModalDialog()
   256 {
   257   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   259   // Don't do anything if this is embedding. We'll assume that if there is no hidden
   260   // window we shouldn't do anything, and that should cover the embedding case.
   261   nsMenuBarX* hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
   262   if (!hiddenWindowMenuBar)
   263     return;
   265   NSWindow* mainWindow = [NSApp mainWindow];
   266   if (!mainWindow)
   267     hiddenWindowMenuBar->Paint();
   268   else
   269     [WindowDelegate paintMenubarForWindow:mainWindow];
   271   NS_OBJC_END_TRY_ABORT_BLOCK;
   272 }
   274 void data_ss_release_callback(void *aDataSourceSurface,
   275                               const void *data,
   276                               size_t size)
   277 {
   278   if (aDataSourceSurface) {
   279     static_cast<DataSourceSurface*>(aDataSourceSurface)->Unmap();
   280     static_cast<DataSourceSurface*>(aDataSourceSurface)->Release();
   281   }
   282 }
   284 nsresult nsCocoaUtils::CreateCGImageFromSurface(SourceSurface* aSurface,
   285                                                 CGImageRef* aResult)
   286 {
   287   RefPtr<DataSourceSurface> dataSurface;
   289   if (aSurface->GetFormat() ==  SurfaceFormat::B8G8R8A8) {
   290     dataSurface = aSurface->GetDataSurface();
   291   } else {
   292     // CGImageCreate only supports 16- and 32-bit bit-depth
   293     // Convert format to SurfaceFormat::B8G8R8A8
   294     dataSurface = gfxUtils::
   295       CopySurfaceToDataSourceSurfaceWithFormat(aSurface,
   296                                                SurfaceFormat::B8G8R8A8);
   297   }
   299   NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
   301   int32_t width = dataSurface->GetSize().width;
   302   int32_t height = dataSurface->GetSize().height;
   303   if (height < 1 || width < 1) {
   304     return NS_ERROR_FAILURE;
   305   }
   307   DataSourceSurface::MappedSurface map;
   308   if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
   309     return NS_ERROR_FAILURE;
   310   }
   311   // The Unmap() call happens in data_ss_release_callback
   313   // Create a CGImageRef with the bits from the image, taking into account
   314   // the alpha ordering and endianness of the machine so we don't have to
   315   // touch the bits ourselves.
   316   CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(dataSurface.forget().drop(),
   317                                                                   map.mData,
   318                                                                   map.mStride * height,
   319                                                                   data_ss_release_callback);
   320   CGColorSpaceRef colorSpace = ::CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
   321   *aResult = ::CGImageCreate(width,
   322                              height,
   323                              8,
   324                              32,
   325                              map.mStride,
   326                              colorSpace,
   327                              kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst,
   328                              dataProvider,
   329                              NULL,
   330                              0,
   331                              kCGRenderingIntentDefault);
   332   ::CGColorSpaceRelease(colorSpace);
   333   ::CGDataProviderRelease(dataProvider);
   334   return *aResult ? NS_OK : NS_ERROR_FAILURE;
   335 }
   337 nsresult nsCocoaUtils::CreateNSImageFromCGImage(CGImageRef aInputImage, NSImage **aResult)
   338 {
   339   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   341   // Be very careful when creating the NSImage that the backing NSImageRep is
   342   // exactly 1:1 with the input image. On a retina display, both [NSImage
   343   // lockFocus] and [NSImage initWithCGImage:size:] will create an image with a
   344   // 2x backing NSImageRep. This prevents NSCursor from recognizing a retina
   345   // cursor, which only occurs if pixelsWide and pixelsHigh are exactly 2x the
   346   // size of the NSImage.
   347   //
   348   // For example, if a 32x32 SVG cursor is rendered on a retina display, then
   349   // aInputImage will be 64x64. The resulting NSImage will be scaled back down
   350   // to 32x32 so it stays the correct size on the screen by changing its size
   351   // (resizing a NSImage only scales the image and doesn't resample the data).
   352   // If aInputImage is converted using [NSImage initWithCGImage:size:] then the
   353   // bitmap will be 128x128 and NSCursor won't recognize a retina cursor, since
   354   // it will expect a 64x64 bitmap.
   356   int32_t width = ::CGImageGetWidth(aInputImage);
   357   int32_t height = ::CGImageGetHeight(aInputImage);
   358   NSRect imageRect = ::NSMakeRect(0.0, 0.0, width, height);
   360   NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc]
   361     initWithBitmapDataPlanes:NULL
   362     pixelsWide:width
   363     pixelsHigh:height
   364     bitsPerSample:8
   365     samplesPerPixel:4
   366     hasAlpha:YES
   367     isPlanar:NO
   368     colorSpaceName:NSDeviceRGBColorSpace
   369     bitmapFormat:NSAlphaFirstBitmapFormat
   370     bytesPerRow:0
   371     bitsPerPixel:0];
   373   NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep];
   374   [NSGraphicsContext saveGraphicsState];
   375   [NSGraphicsContext setCurrentContext:context];
   377   // Get the Quartz context and draw.
   378   CGContextRef imageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
   379   ::CGContextDrawImage(imageContext, *(CGRect*)&imageRect, aInputImage);
   381   [NSGraphicsContext restoreGraphicsState];
   383   *aResult = [[NSImage alloc] initWithSize:NSMakeSize(width, height)];
   384   [*aResult addRepresentation:offscreenRep];
   385   [offscreenRep release];
   386   return NS_OK;
   388   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   389 }
   391 nsresult nsCocoaUtils::CreateNSImageFromImageContainer(imgIContainer *aImage, uint32_t aWhichFrame, NSImage **aResult, CGFloat scaleFactor)
   392 {
   393   RefPtr<SourceSurface> surface;
   394   int32_t width = 0, height = 0;
   395   aImage->GetWidth(&width);
   396   aImage->GetHeight(&height);
   398   // Render a vector image at the correct resolution on a retina display
   399   if (aImage->GetType() == imgIContainer::TYPE_VECTOR && scaleFactor != 1.0f) {
   400     int scaledWidth = (int)ceilf(width * scaleFactor);
   401     int scaledHeight = (int)ceilf(height * scaleFactor);
   403     RefPtr<DrawTarget> drawTarget = gfxPlatform::GetPlatform()->
   404       CreateOffscreenContentDrawTarget(IntSize(scaledWidth, scaledHeight),
   405                                        SurfaceFormat::B8G8R8A8);
   406     if (!drawTarget) {
   407       NS_ERROR("Failed to create DrawTarget");
   408       return NS_ERROR_FAILURE;
   409     }
   411     nsRefPtr<gfxContext> context = new gfxContext(drawTarget);
   412     if (!context) {
   413       NS_ERROR("Failed to create gfxContext");
   414       return NS_ERROR_FAILURE;
   415     }
   417     aImage->Draw(context, GraphicsFilter::FILTER_NEAREST, gfxMatrix(),
   418       gfxRect(0.0f, 0.0f, scaledWidth, scaledHeight),
   419       nsIntRect(0, 0, width, height),
   420       nsIntSize(scaledWidth, scaledHeight),
   421       nullptr, aWhichFrame, imgIContainer::FLAG_SYNC_DECODE);
   423     surface = drawTarget->Snapshot();
   424   } else {
   425     surface = aImage->GetFrame(aWhichFrame, imgIContainer::FLAG_SYNC_DECODE);
   426   }
   428   NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
   430   CGImageRef imageRef = NULL;
   431   nsresult rv = nsCocoaUtils::CreateCGImageFromSurface(surface, &imageRef);
   432   if (NS_FAILED(rv) || !imageRef) {
   433     return NS_ERROR_FAILURE;
   434   }
   436   rv = nsCocoaUtils::CreateNSImageFromCGImage(imageRef, aResult);
   437   if (NS_FAILED(rv) || !aResult) {
   438     return NS_ERROR_FAILURE;
   439   }
   440   ::CGImageRelease(imageRef);
   442   // Ensure the image will be rendered the correct size on a retina display
   443   NSSize size = NSMakeSize(width, height);
   444   [*aResult setSize:size];
   445   [[[*aResult representations] objectAtIndex:0] setSize:size];
   446   return NS_OK;
   447 }
   449 // static
   450 void
   451 nsCocoaUtils::GetStringForNSString(const NSString *aSrc, nsAString& aDist)
   452 {
   453   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   455   if (!aSrc) {
   456     aDist.Truncate();
   457     return;
   458   }
   460   aDist.SetLength([aSrc length]);
   461   [aSrc getCharacters: reinterpret_cast<unichar*>(aDist.BeginWriting())];
   463   NS_OBJC_END_TRY_ABORT_BLOCK;
   464 }
   466 // static
   467 NSString*
   468 nsCocoaUtils::ToNSString(const nsAString& aString)
   469 {
   470   if (aString.IsEmpty()) {
   471     return [NSString string];
   472   }
   473   return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(aString.BeginReading())
   474                                  length:aString.Length()];
   475 }
   477 // static
   478 void
   479 nsCocoaUtils::GeckoRectToNSRect(const nsIntRect& aGeckoRect,
   480                                 NSRect& aOutCocoaRect)
   481 {
   482   aOutCocoaRect.origin.x = aGeckoRect.x;
   483   aOutCocoaRect.origin.y = aGeckoRect.y;
   484   aOutCocoaRect.size.width = aGeckoRect.width;
   485   aOutCocoaRect.size.height = aGeckoRect.height;
   486 }
   488 // static
   489 void
   490 nsCocoaUtils::NSRectToGeckoRect(const NSRect& aCocoaRect,
   491                                 nsIntRect& aOutGeckoRect)
   492 {
   493   aOutGeckoRect.x = NSToIntRound(aCocoaRect.origin.x);
   494   aOutGeckoRect.y = NSToIntRound(aCocoaRect.origin.y);
   495   aOutGeckoRect.width = NSToIntRound(aCocoaRect.origin.x + aCocoaRect.size.width) - aOutGeckoRect.x;
   496   aOutGeckoRect.height = NSToIntRound(aCocoaRect.origin.y + aCocoaRect.size.height) - aOutGeckoRect.y;
   497 }
   499 // static
   500 NSEvent*
   501 nsCocoaUtils::MakeNewCocoaEventWithType(NSEventType aEventType, NSEvent *aEvent)
   502 {
   503   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
   505   NSEvent* newEvent =
   506     [NSEvent     keyEventWithType:aEventType
   507                          location:[aEvent locationInWindow] 
   508                     modifierFlags:[aEvent modifierFlags]
   509                         timestamp:[aEvent timestamp]
   510                      windowNumber:[aEvent windowNumber]
   511                           context:[aEvent context]
   512                        characters:[aEvent characters]
   513       charactersIgnoringModifiers:[aEvent charactersIgnoringModifiers]
   514                         isARepeat:[aEvent isARepeat]
   515                           keyCode:[aEvent keyCode]];
   516   return newEvent;
   518   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
   519 }
   521 // static
   522 void
   523 nsCocoaUtils::InitNPCocoaEvent(NPCocoaEvent* aNPCocoaEvent)
   524 {
   525   memset(aNPCocoaEvent, 0, sizeof(NPCocoaEvent));
   526 }
   528 // static
   529 void
   530 nsCocoaUtils::InitPluginEvent(WidgetPluginEvent &aPluginEvent,
   531                               NPCocoaEvent &aCocoaEvent)
   532 {
   533   aPluginEvent.time = PR_IntervalNow();
   534   aPluginEvent.pluginEvent = (void*)&aCocoaEvent;
   535   aPluginEvent.retargetToFocusedDocument = false;
   536 }
   538 // static
   539 void
   540 nsCocoaUtils::InitInputEvent(WidgetInputEvent& aInputEvent,
   541                              NSEvent* aNativeEvent)
   542 {
   543   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   545   NSUInteger modifiers =
   546     aNativeEvent ? [aNativeEvent modifierFlags] : [NSEvent modifierFlags];
   547   InitInputEvent(aInputEvent, modifiers);
   549   aInputEvent.time = PR_IntervalNow();
   551   NS_OBJC_END_TRY_ABORT_BLOCK;
   552 }
   554 // static
   555 void
   556 nsCocoaUtils::InitInputEvent(WidgetInputEvent& aInputEvent,
   557                              NSUInteger aModifiers)
   558 {
   559   aInputEvent.modifiers = 0;
   560   if (aModifiers & NSShiftKeyMask) {
   561     aInputEvent.modifiers |= MODIFIER_SHIFT;
   562   }
   563   if (aModifiers & NSControlKeyMask) {
   564     aInputEvent.modifiers |= MODIFIER_CONTROL;
   565   }
   566   if (aModifiers & NSAlternateKeyMask) {
   567     aInputEvent.modifiers |= MODIFIER_ALT;
   568     // Mac's option key is similar to other platforms' AltGr key.
   569     // Let's set AltGr flag when option key is pressed for consistency with
   570     // other platforms.
   571     aInputEvent.modifiers |= MODIFIER_ALTGRAPH;
   572   }
   573   if (aModifiers & NSCommandKeyMask) {
   574     aInputEvent.modifiers |= MODIFIER_META;
   575   }
   577   if (aModifiers & NSAlphaShiftKeyMask) {
   578     aInputEvent.modifiers |= MODIFIER_CAPSLOCK;
   579   }
   580   // Mac doesn't have NumLock key.  We can assume that NumLock is always locked
   581   // if user is using a keyboard which has numpad.  Otherwise, if user is using
   582   // a keyboard which doesn't have numpad, e.g., MacBook's keyboard, we can
   583   // assume that NumLock is always unlocked.
   584   // Unfortunately, we cannot know whether current keyboard has numpad or not.
   585   // We should notify locked state only when keys in numpad are pressed.
   586   // By this, web applications may not be confused by unexpected numpad key's
   587   // key event with unlocked state.
   588   if (aModifiers & NSNumericPadKeyMask) {
   589     aInputEvent.modifiers |= MODIFIER_NUMLOCK;
   590   }
   592   // Be aware, NSFunctionKeyMask is included when arrow keys, home key or some
   593   // other keys are pressed. We cannot check whether 'fn' key is pressed or
   594   // not by the flag.
   596 }
   598 // static
   599 UInt32
   600 nsCocoaUtils::ConvertToCarbonModifier(NSUInteger aCocoaModifier)
   601 {
   602   UInt32 carbonModifier = 0;
   603   if (aCocoaModifier & NSAlphaShiftKeyMask) {
   604     carbonModifier |= alphaLock;
   605   }
   606   if (aCocoaModifier & NSControlKeyMask) {
   607     carbonModifier |= controlKey;
   608   }
   609   if (aCocoaModifier & NSAlternateKeyMask) {
   610     carbonModifier |= optionKey;
   611   }
   612   if (aCocoaModifier & NSShiftKeyMask) {
   613     carbonModifier |= shiftKey;
   614   }
   615   if (aCocoaModifier & NSCommandKeyMask) {
   616     carbonModifier |= cmdKey;
   617   }
   618   if (aCocoaModifier & NSNumericPadKeyMask) {
   619     carbonModifier |= kEventKeyModifierNumLockMask;
   620   }
   621   if (aCocoaModifier & NSFunctionKeyMask) {
   622     carbonModifier |= kEventKeyModifierFnMask;
   623   }
   624   return carbonModifier;
   625 }
   627 // While HiDPI support is not 100% complete and tested, we'll have a pref
   628 // to allow it to be turned off in case of problems (or for testing purposes).
   630 // gfx.hidpi.enabled is an integer with the meaning:
   631 //    <= 0 : HiDPI support is disabled
   632 //       1 : HiDPI enabled provided all screens have the same backing resolution
   633 //     > 1 : HiDPI enabled even if there are a mixture of screen modes
   635 // All the following code is to be removed once HiDPI work is more complete.
   637 static bool sHiDPIEnabled = false;
   638 static bool sHiDPIPrefInitialized = false;
   640 // static
   641 bool
   642 nsCocoaUtils::HiDPIEnabled()
   643 {
   644   if (!sHiDPIPrefInitialized) {
   645     sHiDPIPrefInitialized = true;
   647     int prefSetting = Preferences::GetInt("gfx.hidpi.enabled", 1);
   648     if (prefSetting <= 0) {
   649       return false;
   650     }
   652     // prefSetting is at least 1, need to check attached screens...
   654     int scaleFactors = 0; // used as a bitset to track the screen types found
   655     NSEnumerator *screenEnum = [[NSScreen screens] objectEnumerator];
   656     while (NSScreen *screen = [screenEnum nextObject]) {
   657       NSDictionary *desc = [screen deviceDescription];
   658       if ([desc objectForKey:NSDeviceIsScreen] == nil) {
   659         continue;
   660       }
   661       CGFloat scale =
   662         [screen respondsToSelector:@selector(backingScaleFactor)] ?
   663           [screen backingScaleFactor] : 1.0;
   664       // Currently, we only care about differentiating "1.0" and "2.0",
   665       // so we set one of the two low bits to record which.
   666       if (scale > 1.0) {
   667         scaleFactors |= 2;
   668       } else {
   669         scaleFactors |= 1;
   670       }
   671     }
   673     // Now scaleFactors will be:
   674     //   0 if no screens (supporting backingScaleFactor) found
   675     //   1 if only lo-DPI screens
   676     //   2 if only hi-DPI screens
   677     //   3 if both lo- and hi-DPI screens
   678     // We'll enable HiDPI support if there's only a single screen type,
   679     // OR if the pref setting is explicitly greater than 1.
   680     sHiDPIEnabled = (scaleFactors <= 2) || (prefSetting > 1);
   681   }
   683   return sHiDPIEnabled;
   684 }
   686 void
   687 nsCocoaUtils::GetCommandsFromKeyEvent(NSEvent* aEvent,
   688                                       nsTArray<KeyBindingsCommand>& aCommands)
   689 {
   690   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   692   MOZ_ASSERT(aEvent);
   694   static NativeKeyBindingsRecorder* sNativeKeyBindingsRecorder;
   695   if (!sNativeKeyBindingsRecorder) {
   696     sNativeKeyBindingsRecorder = [NativeKeyBindingsRecorder new];
   697   }
   699   [sNativeKeyBindingsRecorder startRecording:aCommands];
   701   // This will trigger 0 - N calls to doCommandBySelector: and insertText:
   702   [sNativeKeyBindingsRecorder
   703     interpretKeyEvents:[NSArray arrayWithObject:aEvent]];
   705   NS_OBJC_END_TRY_ABORT_BLOCK;
   706 }
   708 @implementation NativeKeyBindingsRecorder
   710 - (void)startRecording:(nsTArray<KeyBindingsCommand>&)aCommands
   711 {
   712   mCommands = &aCommands;
   713   mCommands->Clear();
   714 }
   716 - (void)doCommandBySelector:(SEL)aSelector
   717 {
   718   KeyBindingsCommand command = {
   719     aSelector,
   720     nil
   721   };
   723   mCommands->AppendElement(command);
   724 }
   726 - (void)insertText:(id)aString
   727 {
   728   KeyBindingsCommand command = {
   729     @selector(insertText:),
   730     aString
   731   };
   733   mCommands->AppendElement(command);
   734 }
   736 @end // NativeKeyBindingsRecorder
   738 struct KeyConversionData
   739 {
   740   const char* str;
   741   size_t strLength;
   742   uint32_t geckoKeyCode;
   743   uint32_t charCode;
   744 };
   746 static const KeyConversionData gKeyConversions[] = {
   748 #define KEYCODE_ENTRY(aStr, aCode) \
   749   {#aStr, sizeof(#aStr) - 1, NS_##aStr, aCode}
   751 // Some keycodes may have different name in nsIDOMKeyEvent from its key name.
   752 #define KEYCODE_ENTRY2(aStr, aNSName, aCode) \
   753   {#aStr, sizeof(#aStr) - 1, NS_##aNSName, aCode}
   755   KEYCODE_ENTRY(VK_CANCEL, 0x001B),
   756   KEYCODE_ENTRY(VK_DELETE, NSDeleteFunctionKey),
   757   KEYCODE_ENTRY(VK_BACK, NSBackspaceCharacter),
   758   KEYCODE_ENTRY2(VK_BACK_SPACE, VK_BACK, NSBackspaceCharacter),
   759   KEYCODE_ENTRY(VK_TAB, NSTabCharacter),
   760   KEYCODE_ENTRY(VK_CLEAR, NSClearLineFunctionKey),
   761   KEYCODE_ENTRY(VK_RETURN, NSEnterCharacter),
   762   KEYCODE_ENTRY(VK_SHIFT, 0),
   763   KEYCODE_ENTRY(VK_CONTROL, 0),
   764   KEYCODE_ENTRY(VK_ALT, 0),
   765   KEYCODE_ENTRY(VK_PAUSE, NSPauseFunctionKey),
   766   KEYCODE_ENTRY(VK_CAPS_LOCK, 0),
   767   KEYCODE_ENTRY(VK_ESCAPE, 0),
   768   KEYCODE_ENTRY(VK_SPACE, ' '),
   769   KEYCODE_ENTRY(VK_PAGE_UP, NSPageUpFunctionKey),
   770   KEYCODE_ENTRY(VK_PAGE_DOWN, NSPageDownFunctionKey),
   771   KEYCODE_ENTRY(VK_END, NSEndFunctionKey),
   772   KEYCODE_ENTRY(VK_HOME, NSHomeFunctionKey),
   773   KEYCODE_ENTRY(VK_LEFT, NSLeftArrowFunctionKey),
   774   KEYCODE_ENTRY(VK_UP, NSUpArrowFunctionKey),
   775   KEYCODE_ENTRY(VK_RIGHT, NSRightArrowFunctionKey),
   776   KEYCODE_ENTRY(VK_DOWN, NSDownArrowFunctionKey),
   777   KEYCODE_ENTRY(VK_PRINTSCREEN, NSPrintScreenFunctionKey),
   778   KEYCODE_ENTRY(VK_INSERT, NSInsertFunctionKey),
   779   KEYCODE_ENTRY(VK_HELP, NSHelpFunctionKey),
   780   KEYCODE_ENTRY(VK_0, '0'),
   781   KEYCODE_ENTRY(VK_1, '1'),
   782   KEYCODE_ENTRY(VK_2, '2'),
   783   KEYCODE_ENTRY(VK_3, '3'),
   784   KEYCODE_ENTRY(VK_4, '4'),
   785   KEYCODE_ENTRY(VK_5, '5'),
   786   KEYCODE_ENTRY(VK_6, '6'),
   787   KEYCODE_ENTRY(VK_7, '7'),
   788   KEYCODE_ENTRY(VK_8, '8'),
   789   KEYCODE_ENTRY(VK_9, '9'),
   790   KEYCODE_ENTRY(VK_SEMICOLON, ':'),
   791   KEYCODE_ENTRY(VK_EQUALS, '='),
   792   KEYCODE_ENTRY(VK_A, 'A'),
   793   KEYCODE_ENTRY(VK_B, 'B'),
   794   KEYCODE_ENTRY(VK_C, 'C'),
   795   KEYCODE_ENTRY(VK_D, 'D'),
   796   KEYCODE_ENTRY(VK_E, 'E'),
   797   KEYCODE_ENTRY(VK_F, 'F'),
   798   KEYCODE_ENTRY(VK_G, 'G'),
   799   KEYCODE_ENTRY(VK_H, 'H'),
   800   KEYCODE_ENTRY(VK_I, 'I'),
   801   KEYCODE_ENTRY(VK_J, 'J'),
   802   KEYCODE_ENTRY(VK_K, 'K'),
   803   KEYCODE_ENTRY(VK_L, 'L'),
   804   KEYCODE_ENTRY(VK_M, 'M'),
   805   KEYCODE_ENTRY(VK_N, 'N'),
   806   KEYCODE_ENTRY(VK_O, 'O'),
   807   KEYCODE_ENTRY(VK_P, 'P'),
   808   KEYCODE_ENTRY(VK_Q, 'Q'),
   809   KEYCODE_ENTRY(VK_R, 'R'),
   810   KEYCODE_ENTRY(VK_S, 'S'),
   811   KEYCODE_ENTRY(VK_T, 'T'),
   812   KEYCODE_ENTRY(VK_U, 'U'),
   813   KEYCODE_ENTRY(VK_V, 'V'),
   814   KEYCODE_ENTRY(VK_W, 'W'),
   815   KEYCODE_ENTRY(VK_X, 'X'),
   816   KEYCODE_ENTRY(VK_Y, 'Y'),
   817   KEYCODE_ENTRY(VK_Z, 'Z'),
   818   KEYCODE_ENTRY(VK_CONTEXT_MENU, NSMenuFunctionKey),
   819   KEYCODE_ENTRY(VK_NUMPAD0, '0'),
   820   KEYCODE_ENTRY(VK_NUMPAD1, '1'),
   821   KEYCODE_ENTRY(VK_NUMPAD2, '2'),
   822   KEYCODE_ENTRY(VK_NUMPAD3, '3'),
   823   KEYCODE_ENTRY(VK_NUMPAD4, '4'),
   824   KEYCODE_ENTRY(VK_NUMPAD5, '5'),
   825   KEYCODE_ENTRY(VK_NUMPAD6, '6'),
   826   KEYCODE_ENTRY(VK_NUMPAD7, '7'),
   827   KEYCODE_ENTRY(VK_NUMPAD8, '8'),
   828   KEYCODE_ENTRY(VK_NUMPAD9, '9'),
   829   KEYCODE_ENTRY(VK_MULTIPLY, '*'),
   830   KEYCODE_ENTRY(VK_ADD, '+'),
   831   KEYCODE_ENTRY(VK_SEPARATOR, 0),
   832   KEYCODE_ENTRY(VK_SUBTRACT, '-'),
   833   KEYCODE_ENTRY(VK_DECIMAL, '.'),
   834   KEYCODE_ENTRY(VK_DIVIDE, '/'),
   835   KEYCODE_ENTRY(VK_F1, NSF1FunctionKey),
   836   KEYCODE_ENTRY(VK_F2, NSF2FunctionKey),
   837   KEYCODE_ENTRY(VK_F3, NSF3FunctionKey),
   838   KEYCODE_ENTRY(VK_F4, NSF4FunctionKey),
   839   KEYCODE_ENTRY(VK_F5, NSF5FunctionKey),
   840   KEYCODE_ENTRY(VK_F6, NSF6FunctionKey),
   841   KEYCODE_ENTRY(VK_F7, NSF7FunctionKey),
   842   KEYCODE_ENTRY(VK_F8, NSF8FunctionKey),
   843   KEYCODE_ENTRY(VK_F9, NSF9FunctionKey),
   844   KEYCODE_ENTRY(VK_F10, NSF10FunctionKey),
   845   KEYCODE_ENTRY(VK_F11, NSF11FunctionKey),
   846   KEYCODE_ENTRY(VK_F12, NSF12FunctionKey),
   847   KEYCODE_ENTRY(VK_F13, NSF13FunctionKey),
   848   KEYCODE_ENTRY(VK_F14, NSF14FunctionKey),
   849   KEYCODE_ENTRY(VK_F15, NSF15FunctionKey),
   850   KEYCODE_ENTRY(VK_F16, NSF16FunctionKey),
   851   KEYCODE_ENTRY(VK_F17, NSF17FunctionKey),
   852   KEYCODE_ENTRY(VK_F18, NSF18FunctionKey),
   853   KEYCODE_ENTRY(VK_F19, NSF19FunctionKey),
   854   KEYCODE_ENTRY(VK_F20, NSF20FunctionKey),
   855   KEYCODE_ENTRY(VK_F21, NSF21FunctionKey),
   856   KEYCODE_ENTRY(VK_F22, NSF22FunctionKey),
   857   KEYCODE_ENTRY(VK_F23, NSF23FunctionKey),
   858   KEYCODE_ENTRY(VK_F24, NSF24FunctionKey),
   859   KEYCODE_ENTRY(VK_NUM_LOCK, NSClearLineFunctionKey),
   860   KEYCODE_ENTRY(VK_SCROLL_LOCK, NSScrollLockFunctionKey),
   861   KEYCODE_ENTRY(VK_COMMA, ','),
   862   KEYCODE_ENTRY(VK_PERIOD, '.'),
   863   KEYCODE_ENTRY(VK_SLASH, '/'),
   864   KEYCODE_ENTRY(VK_BACK_QUOTE, '`'),
   865   KEYCODE_ENTRY(VK_OPEN_BRACKET, '['),
   866   KEYCODE_ENTRY(VK_BACK_SLASH, '\\'),
   867   KEYCODE_ENTRY(VK_CLOSE_BRACKET, ']'),
   868   KEYCODE_ENTRY(VK_QUOTE, '\'')
   870 #undef KEYCODE_ENTRY
   872 };
   874 uint32_t
   875 nsCocoaUtils::ConvertGeckoNameToMacCharCode(const nsAString& aKeyCodeName)
   876 {
   877   if (aKeyCodeName.IsEmpty()) {
   878     return 0;
   879   }
   881   nsAutoCString keyCodeName;
   882   keyCodeName.AssignWithConversion(aKeyCodeName);
   883   // We want case-insensitive comparison with data stored as uppercase.
   884   ToUpperCase(keyCodeName);
   886   uint32_t keyCodeNameLength = keyCodeName.Length();
   887   const char* keyCodeNameStr = keyCodeName.get();
   888   for (uint16_t i = 0; i < ArrayLength(gKeyConversions); ++i) {
   889     if (keyCodeNameLength == gKeyConversions[i].strLength &&
   890         nsCRT::strcmp(gKeyConversions[i].str, keyCodeNameStr) == 0) {
   891       return gKeyConversions[i].charCode;
   892     }
   893   }
   895   return 0;
   896 }
   898 uint32_t
   899 nsCocoaUtils::ConvertGeckoKeyCodeToMacCharCode(uint32_t aKeyCode)
   900 {
   901   if (!aKeyCode) {
   902     return 0;
   903   }
   905   for (uint16_t i = 0; i < ArrayLength(gKeyConversions); ++i) {
   906     if (gKeyConversions[i].geckoKeyCode == aKeyCode) {
   907       return gKeyConversions[i].charCode;
   908     }
   909   }
   911   return 0;
   912 }

mercurial