This patch replaces four functions of the name AssignWithConversion which
are essentially wrappers around CopyASCIItoUTF16 and LossyCopyUTF16toASCII
with direct calls to the latter two functions. The replaced functions are:
void nsCString::AssignWithConversion( const nsAString& aData )
void nsString::AssignWithConversion( const nsACString& aData )
void nsTString_CharT::AssignWithConversion(
const incompatible_char_type* aData,
int32_t aLength = -1);
The last of the three exists inside the double-included nsTString* world and
so describes two functions, giving four in total.
This has two advantages:
* it removes code
* at the call points, it makes clear (from the replacement name) which
conversion is being carried out. The generic name "AssignWithConversion"
doesn't make that obvious -- one had to infer it from the types.
The patch also removes two commented out lines from
editor/composer/nsComposerCommands.cpp, that appear to be related. They are
at top level, where they would never have compiled. They look like
leftovers from some previous change.
1043 lines
33 KiB
Plaintext
1043 lines
33 KiB
Plaintext
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* 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 <cmath>
|
|
|
|
#include "gfx2DGlue.h"
|
|
#include "gfxPlatform.h"
|
|
#include "gfxUtils.h"
|
|
#include "ImageRegion.h"
|
|
#include "nsCocoaUtils.h"
|
|
#include "nsChildView.h"
|
|
#include "nsMenuBarX.h"
|
|
#include "nsCocoaWindow.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsIInterfaceRequestorUtils.h"
|
|
#include "nsIAppShellService.h"
|
|
#include "nsIXULWindow.h"
|
|
#include "nsIBaseWindow.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsMenuUtilsX.h"
|
|
#include "nsToolkit.h"
|
|
#include "nsCRT.h"
|
|
#include "SVGImageContext.h"
|
|
#include "mozilla/gfx/2D.h"
|
|
#include "mozilla/MiscEvents.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/TextEvents.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::widget;
|
|
|
|
using mozilla::gfx::BackendType;
|
|
using mozilla::gfx::DataSourceSurface;
|
|
using mozilla::gfx::DrawTarget;
|
|
using mozilla::gfx::Factory;
|
|
using mozilla::gfx::SamplingFilter;
|
|
using mozilla::gfx::IntPoint;
|
|
using mozilla::gfx::IntRect;
|
|
using mozilla::gfx::IntSize;
|
|
using mozilla::gfx::SurfaceFormat;
|
|
using mozilla::gfx::SourceSurface;
|
|
using mozilla::image::ImageRegion;
|
|
using std::ceil;
|
|
|
|
static float
|
|
MenuBarScreenHeight()
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
NSArray* allScreens = [NSScreen screens];
|
|
if ([allScreens count]) {
|
|
return [[allScreens objectAtIndex:0] frame].size.height;
|
|
}
|
|
|
|
return 0.0;
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0.0);
|
|
}
|
|
|
|
float
|
|
nsCocoaUtils::FlippedScreenY(float y)
|
|
{
|
|
return MenuBarScreenHeight() - y;
|
|
}
|
|
|
|
NSRect nsCocoaUtils::GeckoRectToCocoaRect(const DesktopIntRect &geckoRect)
|
|
{
|
|
// We only need to change the Y coordinate by starting with the primary screen
|
|
// height and subtracting the gecko Y coordinate of the bottom of the rect.
|
|
return NSMakeRect(geckoRect.x,
|
|
MenuBarScreenHeight() - geckoRect.YMost(),
|
|
geckoRect.width,
|
|
geckoRect.height);
|
|
}
|
|
|
|
NSRect
|
|
nsCocoaUtils::GeckoRectToCocoaRectDevPix(const LayoutDeviceIntRect &aGeckoRect,
|
|
CGFloat aBackingScale)
|
|
{
|
|
return NSMakeRect(aGeckoRect.x / aBackingScale,
|
|
MenuBarScreenHeight() - aGeckoRect.YMost() / aBackingScale,
|
|
aGeckoRect.width / aBackingScale,
|
|
aGeckoRect.height / aBackingScale);
|
|
}
|
|
|
|
DesktopIntRect nsCocoaUtils::CocoaRectToGeckoRect(const NSRect &cocoaRect)
|
|
{
|
|
// We only need to change the Y coordinate by starting with the primary screen
|
|
// height and subtracting both the cocoa y origin and the height of the
|
|
// cocoa rect.
|
|
DesktopIntRect rect;
|
|
rect.x = NSToIntRound(cocoaRect.origin.x);
|
|
rect.y = NSToIntRound(FlippedScreenY(cocoaRect.origin.y + cocoaRect.size.height));
|
|
rect.width = NSToIntRound(cocoaRect.origin.x + cocoaRect.size.width) - rect.x;
|
|
rect.height = NSToIntRound(FlippedScreenY(cocoaRect.origin.y)) - rect.y;
|
|
return rect;
|
|
}
|
|
|
|
LayoutDeviceIntRect nsCocoaUtils::CocoaRectToGeckoRectDevPix(
|
|
const NSRect& aCocoaRect, CGFloat aBackingScale)
|
|
{
|
|
LayoutDeviceIntRect rect;
|
|
rect.x = NSToIntRound(aCocoaRect.origin.x * aBackingScale);
|
|
rect.y = NSToIntRound(FlippedScreenY(aCocoaRect.origin.y + aCocoaRect.size.height) * aBackingScale);
|
|
rect.width = NSToIntRound((aCocoaRect.origin.x + aCocoaRect.size.width) * aBackingScale) - rect.x;
|
|
rect.height = NSToIntRound(FlippedScreenY(aCocoaRect.origin.y) * aBackingScale) - rect.y;
|
|
return rect;
|
|
}
|
|
|
|
NSPoint nsCocoaUtils::ScreenLocationForEvent(NSEvent* anEvent)
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
// Don't trust mouse locations of mouse move events, see bug 443178.
|
|
if (!anEvent || [anEvent type] == NSMouseMoved)
|
|
return [NSEvent mouseLocation];
|
|
|
|
// Pin momentum scroll events to the location of the last user-controlled
|
|
// scroll event.
|
|
if (IsMomentumScrollEvent(anEvent))
|
|
return ChildViewMouseTracker::sLastScrollEventScreenLocation;
|
|
|
|
return nsCocoaUtils::ConvertPointToScreen([anEvent window], [anEvent locationInWindow]);
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakePoint(0.0, 0.0));
|
|
}
|
|
|
|
BOOL nsCocoaUtils::IsEventOverWindow(NSEvent* anEvent, NSWindow* aWindow)
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
return NSPointInRect(ScreenLocationForEvent(anEvent), [aWindow frame]);
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
|
|
}
|
|
|
|
NSPoint nsCocoaUtils::EventLocationForWindow(NSEvent* anEvent, NSWindow* aWindow)
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
|
|
|
return nsCocoaUtils::ConvertPointFromScreen(aWindow, ScreenLocationForEvent(anEvent));
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakePoint(0.0, 0.0));
|
|
}
|
|
|
|
@interface NSEvent (ScrollPhase)
|
|
// 10.5 and 10.6
|
|
- (long long)_scrollPhase;
|
|
// 10.7 and above
|
|
- (NSEventPhase)phase;
|
|
- (NSEventPhase)momentumPhase;
|
|
@end
|
|
|
|
NSEventPhase nsCocoaUtils::EventPhase(NSEvent* aEvent)
|
|
{
|
|
if ([aEvent respondsToSelector:@selector(phase)]) {
|
|
return [aEvent phase];
|
|
}
|
|
return NSEventPhaseNone;
|
|
}
|
|
|
|
NSEventPhase nsCocoaUtils::EventMomentumPhase(NSEvent* aEvent)
|
|
{
|
|
if ([aEvent respondsToSelector:@selector(momentumPhase)]) {
|
|
return [aEvent momentumPhase];
|
|
}
|
|
if ([aEvent respondsToSelector:@selector(_scrollPhase)]) {
|
|
switch ([aEvent _scrollPhase]) {
|
|
case 1: return NSEventPhaseBegan;
|
|
case 2: return NSEventPhaseChanged;
|
|
case 3: return NSEventPhaseEnded;
|
|
default: return NSEventPhaseNone;
|
|
}
|
|
}
|
|
return NSEventPhaseNone;
|
|
}
|
|
|
|
BOOL nsCocoaUtils::IsMomentumScrollEvent(NSEvent* aEvent)
|
|
{
|
|
return [aEvent type] == NSScrollWheel &&
|
|
EventMomentumPhase(aEvent) != NSEventPhaseNone;
|
|
}
|
|
|
|
@interface NSEvent (HasPreciseScrollingDeltas)
|
|
// 10.7 and above
|
|
- (BOOL)hasPreciseScrollingDeltas;
|
|
// For 10.6 and below, see the comment in nsChildView.h about _eventRef
|
|
- (EventRef)_eventRef;
|
|
@end
|
|
|
|
BOOL nsCocoaUtils::HasPreciseScrollingDeltas(NSEvent* aEvent)
|
|
{
|
|
if ([aEvent respondsToSelector:@selector(hasPreciseScrollingDeltas)]) {
|
|
return [aEvent hasPreciseScrollingDeltas];
|
|
}
|
|
|
|
// For events that don't contain pixel scrolling information, the event
|
|
// kind of their underlaying carbon event is kEventMouseWheelMoved instead
|
|
// of kEventMouseScroll.
|
|
EventRef carbonEvent = [aEvent _eventRef];
|
|
return carbonEvent && ::GetEventKind(carbonEvent) == kEventMouseScroll;
|
|
}
|
|
|
|
@interface NSEvent (ScrollingDeltas)
|
|
// 10.6 and below
|
|
- (CGFloat)deviceDeltaX;
|
|
- (CGFloat)deviceDeltaY;
|
|
// 10.7 and above
|
|
- (CGFloat)scrollingDeltaX;
|
|
- (CGFloat)scrollingDeltaY;
|
|
@end
|
|
|
|
void nsCocoaUtils::GetScrollingDeltas(NSEvent* aEvent, CGFloat* aOutDeltaX, CGFloat* aOutDeltaY)
|
|
{
|
|
if ([aEvent respondsToSelector:@selector(scrollingDeltaX)]) {
|
|
*aOutDeltaX = [aEvent scrollingDeltaX];
|
|
*aOutDeltaY = [aEvent scrollingDeltaY];
|
|
return;
|
|
}
|
|
if ([aEvent respondsToSelector:@selector(deviceDeltaX)] &&
|
|
HasPreciseScrollingDeltas(aEvent)) {
|
|
// Calling deviceDeltaX/Y on those events that do not contain pixel
|
|
// scrolling information triggers a Cocoa assertion and an
|
|
// Objective-C NSInternalInconsistencyException.
|
|
*aOutDeltaX = [aEvent deviceDeltaX];
|
|
*aOutDeltaY = [aEvent deviceDeltaY];
|
|
return;
|
|
}
|
|
|
|
// This is only hit pre-10.7 when we are called on a scroll event that does
|
|
// not contain pixel scrolling information.
|
|
CGFloat lineDeltaPixels = 12;
|
|
*aOutDeltaX = [aEvent deltaX] * lineDeltaPixels;
|
|
*aOutDeltaY = [aEvent deltaY] * lineDeltaPixels;
|
|
}
|
|
|
|
BOOL nsCocoaUtils::EventHasPhaseInformation(NSEvent* aEvent)
|
|
{
|
|
if (![aEvent respondsToSelector:@selector(phase)]) {
|
|
return NO;
|
|
}
|
|
return EventPhase(aEvent) != NSEventPhaseNone ||
|
|
EventMomentumPhase(aEvent) != NSEventPhaseNone;
|
|
}
|
|
|
|
void nsCocoaUtils::HideOSChromeOnScreen(bool aShouldHide)
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
// Keep track of how many hiding requests have been made, so that they can
|
|
// be nested.
|
|
static int sHiddenCount = 0;
|
|
|
|
sHiddenCount += aShouldHide ? 1 : -1;
|
|
NS_ASSERTION(sHiddenCount >= 0, "Unbalanced HideMenuAndDockForWindow calls");
|
|
|
|
NSApplicationPresentationOptions options =
|
|
sHiddenCount <= 0 ? NSApplicationPresentationDefault :
|
|
NSApplicationPresentationHideDock | NSApplicationPresentationHideMenuBar;
|
|
[NSApp setPresentationOptions:options];
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
}
|
|
|
|
#define NS_APPSHELLSERVICE_CONTRACTID "@mozilla.org/appshell/appShellService;1"
|
|
nsIWidget* nsCocoaUtils::GetHiddenWindowWidget()
|
|
{
|
|
nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
|
|
if (!appShell) {
|
|
NS_WARNING("Couldn't get AppShellService in order to get hidden window ref");
|
|
return nullptr;
|
|
}
|
|
|
|
nsCOMPtr<nsIXULWindow> hiddenWindow;
|
|
appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
|
|
if (!hiddenWindow) {
|
|
// Don't warn, this happens during shutdown, bug 358607.
|
|
return nullptr;
|
|
}
|
|
|
|
nsCOMPtr<nsIBaseWindow> baseHiddenWindow;
|
|
baseHiddenWindow = do_GetInterface(hiddenWindow);
|
|
if (!baseHiddenWindow) {
|
|
NS_WARNING("Couldn't get nsIBaseWindow from hidden window (nsIXULWindow)");
|
|
return nullptr;
|
|
}
|
|
|
|
nsCOMPtr<nsIWidget> hiddenWindowWidget;
|
|
if (NS_FAILED(baseHiddenWindow->GetMainWidget(getter_AddRefs(hiddenWindowWidget)))) {
|
|
NS_WARNING("Couldn't get nsIWidget from hidden window (nsIBaseWindow)");
|
|
return nullptr;
|
|
}
|
|
|
|
return hiddenWindowWidget;
|
|
}
|
|
|
|
void nsCocoaUtils::PrepareForNativeAppModalDialog()
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
// Don't do anything if this is embedding. We'll assume that if there is no hidden
|
|
// window we shouldn't do anything, and that should cover the embedding case.
|
|
nsMenuBarX* hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
|
|
if (!hiddenWindowMenuBar)
|
|
return;
|
|
|
|
// First put up the hidden window menu bar so that app menu event handling is correct.
|
|
hiddenWindowMenuBar->Paint();
|
|
|
|
NSMenu* mainMenu = [NSApp mainMenu];
|
|
NS_ASSERTION([mainMenu numberOfItems] > 0, "Main menu does not have any items, something is terribly wrong!");
|
|
|
|
// Create new menu bar for use with modal dialog
|
|
NSMenu* newMenuBar = [[NSMenu alloc] initWithTitle:@""];
|
|
|
|
// Swap in our app menu. Note that the event target is whatever window is up when
|
|
// the app modal dialog goes up.
|
|
NSMenuItem* firstMenuItem = [[mainMenu itemAtIndex:0] retain];
|
|
[mainMenu removeItemAtIndex:0];
|
|
[newMenuBar insertItem:firstMenuItem atIndex:0];
|
|
[firstMenuItem release];
|
|
|
|
// Add standard edit menu
|
|
[newMenuBar addItem:nsMenuUtilsX::GetStandardEditMenuItem()];
|
|
|
|
// Show the new menu bar
|
|
[NSApp setMainMenu:newMenuBar];
|
|
[newMenuBar release];
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
}
|
|
|
|
void nsCocoaUtils::CleanUpAfterNativeAppModalDialog()
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
// Don't do anything if this is embedding. We'll assume that if there is no hidden
|
|
// window we shouldn't do anything, and that should cover the embedding case.
|
|
nsMenuBarX* hiddenWindowMenuBar = nsMenuUtilsX::GetHiddenWindowMenuBar();
|
|
if (!hiddenWindowMenuBar)
|
|
return;
|
|
|
|
NSWindow* mainWindow = [NSApp mainWindow];
|
|
if (!mainWindow)
|
|
hiddenWindowMenuBar->Paint();
|
|
else
|
|
[WindowDelegate paintMenubarForWindow:mainWindow];
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
}
|
|
|
|
void data_ss_release_callback(void *aDataSourceSurface,
|
|
const void *data,
|
|
size_t size)
|
|
{
|
|
if (aDataSourceSurface) {
|
|
static_cast<DataSourceSurface*>(aDataSourceSurface)->Unmap();
|
|
static_cast<DataSourceSurface*>(aDataSourceSurface)->Release();
|
|
}
|
|
}
|
|
|
|
nsresult nsCocoaUtils::CreateCGImageFromSurface(SourceSurface* aSurface,
|
|
CGImageRef* aResult)
|
|
{
|
|
RefPtr<DataSourceSurface> dataSurface;
|
|
|
|
if (aSurface->GetFormat() == SurfaceFormat::B8G8R8A8) {
|
|
dataSurface = aSurface->GetDataSurface();
|
|
} else {
|
|
// CGImageCreate only supports 16- and 32-bit bit-depth
|
|
// Convert format to SurfaceFormat::B8G8R8A8
|
|
dataSurface = gfxUtils::
|
|
CopySurfaceToDataSourceSurfaceWithFormat(aSurface,
|
|
SurfaceFormat::B8G8R8A8);
|
|
}
|
|
|
|
NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
|
|
|
|
int32_t width = dataSurface->GetSize().width;
|
|
int32_t height = dataSurface->GetSize().height;
|
|
if (height < 1 || width < 1) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
DataSourceSurface::MappedSurface map;
|
|
if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
// The Unmap() call happens in data_ss_release_callback
|
|
|
|
// Create a CGImageRef with the bits from the image, taking into account
|
|
// the alpha ordering and endianness of the machine so we don't have to
|
|
// touch the bits ourselves.
|
|
CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(dataSurface.forget().take(),
|
|
map.mData,
|
|
map.mStride * height,
|
|
data_ss_release_callback);
|
|
CGColorSpaceRef colorSpace = ::CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
|
|
*aResult = ::CGImageCreate(width,
|
|
height,
|
|
8,
|
|
32,
|
|
map.mStride,
|
|
colorSpace,
|
|
kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst,
|
|
dataProvider,
|
|
NULL,
|
|
0,
|
|
kCGRenderingIntentDefault);
|
|
::CGColorSpaceRelease(colorSpace);
|
|
::CGDataProviderRelease(dataProvider);
|
|
return *aResult ? NS_OK : NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsresult nsCocoaUtils::CreateNSImageFromCGImage(CGImageRef aInputImage, NSImage **aResult)
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
// Be very careful when creating the NSImage that the backing NSImageRep is
|
|
// exactly 1:1 with the input image. On a retina display, both [NSImage
|
|
// lockFocus] and [NSImage initWithCGImage:size:] will create an image with a
|
|
// 2x backing NSImageRep. This prevents NSCursor from recognizing a retina
|
|
// cursor, which only occurs if pixelsWide and pixelsHigh are exactly 2x the
|
|
// size of the NSImage.
|
|
//
|
|
// For example, if a 32x32 SVG cursor is rendered on a retina display, then
|
|
// aInputImage will be 64x64. The resulting NSImage will be scaled back down
|
|
// to 32x32 so it stays the correct size on the screen by changing its size
|
|
// (resizing a NSImage only scales the image and doesn't resample the data).
|
|
// If aInputImage is converted using [NSImage initWithCGImage:size:] then the
|
|
// bitmap will be 128x128 and NSCursor won't recognize a retina cursor, since
|
|
// it will expect a 64x64 bitmap.
|
|
|
|
int32_t width = ::CGImageGetWidth(aInputImage);
|
|
int32_t height = ::CGImageGetHeight(aInputImage);
|
|
NSRect imageRect = ::NSMakeRect(0.0, 0.0, width, height);
|
|
|
|
NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc]
|
|
initWithBitmapDataPlanes:NULL
|
|
pixelsWide:width
|
|
pixelsHigh:height
|
|
bitsPerSample:8
|
|
samplesPerPixel:4
|
|
hasAlpha:YES
|
|
isPlanar:NO
|
|
colorSpaceName:NSDeviceRGBColorSpace
|
|
bitmapFormat:NSAlphaFirstBitmapFormat
|
|
bytesPerRow:0
|
|
bitsPerPixel:0];
|
|
|
|
NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep];
|
|
[NSGraphicsContext saveGraphicsState];
|
|
[NSGraphicsContext setCurrentContext:context];
|
|
|
|
// Get the Quartz context and draw.
|
|
CGContextRef imageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
|
|
::CGContextDrawImage(imageContext, *(CGRect*)&imageRect, aInputImage);
|
|
|
|
[NSGraphicsContext restoreGraphicsState];
|
|
|
|
*aResult = [[NSImage alloc] initWithSize:NSMakeSize(width, height)];
|
|
[*aResult addRepresentation:offscreenRep];
|
|
[offscreenRep release];
|
|
return NS_OK;
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
|
}
|
|
|
|
nsresult nsCocoaUtils::CreateNSImageFromImageContainer(imgIContainer *aImage, uint32_t aWhichFrame, NSImage **aResult, CGFloat scaleFactor)
|
|
{
|
|
RefPtr<SourceSurface> surface;
|
|
int32_t width = 0, height = 0;
|
|
aImage->GetWidth(&width);
|
|
aImage->GetHeight(&height);
|
|
|
|
// Render a vector image at the correct resolution on a retina display
|
|
if (aImage->GetType() == imgIContainer::TYPE_VECTOR && scaleFactor != 1.0f) {
|
|
IntSize scaledSize = IntSize::Ceil(width * scaleFactor, height * scaleFactor);
|
|
|
|
RefPtr<DrawTarget> drawTarget = gfxPlatform::GetPlatform()->
|
|
CreateOffscreenContentDrawTarget(scaledSize, SurfaceFormat::B8G8R8A8);
|
|
if (!drawTarget || !drawTarget->IsValid()) {
|
|
NS_ERROR("Failed to create valid DrawTarget");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
RefPtr<gfxContext> context = gfxContext::CreateOrNull(drawTarget);
|
|
MOZ_ASSERT(context);
|
|
|
|
mozilla::image::DrawResult res =
|
|
aImage->Draw(context, scaledSize, ImageRegion::Create(scaledSize),
|
|
aWhichFrame, SamplingFilter::POINT,
|
|
/* no SVGImageContext */ Nothing(),
|
|
imgIContainer::FLAG_SYNC_DECODE, 1.0);
|
|
|
|
if (res != mozilla::image::DrawResult::SUCCESS) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
surface = drawTarget->Snapshot();
|
|
} else {
|
|
surface = aImage->GetFrame(aWhichFrame, imgIContainer::FLAG_SYNC_DECODE);
|
|
}
|
|
|
|
NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
|
|
|
|
CGImageRef imageRef = NULL;
|
|
nsresult rv = nsCocoaUtils::CreateCGImageFromSurface(surface, &imageRef);
|
|
if (NS_FAILED(rv) || !imageRef) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
rv = nsCocoaUtils::CreateNSImageFromCGImage(imageRef, aResult);
|
|
if (NS_FAILED(rv) || !aResult) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
::CGImageRelease(imageRef);
|
|
|
|
// Ensure the image will be rendered the correct size on a retina display
|
|
NSSize size = NSMakeSize(width, height);
|
|
[*aResult setSize:size];
|
|
[[[*aResult representations] objectAtIndex:0] setSize:size];
|
|
return NS_OK;
|
|
}
|
|
|
|
// static
|
|
void
|
|
nsCocoaUtils::GetStringForNSString(const NSString *aSrc, nsAString& aDist)
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
if (!aSrc) {
|
|
aDist.Truncate();
|
|
return;
|
|
}
|
|
|
|
aDist.SetLength([aSrc length]);
|
|
[aSrc getCharacters: reinterpret_cast<unichar*>(aDist.BeginWriting())];
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
}
|
|
|
|
// static
|
|
NSString*
|
|
nsCocoaUtils::ToNSString(const nsAString& aString)
|
|
{
|
|
if (aString.IsEmpty()) {
|
|
return [NSString string];
|
|
}
|
|
return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(aString.BeginReading())
|
|
length:aString.Length()];
|
|
}
|
|
|
|
// static
|
|
void
|
|
nsCocoaUtils::GeckoRectToNSRect(const nsIntRect& aGeckoRect,
|
|
NSRect& aOutCocoaRect)
|
|
{
|
|
aOutCocoaRect.origin.x = aGeckoRect.x;
|
|
aOutCocoaRect.origin.y = aGeckoRect.y;
|
|
aOutCocoaRect.size.width = aGeckoRect.width;
|
|
aOutCocoaRect.size.height = aGeckoRect.height;
|
|
}
|
|
|
|
// static
|
|
void
|
|
nsCocoaUtils::NSRectToGeckoRect(const NSRect& aCocoaRect,
|
|
nsIntRect& aOutGeckoRect)
|
|
{
|
|
aOutGeckoRect.x = NSToIntRound(aCocoaRect.origin.x);
|
|
aOutGeckoRect.y = NSToIntRound(aCocoaRect.origin.y);
|
|
aOutGeckoRect.width = NSToIntRound(aCocoaRect.origin.x + aCocoaRect.size.width) - aOutGeckoRect.x;
|
|
aOutGeckoRect.height = NSToIntRound(aCocoaRect.origin.y + aCocoaRect.size.height) - aOutGeckoRect.y;
|
|
}
|
|
|
|
// static
|
|
NSEvent*
|
|
nsCocoaUtils::MakeNewCocoaEventWithType(NSEventType aEventType, NSEvent *aEvent)
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
|
|
|
NSEvent* newEvent =
|
|
[NSEvent keyEventWithType:aEventType
|
|
location:[aEvent locationInWindow]
|
|
modifierFlags:[aEvent modifierFlags]
|
|
timestamp:[aEvent timestamp]
|
|
windowNumber:[aEvent windowNumber]
|
|
context:[aEvent context]
|
|
characters:[aEvent characters]
|
|
charactersIgnoringModifiers:[aEvent charactersIgnoringModifiers]
|
|
isARepeat:[aEvent isARepeat]
|
|
keyCode:[aEvent keyCode]];
|
|
return newEvent;
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
|
}
|
|
|
|
// static
|
|
void
|
|
nsCocoaUtils::InitNPCocoaEvent(NPCocoaEvent* aNPCocoaEvent)
|
|
{
|
|
memset(aNPCocoaEvent, 0, sizeof(NPCocoaEvent));
|
|
}
|
|
|
|
// static
|
|
void
|
|
nsCocoaUtils::InitInputEvent(WidgetInputEvent& aInputEvent,
|
|
NSEvent* aNativeEvent)
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
aInputEvent.mModifiers = ModifiersForEvent(aNativeEvent);
|
|
aInputEvent.mTime = PR_IntervalNow();
|
|
aInputEvent.mTimeStamp = GetEventTimeStamp([aNativeEvent timestamp]);
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
}
|
|
|
|
// static
|
|
Modifiers
|
|
nsCocoaUtils::ModifiersForEvent(NSEvent* aNativeEvent)
|
|
{
|
|
NSUInteger modifiers =
|
|
aNativeEvent ? [aNativeEvent modifierFlags] : [NSEvent modifierFlags];
|
|
Modifiers result = 0;
|
|
if (modifiers & NSShiftKeyMask) {
|
|
result |= MODIFIER_SHIFT;
|
|
}
|
|
if (modifiers & NSControlKeyMask) {
|
|
result |= MODIFIER_CONTROL;
|
|
}
|
|
if (modifiers & NSAlternateKeyMask) {
|
|
result |= MODIFIER_ALT;
|
|
// Mac's option key is similar to other platforms' AltGr key.
|
|
// Let's set AltGr flag when option key is pressed for consistency with
|
|
// other platforms.
|
|
result |= MODIFIER_ALTGRAPH;
|
|
}
|
|
if (modifiers & NSCommandKeyMask) {
|
|
result |= MODIFIER_META;
|
|
}
|
|
|
|
if (modifiers & NSAlphaShiftKeyMask) {
|
|
result |= MODIFIER_CAPSLOCK;
|
|
}
|
|
// Mac doesn't have NumLock key. We can assume that NumLock is always locked
|
|
// if user is using a keyboard which has numpad. Otherwise, if user is using
|
|
// a keyboard which doesn't have numpad, e.g., MacBook's keyboard, we can
|
|
// assume that NumLock is always unlocked.
|
|
// Unfortunately, we cannot know whether current keyboard has numpad or not.
|
|
// We should notify locked state only when keys in numpad are pressed.
|
|
// By this, web applications may not be confused by unexpected numpad key's
|
|
// key event with unlocked state.
|
|
if (modifiers & NSNumericPadKeyMask) {
|
|
result |= MODIFIER_NUMLOCK;
|
|
}
|
|
|
|
// Be aware, NSFunctionKeyMask is included when arrow keys, home key or some
|
|
// other keys are pressed. We cannot check whether 'fn' key is pressed or
|
|
// not by the flag.
|
|
|
|
return result;
|
|
}
|
|
|
|
// static
|
|
UInt32
|
|
nsCocoaUtils::ConvertToCarbonModifier(NSUInteger aCocoaModifier)
|
|
{
|
|
UInt32 carbonModifier = 0;
|
|
if (aCocoaModifier & NSAlphaShiftKeyMask) {
|
|
carbonModifier |= alphaLock;
|
|
}
|
|
if (aCocoaModifier & NSControlKeyMask) {
|
|
carbonModifier |= controlKey;
|
|
}
|
|
if (aCocoaModifier & NSAlternateKeyMask) {
|
|
carbonModifier |= optionKey;
|
|
}
|
|
if (aCocoaModifier & NSShiftKeyMask) {
|
|
carbonModifier |= shiftKey;
|
|
}
|
|
if (aCocoaModifier & NSCommandKeyMask) {
|
|
carbonModifier |= cmdKey;
|
|
}
|
|
if (aCocoaModifier & NSNumericPadKeyMask) {
|
|
carbonModifier |= kEventKeyModifierNumLockMask;
|
|
}
|
|
if (aCocoaModifier & NSFunctionKeyMask) {
|
|
carbonModifier |= kEventKeyModifierFnMask;
|
|
}
|
|
return carbonModifier;
|
|
}
|
|
|
|
// While HiDPI support is not 100% complete and tested, we'll have a pref
|
|
// to allow it to be turned off in case of problems (or for testing purposes).
|
|
|
|
// gfx.hidpi.enabled is an integer with the meaning:
|
|
// <= 0 : HiDPI support is disabled
|
|
// 1 : HiDPI enabled provided all screens have the same backing resolution
|
|
// > 1 : HiDPI enabled even if there are a mixture of screen modes
|
|
|
|
// All the following code is to be removed once HiDPI work is more complete.
|
|
|
|
static bool sHiDPIEnabled = false;
|
|
static bool sHiDPIPrefInitialized = false;
|
|
|
|
// static
|
|
bool
|
|
nsCocoaUtils::HiDPIEnabled()
|
|
{
|
|
if (!sHiDPIPrefInitialized) {
|
|
sHiDPIPrefInitialized = true;
|
|
|
|
int prefSetting = Preferences::GetInt("gfx.hidpi.enabled", 1);
|
|
if (prefSetting <= 0) {
|
|
return false;
|
|
}
|
|
|
|
// prefSetting is at least 1, need to check attached screens...
|
|
|
|
int scaleFactors = 0; // used as a bitset to track the screen types found
|
|
NSEnumerator *screenEnum = [[NSScreen screens] objectEnumerator];
|
|
while (NSScreen *screen = [screenEnum nextObject]) {
|
|
NSDictionary *desc = [screen deviceDescription];
|
|
if ([desc objectForKey:NSDeviceIsScreen] == nil) {
|
|
continue;
|
|
}
|
|
CGFloat scale =
|
|
[screen respondsToSelector:@selector(backingScaleFactor)] ?
|
|
[screen backingScaleFactor] : 1.0;
|
|
// Currently, we only care about differentiating "1.0" and "2.0",
|
|
// so we set one of the two low bits to record which.
|
|
if (scale > 1.0) {
|
|
scaleFactors |= 2;
|
|
} else {
|
|
scaleFactors |= 1;
|
|
}
|
|
}
|
|
|
|
// Now scaleFactors will be:
|
|
// 0 if no screens (supporting backingScaleFactor) found
|
|
// 1 if only lo-DPI screens
|
|
// 2 if only hi-DPI screens
|
|
// 3 if both lo- and hi-DPI screens
|
|
// We'll enable HiDPI support if there's only a single screen type,
|
|
// OR if the pref setting is explicitly greater than 1.
|
|
sHiDPIEnabled = (scaleFactors <= 2) || (prefSetting > 1);
|
|
}
|
|
|
|
return sHiDPIEnabled;
|
|
}
|
|
|
|
void
|
|
nsCocoaUtils::GetCommandsFromKeyEvent(NSEvent* aEvent,
|
|
nsTArray<KeyBindingsCommand>& aCommands)
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
MOZ_ASSERT(aEvent);
|
|
|
|
static NativeKeyBindingsRecorder* sNativeKeyBindingsRecorder;
|
|
if (!sNativeKeyBindingsRecorder) {
|
|
sNativeKeyBindingsRecorder = [NativeKeyBindingsRecorder new];
|
|
}
|
|
|
|
[sNativeKeyBindingsRecorder startRecording:aCommands];
|
|
|
|
// This will trigger 0 - N calls to doCommandBySelector: and insertText:
|
|
[sNativeKeyBindingsRecorder
|
|
interpretKeyEvents:[NSArray arrayWithObject:aEvent]];
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK;
|
|
}
|
|
|
|
@implementation NativeKeyBindingsRecorder
|
|
|
|
- (void)startRecording:(nsTArray<KeyBindingsCommand>&)aCommands
|
|
{
|
|
mCommands = &aCommands;
|
|
mCommands->Clear();
|
|
}
|
|
|
|
- (void)doCommandBySelector:(SEL)aSelector
|
|
{
|
|
KeyBindingsCommand command = {
|
|
aSelector,
|
|
nil
|
|
};
|
|
|
|
mCommands->AppendElement(command);
|
|
}
|
|
|
|
- (void)insertText:(id)aString
|
|
{
|
|
KeyBindingsCommand command = {
|
|
@selector(insertText:),
|
|
aString
|
|
};
|
|
|
|
mCommands->AppendElement(command);
|
|
}
|
|
|
|
@end // NativeKeyBindingsRecorder
|
|
|
|
struct KeyConversionData
|
|
{
|
|
const char* str;
|
|
size_t strLength;
|
|
uint32_t geckoKeyCode;
|
|
uint32_t charCode;
|
|
};
|
|
|
|
static const KeyConversionData gKeyConversions[] = {
|
|
|
|
#define KEYCODE_ENTRY(aStr, aCode) \
|
|
{#aStr, sizeof(#aStr) - 1, NS_##aStr, aCode}
|
|
|
|
// Some keycodes may have different name in nsIDOMKeyEvent from its key name.
|
|
#define KEYCODE_ENTRY2(aStr, aNSName, aCode) \
|
|
{#aStr, sizeof(#aStr) - 1, NS_##aNSName, aCode}
|
|
|
|
KEYCODE_ENTRY(VK_CANCEL, 0x001B),
|
|
KEYCODE_ENTRY(VK_DELETE, NSDeleteFunctionKey),
|
|
KEYCODE_ENTRY(VK_BACK, NSBackspaceCharacter),
|
|
KEYCODE_ENTRY2(VK_BACK_SPACE, VK_BACK, NSBackspaceCharacter),
|
|
KEYCODE_ENTRY(VK_TAB, NSTabCharacter),
|
|
KEYCODE_ENTRY(VK_CLEAR, NSClearLineFunctionKey),
|
|
KEYCODE_ENTRY(VK_RETURN, NSEnterCharacter),
|
|
KEYCODE_ENTRY(VK_SHIFT, 0),
|
|
KEYCODE_ENTRY(VK_CONTROL, 0),
|
|
KEYCODE_ENTRY(VK_ALT, 0),
|
|
KEYCODE_ENTRY(VK_PAUSE, NSPauseFunctionKey),
|
|
KEYCODE_ENTRY(VK_CAPS_LOCK, 0),
|
|
KEYCODE_ENTRY(VK_ESCAPE, 0),
|
|
KEYCODE_ENTRY(VK_SPACE, ' '),
|
|
KEYCODE_ENTRY(VK_PAGE_UP, NSPageUpFunctionKey),
|
|
KEYCODE_ENTRY(VK_PAGE_DOWN, NSPageDownFunctionKey),
|
|
KEYCODE_ENTRY(VK_END, NSEndFunctionKey),
|
|
KEYCODE_ENTRY(VK_HOME, NSHomeFunctionKey),
|
|
KEYCODE_ENTRY(VK_LEFT, NSLeftArrowFunctionKey),
|
|
KEYCODE_ENTRY(VK_UP, NSUpArrowFunctionKey),
|
|
KEYCODE_ENTRY(VK_RIGHT, NSRightArrowFunctionKey),
|
|
KEYCODE_ENTRY(VK_DOWN, NSDownArrowFunctionKey),
|
|
KEYCODE_ENTRY(VK_PRINTSCREEN, NSPrintScreenFunctionKey),
|
|
KEYCODE_ENTRY(VK_INSERT, NSInsertFunctionKey),
|
|
KEYCODE_ENTRY(VK_HELP, NSHelpFunctionKey),
|
|
KEYCODE_ENTRY(VK_0, '0'),
|
|
KEYCODE_ENTRY(VK_1, '1'),
|
|
KEYCODE_ENTRY(VK_2, '2'),
|
|
KEYCODE_ENTRY(VK_3, '3'),
|
|
KEYCODE_ENTRY(VK_4, '4'),
|
|
KEYCODE_ENTRY(VK_5, '5'),
|
|
KEYCODE_ENTRY(VK_6, '6'),
|
|
KEYCODE_ENTRY(VK_7, '7'),
|
|
KEYCODE_ENTRY(VK_8, '8'),
|
|
KEYCODE_ENTRY(VK_9, '9'),
|
|
KEYCODE_ENTRY(VK_SEMICOLON, ':'),
|
|
KEYCODE_ENTRY(VK_EQUALS, '='),
|
|
KEYCODE_ENTRY(VK_A, 'A'),
|
|
KEYCODE_ENTRY(VK_B, 'B'),
|
|
KEYCODE_ENTRY(VK_C, 'C'),
|
|
KEYCODE_ENTRY(VK_D, 'D'),
|
|
KEYCODE_ENTRY(VK_E, 'E'),
|
|
KEYCODE_ENTRY(VK_F, 'F'),
|
|
KEYCODE_ENTRY(VK_G, 'G'),
|
|
KEYCODE_ENTRY(VK_H, 'H'),
|
|
KEYCODE_ENTRY(VK_I, 'I'),
|
|
KEYCODE_ENTRY(VK_J, 'J'),
|
|
KEYCODE_ENTRY(VK_K, 'K'),
|
|
KEYCODE_ENTRY(VK_L, 'L'),
|
|
KEYCODE_ENTRY(VK_M, 'M'),
|
|
KEYCODE_ENTRY(VK_N, 'N'),
|
|
KEYCODE_ENTRY(VK_O, 'O'),
|
|
KEYCODE_ENTRY(VK_P, 'P'),
|
|
KEYCODE_ENTRY(VK_Q, 'Q'),
|
|
KEYCODE_ENTRY(VK_R, 'R'),
|
|
KEYCODE_ENTRY(VK_S, 'S'),
|
|
KEYCODE_ENTRY(VK_T, 'T'),
|
|
KEYCODE_ENTRY(VK_U, 'U'),
|
|
KEYCODE_ENTRY(VK_V, 'V'),
|
|
KEYCODE_ENTRY(VK_W, 'W'),
|
|
KEYCODE_ENTRY(VK_X, 'X'),
|
|
KEYCODE_ENTRY(VK_Y, 'Y'),
|
|
KEYCODE_ENTRY(VK_Z, 'Z'),
|
|
KEYCODE_ENTRY(VK_CONTEXT_MENU, NSMenuFunctionKey),
|
|
KEYCODE_ENTRY(VK_NUMPAD0, '0'),
|
|
KEYCODE_ENTRY(VK_NUMPAD1, '1'),
|
|
KEYCODE_ENTRY(VK_NUMPAD2, '2'),
|
|
KEYCODE_ENTRY(VK_NUMPAD3, '3'),
|
|
KEYCODE_ENTRY(VK_NUMPAD4, '4'),
|
|
KEYCODE_ENTRY(VK_NUMPAD5, '5'),
|
|
KEYCODE_ENTRY(VK_NUMPAD6, '6'),
|
|
KEYCODE_ENTRY(VK_NUMPAD7, '7'),
|
|
KEYCODE_ENTRY(VK_NUMPAD8, '8'),
|
|
KEYCODE_ENTRY(VK_NUMPAD9, '9'),
|
|
KEYCODE_ENTRY(VK_MULTIPLY, '*'),
|
|
KEYCODE_ENTRY(VK_ADD, '+'),
|
|
KEYCODE_ENTRY(VK_SEPARATOR, 0),
|
|
KEYCODE_ENTRY(VK_SUBTRACT, '-'),
|
|
KEYCODE_ENTRY(VK_DECIMAL, '.'),
|
|
KEYCODE_ENTRY(VK_DIVIDE, '/'),
|
|
KEYCODE_ENTRY(VK_F1, NSF1FunctionKey),
|
|
KEYCODE_ENTRY(VK_F2, NSF2FunctionKey),
|
|
KEYCODE_ENTRY(VK_F3, NSF3FunctionKey),
|
|
KEYCODE_ENTRY(VK_F4, NSF4FunctionKey),
|
|
KEYCODE_ENTRY(VK_F5, NSF5FunctionKey),
|
|
KEYCODE_ENTRY(VK_F6, NSF6FunctionKey),
|
|
KEYCODE_ENTRY(VK_F7, NSF7FunctionKey),
|
|
KEYCODE_ENTRY(VK_F8, NSF8FunctionKey),
|
|
KEYCODE_ENTRY(VK_F9, NSF9FunctionKey),
|
|
KEYCODE_ENTRY(VK_F10, NSF10FunctionKey),
|
|
KEYCODE_ENTRY(VK_F11, NSF11FunctionKey),
|
|
KEYCODE_ENTRY(VK_F12, NSF12FunctionKey),
|
|
KEYCODE_ENTRY(VK_F13, NSF13FunctionKey),
|
|
KEYCODE_ENTRY(VK_F14, NSF14FunctionKey),
|
|
KEYCODE_ENTRY(VK_F15, NSF15FunctionKey),
|
|
KEYCODE_ENTRY(VK_F16, NSF16FunctionKey),
|
|
KEYCODE_ENTRY(VK_F17, NSF17FunctionKey),
|
|
KEYCODE_ENTRY(VK_F18, NSF18FunctionKey),
|
|
KEYCODE_ENTRY(VK_F19, NSF19FunctionKey),
|
|
KEYCODE_ENTRY(VK_F20, NSF20FunctionKey),
|
|
KEYCODE_ENTRY(VK_F21, NSF21FunctionKey),
|
|
KEYCODE_ENTRY(VK_F22, NSF22FunctionKey),
|
|
KEYCODE_ENTRY(VK_F23, NSF23FunctionKey),
|
|
KEYCODE_ENTRY(VK_F24, NSF24FunctionKey),
|
|
KEYCODE_ENTRY(VK_NUM_LOCK, NSClearLineFunctionKey),
|
|
KEYCODE_ENTRY(VK_SCROLL_LOCK, NSScrollLockFunctionKey),
|
|
KEYCODE_ENTRY(VK_COMMA, ','),
|
|
KEYCODE_ENTRY(VK_PERIOD, '.'),
|
|
KEYCODE_ENTRY(VK_SLASH, '/'),
|
|
KEYCODE_ENTRY(VK_BACK_QUOTE, '`'),
|
|
KEYCODE_ENTRY(VK_OPEN_BRACKET, '['),
|
|
KEYCODE_ENTRY(VK_BACK_SLASH, '\\'),
|
|
KEYCODE_ENTRY(VK_CLOSE_BRACKET, ']'),
|
|
KEYCODE_ENTRY(VK_QUOTE, '\'')
|
|
|
|
#undef KEYCODE_ENTRY
|
|
|
|
};
|
|
|
|
uint32_t
|
|
nsCocoaUtils::ConvertGeckoNameToMacCharCode(const nsAString& aKeyCodeName)
|
|
{
|
|
if (aKeyCodeName.IsEmpty()) {
|
|
return 0;
|
|
}
|
|
|
|
nsAutoCString keyCodeName;
|
|
LossyCopyUTF16toASCII(aKeyCodeName, keyCodeName);
|
|
// We want case-insensitive comparison with data stored as uppercase.
|
|
ToUpperCase(keyCodeName);
|
|
|
|
uint32_t keyCodeNameLength = keyCodeName.Length();
|
|
const char* keyCodeNameStr = keyCodeName.get();
|
|
for (uint16_t i = 0; i < ArrayLength(gKeyConversions); ++i) {
|
|
if (keyCodeNameLength == gKeyConversions[i].strLength &&
|
|
nsCRT::strcmp(gKeyConversions[i].str, keyCodeNameStr) == 0) {
|
|
return gKeyConversions[i].charCode;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint32_t
|
|
nsCocoaUtils::ConvertGeckoKeyCodeToMacCharCode(uint32_t aKeyCode)
|
|
{
|
|
if (!aKeyCode) {
|
|
return 0;
|
|
}
|
|
|
|
for (uint16_t i = 0; i < ArrayLength(gKeyConversions); ++i) {
|
|
if (gKeyConversions[i].geckoKeyCode == aKeyCode) {
|
|
return gKeyConversions[i].charCode;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
NSMutableAttributedString*
|
|
nsCocoaUtils::GetNSMutableAttributedString(
|
|
const nsAString& aText,
|
|
const nsTArray<mozilla::FontRange>& aFontRanges,
|
|
const bool aIsVertical,
|
|
const CGFloat aBackingScaleFactor)
|
|
{
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL
|
|
|
|
NSString* nsstr = nsCocoaUtils::ToNSString(aText);
|
|
NSMutableAttributedString* attrStr =
|
|
[[[NSMutableAttributedString alloc] initWithString:nsstr
|
|
attributes:nil] autorelease];
|
|
|
|
int32_t lastOffset = aText.Length();
|
|
for (auto i = aFontRanges.Length(); i > 0; --i) {
|
|
const FontRange& fontRange = aFontRanges[i - 1];
|
|
NSString* fontName = nsCocoaUtils::ToNSString(fontRange.mFontName);
|
|
CGFloat fontSize = fontRange.mFontSize / aBackingScaleFactor;
|
|
NSFont* font = [NSFont fontWithName:fontName size:fontSize];
|
|
if (!font) {
|
|
font = [NSFont systemFontOfSize:fontSize];
|
|
}
|
|
|
|
NSDictionary* attrs = @{ NSFontAttributeName: font };
|
|
NSRange range = NSMakeRange(fontRange.mStartOffset,
|
|
lastOffset - fontRange.mStartOffset);
|
|
[attrStr setAttributes:attrs range:range];
|
|
lastOffset = fontRange.mStartOffset;
|
|
}
|
|
|
|
if (aIsVertical) {
|
|
[attrStr addAttribute:NSVerticalGlyphFormAttributeName
|
|
value:[NSNumber numberWithInt: 1]
|
|
range:NSMakeRange(0, [attrStr length])];
|
|
}
|
|
|
|
return attrStr;
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NIL
|
|
}
|
|
|
|
TimeStamp
|
|
nsCocoaUtils::GetEventTimeStamp(NSTimeInterval aEventTime)
|
|
{
|
|
if (!aEventTime) {
|
|
// If the event is generated by a 3rd party application, its timestamp
|
|
// may be 0. In this case, just return current timestamp.
|
|
// XXX Should we cache last event time?
|
|
return TimeStamp::Now();
|
|
}
|
|
// The internal value of the macOS implementation of TimeStamp is based on
|
|
// mach_absolute_time(), which measures "ticks" since boot.
|
|
// Event timestamps are NSTimeIntervals (seconds) since boot. So the two time
|
|
// representations already have the same base; we only need to convert
|
|
// seconds into ticks.
|
|
int64_t tick =
|
|
BaseTimeDurationPlatformUtils::TicksFromMilliseconds(aEventTime * 1000.0);
|
|
return TimeStamp::FromSystemTime(tick);
|
|
}
|